1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Driver for the TUSB1046-DCI USB Type-C crosspoint switch 4 * 5 * Copyright (C) 2024 Bootlin 6 */ 7 8 #include <linux/bits.h> 9 #include <linux/i2c.h> 10 #include <linux/usb/typec_mux.h> 11 #include <linux/usb/typec_dp.h> 12 #include <linux/usb/typec_altmode.h> 13 #include <linux/module.h> 14 #include <linux/mod_devicetable.h> 15 #include <linux/err.h> 16 #include <linux/of_device.h> 17 #include <linux/device.h> 18 #include <linux/mutex.h> 19 20 #define TUSB1046_REG_GENERAL 0xa 21 22 /* General register bits */ 23 #define TUSB1046_GENERAL_FLIPSEL BIT(2) 24 #define TUSB1046_GENERAL_CTLSEL GENMASK(1, 0) 25 26 /* Mux modes */ 27 #define TUSB1046_CTLSEL_DISABLED 0x0 28 #define TUSB1046_CTLSEL_USB3 0x1 29 #define TUSB1046_CTLSEL_4LANE_DP 0x2 30 #define TUSB1046_CTLSEL_USB3_AND_2LANE_DP 0x3 31 32 struct tusb1046_priv { 33 struct i2c_client *client; 34 struct typec_switch_dev *sw; 35 struct typec_mux_dev *mux; 36 37 /* Lock General register during accesses */ 38 struct mutex general_reg_lock; 39 }; 40 41 static int tusb1046_mux_set(struct typec_mux_dev *mux, 42 struct typec_mux_state *state) 43 { 44 struct tusb1046_priv *priv = typec_mux_get_drvdata(mux); 45 struct i2c_client *client = priv->client; 46 struct device *dev = &client->dev; 47 int mode, val, ret = 0; 48 49 if (state->mode >= TYPEC_STATE_MODAL && 50 state->alt->svid != USB_TYPEC_DP_SID) 51 return -EINVAL; 52 53 dev_dbg(dev, "mux mode requested: %lu\n", state->mode); 54 55 mutex_lock(&priv->general_reg_lock); 56 57 val = i2c_smbus_read_byte_data(client, TUSB1046_REG_GENERAL); 58 if (val < 0) { 59 dev_err(dev, "failed to read ctlsel status, err %d\n", val); 60 ret = val; 61 goto out_unlock; 62 } 63 64 switch (state->mode) { 65 case TYPEC_STATE_USB: 66 mode = TUSB1046_CTLSEL_USB3; 67 break; 68 case TYPEC_DP_STATE_C: 69 case TYPEC_DP_STATE_E: 70 mode = TUSB1046_CTLSEL_4LANE_DP; 71 break; 72 case TYPEC_DP_STATE_D: 73 mode = TUSB1046_CTLSEL_USB3_AND_2LANE_DP; 74 break; 75 case TYPEC_STATE_SAFE: 76 default: 77 mode = TUSB1046_CTLSEL_DISABLED; 78 break; 79 } 80 81 val &= ~TUSB1046_GENERAL_CTLSEL; 82 val |= mode; 83 84 ret = i2c_smbus_write_byte_data(client, TUSB1046_REG_GENERAL, val); 85 86 out_unlock: 87 mutex_unlock(&priv->general_reg_lock); 88 return ret; 89 } 90 91 static int tusb1046_switch_set(struct typec_switch_dev *sw, 92 enum typec_orientation orientation) 93 { 94 struct tusb1046_priv *priv = typec_switch_get_drvdata(sw); 95 struct i2c_client *client = priv->client; 96 struct device *dev = &client->dev; 97 int val, ret = 0; 98 99 dev_dbg(dev, "setting USB3.0 lane flip for orientation %d\n", orientation); 100 101 mutex_lock(&priv->general_reg_lock); 102 103 val = i2c_smbus_read_byte_data(client, TUSB1046_REG_GENERAL); 104 if (val < 0) { 105 dev_err(dev, "failed to read flipsel status, err %d\n", val); 106 ret = val; 107 goto out_unlock; 108 } 109 110 if (orientation == TYPEC_ORIENTATION_REVERSE) 111 val |= TUSB1046_GENERAL_FLIPSEL; 112 else 113 val &= ~TUSB1046_GENERAL_FLIPSEL; 114 115 ret = i2c_smbus_write_byte_data(client, TUSB1046_REG_GENERAL, val); 116 117 out_unlock: 118 mutex_unlock(&priv->general_reg_lock); 119 return ret; 120 } 121 122 static int tusb1046_i2c_probe(struct i2c_client *client) 123 { 124 struct typec_switch_desc sw_desc = { }; 125 struct typec_mux_desc mux_desc = { }; 126 struct device *dev = &client->dev; 127 struct tusb1046_priv *priv; 128 int ret = 0; 129 130 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 131 if (!priv) 132 return dev_err_probe(dev, -ENOMEM, "failed to allocate driver data\n"); 133 134 priv->client = client; 135 136 mutex_init(&priv->general_reg_lock); 137 138 sw_desc.drvdata = priv; 139 sw_desc.fwnode = dev_fwnode(dev); 140 sw_desc.set = tusb1046_switch_set; 141 142 priv->sw = typec_switch_register(dev, &sw_desc); 143 if (IS_ERR(priv->sw)) { 144 ret = dev_err_probe(dev, PTR_ERR(priv->sw), "failed to register type-c switch\n"); 145 goto err_destroy_mutex; 146 } 147 148 mux_desc.drvdata = priv; 149 mux_desc.fwnode = dev_fwnode(dev); 150 mux_desc.set = tusb1046_mux_set; 151 152 priv->mux = typec_mux_register(dev, &mux_desc); 153 if (IS_ERR(priv->mux)) { 154 ret = dev_err_probe(dev, PTR_ERR(priv->mux), "failed to register type-c mux\n"); 155 goto err_unregister_switch; 156 } 157 158 i2c_set_clientdata(client, priv); 159 160 return 0; 161 162 err_unregister_switch: 163 typec_switch_unregister(priv->sw); 164 err_destroy_mutex: 165 mutex_destroy(&priv->general_reg_lock); 166 return ret; 167 } 168 169 static void tusb1046_i2c_remove(struct i2c_client *client) 170 { 171 struct tusb1046_priv *priv = i2c_get_clientdata(client); 172 173 typec_switch_unregister(priv->sw); 174 typec_mux_unregister(priv->mux); 175 mutex_destroy(&priv->general_reg_lock); 176 } 177 178 static const struct of_device_id tusb1046_match_table[] = { 179 {.compatible = "ti,tusb1046"}, 180 {}, 181 }; 182 183 static struct i2c_driver tusb1046_driver = { 184 .driver = { 185 .name = "tusb1046", 186 .of_match_table = tusb1046_match_table, 187 }, 188 .probe = tusb1046_i2c_probe, 189 .remove = tusb1046_i2c_remove, 190 }; 191 192 module_i2c_driver(tusb1046_driver); 193 194 MODULE_DESCRIPTION("TUSB1046 USB Type-C switch driver"); 195 MODULE_AUTHOR("Romain Gantois <romain.gantois@bootlin.com>"); 196 MODULE_LICENSE("GPL"); 197