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"); 173