This patch adds a simple typec switch driver for cases which only needs some simple operations but a dedicated driver is required, current driver only supports GPIO toggle to switch the super speed active channel according to typec orientation. Signed-off-by: Li Jun <jun.li@xxxxxxx> --- Changes for v6: - Use general mux control for typec switch, currently only support max 2 GPIOs to control one typec switch with 3 states: orientation normal(cc1), reverse(cc2), none(deselected both channels). Changes for v5: - A few changes address Andy's comment, remove gpio check as it's optional, add module name for Kconfig, use correct header files, and other minor changes. - Remove the mutex lock as it's not required currently. Changes for v4: - Change driver name to be switch simple from switch GPIO, to make it generic for possible extention. - Use compatiable "typec-orientation-switch" instead of bool property for switch matching. - Make acitve channel selection GPIO to be optional. - Remove Andy's R-b tag since the driver changes a lot. Change for v3: - Remove file name in driver description. - Add Andy Shevchenko's Reviewed-by tag. Changes for v2: - Use the correct head files for gpio api and of_device_id: #include <linux/gpio/consumer.h> #include <linux/mod_devicetable.h> - Add driver dependency on GPIOLIB drivers/usb/typec/mux/Kconfig | 10 +++ drivers/usb/typec/mux/Makefile | 1 + drivers/usb/typec/mux/switch-simple.c | 126 ++++++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+) diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig index a4dbd11..11320d7 100644 --- a/drivers/usb/typec/mux/Kconfig +++ b/drivers/usb/typec/mux/Kconfig @@ -18,4 +18,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_SWITCH_SIMPLE + tristate "Type-C orientation switch simple driver" + depends on GPIOLIB + help + Say Y or M if your system need a simple driver for typec switch + control, like use GPIO to select active channel. + + To compile this driver as a module, choose M here: the + module will be called switch-simple. + endmenu diff --git a/drivers/usb/typec/mux/Makefile b/drivers/usb/typec/mux/Makefile index 280a6f5..712d0ad 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_SWITCH_SIMPLE) += switch-simple.o diff --git a/drivers/usb/typec/mux/switch-simple.c b/drivers/usb/typec/mux/switch-simple.c new file mode 100644 index 0000000..4671169 --- /dev/null +++ b/drivers/usb/typec/mux/switch-simple.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Type-C switch simple control driver + * + * Copyright 2020 NXP + * Author: Jun Li <jun.li@xxxxxxx> + */ + +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/mutex.h> +#include <linux/mux/driver.h> +#include <linux/platform_device.h> +#include <linux/usb/typec_mux.h> + +struct typec_switch_simple { + struct typec_switch *sw; + struct mux_control *mux; + struct gpio_descs *switch_gpios; +}; + +static int typec_switch_simple_mux_set(struct mux_control *mux, int state) +{ + struct typec_switch_simple *typec_ss = mux_chip_priv(mux->chip); + DECLARE_BITMAP(values, BITS_PER_TYPE(state)); + + values[0] = state; + gpiod_set_array_value_cansleep(typec_ss->switch_gpios->ndescs, + typec_ss->switch_gpios->desc, + typec_ss->switch_gpios->info, + values); + + return 0; +} + +static const struct mux_control_ops typec_switch_mux_ops = { + .set = typec_switch_simple_mux_set, +}; + +static int typec_switch_simple_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mux_chip *mux_chip; + struct typec_switch_desc sw_desc; + struct typec_switch_simple *typec_ss; + int idle_state; + int pins; + int ret; + + pins = gpiod_count(dev, "switch"); + mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*typec_ss)); + if (IS_ERR(mux_chip)) + return PTR_ERR(mux_chip); + + typec_ss = mux_chip_priv(mux_chip); + mux_chip->ops = &typec_switch_mux_ops; + + /* Get the super speed active channel selection GPIO */ + typec_ss->switch_gpios = devm_gpiod_get_array_optional(dev, + "switch", GPIOD_OUT_LOW); + if (IS_ERR(typec_ss->switch_gpios)) + return PTR_ERR(typec_ss->switch_gpios); + + if (pins) { + WARN_ON(pins != typec_ss->switch_gpios->ndescs || pins > 2); + mux_chip->mux->states = pins + 1; + } + + ret = device_property_read_u32(dev, "idle-state", (u32 *)&idle_state); + if (ret >= 0 && idle_state != MUX_IDLE_AS_IS) { + if (idle_state < 0 || idle_state >= mux_chip->mux->states) { + dev_err(dev, "invalid idle-state %u\n", idle_state); + return -EINVAL; + } + mux_chip->mux->idle_state = idle_state; + } + + ret = devm_mux_chip_register(dev, mux_chip); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, typec_ss); + sw_desc.drvdata = typec_ss; + sw_desc.fwnode = dev->fwnode; + + typec_ss->sw = typec_switch_register(&mux_chip->dev, &sw_desc); + if (IS_ERR(typec_ss->sw)) { + dev_err(dev, "Error registering typec switch: %ld\n", + PTR_ERR(typec_ss->sw)); + return PTR_ERR(typec_ss->sw); + } + + return 0; +} + +static int typec_switch_simple_remove(struct platform_device *pdev) +{ + struct typec_switch_simple *typec_ss = platform_get_drvdata(pdev); + + typec_switch_unregister(typec_ss->sw); + + return 0; +} + +static const struct of_device_id of_typec_switch_simple_match[] = { + { .compatible = "typec-orientation-switch" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, of_typec_switch_simple_match); + +static struct platform_driver typec_switch_simple_driver = { + .probe = typec_switch_simple_probe, + .remove = typec_switch_simple_remove, + .driver = { + .name = "typec-switch-simple", + .of_match_table = of_typec_switch_simple_match, + }, +}; + +module_platform_driver(typec_switch_simple_driver); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TypeC Orientation Switch Simple driver"); +MODULE_AUTHOR("Jun Li <jun.li@xxxxxxx>"); -- 2.7.4