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
tusb1046_mux_set(struct typec_mux_dev * mux,struct typec_mux_state * state)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
tusb1046_switch_set(struct typec_switch_dev * sw,enum typec_orientation orientation)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
tusb1046_i2c_probe(struct i2c_client * client)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
tusb1046_i2c_remove(struct i2c_client * client)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 <[email protected]>");
196 MODULE_LICENSE("GPL");
197