From: Alvin Šipraga <alsi@xxxxxxxxxxxxxxx> The TS5USBA224 USB High Speed/Audio switch mux is typically used together with a Type-C port controller such as the TUSB320LAI. Below is a simplified typical application on an embedded platform: ┌─────────────────────┐ ┌───────────────────┐ │ │ I2C │ │ │ ├──────────────────────┤ I2C │ │ TUSB320LAI │ INT_N │ │ │ ├──────────────────────┤ System-on- │ │ │ │ Chip │ │ CC1 CC2 │ ┌──────────┤ GPIO │ └──┬───┬──────────────┘ │ │ │ │ │ │ │ USBOTG │ │ │ │ │ VBUS D+ D- │ │ │ │ └──────┬─────┬───┬──┘ │ │ ┌──────────────────────│─────────────────┘ │ │ │ │ │ ┌───────────┴──┐ │ │ │ │ │ │ A_SEL │ │ │ │ │ │ │ │ │ │ │ │ │ │ D+ ├────────────────────┘ │ │ │ │ │ │ │ │ │ │ ┌──────┤ D+/L D- ├────────────────────────┘ │ │ │ │ │ │ │ │ │ │ ┌──┤ D-/R │ Line_L │ │ │ │ │ │ L ├───────────────────────── ┴ ┴ ┴ ┴ ┴ │ │ Line_R USB Type-C port │ R ├───────────────────────── │ │ │ TS5USBA224 │ └──────────────┘ This driver controls the TS5USBA224 via the A_SEL pin. In addition, it registers itself as a Type-C mux device. In the above scenario, the driver may be notified via the port controller driver if the CC pins indicate that an Audio Accessory is connected or not. Signed-off-by: Alvin Šipraga <alsi@xxxxxxxxxxxxxxx> --- drivers/usb/typec/mux/Kconfig | 10 +++ drivers/usb/typec/mux/Makefile | 1 + drivers/usb/typec/mux/ts5usba224.c | 102 +++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 drivers/usb/typec/mux/ts5usba224.c diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig index edead555835e..82c25b117652 100644 --- a/drivers/usb/typec/mux/Kconfig +++ b/drivers/usb/typec/mux/Kconfig @@ -19,4 +19,14 @@ config TYPEC_MUX_INTEL_PMC control the USB role switch and also the multiplexer/demultiplexer switches used with USB Type-C Alternate Modes. +config TYPEC_MUX_TS5USBA224 + tristate "TI TS5USBA224 USB High Speed/Audio switch mux driver" + select GPIOLIB + help + Say Y or M if your system has a TI TS5USBA224 USB High Speed/Audio + switch mux controller paired with a USB Type-C port controller. + + If you choose to build this driver as a dynamically linked module, the + module will be called ts5usba224.ko. + endmenu diff --git a/drivers/usb/typec/mux/Makefile b/drivers/usb/typec/mux/Makefile index 280a6f553115..4b5f318656a0 100644 --- a/drivers/usb/typec/mux/Makefile +++ b/drivers/usb/typec/mux/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_TYPEC_MUX_PI3USB30532) += pi3usb30532.o obj-$(CONFIG_TYPEC_MUX_INTEL_PMC) += intel_pmc_mux.o +obj-$(CONFIG_TYPEC_MUX_TS5USBA224) += ts5usba224.o diff --git a/drivers/usb/typec/mux/ts5usba224.c b/drivers/usb/typec/mux/ts5usba224.c new file mode 100644 index 000000000000..4935dc8a0725 --- /dev/null +++ b/drivers/usb/typec/mux/ts5usba224.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * TI TS5USBA224 USB 2.0/audio switch mux driver + * + * Copyright (c) 2021 Alvin Šipraga <alsi@xxxxxxxxxxxxxxx> + */ + +#include <linux/gpio/consumer.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/usb/typec_altmode.h> +#include <linux/usb/typec_mux.h> + +struct ts5usba224 { + struct device *dev; + struct typec_mux *mux; + struct gpio_desc *a_sel; +}; + +static int ts5usba224_mux_set(struct typec_mux *mux, + struct typec_mux_state *state) +{ + struct ts5usba224 *chip = typec_mux_get_drvdata(mux); + + switch (state->mode) { + case TYPEC_MODE_AUDIO: + gpiod_set_value_cansleep(chip->a_sel, 1); + dev_dbg(chip->dev, "audio switch enabled\n"); + break; + case TYPEC_STATE_USB: + case TYPEC_MODE_USB2: + default: + dev_dbg(chip->dev, "audio switch disabled\n"); + gpiod_set_value_cansleep(chip->a_sel, 0); + break; + } + + return 0; +} + +static int ts5usba224_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct typec_mux_desc mux_desc = {}; + struct ts5usba224 *chip; + int ret; + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = dev; + + chip->a_sel = devm_gpiod_get(dev, "asel", GPIOD_OUT_LOW); + if (IS_ERR(chip->a_sel)) { + ret = PTR_ERR(chip->a_sel); + return dev_err_probe(dev, ret, "failed to get A_SEL GPIO\n"); + } + + mux_desc.drvdata = chip; + mux_desc.fwnode = dev->fwnode; + mux_desc.set = ts5usba224_mux_set; + + chip->mux = typec_mux_register(dev, &mux_desc); + if (IS_ERR(chip->mux)) + return PTR_ERR(chip->mux); + + platform_set_drvdata(pdev, chip); + + return 0; +} + +static int ts5usba224_remove(struct platform_device *pdev) +{ + struct ts5usba224 *chip = platform_get_drvdata(pdev); + + typec_mux_unregister(chip->mux); + + return 0; +} + +static const struct of_device_id ts5usba224_of_match[] = { + { .compatible = "ti,ts5usba224", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ts5usba224_of_match); + +static struct platform_driver ts5usba224_driver = { + .driver = { + .name = "ts5usba224", + .of_match_table = of_match_ptr(ts5usba224_of_match), + }, + .probe = ts5usba224_probe, + .remove = ts5usba224_remove, +}; + +module_platform_driver(ts5usba224_driver); + +MODULE_AUTHOR("Alvin Šipraga <alsi@xxxxxxxxxxxxxxx>"); +MODULE_DESCRIPTION("TI TS5USBA224 mux driver"); +MODULE_LICENSE("GPL"); -- 2.35.1