1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * extcon-fsa9480.c - Fairchild Semiconductor FSA9480 extcon driver |
4 | * |
5 | * Copyright (c) 2019 Tomasz Figa <tomasz.figa@gmail.com> |
6 | * |
7 | * Loosely based on old fsa9480 misc-device driver. |
8 | */ |
9 | |
10 | #include <linux/kernel.h> |
11 | #include <linux/module.h> |
12 | #include <linux/types.h> |
13 | #include <linux/i2c.h> |
14 | #include <linux/slab.h> |
15 | #include <linux/bitops.h> |
16 | #include <linux/interrupt.h> |
17 | #include <linux/err.h> |
18 | #include <linux/platform_device.h> |
19 | #include <linux/kobject.h> |
20 | #include <linux/extcon-provider.h> |
21 | #include <linux/irqdomain.h> |
22 | #include <linux/regmap.h> |
23 | |
24 | /* FSA9480 I2C registers */ |
25 | #define FSA9480_REG_DEVID 0x01 |
26 | #define FSA9480_REG_CTRL 0x02 |
27 | #define FSA9480_REG_INT1 0x03 |
28 | #define FSA9480_REG_INT2 0x04 |
29 | #define FSA9480_REG_INT1_MASK 0x05 |
30 | #define FSA9480_REG_INT2_MASK 0x06 |
31 | #define FSA9480_REG_ADC 0x07 |
32 | #define FSA9480_REG_TIMING1 0x08 |
33 | #define FSA9480_REG_TIMING2 0x09 |
34 | #define FSA9480_REG_DEV_T1 0x0a |
35 | #define FSA9480_REG_DEV_T2 0x0b |
36 | #define FSA9480_REG_BTN1 0x0c |
37 | #define FSA9480_REG_BTN2 0x0d |
38 | #define FSA9480_REG_CK 0x0e |
39 | #define FSA9480_REG_CK_INT1 0x0f |
40 | #define FSA9480_REG_CK_INT2 0x10 |
41 | #define FSA9480_REG_CK_INTMASK1 0x11 |
42 | #define FSA9480_REG_CK_INTMASK2 0x12 |
43 | #define FSA9480_REG_MANSW1 0x13 |
44 | #define FSA9480_REG_MANSW2 0x14 |
45 | #define FSA9480_REG_END 0x15 |
46 | |
47 | /* Control */ |
48 | #define CON_SWITCH_OPEN (1 << 4) |
49 | #define CON_RAW_DATA (1 << 3) |
50 | #define CON_MANUAL_SW (1 << 2) |
51 | #define CON_WAIT (1 << 1) |
52 | #define CON_INT_MASK (1 << 0) |
53 | #define CON_MASK (CON_SWITCH_OPEN | CON_RAW_DATA | \ |
54 | CON_MANUAL_SW | CON_WAIT) |
55 | |
56 | /* Device Type 1 */ |
57 | #define DEV_USB_OTG 7 |
58 | #define DEV_DEDICATED_CHG 6 |
59 | #define DEV_USB_CHG 5 |
60 | #define DEV_CAR_KIT 4 |
61 | #define DEV_UART 3 |
62 | #define DEV_USB 2 |
63 | #define DEV_AUDIO_2 1 |
64 | #define DEV_AUDIO_1 0 |
65 | |
66 | #define DEV_T1_USB_MASK (DEV_USB_OTG | DEV_USB) |
67 | #define DEV_T1_UART_MASK (DEV_UART) |
68 | #define DEV_T1_CHARGER_MASK (DEV_DEDICATED_CHG | DEV_USB_CHG) |
69 | |
70 | /* Device Type 2 */ |
71 | #define DEV_AV 14 |
72 | #define DEV_TTY 13 |
73 | #define DEV_PPD 12 |
74 | #define DEV_JIG_UART_OFF 11 |
75 | #define DEV_JIG_UART_ON 10 |
76 | #define DEV_JIG_USB_OFF 9 |
77 | #define DEV_JIG_USB_ON 8 |
78 | |
79 | #define DEV_T2_USB_MASK (DEV_JIG_USB_OFF | DEV_JIG_USB_ON) |
80 | #define DEV_T2_UART_MASK (DEV_JIG_UART_OFF | DEV_JIG_UART_ON) |
81 | #define DEV_T2_JIG_MASK (DEV_JIG_USB_OFF | DEV_JIG_USB_ON | \ |
82 | DEV_JIG_UART_OFF | DEV_JIG_UART_ON) |
83 | |
84 | /* |
85 | * Manual Switch |
86 | * D- [7:5] / D+ [4:2] |
87 | * 000: Open all / 001: USB / 010: AUDIO / 011: UART / 100: V_AUDIO |
88 | */ |
89 | #define SW_VAUDIO ((4 << 5) | (4 << 2)) |
90 | #define SW_UART ((3 << 5) | (3 << 2)) |
91 | #define SW_AUDIO ((2 << 5) | (2 << 2)) |
92 | #define SW_DHOST ((1 << 5) | (1 << 2)) |
93 | #define SW_AUTO ((0 << 5) | (0 << 2)) |
94 | |
95 | /* Interrupt 1 */ |
96 | #define INT1_MASK (0xff << 0) |
97 | #define INT_DETACH (1 << 1) |
98 | #define INT_ATTACH (1 << 0) |
99 | |
100 | /* Interrupt 2 mask */ |
101 | #define INT2_MASK (0x1f << 0) |
102 | |
103 | /* Timing Set 1 */ |
104 | #define TIMING1_ADC_500MS (0x6 << 0) |
105 | |
106 | struct fsa9480_usbsw { |
107 | struct device *dev; |
108 | struct regmap *regmap; |
109 | struct extcon_dev *edev; |
110 | u16 cable; |
111 | }; |
112 | |
113 | static const unsigned int fsa9480_extcon_cable[] = { |
114 | EXTCON_USB_HOST, |
115 | EXTCON_USB, |
116 | EXTCON_CHG_USB_DCP, |
117 | EXTCON_CHG_USB_SDP, |
118 | EXTCON_CHG_USB_ACA, |
119 | EXTCON_JACK_LINE_OUT, |
120 | EXTCON_JACK_VIDEO_OUT, |
121 | EXTCON_JIG, |
122 | |
123 | EXTCON_NONE, |
124 | }; |
125 | |
126 | static const u64 cable_types[] = { |
127 | [DEV_USB_OTG] = BIT_ULL(EXTCON_USB_HOST), |
128 | [DEV_DEDICATED_CHG] = BIT_ULL(EXTCON_USB) | BIT_ULL(EXTCON_CHG_USB_DCP), |
129 | [DEV_USB_CHG] = BIT_ULL(EXTCON_USB) | BIT_ULL(EXTCON_CHG_USB_SDP), |
130 | [DEV_CAR_KIT] = BIT_ULL(EXTCON_USB) | BIT_ULL(EXTCON_CHG_USB_SDP) |
131 | | BIT_ULL(EXTCON_JACK_LINE_OUT), |
132 | [DEV_UART] = BIT_ULL(EXTCON_JIG), |
133 | [DEV_USB] = BIT_ULL(EXTCON_USB) | BIT_ULL(EXTCON_CHG_USB_SDP), |
134 | [DEV_AUDIO_2] = BIT_ULL(EXTCON_JACK_LINE_OUT), |
135 | [DEV_AUDIO_1] = BIT_ULL(EXTCON_JACK_LINE_OUT), |
136 | [DEV_AV] = BIT_ULL(EXTCON_JACK_LINE_OUT) |
137 | | BIT_ULL(EXTCON_JACK_VIDEO_OUT), |
138 | [DEV_TTY] = BIT_ULL(EXTCON_JIG), |
139 | [DEV_PPD] = BIT_ULL(EXTCON_JACK_LINE_OUT) | BIT_ULL(EXTCON_CHG_USB_ACA), |
140 | [DEV_JIG_UART_OFF] = BIT_ULL(EXTCON_JIG), |
141 | [DEV_JIG_UART_ON] = BIT_ULL(EXTCON_JIG), |
142 | [DEV_JIG_USB_OFF] = BIT_ULL(EXTCON_USB) | BIT_ULL(EXTCON_JIG), |
143 | [DEV_JIG_USB_ON] = BIT_ULL(EXTCON_USB) | BIT_ULL(EXTCON_JIG), |
144 | }; |
145 | |
146 | /* Define regmap configuration of FSA9480 for I2C communication */ |
147 | static bool fsa9480_volatile_reg(struct device *dev, unsigned int reg) |
148 | { |
149 | switch (reg) { |
150 | case FSA9480_REG_INT1_MASK: |
151 | return true; |
152 | default: |
153 | break; |
154 | } |
155 | return false; |
156 | } |
157 | |
158 | static const struct regmap_config fsa9480_regmap_config = { |
159 | .reg_bits = 8, |
160 | .val_bits = 8, |
161 | .volatile_reg = fsa9480_volatile_reg, |
162 | .max_register = FSA9480_REG_END, |
163 | }; |
164 | |
165 | static int fsa9480_write_reg(struct fsa9480_usbsw *usbsw, int reg, int value) |
166 | { |
167 | int ret; |
168 | |
169 | ret = regmap_write(map: usbsw->regmap, reg, val: value); |
170 | if (ret < 0) |
171 | dev_err(usbsw->dev, "%s: err %d\n" , __func__, ret); |
172 | |
173 | return ret; |
174 | } |
175 | |
176 | static int fsa9480_read_reg(struct fsa9480_usbsw *usbsw, int reg) |
177 | { |
178 | int ret, val; |
179 | |
180 | ret = regmap_read(map: usbsw->regmap, reg, val: &val); |
181 | if (ret < 0) { |
182 | dev_err(usbsw->dev, "%s: err %d\n" , __func__, ret); |
183 | return ret; |
184 | } |
185 | |
186 | return val; |
187 | } |
188 | |
189 | static int fsa9480_read_irq(struct fsa9480_usbsw *usbsw, int *value) |
190 | { |
191 | u8 regs[2]; |
192 | int ret; |
193 | |
194 | ret = regmap_bulk_read(map: usbsw->regmap, FSA9480_REG_INT1, val: regs, val_count: 2); |
195 | if (ret < 0) |
196 | dev_err(usbsw->dev, "%s: err %d\n" , __func__, ret); |
197 | |
198 | *value = regs[1] << 8 | regs[0]; |
199 | return ret; |
200 | } |
201 | |
202 | static void fsa9480_handle_change(struct fsa9480_usbsw *usbsw, |
203 | u16 mask, bool attached) |
204 | { |
205 | while (mask) { |
206 | int dev = fls64(x: mask) - 1; |
207 | u64 cables = cable_types[dev]; |
208 | |
209 | while (cables) { |
210 | int cable = fls64(x: cables) - 1; |
211 | |
212 | extcon_set_state_sync(edev: usbsw->edev, id: cable, state: attached); |
213 | cables &= ~BIT_ULL(cable); |
214 | } |
215 | |
216 | mask &= ~BIT_ULL(dev); |
217 | } |
218 | } |
219 | |
220 | static void fsa9480_detect_dev(struct fsa9480_usbsw *usbsw) |
221 | { |
222 | int val1, val2; |
223 | u16 val; |
224 | |
225 | val1 = fsa9480_read_reg(usbsw, FSA9480_REG_DEV_T1); |
226 | val2 = fsa9480_read_reg(usbsw, FSA9480_REG_DEV_T2); |
227 | if (val1 < 0 || val2 < 0) { |
228 | dev_err(usbsw->dev, "%s: failed to read registers" , __func__); |
229 | return; |
230 | } |
231 | val = val2 << 8 | val1; |
232 | |
233 | dev_info(usbsw->dev, "dev1: 0x%x, dev2: 0x%x\n" , val1, val2); |
234 | |
235 | /* handle detached cables first */ |
236 | fsa9480_handle_change(usbsw, mask: usbsw->cable & ~val, attached: false); |
237 | |
238 | /* then handle attached ones */ |
239 | fsa9480_handle_change(usbsw, mask: val & ~usbsw->cable, attached: true); |
240 | |
241 | usbsw->cable = val; |
242 | } |
243 | |
244 | static irqreturn_t fsa9480_irq_handler(int irq, void *data) |
245 | { |
246 | struct fsa9480_usbsw *usbsw = data; |
247 | int intr = 0; |
248 | |
249 | /* clear interrupt */ |
250 | fsa9480_read_irq(usbsw, value: &intr); |
251 | if (!intr) |
252 | return IRQ_NONE; |
253 | |
254 | /* device detection */ |
255 | fsa9480_detect_dev(usbsw); |
256 | |
257 | return IRQ_HANDLED; |
258 | } |
259 | |
260 | static int fsa9480_probe(struct i2c_client *client) |
261 | { |
262 | struct fsa9480_usbsw *info; |
263 | int ret; |
264 | |
265 | if (!client->irq) { |
266 | dev_err(&client->dev, "no interrupt provided\n" ); |
267 | return -EINVAL; |
268 | } |
269 | |
270 | info = devm_kzalloc(dev: &client->dev, size: sizeof(*info), GFP_KERNEL); |
271 | if (!info) |
272 | return -ENOMEM; |
273 | info->dev = &client->dev; |
274 | |
275 | i2c_set_clientdata(client, data: info); |
276 | |
277 | /* External connector */ |
278 | info->edev = devm_extcon_dev_allocate(dev: info->dev, |
279 | cable: fsa9480_extcon_cable); |
280 | if (IS_ERR(ptr: info->edev)) { |
281 | dev_err(info->dev, "failed to allocate memory for extcon\n" ); |
282 | ret = -ENOMEM; |
283 | return ret; |
284 | } |
285 | |
286 | ret = devm_extcon_dev_register(dev: info->dev, edev: info->edev); |
287 | if (ret) { |
288 | dev_err(info->dev, "failed to register extcon device\n" ); |
289 | return ret; |
290 | } |
291 | |
292 | info->regmap = devm_regmap_init_i2c(client, &fsa9480_regmap_config); |
293 | if (IS_ERR(ptr: info->regmap)) { |
294 | ret = PTR_ERR(ptr: info->regmap); |
295 | dev_err(info->dev, "failed to allocate register map: %d\n" , |
296 | ret); |
297 | return ret; |
298 | } |
299 | |
300 | /* ADC Detect Time: 500ms */ |
301 | fsa9480_write_reg(usbsw: info, FSA9480_REG_TIMING1, TIMING1_ADC_500MS); |
302 | |
303 | /* configure automatic switching */ |
304 | fsa9480_write_reg(usbsw: info, FSA9480_REG_CTRL, CON_MASK); |
305 | |
306 | /* unmask interrupt (attach/detach only) */ |
307 | fsa9480_write_reg(usbsw: info, FSA9480_REG_INT1_MASK, |
308 | INT1_MASK & ~(INT_ATTACH | INT_DETACH)); |
309 | fsa9480_write_reg(usbsw: info, FSA9480_REG_INT2_MASK, INT2_MASK); |
310 | |
311 | ret = devm_request_threaded_irq(dev: info->dev, irq: client->irq, NULL, |
312 | thread_fn: fsa9480_irq_handler, |
313 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, |
314 | devname: "fsa9480" , dev_id: info); |
315 | if (ret) { |
316 | dev_err(info->dev, "failed to request IRQ\n" ); |
317 | return ret; |
318 | } |
319 | |
320 | device_init_wakeup(dev: info->dev, enable: true); |
321 | fsa9480_detect_dev(usbsw: info); |
322 | |
323 | return 0; |
324 | } |
325 | |
326 | #ifdef CONFIG_PM_SLEEP |
327 | static int fsa9480_suspend(struct device *dev) |
328 | { |
329 | struct i2c_client *client = to_i2c_client(dev); |
330 | |
331 | if (device_may_wakeup(dev: &client->dev) && client->irq) |
332 | enable_irq_wake(irq: client->irq); |
333 | |
334 | return 0; |
335 | } |
336 | |
337 | static int fsa9480_resume(struct device *dev) |
338 | { |
339 | struct i2c_client *client = to_i2c_client(dev); |
340 | |
341 | if (device_may_wakeup(dev: &client->dev) && client->irq) |
342 | disable_irq_wake(irq: client->irq); |
343 | |
344 | return 0; |
345 | } |
346 | #endif |
347 | |
348 | static const struct dev_pm_ops fsa9480_pm_ops = { |
349 | SET_SYSTEM_SLEEP_PM_OPS(fsa9480_suspend, fsa9480_resume) |
350 | }; |
351 | |
352 | static const struct i2c_device_id fsa9480_id[] = { |
353 | { "fsa9480" , 0 }, |
354 | {} |
355 | }; |
356 | MODULE_DEVICE_TABLE(i2c, fsa9480_id); |
357 | |
358 | static const struct of_device_id fsa9480_of_match[] = { |
359 | { .compatible = "fcs,fsa9480" , }, |
360 | { .compatible = "fcs,fsa880" , }, |
361 | { .compatible = "ti,tsu6111" , }, |
362 | { }, |
363 | }; |
364 | MODULE_DEVICE_TABLE(of, fsa9480_of_match); |
365 | |
366 | static struct i2c_driver fsa9480_i2c_driver = { |
367 | .driver = { |
368 | .name = "fsa9480" , |
369 | .pm = &fsa9480_pm_ops, |
370 | .of_match_table = fsa9480_of_match, |
371 | }, |
372 | .probe = fsa9480_probe, |
373 | .id_table = fsa9480_id, |
374 | }; |
375 | |
376 | static int __init fsa9480_module_init(void) |
377 | { |
378 | return i2c_add_driver(&fsa9480_i2c_driver); |
379 | } |
380 | subsys_initcall(fsa9480_module_init); |
381 | |
382 | static void __exit fsa9480_module_exit(void) |
383 | { |
384 | i2c_del_driver(driver: &fsa9480_i2c_driver); |
385 | } |
386 | module_exit(fsa9480_module_exit); |
387 | |
388 | MODULE_DESCRIPTION("Fairchild Semiconductor FSA9480 extcon driver" ); |
389 | MODULE_AUTHOR("Tomasz Figa <tomasz.figa@gmail.com>" ); |
390 | MODULE_LICENSE("GPL" ); |
391 | |