Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: typec: mux: Introduce GPIO-based SBU mux

A design found in various Qualcomm-based boards is to use a USB switch,
controlled through a pair of GPIO lines to connect, disconnect and
switch the orientation of the SBU lines in USB Type-C applications.

This introduces a generic driver, which implements the typec_switch and
typec_mux interfaces to perform these operations.

Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Signed-off-by: Bjorn Andersson <quic_bjorande@quicinc.com>
Link: https://lore.kernel.org/r/20230113041115.4189210-2-quic_bjorande@quicinc.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Bjorn Andersson and committed by
Greg Kroah-Hartman
065ded31 b39483d6

+179
+6
drivers/usb/typec/mux/Kconfig
··· 12 12 common USB Type-C connector. 13 13 If compiled as a module, the module will be named fsa4480. 14 14 15 + config TYPEC_MUX_GPIO_SBU 16 + tristate "Generic GPIO based SBU mux for USB Type-C applications" 17 + help 18 + Say Y or M if your system uses a GPIO based mux for managing the 19 + connected state and the swapping of the SBU lines in a Type-C port. 20 + 15 21 config TYPEC_MUX_PI3USB30532 16 22 tristate "Pericom PI3USB30532 Type-C cross switch driver" 17 23 depends on I2C
+1
drivers/usb/typec/mux/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 2 3 3 obj-$(CONFIG_TYPEC_MUX_FSA4480) += fsa4480.o 4 + obj-$(CONFIG_TYPEC_MUX_GPIO_SBU) += gpio-sbu-mux.o 4 5 obj-$(CONFIG_TYPEC_MUX_PI3USB30532) += pi3usb30532.o 5 6 obj-$(CONFIG_TYPEC_MUX_INTEL_PMC) += intel_pmc_mux.o
+172
drivers/usb/typec/mux/gpio-sbu-mux.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2022 Linaro Ltd. 4 + */ 5 + 6 + #include <linux/bits.h> 7 + #include <linux/i2c.h> 8 + #include <linux/kernel.h> 9 + #include <linux/module.h> 10 + #include <linux/mutex.h> 11 + #include <linux/gpio/consumer.h> 12 + #include <linux/platform_device.h> 13 + #include <linux/regmap.h> 14 + #include <linux/usb/typec_dp.h> 15 + #include <linux/usb/typec_mux.h> 16 + 17 + struct gpio_sbu_mux { 18 + struct gpio_desc *enable_gpio; 19 + struct gpio_desc *select_gpio; 20 + 21 + struct typec_switch_dev *sw; 22 + struct typec_mux_dev *mux; 23 + 24 + struct mutex lock; /* protect enabled and swapped */ 25 + bool enabled; 26 + bool swapped; 27 + }; 28 + 29 + static int gpio_sbu_switch_set(struct typec_switch_dev *sw, 30 + enum typec_orientation orientation) 31 + { 32 + struct gpio_sbu_mux *sbu_mux = typec_switch_get_drvdata(sw); 33 + bool enabled; 34 + bool swapped; 35 + 36 + mutex_lock(&sbu_mux->lock); 37 + 38 + enabled = sbu_mux->enabled; 39 + swapped = sbu_mux->swapped; 40 + 41 + switch (orientation) { 42 + case TYPEC_ORIENTATION_NONE: 43 + enabled = false; 44 + break; 45 + case TYPEC_ORIENTATION_NORMAL: 46 + swapped = false; 47 + break; 48 + case TYPEC_ORIENTATION_REVERSE: 49 + swapped = true; 50 + break; 51 + } 52 + 53 + if (enabled != sbu_mux->enabled) 54 + gpiod_set_value(sbu_mux->enable_gpio, enabled); 55 + 56 + if (swapped != sbu_mux->swapped) 57 + gpiod_set_value(sbu_mux->select_gpio, swapped); 58 + 59 + sbu_mux->enabled = enabled; 60 + sbu_mux->swapped = swapped; 61 + 62 + mutex_unlock(&sbu_mux->lock); 63 + 64 + return 0; 65 + } 66 + 67 + static int gpio_sbu_mux_set(struct typec_mux_dev *mux, 68 + struct typec_mux_state *state) 69 + { 70 + struct gpio_sbu_mux *sbu_mux = typec_mux_get_drvdata(mux); 71 + 72 + mutex_lock(&sbu_mux->lock); 73 + 74 + switch (state->mode) { 75 + case TYPEC_STATE_SAFE: 76 + case TYPEC_STATE_USB: 77 + sbu_mux->enabled = false; 78 + break; 79 + case TYPEC_DP_STATE_C: 80 + case TYPEC_DP_STATE_D: 81 + case TYPEC_DP_STATE_E: 82 + sbu_mux->enabled = true; 83 + break; 84 + default: 85 + break; 86 + } 87 + 88 + gpiod_set_value(sbu_mux->enable_gpio, sbu_mux->enabled); 89 + 90 + mutex_unlock(&sbu_mux->lock); 91 + 92 + return 0; 93 + } 94 + 95 + static int gpio_sbu_mux_probe(struct platform_device *pdev) 96 + { 97 + struct typec_switch_desc sw_desc = { }; 98 + struct typec_mux_desc mux_desc = { }; 99 + struct device *dev = &pdev->dev; 100 + struct gpio_sbu_mux *sbu_mux; 101 + 102 + sbu_mux = devm_kzalloc(dev, sizeof(*sbu_mux), GFP_KERNEL); 103 + if (!sbu_mux) 104 + return -ENOMEM; 105 + 106 + mutex_init(&sbu_mux->lock); 107 + 108 + sbu_mux->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); 109 + if (IS_ERR(sbu_mux->enable_gpio)) 110 + return dev_err_probe(dev, PTR_ERR(sbu_mux->enable_gpio), 111 + "unable to acquire enable gpio\n"); 112 + 113 + sbu_mux->select_gpio = devm_gpiod_get(dev, "select", GPIOD_OUT_LOW); 114 + if (IS_ERR(sbu_mux->select_gpio)) 115 + return dev_err_probe(dev, PTR_ERR(sbu_mux->select_gpio), 116 + "unable to acquire select gpio\n"); 117 + 118 + sw_desc.drvdata = sbu_mux; 119 + sw_desc.fwnode = dev_fwnode(dev); 120 + sw_desc.set = gpio_sbu_switch_set; 121 + 122 + sbu_mux->sw = typec_switch_register(dev, &sw_desc); 123 + if (IS_ERR(sbu_mux->sw)) 124 + return dev_err_probe(dev, PTR_ERR(sbu_mux->sw), 125 + "failed to register typec switch\n"); 126 + 127 + mux_desc.drvdata = sbu_mux; 128 + mux_desc.fwnode = dev_fwnode(dev); 129 + mux_desc.set = gpio_sbu_mux_set; 130 + 131 + sbu_mux->mux = typec_mux_register(dev, &mux_desc); 132 + if (IS_ERR(sbu_mux->mux)) { 133 + typec_switch_unregister(sbu_mux->sw); 134 + return dev_err_probe(dev, PTR_ERR(sbu_mux->mux), 135 + "failed to register typec mux\n"); 136 + } 137 + 138 + platform_set_drvdata(pdev, sbu_mux); 139 + 140 + return 0; 141 + } 142 + 143 + static int gpio_sbu_mux_remove(struct platform_device *pdev) 144 + { 145 + struct gpio_sbu_mux *sbu_mux = platform_get_drvdata(pdev); 146 + 147 + gpiod_set_value(sbu_mux->enable_gpio, 0); 148 + 149 + typec_mux_unregister(sbu_mux->mux); 150 + typec_switch_unregister(sbu_mux->sw); 151 + 152 + return 0; 153 + } 154 + 155 + static const struct of_device_id gpio_sbu_mux_match[] = { 156 + { .compatible = "gpio-sbu-mux", }, 157 + {} 158 + }; 159 + MODULE_DEVICE_TABLE(of, gpio_sbu_mux_match); 160 + 161 + static struct platform_driver gpio_sbu_mux_driver = { 162 + .probe = gpio_sbu_mux_probe, 163 + .remove = gpio_sbu_mux_remove, 164 + .driver = { 165 + .name = "gpio_sbu_mux", 166 + .of_match_table = gpio_sbu_mux_match, 167 + }, 168 + }; 169 + module_platform_driver(gpio_sbu_mux_driver); 170 + 171 + MODULE_DESCRIPTION("GPIO based SBU mux driver"); 172 + MODULE_LICENSE("GPL");