1
2
3
4
5
6
7
8
9#include <linux/extcon-provider.h>
10#include <linux/i2c.h>
11#include <linux/init.h>
12#include <linux/interrupt.h>
13#include <linux/kernel.h>
14#include <linux/module.h>
15#include <linux/regmap.h>
16
17#define TUSB320_REG9 0x9
18#define TUSB320_REG9_ATTACHED_STATE_SHIFT 6
19#define TUSB320_REG9_ATTACHED_STATE_MASK 0x3
20#define TUSB320_REG9_CABLE_DIRECTION BIT(5)
21#define TUSB320_REG9_INTERRUPT_STATUS BIT(4)
22
23#define TUSB320_REGA 0xa
24#define TUSB320L_REGA_DISABLE_TERM BIT(0)
25#define TUSB320_REGA_I2C_SOFT_RESET BIT(3)
26#define TUSB320_REGA_MODE_SELECT_SHIFT 4
27#define TUSB320_REGA_MODE_SELECT_MASK 0x3
28
29#define TUSB320L_REGA0_REVISION 0xa0
30
31enum tusb320_attached_state {
32 TUSB320_ATTACHED_STATE_NONE,
33 TUSB320_ATTACHED_STATE_DFP,
34 TUSB320_ATTACHED_STATE_UFP,
35 TUSB320_ATTACHED_STATE_ACC,
36};
37
38enum tusb320_mode {
39 TUSB320_MODE_PORT,
40 TUSB320_MODE_UFP,
41 TUSB320_MODE_DFP,
42 TUSB320_MODE_DRP,
43};
44
45struct tusb320_priv;
46
47struct tusb320_ops {
48 int (*set_mode)(struct tusb320_priv *priv, enum tusb320_mode mode);
49 int (*get_revision)(struct tusb320_priv *priv, unsigned int *revision);
50};
51
52struct tusb320_priv {
53 struct device *dev;
54 struct regmap *regmap;
55 struct extcon_dev *edev;
56 struct tusb320_ops *ops;
57 enum tusb320_attached_state state;
58};
59
60static const char * const tusb_attached_states[] = {
61 [TUSB320_ATTACHED_STATE_NONE] = "not attached",
62 [TUSB320_ATTACHED_STATE_DFP] = "downstream facing port",
63 [TUSB320_ATTACHED_STATE_UFP] = "upstream facing port",
64 [TUSB320_ATTACHED_STATE_ACC] = "accessory",
65};
66
67static const unsigned int tusb320_extcon_cable[] = {
68 EXTCON_USB,
69 EXTCON_USB_HOST,
70 EXTCON_NONE,
71};
72
73static int tusb320_check_signature(struct tusb320_priv *priv)
74{
75 static const char sig[] = { '\0', 'T', 'U', 'S', 'B', '3', '2', '0' };
76 unsigned val;
77 int i, ret;
78
79 for (i = 0; i < sizeof(sig); i++) {
80 ret = regmap_read(priv->regmap, sizeof(sig) - 1 - i, &val);
81 if (ret < 0)
82 return ret;
83 if (val != sig[i]) {
84 dev_err(priv->dev, "signature mismatch!\n");
85 return -ENODEV;
86 }
87 }
88
89 return 0;
90}
91
92static int tusb320_set_mode(struct tusb320_priv *priv, enum tusb320_mode mode)
93{
94 int ret;
95
96
97 if (priv->state != TUSB320_ATTACHED_STATE_NONE)
98 return -EBUSY;
99
100
101 ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
102 TUSB320_REGA_MODE_SELECT_MASK << TUSB320_REGA_MODE_SELECT_SHIFT,
103 mode << TUSB320_REGA_MODE_SELECT_SHIFT);
104 if (ret) {
105 dev_err(priv->dev, "failed to write mode: %d\n", ret);
106 return ret;
107 }
108
109 return 0;
110}
111
112static int tusb320l_set_mode(struct tusb320_priv *priv, enum tusb320_mode mode)
113{
114 int ret;
115
116
117 ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
118 TUSB320L_REGA_DISABLE_TERM, 1);
119 if (ret) {
120 dev_err(priv->dev,
121 "failed to disable CC state machine: %d\n", ret);
122 return ret;
123 }
124
125
126 ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
127 TUSB320_REGA_MODE_SELECT_MASK << TUSB320_REGA_MODE_SELECT_SHIFT,
128 mode << TUSB320_REGA_MODE_SELECT_SHIFT);
129 if (ret) {
130 dev_err(priv->dev, "failed to write mode: %d\n", ret);
131 goto err;
132 }
133
134 msleep(5);
135err:
136
137 ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
138 TUSB320L_REGA_DISABLE_TERM, 0);
139 if (ret)
140 dev_err(priv->dev,
141 "failed to re-enable CC state machine: %d\n", ret);
142
143 return ret;
144}
145
146static int tusb320_reset(struct tusb320_priv *priv)
147{
148 int ret;
149
150
151 ret = priv->ops->set_mode(priv, TUSB320_MODE_PORT);
152 if (ret && ret != -EBUSY) {
153 dev_err(priv->dev,
154 "failed to set mode to PORT: %d\n", ret);
155 return ret;
156 }
157
158
159 ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
160 TUSB320_REGA_I2C_SOFT_RESET, 1);
161 if (ret) {
162 dev_err(priv->dev,
163 "failed to write soft reset bit: %d\n", ret);
164 return ret;
165 }
166
167
168 msleep(95);
169
170 return 0;
171}
172
173static int tusb320l_get_revision(struct tusb320_priv *priv, unsigned int *revision)
174{
175 return regmap_read(priv->regmap, TUSB320L_REGA0_REVISION, revision);
176}
177
178static struct tusb320_ops tusb320_ops = {
179 .set_mode = tusb320_set_mode,
180};
181
182static struct tusb320_ops tusb320l_ops = {
183 .set_mode = tusb320l_set_mode,
184 .get_revision = tusb320l_get_revision,
185};
186
187static irqreturn_t tusb320_irq_handler(int irq, void *dev_id)
188{
189 struct tusb320_priv *priv = dev_id;
190 int state, polarity;
191 unsigned reg;
192
193 if (regmap_read(priv->regmap, TUSB320_REG9, ®)) {
194 dev_err(priv->dev, "error during i2c read!\n");
195 return IRQ_NONE;
196 }
197
198 if (!(reg & TUSB320_REG9_INTERRUPT_STATUS))
199 return IRQ_NONE;
200
201 state = (reg >> TUSB320_REG9_ATTACHED_STATE_SHIFT) &
202 TUSB320_REG9_ATTACHED_STATE_MASK;
203 polarity = !!(reg & TUSB320_REG9_CABLE_DIRECTION);
204
205 dev_dbg(priv->dev, "attached state: %s, polarity: %d\n",
206 tusb_attached_states[state], polarity);
207
208 extcon_set_state(priv->edev, EXTCON_USB,
209 state == TUSB320_ATTACHED_STATE_UFP);
210 extcon_set_state(priv->edev, EXTCON_USB_HOST,
211 state == TUSB320_ATTACHED_STATE_DFP);
212 extcon_set_property(priv->edev, EXTCON_USB,
213 EXTCON_PROP_USB_TYPEC_POLARITY,
214 (union extcon_property_value)polarity);
215 extcon_set_property(priv->edev, EXTCON_USB_HOST,
216 EXTCON_PROP_USB_TYPEC_POLARITY,
217 (union extcon_property_value)polarity);
218 extcon_sync(priv->edev, EXTCON_USB);
219 extcon_sync(priv->edev, EXTCON_USB_HOST);
220
221 priv->state = state;
222
223 regmap_write(priv->regmap, TUSB320_REG9, reg);
224
225 return IRQ_HANDLED;
226}
227
228static const struct regmap_config tusb320_regmap_config = {
229 .reg_bits = 8,
230 .val_bits = 8,
231};
232
233static int tusb320_extcon_probe(struct i2c_client *client,
234 const struct i2c_device_id *id)
235{
236 struct tusb320_priv *priv;
237 const void *match_data;
238 unsigned int revision;
239 int ret;
240
241 priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
242 if (!priv)
243 return -ENOMEM;
244 priv->dev = &client->dev;
245
246 priv->regmap = devm_regmap_init_i2c(client, &tusb320_regmap_config);
247 if (IS_ERR(priv->regmap))
248 return PTR_ERR(priv->regmap);
249
250 ret = tusb320_check_signature(priv);
251 if (ret)
252 return ret;
253
254 match_data = device_get_match_data(&client->dev);
255 if (!match_data)
256 return -EINVAL;
257
258 priv->ops = (struct tusb320_ops*)match_data;
259
260 priv->edev = devm_extcon_dev_allocate(priv->dev, tusb320_extcon_cable);
261 if (IS_ERR(priv->edev)) {
262 dev_err(priv->dev, "failed to allocate extcon device\n");
263 return PTR_ERR(priv->edev);
264 }
265
266 if (priv->ops->get_revision) {
267 ret = priv->ops->get_revision(priv, &revision);
268 if (ret)
269 dev_warn(priv->dev,
270 "failed to read revision register: %d\n", ret);
271 else
272 dev_info(priv->dev, "chip revision %d\n", revision);
273 }
274
275 ret = devm_extcon_dev_register(priv->dev, priv->edev);
276 if (ret < 0) {
277 dev_err(priv->dev, "failed to register extcon device\n");
278 return ret;
279 }
280
281 extcon_set_property_capability(priv->edev, EXTCON_USB,
282 EXTCON_PROP_USB_TYPEC_POLARITY);
283 extcon_set_property_capability(priv->edev, EXTCON_USB_HOST,
284 EXTCON_PROP_USB_TYPEC_POLARITY);
285
286
287 tusb320_irq_handler(client->irq, priv);
288
289
290 ret = tusb320_reset(priv);
291 if (ret)
292 dev_warn(priv->dev, "failed to reset chip: %d\n", ret);
293 else
294
295
296
297
298 tusb320_irq_handler(client->irq, priv);
299
300 ret = devm_request_threaded_irq(priv->dev, client->irq, NULL,
301 tusb320_irq_handler,
302 IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
303 client->name, priv);
304
305 return ret;
306}
307
308static const struct of_device_id tusb320_extcon_dt_match[] = {
309 { .compatible = "ti,tusb320", .data = &tusb320_ops, },
310 { .compatible = "ti,tusb320l", .data = &tusb320l_ops, },
311 { }
312};
313MODULE_DEVICE_TABLE(of, tusb320_extcon_dt_match);
314
315static struct i2c_driver tusb320_extcon_driver = {
316 .probe = tusb320_extcon_probe,
317 .driver = {
318 .name = "extcon-tusb320",
319 .of_match_table = tusb320_extcon_dt_match,
320 },
321};
322
323static int __init tusb320_init(void)
324{
325 return i2c_add_driver(&tusb320_extcon_driver);
326}
327subsys_initcall(tusb320_init);
328
329static void __exit tusb320_exit(void)
330{
331 i2c_del_driver(&tusb320_extcon_driver);
332}
333module_exit(tusb320_exit);
334
335MODULE_AUTHOR("Michael Auchter <michael.auchter@ni.com>");
336MODULE_DESCRIPTION("TI TUSB320 extcon driver");
337MODULE_LICENSE("GPL v2");
338