1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * drivers/extcon/extcon-tusb320.c - TUSB320 extcon driver |
4 | * |
5 | * Copyright (C) 2020 National Instruments Corporation |
6 | * Author: Michael Auchter <michael.auchter@ni.com> |
7 | */ |
8 | |
9 | #include <linux/bitfield.h> |
10 | #include <linux/extcon-provider.h> |
11 | #include <linux/i2c.h> |
12 | #include <linux/init.h> |
13 | #include <linux/interrupt.h> |
14 | #include <linux/kernel.h> |
15 | #include <linux/module.h> |
16 | #include <linux/regmap.h> |
17 | #include <linux/usb/typec.h> |
18 | #include <linux/usb/typec_altmode.h> |
19 | #include <linux/usb/role.h> |
20 | #include <linux/irq.h> |
21 | |
22 | #define TUSB320_REG8 0x8 |
23 | #define TUSB320_REG8_CURRENT_MODE_ADVERTISE GENMASK(7, 6) |
24 | #define TUSB320_REG8_CURRENT_MODE_ADVERTISE_USB 0x0 |
25 | #define TUSB320_REG8_CURRENT_MODE_ADVERTISE_15A 0x1 |
26 | #define TUSB320_REG8_CURRENT_MODE_ADVERTISE_30A 0x2 |
27 | #define TUSB320_REG8_CURRENT_MODE_DETECT GENMASK(5, 4) |
28 | #define TUSB320_REG8_CURRENT_MODE_DETECT_DEF 0x0 |
29 | #define TUSB320_REG8_CURRENT_MODE_DETECT_MED 0x1 |
30 | #define TUSB320_REG8_CURRENT_MODE_DETECT_ACC 0x2 |
31 | #define TUSB320_REG8_CURRENT_MODE_DETECT_HI 0x3 |
32 | #define TUSB320_REG8_ACCESSORY_CONNECTED GENMASK(3, 1) |
33 | #define TUSB320_REG8_ACCESSORY_CONNECTED_NONE 0x0 |
34 | #define TUSB320_REG8_ACCESSORY_CONNECTED_AUDIO 0x4 |
35 | #define TUSB320_REG8_ACCESSORY_CONNECTED_ACHRG 0x5 |
36 | #define TUSB320_REG8_ACCESSORY_CONNECTED_DBGDFP 0x6 |
37 | #define TUSB320_REG8_ACCESSORY_CONNECTED_DBGUFP 0x7 |
38 | #define TUSB320_REG8_ACTIVE_CABLE_DETECTION BIT(0) |
39 | |
40 | #define TUSB320_REG9 0x9 |
41 | #define TUSB320_REG9_ATTACHED_STATE GENMASK(7, 6) |
42 | #define TUSB320_REG9_CABLE_DIRECTION BIT(5) |
43 | #define TUSB320_REG9_INTERRUPT_STATUS BIT(4) |
44 | |
45 | #define TUSB320_REGA 0xa |
46 | #define TUSB320L_REGA_DISABLE_TERM BIT(0) |
47 | #define TUSB320_REGA_I2C_SOFT_RESET BIT(3) |
48 | #define TUSB320_REGA_MODE_SELECT_SHIFT 4 |
49 | #define TUSB320_REGA_MODE_SELECT_MASK 0x3 |
50 | |
51 | #define TUSB320L_REGA0_REVISION 0xa0 |
52 | |
53 | enum tusb320_attached_state { |
54 | TUSB320_ATTACHED_STATE_NONE, |
55 | TUSB320_ATTACHED_STATE_DFP, |
56 | TUSB320_ATTACHED_STATE_UFP, |
57 | TUSB320_ATTACHED_STATE_ACC, |
58 | }; |
59 | |
60 | enum tusb320_mode { |
61 | TUSB320_MODE_PORT, |
62 | TUSB320_MODE_UFP, |
63 | TUSB320_MODE_DFP, |
64 | TUSB320_MODE_DRP, |
65 | }; |
66 | |
67 | struct tusb320_priv; |
68 | |
69 | struct tusb320_ops { |
70 | int (*set_mode)(struct tusb320_priv *priv, enum tusb320_mode mode); |
71 | int (*get_revision)(struct tusb320_priv *priv, unsigned int *revision); |
72 | }; |
73 | |
74 | struct tusb320_priv { |
75 | struct device *dev; |
76 | struct regmap *regmap; |
77 | struct extcon_dev *edev; |
78 | struct tusb320_ops *ops; |
79 | enum tusb320_attached_state state; |
80 | struct typec_port *port; |
81 | struct typec_capability cap; |
82 | enum typec_port_type port_type; |
83 | enum typec_pwr_opmode pwr_opmode; |
84 | struct fwnode_handle *connector_fwnode; |
85 | struct usb_role_switch *role_sw; |
86 | }; |
87 | |
88 | static const char * const tusb_attached_states[] = { |
89 | [TUSB320_ATTACHED_STATE_NONE] = "not attached" , |
90 | [TUSB320_ATTACHED_STATE_DFP] = "downstream facing port" , |
91 | [TUSB320_ATTACHED_STATE_UFP] = "upstream facing port" , |
92 | [TUSB320_ATTACHED_STATE_ACC] = "accessory" , |
93 | }; |
94 | |
95 | static const unsigned int tusb320_extcon_cable[] = { |
96 | EXTCON_USB, |
97 | EXTCON_USB_HOST, |
98 | EXTCON_NONE, |
99 | }; |
100 | |
101 | static int tusb320_check_signature(struct tusb320_priv *priv) |
102 | { |
103 | static const char sig[] = { '\0', 'T', 'U', 'S', 'B', '3', '2', '0' }; |
104 | unsigned val; |
105 | int i, ret; |
106 | |
107 | for (i = 0; i < sizeof(sig); i++) { |
108 | ret = regmap_read(map: priv->regmap, reg: sizeof(sig) - 1 - i, val: &val); |
109 | if (ret < 0) |
110 | return ret; |
111 | if (val != sig[i]) { |
112 | dev_err(priv->dev, "signature mismatch!\n" ); |
113 | return -ENODEV; |
114 | } |
115 | } |
116 | |
117 | return 0; |
118 | } |
119 | |
120 | static int tusb320_set_mode(struct tusb320_priv *priv, enum tusb320_mode mode) |
121 | { |
122 | int ret; |
123 | |
124 | /* Mode cannot be changed while cable is attached */ |
125 | if (priv->state != TUSB320_ATTACHED_STATE_NONE) |
126 | return -EBUSY; |
127 | |
128 | /* Write mode */ |
129 | ret = regmap_write_bits(map: priv->regmap, TUSB320_REGA, |
130 | TUSB320_REGA_MODE_SELECT_MASK << TUSB320_REGA_MODE_SELECT_SHIFT, |
131 | val: mode << TUSB320_REGA_MODE_SELECT_SHIFT); |
132 | if (ret) { |
133 | dev_err(priv->dev, "failed to write mode: %d\n" , ret); |
134 | return ret; |
135 | } |
136 | |
137 | return 0; |
138 | } |
139 | |
140 | static int tusb320l_set_mode(struct tusb320_priv *priv, enum tusb320_mode mode) |
141 | { |
142 | int ret; |
143 | |
144 | /* Disable CC state machine */ |
145 | ret = regmap_write_bits(map: priv->regmap, TUSB320_REGA, |
146 | TUSB320L_REGA_DISABLE_TERM, val: 1); |
147 | if (ret) { |
148 | dev_err(priv->dev, |
149 | "failed to disable CC state machine: %d\n" , ret); |
150 | return ret; |
151 | } |
152 | |
153 | /* Write mode */ |
154 | ret = regmap_write_bits(map: priv->regmap, TUSB320_REGA, |
155 | TUSB320_REGA_MODE_SELECT_MASK << TUSB320_REGA_MODE_SELECT_SHIFT, |
156 | val: mode << TUSB320_REGA_MODE_SELECT_SHIFT); |
157 | if (ret) { |
158 | dev_err(priv->dev, "failed to write mode: %d\n" , ret); |
159 | goto err; |
160 | } |
161 | |
162 | msleep(msecs: 5); |
163 | err: |
164 | /* Re-enable CC state machine */ |
165 | ret = regmap_write_bits(map: priv->regmap, TUSB320_REGA, |
166 | TUSB320L_REGA_DISABLE_TERM, val: 0); |
167 | if (ret) |
168 | dev_err(priv->dev, |
169 | "failed to re-enable CC state machine: %d\n" , ret); |
170 | |
171 | return ret; |
172 | } |
173 | |
174 | static int tusb320_reset(struct tusb320_priv *priv) |
175 | { |
176 | int ret; |
177 | |
178 | /* Set mode to default (follow PORT pin) */ |
179 | ret = priv->ops->set_mode(priv, TUSB320_MODE_PORT); |
180 | if (ret && ret != -EBUSY) { |
181 | dev_err(priv->dev, |
182 | "failed to set mode to PORT: %d\n" , ret); |
183 | return ret; |
184 | } |
185 | |
186 | /* Perform soft reset */ |
187 | ret = regmap_write_bits(map: priv->regmap, TUSB320_REGA, |
188 | TUSB320_REGA_I2C_SOFT_RESET, val: 1); |
189 | if (ret) { |
190 | dev_err(priv->dev, |
191 | "failed to write soft reset bit: %d\n" , ret); |
192 | return ret; |
193 | } |
194 | |
195 | /* Wait for chip to go through reset */ |
196 | msleep(msecs: 95); |
197 | |
198 | return 0; |
199 | } |
200 | |
201 | static int tusb320l_get_revision(struct tusb320_priv *priv, unsigned int *revision) |
202 | { |
203 | return regmap_read(map: priv->regmap, TUSB320L_REGA0_REVISION, val: revision); |
204 | } |
205 | |
206 | static struct tusb320_ops tusb320_ops = { |
207 | .set_mode = tusb320_set_mode, |
208 | }; |
209 | |
210 | static struct tusb320_ops tusb320l_ops = { |
211 | .set_mode = tusb320l_set_mode, |
212 | .get_revision = tusb320l_get_revision, |
213 | }; |
214 | |
215 | static int tusb320_set_adv_pwr_mode(struct tusb320_priv *priv) |
216 | { |
217 | u8 mode; |
218 | |
219 | if (priv->pwr_opmode == TYPEC_PWR_MODE_USB) |
220 | mode = TUSB320_REG8_CURRENT_MODE_ADVERTISE_USB; |
221 | else if (priv->pwr_opmode == TYPEC_PWR_MODE_1_5A) |
222 | mode = TUSB320_REG8_CURRENT_MODE_ADVERTISE_15A; |
223 | else if (priv->pwr_opmode == TYPEC_PWR_MODE_3_0A) |
224 | mode = TUSB320_REG8_CURRENT_MODE_ADVERTISE_30A; |
225 | else /* No other mode is supported. */ |
226 | return -EINVAL; |
227 | |
228 | return regmap_write_bits(map: priv->regmap, TUSB320_REG8, |
229 | TUSB320_REG8_CURRENT_MODE_ADVERTISE, |
230 | FIELD_PREP(TUSB320_REG8_CURRENT_MODE_ADVERTISE, |
231 | mode)); |
232 | } |
233 | |
234 | static int tusb320_port_type_set(struct typec_port *port, |
235 | enum typec_port_type type) |
236 | { |
237 | struct tusb320_priv *priv = typec_get_drvdata(port); |
238 | |
239 | if (type == TYPEC_PORT_SRC) |
240 | return priv->ops->set_mode(priv, TUSB320_MODE_DFP); |
241 | else if (type == TYPEC_PORT_SNK) |
242 | return priv->ops->set_mode(priv, TUSB320_MODE_UFP); |
243 | else if (type == TYPEC_PORT_DRP) |
244 | return priv->ops->set_mode(priv, TUSB320_MODE_DRP); |
245 | else |
246 | return priv->ops->set_mode(priv, TUSB320_MODE_PORT); |
247 | } |
248 | |
249 | static const struct typec_operations tusb320_typec_ops = { |
250 | .port_type_set = tusb320_port_type_set, |
251 | }; |
252 | |
253 | static void tusb320_extcon_irq_handler(struct tusb320_priv *priv, u8 reg) |
254 | { |
255 | int state, polarity; |
256 | |
257 | state = FIELD_GET(TUSB320_REG9_ATTACHED_STATE, reg); |
258 | polarity = !!(reg & TUSB320_REG9_CABLE_DIRECTION); |
259 | |
260 | dev_dbg(priv->dev, "attached state: %s, polarity: %d\n" , |
261 | tusb_attached_states[state], polarity); |
262 | |
263 | extcon_set_state(edev: priv->edev, EXTCON_USB, |
264 | state: state == TUSB320_ATTACHED_STATE_UFP); |
265 | extcon_set_state(edev: priv->edev, EXTCON_USB_HOST, |
266 | state: state == TUSB320_ATTACHED_STATE_DFP); |
267 | extcon_set_property(edev: priv->edev, EXTCON_USB, |
268 | EXTCON_PROP_USB_TYPEC_POLARITY, |
269 | prop_val: (union extcon_property_value)polarity); |
270 | extcon_set_property(edev: priv->edev, EXTCON_USB_HOST, |
271 | EXTCON_PROP_USB_TYPEC_POLARITY, |
272 | prop_val: (union extcon_property_value)polarity); |
273 | extcon_sync(edev: priv->edev, EXTCON_USB); |
274 | extcon_sync(edev: priv->edev, EXTCON_USB_HOST); |
275 | |
276 | priv->state = state; |
277 | } |
278 | |
279 | static void tusb320_typec_irq_handler(struct tusb320_priv *priv, u8 reg9) |
280 | { |
281 | struct typec_port *port = priv->port; |
282 | struct device *dev = priv->dev; |
283 | int typec_mode; |
284 | enum usb_role usb_role; |
285 | enum typec_role pwr_role; |
286 | enum typec_data_role data_role; |
287 | u8 state, mode, accessory; |
288 | int ret, reg8; |
289 | bool ori; |
290 | |
291 | ret = regmap_read(map: priv->regmap, TUSB320_REG8, val: ®8); |
292 | if (ret) { |
293 | dev_err(dev, "error during reg8 i2c read, ret=%d!\n" , ret); |
294 | return; |
295 | } |
296 | |
297 | ori = reg9 & TUSB320_REG9_CABLE_DIRECTION; |
298 | typec_set_orientation(port, orientation: ori ? TYPEC_ORIENTATION_REVERSE : |
299 | TYPEC_ORIENTATION_NORMAL); |
300 | |
301 | state = FIELD_GET(TUSB320_REG9_ATTACHED_STATE, reg9); |
302 | accessory = FIELD_GET(TUSB320_REG8_ACCESSORY_CONNECTED, reg8); |
303 | |
304 | switch (state) { |
305 | case TUSB320_ATTACHED_STATE_DFP: |
306 | typec_mode = TYPEC_MODE_USB2; |
307 | usb_role = USB_ROLE_HOST; |
308 | pwr_role = TYPEC_SOURCE; |
309 | data_role = TYPEC_HOST; |
310 | break; |
311 | case TUSB320_ATTACHED_STATE_UFP: |
312 | typec_mode = TYPEC_MODE_USB2; |
313 | usb_role = USB_ROLE_DEVICE; |
314 | pwr_role = TYPEC_SINK; |
315 | data_role = TYPEC_DEVICE; |
316 | break; |
317 | case TUSB320_ATTACHED_STATE_ACC: |
318 | /* |
319 | * Accessory detected. For debug accessories, just make some |
320 | * qualified guesses as to the role for lack of a better option. |
321 | */ |
322 | if (accessory == TUSB320_REG8_ACCESSORY_CONNECTED_AUDIO || |
323 | accessory == TUSB320_REG8_ACCESSORY_CONNECTED_ACHRG) { |
324 | typec_mode = TYPEC_MODE_AUDIO; |
325 | usb_role = USB_ROLE_NONE; |
326 | pwr_role = TYPEC_SINK; |
327 | data_role = TYPEC_DEVICE; |
328 | break; |
329 | } else if (accessory == |
330 | TUSB320_REG8_ACCESSORY_CONNECTED_DBGDFP) { |
331 | typec_mode = TYPEC_MODE_DEBUG; |
332 | pwr_role = TYPEC_SOURCE; |
333 | usb_role = USB_ROLE_HOST; |
334 | data_role = TYPEC_HOST; |
335 | break; |
336 | } else if (accessory == |
337 | TUSB320_REG8_ACCESSORY_CONNECTED_DBGUFP) { |
338 | typec_mode = TYPEC_MODE_DEBUG; |
339 | pwr_role = TYPEC_SINK; |
340 | usb_role = USB_ROLE_DEVICE; |
341 | data_role = TYPEC_DEVICE; |
342 | break; |
343 | } |
344 | |
345 | dev_warn(priv->dev, "unexpected ACCESSORY_CONNECTED state %d\n" , |
346 | accessory); |
347 | |
348 | fallthrough; |
349 | default: |
350 | typec_mode = TYPEC_MODE_USB2; |
351 | usb_role = USB_ROLE_NONE; |
352 | pwr_role = TYPEC_SINK; |
353 | data_role = TYPEC_DEVICE; |
354 | break; |
355 | } |
356 | |
357 | typec_set_vconn_role(port, role: pwr_role); |
358 | typec_set_pwr_role(port, role: pwr_role); |
359 | typec_set_data_role(port, role: data_role); |
360 | typec_set_mode(port, mode: typec_mode); |
361 | usb_role_switch_set_role(sw: priv->role_sw, role: usb_role); |
362 | |
363 | mode = FIELD_GET(TUSB320_REG8_CURRENT_MODE_DETECT, reg8); |
364 | if (mode == TUSB320_REG8_CURRENT_MODE_DETECT_DEF) |
365 | typec_set_pwr_opmode(port, mode: TYPEC_PWR_MODE_USB); |
366 | else if (mode == TUSB320_REG8_CURRENT_MODE_DETECT_MED) |
367 | typec_set_pwr_opmode(port, mode: TYPEC_PWR_MODE_1_5A); |
368 | else if (mode == TUSB320_REG8_CURRENT_MODE_DETECT_HI) |
369 | typec_set_pwr_opmode(port, mode: TYPEC_PWR_MODE_3_0A); |
370 | else /* Charge through accessory */ |
371 | typec_set_pwr_opmode(port, mode: TYPEC_PWR_MODE_USB); |
372 | } |
373 | |
374 | static irqreturn_t tusb320_state_update_handler(struct tusb320_priv *priv, |
375 | bool force_update) |
376 | { |
377 | unsigned int reg; |
378 | |
379 | if (regmap_read(map: priv->regmap, TUSB320_REG9, val: ®)) { |
380 | dev_err(priv->dev, "error during i2c read!\n" ); |
381 | return IRQ_NONE; |
382 | } |
383 | |
384 | if (!force_update && !(reg & TUSB320_REG9_INTERRUPT_STATUS)) |
385 | return IRQ_NONE; |
386 | |
387 | tusb320_extcon_irq_handler(priv, reg); |
388 | |
389 | /* |
390 | * Type-C support is optional. Only call the Type-C handler if a |
391 | * port had been registered previously. |
392 | */ |
393 | if (priv->port) |
394 | tusb320_typec_irq_handler(priv, reg9: reg); |
395 | |
396 | regmap_write(map: priv->regmap, TUSB320_REG9, val: reg); |
397 | |
398 | return IRQ_HANDLED; |
399 | } |
400 | |
401 | static irqreturn_t tusb320_irq_handler(int irq, void *dev_id) |
402 | { |
403 | struct tusb320_priv *priv = dev_id; |
404 | |
405 | return tusb320_state_update_handler(priv, force_update: false); |
406 | } |
407 | |
408 | static const struct regmap_config tusb320_regmap_config = { |
409 | .reg_bits = 8, |
410 | .val_bits = 8, |
411 | }; |
412 | |
413 | static int tusb320_extcon_probe(struct tusb320_priv *priv) |
414 | { |
415 | int ret; |
416 | |
417 | priv->edev = devm_extcon_dev_allocate(dev: priv->dev, cable: tusb320_extcon_cable); |
418 | if (IS_ERR(ptr: priv->edev)) { |
419 | dev_err(priv->dev, "failed to allocate extcon device\n" ); |
420 | return PTR_ERR(ptr: priv->edev); |
421 | } |
422 | |
423 | ret = devm_extcon_dev_register(dev: priv->dev, edev: priv->edev); |
424 | if (ret < 0) { |
425 | dev_err(priv->dev, "failed to register extcon device\n" ); |
426 | return ret; |
427 | } |
428 | |
429 | extcon_set_property_capability(edev: priv->edev, EXTCON_USB, |
430 | EXTCON_PROP_USB_TYPEC_POLARITY); |
431 | extcon_set_property_capability(edev: priv->edev, EXTCON_USB_HOST, |
432 | EXTCON_PROP_USB_TYPEC_POLARITY); |
433 | |
434 | return 0; |
435 | } |
436 | |
437 | static int tusb320_typec_probe(struct i2c_client *client, |
438 | struct tusb320_priv *priv) |
439 | { |
440 | struct fwnode_handle *connector; |
441 | const char *cap_str; |
442 | int ret; |
443 | |
444 | /* The Type-C connector is optional, for backward compatibility. */ |
445 | connector = device_get_named_child_node(dev: &client->dev, childname: "connector" ); |
446 | if (!connector) |
447 | return 0; |
448 | |
449 | /* Type-C connector found. */ |
450 | ret = typec_get_fw_cap(cap: &priv->cap, fwnode: connector); |
451 | if (ret) |
452 | goto err_put; |
453 | |
454 | priv->port_type = priv->cap.type; |
455 | |
456 | /* This goes into register 0x8 field CURRENT_MODE_ADVERTISE */ |
457 | ret = fwnode_property_read_string(fwnode: connector, propname: "typec-power-opmode" , val: &cap_str); |
458 | if (ret) |
459 | goto err_put; |
460 | |
461 | ret = typec_find_pwr_opmode(name: cap_str); |
462 | if (ret < 0) |
463 | goto err_put; |
464 | |
465 | priv->pwr_opmode = ret; |
466 | |
467 | /* Initialize the hardware with the devicetree settings. */ |
468 | ret = tusb320_set_adv_pwr_mode(priv); |
469 | if (ret) |
470 | goto err_put; |
471 | |
472 | priv->cap.revision = USB_TYPEC_REV_1_1; |
473 | priv->cap.accessory[0] = TYPEC_ACCESSORY_AUDIO; |
474 | priv->cap.accessory[1] = TYPEC_ACCESSORY_DEBUG; |
475 | priv->cap.orientation_aware = true; |
476 | priv->cap.driver_data = priv; |
477 | priv->cap.ops = &tusb320_typec_ops; |
478 | priv->cap.fwnode = connector; |
479 | |
480 | priv->port = typec_register_port(parent: &client->dev, cap: &priv->cap); |
481 | if (IS_ERR(ptr: priv->port)) { |
482 | ret = PTR_ERR(ptr: priv->port); |
483 | goto err_put; |
484 | } |
485 | |
486 | /* Find any optional USB role switch that needs reporting to */ |
487 | priv->role_sw = fwnode_usb_role_switch_get(node: connector); |
488 | if (IS_ERR(ptr: priv->role_sw)) { |
489 | ret = PTR_ERR(ptr: priv->role_sw); |
490 | goto err_unreg; |
491 | } |
492 | |
493 | priv->connector_fwnode = connector; |
494 | |
495 | return 0; |
496 | |
497 | err_unreg: |
498 | typec_unregister_port(port: priv->port); |
499 | |
500 | err_put: |
501 | fwnode_handle_put(fwnode: connector); |
502 | |
503 | return ret; |
504 | } |
505 | |
506 | static void tusb320_typec_remove(struct tusb320_priv *priv) |
507 | { |
508 | usb_role_switch_put(sw: priv->role_sw); |
509 | typec_unregister_port(port: priv->port); |
510 | fwnode_handle_put(fwnode: priv->connector_fwnode); |
511 | } |
512 | |
513 | static int tusb320_probe(struct i2c_client *client) |
514 | { |
515 | struct tusb320_priv *priv; |
516 | const void *match_data; |
517 | unsigned int revision; |
518 | int ret; |
519 | u32 irq_trigger_type = IRQF_TRIGGER_FALLING; |
520 | struct irq_data *irq_d; |
521 | |
522 | priv = devm_kzalloc(dev: &client->dev, size: sizeof(*priv), GFP_KERNEL); |
523 | if (!priv) |
524 | return -ENOMEM; |
525 | |
526 | priv->dev = &client->dev; |
527 | i2c_set_clientdata(client, data: priv); |
528 | |
529 | priv->regmap = devm_regmap_init_i2c(client, &tusb320_regmap_config); |
530 | if (IS_ERR(ptr: priv->regmap)) |
531 | return PTR_ERR(ptr: priv->regmap); |
532 | |
533 | ret = tusb320_check_signature(priv); |
534 | if (ret) |
535 | return ret; |
536 | |
537 | match_data = device_get_match_data(dev: &client->dev); |
538 | if (!match_data) |
539 | return -EINVAL; |
540 | |
541 | priv->ops = (struct tusb320_ops*)match_data; |
542 | |
543 | if (priv->ops->get_revision) { |
544 | ret = priv->ops->get_revision(priv, &revision); |
545 | if (ret) |
546 | dev_warn(priv->dev, |
547 | "failed to read revision register: %d\n" , ret); |
548 | else |
549 | dev_info(priv->dev, "chip revision %d\n" , revision); |
550 | } |
551 | |
552 | ret = tusb320_extcon_probe(priv); |
553 | if (ret) |
554 | return ret; |
555 | |
556 | ret = tusb320_typec_probe(client, priv); |
557 | if (ret) |
558 | return ret; |
559 | |
560 | /* update initial state */ |
561 | tusb320_state_update_handler(priv, force_update: true); |
562 | |
563 | /* Reset chip to its default state */ |
564 | ret = tusb320_reset(priv); |
565 | if (ret) |
566 | dev_warn(priv->dev, "failed to reset chip: %d\n" , ret); |
567 | else |
568 | /* |
569 | * State and polarity might change after a reset, so update |
570 | * them again and make sure the interrupt status bit is cleared. |
571 | */ |
572 | tusb320_state_update_handler(priv, force_update: true); |
573 | |
574 | irq_d = irq_get_irq_data(irq: client->irq); |
575 | if (irq_d) |
576 | irq_trigger_type = irqd_get_trigger_type(d: irq_d); |
577 | |
578 | ret = devm_request_threaded_irq(dev: priv->dev, irq: client->irq, NULL, |
579 | thread_fn: tusb320_irq_handler, |
580 | IRQF_ONESHOT | irq_trigger_type, |
581 | devname: client->name, dev_id: priv); |
582 | if (ret) |
583 | tusb320_typec_remove(priv); |
584 | |
585 | return ret; |
586 | } |
587 | |
588 | static void tusb320_remove(struct i2c_client *client) |
589 | { |
590 | struct tusb320_priv *priv = i2c_get_clientdata(client); |
591 | |
592 | tusb320_typec_remove(priv); |
593 | } |
594 | |
595 | static const struct of_device_id tusb320_extcon_dt_match[] = { |
596 | { .compatible = "ti,tusb320" , .data = &tusb320_ops, }, |
597 | { .compatible = "ti,tusb320l" , .data = &tusb320l_ops, }, |
598 | { } |
599 | }; |
600 | MODULE_DEVICE_TABLE(of, tusb320_extcon_dt_match); |
601 | |
602 | static struct i2c_driver tusb320_extcon_driver = { |
603 | .probe = tusb320_probe, |
604 | .remove = tusb320_remove, |
605 | .driver = { |
606 | .name = "extcon-tusb320" , |
607 | .of_match_table = tusb320_extcon_dt_match, |
608 | }, |
609 | }; |
610 | |
611 | static int __init tusb320_init(void) |
612 | { |
613 | return i2c_add_driver(&tusb320_extcon_driver); |
614 | } |
615 | subsys_initcall(tusb320_init); |
616 | |
617 | static void __exit tusb320_exit(void) |
618 | { |
619 | i2c_del_driver(driver: &tusb320_extcon_driver); |
620 | } |
621 | module_exit(tusb320_exit); |
622 | |
623 | MODULE_AUTHOR("Michael Auchter <michael.auchter@ni.com>" ); |
624 | MODULE_DESCRIPTION("TI TUSB320 extcon driver" ); |
625 | MODULE_LICENSE("GPL v2" ); |
626 | |