1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Pericom PI3USB30532 Type-C cross switch / mux driver 4 * 5 * Copyright (c) 2017-2018 Hans de Goede <hdegoede@redhat.com> 6 */ 7 8 #include <linux/i2c.h> 9 #include <linux/kernel.h> 10 #include <linux/module.h> 11 #include <linux/mutex.h> 12 #include <linux/usb/typec_dp.h> 13 #include <linux/usb/typec_mux.h> 14 15 #define PI3USB30532_CONF 0x00 16 17 #define PI3USB30532_CONF_OPEN 0x00 18 #define PI3USB30532_CONF_SWAP 0x01 19 #define PI3USB30532_CONF_4LANE_DP 0x02 20 #define PI3USB30532_CONF_USB3 0x04 21 #define PI3USB30532_CONF_USB3_AND_2LANE_DP 0x06 22 23 struct pi3usb30532 { 24 struct i2c_client *client; 25 struct mutex lock; /* protects the cached conf register */ 26 struct typec_switch *sw; 27 struct typec_mux *mux; 28 u8 conf; 29 }; 30 31 static int pi3usb30532_set_conf(struct pi3usb30532 *pi, u8 new_conf) 32 { 33 int ret = 0; 34 35 if (pi->conf == new_conf) 36 return 0; 37 38 ret = i2c_smbus_write_byte_data(pi->client, PI3USB30532_CONF, new_conf); 39 if (ret) { 40 dev_err(&pi->client->dev, "Error writing conf: %d\n", ret); 41 return ret; 42 } 43 44 pi->conf = new_conf; 45 return 0; 46 } 47 48 static int pi3usb30532_sw_set(struct typec_switch *sw, 49 enum typec_orientation orientation) 50 { 51 struct pi3usb30532 *pi = typec_switch_get_drvdata(sw); 52 u8 new_conf; 53 int ret; 54 55 mutex_lock(&pi->lock); 56 new_conf = pi->conf; 57 58 switch (orientation) { 59 case TYPEC_ORIENTATION_NONE: 60 new_conf = PI3USB30532_CONF_OPEN; 61 break; 62 case TYPEC_ORIENTATION_NORMAL: 63 new_conf &= ~PI3USB30532_CONF_SWAP; 64 break; 65 case TYPEC_ORIENTATION_REVERSE: 66 new_conf |= PI3USB30532_CONF_SWAP; 67 break; 68 } 69 70 ret = pi3usb30532_set_conf(pi, new_conf); 71 mutex_unlock(&pi->lock); 72 73 return ret; 74 } 75 76 static int 77 pi3usb30532_mux_set(struct typec_mux *mux, struct typec_mux_state *state) 78 { 79 struct pi3usb30532 *pi = typec_mux_get_drvdata(mux); 80 u8 new_conf; 81 int ret; 82 83 mutex_lock(&pi->lock); 84 new_conf = pi->conf; 85 86 switch (state->mode) { 87 case TYPEC_STATE_SAFE: 88 new_conf = (new_conf & PI3USB30532_CONF_SWAP) | 89 PI3USB30532_CONF_OPEN; 90 break; 91 case TYPEC_STATE_USB: 92 new_conf = (new_conf & PI3USB30532_CONF_SWAP) | 93 PI3USB30532_CONF_USB3; 94 break; 95 case TYPEC_DP_STATE_C: 96 case TYPEC_DP_STATE_E: 97 new_conf = (new_conf & PI3USB30532_CONF_SWAP) | 98 PI3USB30532_CONF_4LANE_DP; 99 break; 100 case TYPEC_DP_STATE_D: 101 new_conf = (new_conf & PI3USB30532_CONF_SWAP) | 102 PI3USB30532_CONF_USB3_AND_2LANE_DP; 103 break; 104 default: 105 break; 106 } 107 108 ret = pi3usb30532_set_conf(pi, new_conf); 109 mutex_unlock(&pi->lock); 110 111 return ret; 112 } 113 114 static int pi3usb30532_probe(struct i2c_client *client) 115 { 116 struct device *dev = &client->dev; 117 struct typec_switch_desc sw_desc = { }; 118 struct typec_mux_desc mux_desc = { }; 119 struct pi3usb30532 *pi; 120 int ret; 121 122 pi = devm_kzalloc(dev, sizeof(*pi), GFP_KERNEL); 123 if (!pi) 124 return -ENOMEM; 125 126 pi->client = client; 127 mutex_init(&pi->lock); 128 129 ret = i2c_smbus_read_byte_data(client, PI3USB30532_CONF); 130 if (ret < 0) { 131 dev_err(dev, "Error reading config register %d\n", ret); 132 return ret; 133 } 134 pi->conf = ret; 135 136 sw_desc.drvdata = pi; 137 sw_desc.fwnode = dev->fwnode; 138 sw_desc.set = pi3usb30532_sw_set; 139 140 pi->sw = typec_switch_register(dev, &sw_desc); 141 if (IS_ERR(pi->sw)) { 142 dev_err(dev, "Error registering typec switch: %ld\n", 143 PTR_ERR(pi->sw)); 144 return PTR_ERR(pi->sw); 145 } 146 147 mux_desc.drvdata = pi; 148 mux_desc.fwnode = dev->fwnode; 149 mux_desc.set = pi3usb30532_mux_set; 150 151 pi->mux = typec_mux_register(dev, &mux_desc); 152 if (IS_ERR(pi->mux)) { 153 typec_switch_unregister(pi->sw); 154 dev_err(dev, "Error registering typec mux: %ld\n", 155 PTR_ERR(pi->mux)); 156 return PTR_ERR(pi->mux); 157 } 158 159 i2c_set_clientdata(client, pi); 160 return 0; 161 } 162 163 static int pi3usb30532_remove(struct i2c_client *client) 164 { 165 struct pi3usb30532 *pi = i2c_get_clientdata(client); 166 167 typec_mux_unregister(pi->mux); 168 typec_switch_unregister(pi->sw); 169 return 0; 170 } 171 172 static const struct i2c_device_id pi3usb30532_table[] = { 173 { "pi3usb30532" }, 174 { } 175 }; 176 MODULE_DEVICE_TABLE(i2c, pi3usb30532_table); 177 178 static struct i2c_driver pi3usb30532_driver = { 179 .driver = { 180 .name = "pi3usb30532", 181 }, 182 .probe_new = pi3usb30532_probe, 183 .remove = pi3usb30532_remove, 184 .id_table = pi3usb30532_table, 185 }; 186 187 module_i2c_driver(pi3usb30532_driver); 188 189 MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 190 MODULE_DESCRIPTION("Pericom PI3USB30532 Type-C mux driver"); 191 MODULE_LICENSE("GPL"); 192