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 = container_of(sw, struct pi3usb30532, 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 pi3usb30532_mux_set(struct typec_mux *mux, int state) 77 { 78 struct pi3usb30532 *pi = container_of(mux, struct pi3usb30532, mux); 79 u8 new_conf; 80 int ret; 81 82 mutex_lock(&pi->lock); 83 new_conf = pi->conf; 84 85 switch (state) { 86 case TYPEC_STATE_SAFE: 87 new_conf = PI3USB30532_CONF_OPEN; 88 break; 89 case TYPEC_STATE_USB: 90 new_conf = (new_conf & PI3USB30532_CONF_SWAP) | 91 PI3USB30532_CONF_USB3; 92 break; 93 case TYPEC_DP_STATE_C: 94 case TYPEC_DP_STATE_E: 95 new_conf = (new_conf & PI3USB30532_CONF_SWAP) | 96 PI3USB30532_CONF_4LANE_DP; 97 break; 98 case TYPEC_DP_STATE_D: 99 new_conf = (new_conf & PI3USB30532_CONF_SWAP) | 100 PI3USB30532_CONF_USB3_AND_2LANE_DP; 101 break; 102 default: 103 break; 104 } 105 106 ret = pi3usb30532_set_conf(pi, new_conf); 107 mutex_unlock(&pi->lock); 108 109 return ret; 110 } 111 112 static int pi3usb30532_probe(struct i2c_client *client) 113 { 114 struct device *dev = &client->dev; 115 struct pi3usb30532 *pi; 116 int ret; 117 118 pi = devm_kzalloc(dev, sizeof(*pi), GFP_KERNEL); 119 if (!pi) 120 return -ENOMEM; 121 122 pi->client = client; 123 pi->sw.dev = dev; 124 pi->sw.set = pi3usb30532_sw_set; 125 pi->mux.dev = dev; 126 pi->mux.set = pi3usb30532_mux_set; 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 ret = typec_switch_register(&pi->sw); 137 if (ret) { 138 dev_err(dev, "Error registering typec switch: %d\n", ret); 139 return ret; 140 } 141 142 ret = typec_mux_register(&pi->mux); 143 if (ret) { 144 typec_switch_unregister(&pi->sw); 145 dev_err(dev, "Error registering typec mux: %d\n", ret); 146 return ret; 147 } 148 149 i2c_set_clientdata(client, pi); 150 return 0; 151 } 152 153 static int pi3usb30532_remove(struct i2c_client *client) 154 { 155 struct pi3usb30532 *pi = i2c_get_clientdata(client); 156 157 typec_mux_unregister(&pi->mux); 158 typec_switch_unregister(&pi->sw); 159 return 0; 160 } 161 162 static const struct i2c_device_id pi3usb30532_table[] = { 163 { "pi3usb30532" }, 164 { } 165 }; 166 MODULE_DEVICE_TABLE(i2c, pi3usb30532_table); 167 168 static struct i2c_driver pi3usb30532_driver = { 169 .driver = { 170 .name = "pi3usb30532", 171 }, 172 .probe_new = pi3usb30532_probe, 173 .remove = pi3usb30532_remove, 174 .id_table = pi3usb30532_table, 175 }; 176 177 module_i2c_driver(pi3usb30532_driver); 178 179 MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 180 MODULE_DESCRIPTION("Pericom PI3USB30532 Type-C mux driver"); 181 MODULE_LICENSE("GPL"); 182