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