blob: 17d6bd76edb42c7e14d6e07077fdf42980f26b19 [file] [log] [blame]
Jaewon Kim27a28d32015-02-04 13:56:07 +09001/*
2 * extcon-max77843.c - Maxim MAX77843 extcon driver to support
3 * MUIC(Micro USB Interface Controller)
4 *
5 * Copyright (C) 2015 Samsung Electronics
6 * Author: Jaewon Kim <jaewon02.kim@samsung.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
Chanwoo Choi176aa362017-09-21 12:11:24 +090014#include <linux/extcon-provider.h>
Jaewon Kim27a28d32015-02-04 13:56:07 +090015#include <linux/i2c.h>
16#include <linux/interrupt.h>
17#include <linux/kernel.h>
Krzysztof Kozlowskibc1aadc2015-07-15 21:59:51 +090018#include <linux/mfd/max77693-common.h>
Jaewon Kim27a28d32015-02-04 13:56:07 +090019#include <linux/mfd/max77843-private.h>
20#include <linux/module.h>
21#include <linux/platform_device.h>
22#include <linux/workqueue.h>
23
24#define DELAY_MS_DEFAULT 15000 /* unit: millisecond */
25
26enum max77843_muic_status {
27 MAX77843_MUIC_STATUS1 = 0,
28 MAX77843_MUIC_STATUS2,
29 MAX77843_MUIC_STATUS3,
30
31 MAX77843_MUIC_STATUS_NUM,
32};
33
34struct max77843_muic_info {
35 struct device *dev;
Krzysztof Kozlowskibc1aadc2015-07-15 21:59:51 +090036 struct max77693_dev *max77843;
Jaewon Kim27a28d32015-02-04 13:56:07 +090037 struct extcon_dev *edev;
38
39 struct mutex mutex;
40 struct work_struct irq_work;
41 struct delayed_work wq_detcable;
42
43 u8 status[MAX77843_MUIC_STATUS_NUM];
44 int prev_cable_type;
45 int prev_chg_type;
46 int prev_gnd_type;
47
48 bool irq_adc;
49 bool irq_chg;
50};
51
52enum max77843_muic_cable_group {
53 MAX77843_CABLE_GROUP_ADC = 0,
54 MAX77843_CABLE_GROUP_ADC_GND,
55 MAX77843_CABLE_GROUP_CHG,
56};
57
58enum max77843_muic_adc_debounce_time {
59 MAX77843_DEBOUNCE_TIME_5MS = 0,
60 MAX77843_DEBOUNCE_TIME_10MS,
61 MAX77843_DEBOUNCE_TIME_25MS,
62 MAX77843_DEBOUNCE_TIME_38_62MS,
63};
64
65/* Define accessory cable type */
66enum max77843_muic_accessory_type {
67 MAX77843_MUIC_ADC_GROUND = 0,
68 MAX77843_MUIC_ADC_SEND_END_BUTTON,
69 MAX77843_MUIC_ADC_REMOTE_S1_BUTTON,
70 MAX77843_MUIC_ADC_REMOTE_S2_BUTTON,
71 MAX77843_MUIC_ADC_REMOTE_S3_BUTTON,
72 MAX77843_MUIC_ADC_REMOTE_S4_BUTTON,
73 MAX77843_MUIC_ADC_REMOTE_S5_BUTTON,
74 MAX77843_MUIC_ADC_REMOTE_S6_BUTTON,
75 MAX77843_MUIC_ADC_REMOTE_S7_BUTTON,
76 MAX77843_MUIC_ADC_REMOTE_S8_BUTTON,
77 MAX77843_MUIC_ADC_REMOTE_S9_BUTTON,
78 MAX77843_MUIC_ADC_REMOTE_S10_BUTTON,
79 MAX77843_MUIC_ADC_REMOTE_S11_BUTTON,
80 MAX77843_MUIC_ADC_REMOTE_S12_BUTTON,
81 MAX77843_MUIC_ADC_RESERVED_ACC_1,
82 MAX77843_MUIC_ADC_RESERVED_ACC_2,
83 MAX77843_MUIC_ADC_RESERVED_ACC_3,
84 MAX77843_MUIC_ADC_RESERVED_ACC_4,
85 MAX77843_MUIC_ADC_RESERVED_ACC_5,
86 MAX77843_MUIC_ADC_AUDIO_DEVICE_TYPE2,
87 MAX77843_MUIC_ADC_PHONE_POWERED_DEV,
88 MAX77843_MUIC_ADC_TTY_CONVERTER,
89 MAX77843_MUIC_ADC_UART_CABLE,
90 MAX77843_MUIC_ADC_CEA936A_TYPE1_CHG,
91 MAX77843_MUIC_ADC_FACTORY_MODE_USB_OFF,
92 MAX77843_MUIC_ADC_FACTORY_MODE_USB_ON,
93 MAX77843_MUIC_ADC_AV_CABLE_NOLOAD,
94 MAX77843_MUIC_ADC_CEA936A_TYPE2_CHG,
95 MAX77843_MUIC_ADC_FACTORY_MODE_UART_OFF,
96 MAX77843_MUIC_ADC_FACTORY_MODE_UART_ON,
97 MAX77843_MUIC_ADC_AUDIO_DEVICE_TYPE1,
98 MAX77843_MUIC_ADC_OPEN,
99
Srikant Ritoliaaf57fa42016-12-07 17:29:39 +0530100 /*
101 * The below accessories should check
102 * not only ADC value but also ADC1K and VBVolt value.
103 */
Jaewon Kim27a28d32015-02-04 13:56:07 +0900104 /* Offset|ADC1K|VBVolt| */
105 MAX77843_MUIC_GND_USB_HOST = 0x100, /* 0x1| 0| 0| */
106 MAX77843_MUIC_GND_USB_HOST_VB = 0x101, /* 0x1| 0| 1| */
107 MAX77843_MUIC_GND_MHL = 0x102, /* 0x1| 1| 0| */
108 MAX77843_MUIC_GND_MHL_VB = 0x103, /* 0x1| 1| 1| */
109};
110
111/* Define charger cable type */
112enum max77843_muic_charger_type {
113 MAX77843_MUIC_CHG_NONE = 0,
114 MAX77843_MUIC_CHG_USB,
115 MAX77843_MUIC_CHG_DOWNSTREAM,
116 MAX77843_MUIC_CHG_DEDICATED,
117 MAX77843_MUIC_CHG_SPECIAL_500MA,
118 MAX77843_MUIC_CHG_SPECIAL_1A,
119 MAX77843_MUIC_CHG_SPECIAL_BIAS,
120 MAX77843_MUIC_CHG_RESERVED,
121 MAX77843_MUIC_CHG_GND,
122};
123
Chanwoo Choi73b6ecd2015-06-12 11:10:06 +0900124static const unsigned int max77843_extcon_cable[] = {
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900125 EXTCON_USB,
126 EXTCON_USB_HOST,
Chanwoo Choi8b45b6a2015-11-09 10:10:15 +0900127 EXTCON_CHG_USB_SDP,
Chanwoo Choi11eecf92015-10-03 14:15:13 +0900128 EXTCON_CHG_USB_DCP,
129 EXTCON_CHG_USB_CDP,
130 EXTCON_CHG_USB_FAST,
131 EXTCON_CHG_USB_SLOW,
132 EXTCON_DISP_MHL,
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900133 EXTCON_JIG,
134 EXTCON_NONE,
Jaewon Kim27a28d32015-02-04 13:56:07 +0900135};
136
137struct max77843_muic_irq {
138 unsigned int irq;
139 const char *name;
140 unsigned int virq;
141};
142
143static struct max77843_muic_irq max77843_muic_irqs[] = {
144 { MAX77843_MUIC_IRQ_INT1_ADC, "MUIC-ADC" },
145 { MAX77843_MUIC_IRQ_INT1_ADCERROR, "MUIC-ADC_ERROR" },
146 { MAX77843_MUIC_IRQ_INT1_ADC1K, "MUIC-ADC1K" },
147 { MAX77843_MUIC_IRQ_INT2_CHGTYP, "MUIC-CHGTYP" },
148 { MAX77843_MUIC_IRQ_INT2_CHGDETRUN, "MUIC-CHGDETRUN" },
149 { MAX77843_MUIC_IRQ_INT2_DCDTMR, "MUIC-DCDTMR" },
150 { MAX77843_MUIC_IRQ_INT2_DXOVP, "MUIC-DXOVP" },
151 { MAX77843_MUIC_IRQ_INT2_VBVOLT, "MUIC-VBVOLT" },
152 { MAX77843_MUIC_IRQ_INT3_VBADC, "MUIC-VBADC" },
153 { MAX77843_MUIC_IRQ_INT3_VDNMON, "MUIC-VDNMON" },
154 { MAX77843_MUIC_IRQ_INT3_DNRES, "MUIC-DNRES" },
155 { MAX77843_MUIC_IRQ_INT3_MPNACK, "MUIC-MPNACK"},
156 { MAX77843_MUIC_IRQ_INT3_MRXBUFOW, "MUIC-MRXBUFOW"},
157 { MAX77843_MUIC_IRQ_INT3_MRXTRF, "MUIC-MRXTRF"},
158 { MAX77843_MUIC_IRQ_INT3_MRXPERR, "MUIC-MRXPERR"},
159 { MAX77843_MUIC_IRQ_INT3_MRXRDY, "MUIC-MRXRDY"},
160};
161
162static const struct regmap_config max77843_muic_regmap_config = {
163 .reg_bits = 8,
164 .val_bits = 8,
165 .max_register = MAX77843_MUIC_REG_END,
166};
167
168static const struct regmap_irq max77843_muic_irq[] = {
169 /* INT1 interrupt */
170 { .reg_offset = 0, .mask = MAX77843_MUIC_ADC, },
171 { .reg_offset = 0, .mask = MAX77843_MUIC_ADCERROR, },
172 { .reg_offset = 0, .mask = MAX77843_MUIC_ADC1K, },
173
174 /* INT2 interrupt */
175 { .reg_offset = 1, .mask = MAX77843_MUIC_CHGTYP, },
176 { .reg_offset = 1, .mask = MAX77843_MUIC_CHGDETRUN, },
177 { .reg_offset = 1, .mask = MAX77843_MUIC_DCDTMR, },
178 { .reg_offset = 1, .mask = MAX77843_MUIC_DXOVP, },
179 { .reg_offset = 1, .mask = MAX77843_MUIC_VBVOLT, },
180
181 /* INT3 interrupt */
182 { .reg_offset = 2, .mask = MAX77843_MUIC_VBADC, },
183 { .reg_offset = 2, .mask = MAX77843_MUIC_VDNMON, },
184 { .reg_offset = 2, .mask = MAX77843_MUIC_DNRES, },
185 { .reg_offset = 2, .mask = MAX77843_MUIC_MPNACK, },
186 { .reg_offset = 2, .mask = MAX77843_MUIC_MRXBUFOW, },
187 { .reg_offset = 2, .mask = MAX77843_MUIC_MRXTRF, },
188 { .reg_offset = 2, .mask = MAX77843_MUIC_MRXPERR, },
189 { .reg_offset = 2, .mask = MAX77843_MUIC_MRXRDY, },
190};
191
192static const struct regmap_irq_chip max77843_muic_irq_chip = {
193 .name = "max77843-muic",
194 .status_base = MAX77843_MUIC_REG_INT1,
195 .mask_base = MAX77843_MUIC_REG_INTMASK1,
196 .mask_invert = true,
197 .num_regs = 3,
198 .irqs = max77843_muic_irq,
199 .num_irqs = ARRAY_SIZE(max77843_muic_irq),
200};
201
202static int max77843_muic_set_path(struct max77843_muic_info *info,
203 u8 val, bool attached)
204{
Krzysztof Kozlowskibc1aadc2015-07-15 21:59:51 +0900205 struct max77693_dev *max77843 = info->max77843;
Jaewon Kim27a28d32015-02-04 13:56:07 +0900206 int ret = 0;
207 unsigned int ctrl1, ctrl2;
208
209 if (attached)
210 ctrl1 = val;
211 else
Krzysztof Kozlowski309a3e02015-07-15 21:59:53 +0900212 ctrl1 = MAX77843_MUIC_CONTROL1_SW_OPEN;
Jaewon Kim27a28d32015-02-04 13:56:07 +0900213
214 ret = regmap_update_bits(max77843->regmap_muic,
215 MAX77843_MUIC_REG_CONTROL1,
Krzysztof Kozlowski309a3e02015-07-15 21:59:53 +0900216 MAX77843_MUIC_CONTROL1_COM_SW, ctrl1);
Jaewon Kim27a28d32015-02-04 13:56:07 +0900217 if (ret < 0) {
218 dev_err(info->dev, "Cannot switch MUIC port\n");
219 return ret;
220 }
221
222 if (attached)
223 ctrl2 = MAX77843_MUIC_CONTROL2_CPEN_MASK;
224 else
225 ctrl2 = MAX77843_MUIC_CONTROL2_LOWPWR_MASK;
226
227 ret = regmap_update_bits(max77843->regmap_muic,
228 MAX77843_MUIC_REG_CONTROL2,
229 MAX77843_MUIC_CONTROL2_LOWPWR_MASK |
230 MAX77843_MUIC_CONTROL2_CPEN_MASK, ctrl2);
231 if (ret < 0) {
232 dev_err(info->dev, "Cannot update lowpower mode\n");
233 return ret;
234 }
235
236 dev_dbg(info->dev,
237 "CONTROL1 : 0x%02x, CONTROL2 : 0x%02x, state : %s\n",
238 ctrl1, ctrl2, attached ? "attached" : "detached");
239
240 return 0;
241}
242
Marek Szyprowski7b9651102017-10-18 11:56:21 +0200243static void max77843_charger_set_otg_vbus(struct max77843_muic_info *info,
244 bool on)
245{
246 struct max77693_dev *max77843 = info->max77843;
247 unsigned int cnfg00;
248
249 if (on)
250 cnfg00 = MAX77843_CHG_OTG_MASK | MAX77843_CHG_BOOST_MASK;
251 else
252 cnfg00 = MAX77843_CHG_ENABLE | MAX77843_CHG_BUCK_MASK;
253
254 regmap_update_bits(max77843->regmap_chg, MAX77843_CHG_REG_CHG_CNFG_00,
255 MAX77843_CHG_MODE_MASK, cnfg00);
256}
257
Jaewon Kim27a28d32015-02-04 13:56:07 +0900258static int max77843_muic_get_cable_type(struct max77843_muic_info *info,
259 enum max77843_muic_cable_group group, bool *attached)
260{
261 int adc, chg_type, cable_type, gnd_type;
262
263 adc = info->status[MAX77843_MUIC_STATUS1] &
264 MAX77843_MUIC_STATUS1_ADC_MASK;
Krzysztof Kozlowski309a3e02015-07-15 21:59:53 +0900265 adc >>= MAX77843_MUIC_STATUS1_ADC_SHIFT;
Jaewon Kim27a28d32015-02-04 13:56:07 +0900266
267 switch (group) {
268 case MAX77843_CABLE_GROUP_ADC:
269 if (adc == MAX77843_MUIC_ADC_OPEN) {
270 *attached = false;
271 cable_type = info->prev_cable_type;
272 info->prev_cable_type = MAX77843_MUIC_ADC_OPEN;
273 } else {
274 *attached = true;
275 cable_type = info->prev_cable_type = adc;
276 }
277 break;
278 case MAX77843_CABLE_GROUP_CHG:
279 chg_type = info->status[MAX77843_MUIC_STATUS2] &
280 MAX77843_MUIC_STATUS2_CHGTYP_MASK;
281
282 /* Check GROUND accessory with charger cable */
283 if (adc == MAX77843_MUIC_ADC_GROUND) {
284 if (chg_type == MAX77843_MUIC_CHG_NONE) {
Srikant Ritoliaaf57fa42016-12-07 17:29:39 +0530285 /*
286 * The following state when charger cable is
Jaewon Kim27a28d32015-02-04 13:56:07 +0900287 * disconnected but the GROUND accessory still
Srikant Ritoliaaf57fa42016-12-07 17:29:39 +0530288 * connected.
289 */
Jaewon Kim27a28d32015-02-04 13:56:07 +0900290 *attached = false;
291 cable_type = info->prev_chg_type;
292 info->prev_chg_type = MAX77843_MUIC_CHG_NONE;
293 } else {
294
Srikant Ritoliaaf57fa42016-12-07 17:29:39 +0530295 /*
296 * The following state when charger cable is
297 * connected on the GROUND accessory.
298 */
Jaewon Kim27a28d32015-02-04 13:56:07 +0900299 *attached = true;
300 cable_type = MAX77843_MUIC_CHG_GND;
301 info->prev_chg_type = MAX77843_MUIC_CHG_GND;
302 }
303 break;
304 }
305
306 if (chg_type == MAX77843_MUIC_CHG_NONE) {
307 *attached = false;
308 cable_type = info->prev_chg_type;
309 info->prev_chg_type = MAX77843_MUIC_CHG_NONE;
310 } else {
311 *attached = true;
312 cable_type = info->prev_chg_type = chg_type;
313 }
314 break;
315 case MAX77843_CABLE_GROUP_ADC_GND:
316 if (adc == MAX77843_MUIC_ADC_OPEN) {
317 *attached = false;
318 cable_type = info->prev_gnd_type;
319 info->prev_gnd_type = MAX77843_MUIC_ADC_OPEN;
320 } else {
321 *attached = true;
322
Srikant Ritoliaaf57fa42016-12-07 17:29:39 +0530323 /*
324 * Offset|ADC1K|VBVolt|
Jaewon Kim27a28d32015-02-04 13:56:07 +0900325 * 0x1| 0| 0| USB-HOST
326 * 0x1| 0| 1| USB-HOST with VB
327 * 0x1| 1| 0| MHL
Srikant Ritoliaaf57fa42016-12-07 17:29:39 +0530328 * 0x1| 1| 1| MHL with VB
329 */
Jaewon Kim27a28d32015-02-04 13:56:07 +0900330 /* Get ADC1K register bit */
331 gnd_type = (info->status[MAX77843_MUIC_STATUS1] &
332 MAX77843_MUIC_STATUS1_ADC1K_MASK);
333
334 /* Get VBVolt register bit */
335 gnd_type |= (info->status[MAX77843_MUIC_STATUS2] &
336 MAX77843_MUIC_STATUS2_VBVOLT_MASK);
Krzysztof Kozlowski309a3e02015-07-15 21:59:53 +0900337 gnd_type >>= MAX77843_MUIC_STATUS2_VBVOLT_SHIFT;
Jaewon Kim27a28d32015-02-04 13:56:07 +0900338
339 /* Offset of GND cable */
340 gnd_type |= MAX77843_MUIC_GND_USB_HOST;
341 cable_type = info->prev_gnd_type = gnd_type;
342 }
343 break;
344 default:
345 dev_err(info->dev, "Unknown cable group (%d)\n", group);
346 cable_type = -EINVAL;
347 break;
348 }
349
350 return cable_type;
351}
352
353static int max77843_muic_adc_gnd_handler(struct max77843_muic_info *info)
354{
355 int ret, gnd_cable_type;
356 bool attached;
357
358 gnd_cable_type = max77843_muic_get_cable_type(info,
359 MAX77843_CABLE_GROUP_ADC_GND, &attached);
360 dev_dbg(info->dev, "external connector is %s (gnd:0x%02x)\n",
361 attached ? "attached" : "detached", gnd_cable_type);
362
363 switch (gnd_cable_type) {
364 case MAX77843_MUIC_GND_USB_HOST:
365 case MAX77843_MUIC_GND_USB_HOST_VB:
Krzysztof Kozlowski309a3e02015-07-15 21:59:53 +0900366 ret = max77843_muic_set_path(info,
367 MAX77843_MUIC_CONTROL1_SW_USB,
368 attached);
Jaewon Kim27a28d32015-02-04 13:56:07 +0900369 if (ret < 0)
370 return ret;
371
Chanwoo Choi8670b452016-08-16 15:55:34 +0900372 extcon_set_state_sync(info->edev, EXTCON_USB_HOST, attached);
Marek Szyprowski7b9651102017-10-18 11:56:21 +0200373 max77843_charger_set_otg_vbus(info, attached);
Jaewon Kim27a28d32015-02-04 13:56:07 +0900374 break;
375 case MAX77843_MUIC_GND_MHL_VB:
376 case MAX77843_MUIC_GND_MHL:
Krzysztof Kozlowski309a3e02015-07-15 21:59:53 +0900377 ret = max77843_muic_set_path(info,
378 MAX77843_MUIC_CONTROL1_SW_OPEN,
379 attached);
Jaewon Kim27a28d32015-02-04 13:56:07 +0900380 if (ret < 0)
381 return ret;
382
Chanwoo Choi8670b452016-08-16 15:55:34 +0900383 extcon_set_state_sync(info->edev, EXTCON_DISP_MHL, attached);
Jaewon Kim27a28d32015-02-04 13:56:07 +0900384 break;
385 default:
386 dev_err(info->dev, "failed to detect %s accessory(gnd:0x%x)\n",
387 attached ? "attached" : "detached", gnd_cable_type);
388 return -EINVAL;
389 }
390
391 return 0;
392}
393
394static int max77843_muic_jig_handler(struct max77843_muic_info *info,
395 int cable_type, bool attached)
396{
397 int ret;
Krzysztof Kozlowski309a3e02015-07-15 21:59:53 +0900398 u8 path = MAX77843_MUIC_CONTROL1_SW_OPEN;
Jaewon Kim27a28d32015-02-04 13:56:07 +0900399
400 dev_dbg(info->dev, "external connector is %s (adc:0x%02x)\n",
401 attached ? "attached" : "detached", cable_type);
402
403 switch (cable_type) {
404 case MAX77843_MUIC_ADC_FACTORY_MODE_USB_OFF:
Jaewon Kim27a28d32015-02-04 13:56:07 +0900405 case MAX77843_MUIC_ADC_FACTORY_MODE_USB_ON:
Krzysztof Kozlowski309a3e02015-07-15 21:59:53 +0900406 path = MAX77843_MUIC_CONTROL1_SW_USB;
Jaewon Kim27a28d32015-02-04 13:56:07 +0900407 break;
408 case MAX77843_MUIC_ADC_FACTORY_MODE_UART_OFF:
Krzysztof Kozlowski309a3e02015-07-15 21:59:53 +0900409 path = MAX77843_MUIC_CONTROL1_SW_UART;
Jaewon Kim27a28d32015-02-04 13:56:07 +0900410 break;
411 default:
Chanwoo Choi41b3c012015-04-24 19:50:48 +0900412 return -EINVAL;
Jaewon Kim27a28d32015-02-04 13:56:07 +0900413 }
414
Chanwoo Choi41b3c012015-04-24 19:50:48 +0900415 ret = max77843_muic_set_path(info, path, attached);
416 if (ret < 0)
417 return ret;
418
Chanwoo Choi8670b452016-08-16 15:55:34 +0900419 extcon_set_state_sync(info->edev, EXTCON_JIG, attached);
Chanwoo Choi41b3c012015-04-24 19:50:48 +0900420
Jaewon Kim27a28d32015-02-04 13:56:07 +0900421 return 0;
422}
423
424static int max77843_muic_adc_handler(struct max77843_muic_info *info)
425{
426 int ret, cable_type;
427 bool attached;
428
429 cable_type = max77843_muic_get_cable_type(info,
430 MAX77843_CABLE_GROUP_ADC, &attached);
431
432 dev_dbg(info->dev,
433 "external connector is %s (adc:0x%02x, prev_adc:0x%x)\n",
434 attached ? "attached" : "detached", cable_type,
435 info->prev_cable_type);
436
437 switch (cable_type) {
438 case MAX77843_MUIC_ADC_GROUND:
439 ret = max77843_muic_adc_gnd_handler(info);
440 if (ret < 0)
441 return ret;
442 break;
443 case MAX77843_MUIC_ADC_FACTORY_MODE_USB_OFF:
444 case MAX77843_MUIC_ADC_FACTORY_MODE_USB_ON:
445 case MAX77843_MUIC_ADC_FACTORY_MODE_UART_OFF:
446 ret = max77843_muic_jig_handler(info, cable_type, attached);
447 if (ret < 0)
448 return ret;
449 break;
450 case MAX77843_MUIC_ADC_SEND_END_BUTTON:
451 case MAX77843_MUIC_ADC_REMOTE_S1_BUTTON:
452 case MAX77843_MUIC_ADC_REMOTE_S2_BUTTON:
453 case MAX77843_MUIC_ADC_REMOTE_S3_BUTTON:
454 case MAX77843_MUIC_ADC_REMOTE_S4_BUTTON:
455 case MAX77843_MUIC_ADC_REMOTE_S5_BUTTON:
456 case MAX77843_MUIC_ADC_REMOTE_S6_BUTTON:
457 case MAX77843_MUIC_ADC_REMOTE_S7_BUTTON:
458 case MAX77843_MUIC_ADC_REMOTE_S8_BUTTON:
459 case MAX77843_MUIC_ADC_REMOTE_S9_BUTTON:
460 case MAX77843_MUIC_ADC_REMOTE_S10_BUTTON:
461 case MAX77843_MUIC_ADC_REMOTE_S11_BUTTON:
462 case MAX77843_MUIC_ADC_REMOTE_S12_BUTTON:
463 case MAX77843_MUIC_ADC_RESERVED_ACC_1:
464 case MAX77843_MUIC_ADC_RESERVED_ACC_2:
465 case MAX77843_MUIC_ADC_RESERVED_ACC_3:
466 case MAX77843_MUIC_ADC_RESERVED_ACC_4:
467 case MAX77843_MUIC_ADC_RESERVED_ACC_5:
468 case MAX77843_MUIC_ADC_AUDIO_DEVICE_TYPE2:
469 case MAX77843_MUIC_ADC_PHONE_POWERED_DEV:
470 case MAX77843_MUIC_ADC_TTY_CONVERTER:
471 case MAX77843_MUIC_ADC_UART_CABLE:
472 case MAX77843_MUIC_ADC_CEA936A_TYPE1_CHG:
473 case MAX77843_MUIC_ADC_AV_CABLE_NOLOAD:
474 case MAX77843_MUIC_ADC_CEA936A_TYPE2_CHG:
475 case MAX77843_MUIC_ADC_FACTORY_MODE_UART_ON:
476 case MAX77843_MUIC_ADC_AUDIO_DEVICE_TYPE1:
477 case MAX77843_MUIC_ADC_OPEN:
478 dev_err(info->dev,
479 "accessory is %s but it isn't used (adc:0x%x)\n",
480 attached ? "attached" : "detached", cable_type);
481 return -EAGAIN;
482 default:
483 dev_err(info->dev,
484 "failed to detect %s accessory (adc:0x%x)\n",
485 attached ? "attached" : "detached", cable_type);
486 return -EINVAL;
487 }
488
489 return 0;
490}
491
492static int max77843_muic_chg_handler(struct max77843_muic_info *info)
493{
494 int ret, chg_type, gnd_type;
495 bool attached;
496
497 chg_type = max77843_muic_get_cable_type(info,
498 MAX77843_CABLE_GROUP_CHG, &attached);
499
500 dev_dbg(info->dev,
501 "external connector is %s(chg_type:0x%x, prev_chg_type:0x%x)\n",
502 attached ? "attached" : "detached",
503 chg_type, info->prev_chg_type);
504
505 switch (chg_type) {
506 case MAX77843_MUIC_CHG_USB:
Krzysztof Kozlowski309a3e02015-07-15 21:59:53 +0900507 ret = max77843_muic_set_path(info,
508 MAX77843_MUIC_CONTROL1_SW_USB,
509 attached);
Jaewon Kim27a28d32015-02-04 13:56:07 +0900510 if (ret < 0)
511 return ret;
512
Chanwoo Choi8670b452016-08-16 15:55:34 +0900513 extcon_set_state_sync(info->edev, EXTCON_USB, attached);
514 extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP,
Chanwoo Choi8b45b6a2015-11-09 10:10:15 +0900515 attached);
Jaewon Kim27a28d32015-02-04 13:56:07 +0900516 break;
517 case MAX77843_MUIC_CHG_DOWNSTREAM:
Krzysztof Kozlowski309a3e02015-07-15 21:59:53 +0900518 ret = max77843_muic_set_path(info,
519 MAX77843_MUIC_CONTROL1_SW_OPEN,
520 attached);
Jaewon Kim27a28d32015-02-04 13:56:07 +0900521 if (ret < 0)
522 return ret;
523
Chanwoo Choi8670b452016-08-16 15:55:34 +0900524 extcon_set_state_sync(info->edev, EXTCON_CHG_USB_CDP,
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900525 attached);
Jaewon Kim27a28d32015-02-04 13:56:07 +0900526 break;
527 case MAX77843_MUIC_CHG_DEDICATED:
Krzysztof Kozlowski309a3e02015-07-15 21:59:53 +0900528 ret = max77843_muic_set_path(info,
529 MAX77843_MUIC_CONTROL1_SW_OPEN,
530 attached);
Jaewon Kim27a28d32015-02-04 13:56:07 +0900531 if (ret < 0)
532 return ret;
533
Chanwoo Choi8670b452016-08-16 15:55:34 +0900534 extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP,
Chanwoo Choi11eecf92015-10-03 14:15:13 +0900535 attached);
Jaewon Kim27a28d32015-02-04 13:56:07 +0900536 break;
537 case MAX77843_MUIC_CHG_SPECIAL_500MA:
Krzysztof Kozlowski309a3e02015-07-15 21:59:53 +0900538 ret = max77843_muic_set_path(info,
539 MAX77843_MUIC_CONTROL1_SW_OPEN,
540 attached);
Jaewon Kim27a28d32015-02-04 13:56:07 +0900541 if (ret < 0)
542 return ret;
543
Chanwoo Choi8670b452016-08-16 15:55:34 +0900544 extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SLOW,
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900545 attached);
Jaewon Kim27a28d32015-02-04 13:56:07 +0900546 break;
547 case MAX77843_MUIC_CHG_SPECIAL_1A:
Krzysztof Kozlowski309a3e02015-07-15 21:59:53 +0900548 ret = max77843_muic_set_path(info,
549 MAX77843_MUIC_CONTROL1_SW_OPEN,
550 attached);
Jaewon Kim27a28d32015-02-04 13:56:07 +0900551 if (ret < 0)
552 return ret;
553
Chanwoo Choi8670b452016-08-16 15:55:34 +0900554 extcon_set_state_sync(info->edev, EXTCON_CHG_USB_FAST,
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900555 attached);
Jaewon Kim27a28d32015-02-04 13:56:07 +0900556 break;
557 case MAX77843_MUIC_CHG_GND:
558 gnd_type = max77843_muic_get_cable_type(info,
559 MAX77843_CABLE_GROUP_ADC_GND, &attached);
560
561 /* Charger cable on MHL accessory is attach or detach */
562 if (gnd_type == MAX77843_MUIC_GND_MHL_VB)
Chanwoo Choi8670b452016-08-16 15:55:34 +0900563 extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP,
Chanwoo Choi11eecf92015-10-03 14:15:13 +0900564 true);
Jaewon Kim27a28d32015-02-04 13:56:07 +0900565 else if (gnd_type == MAX77843_MUIC_GND_MHL)
Chanwoo Choi8670b452016-08-16 15:55:34 +0900566 extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP,
Chanwoo Choi11eecf92015-10-03 14:15:13 +0900567 false);
Jaewon Kim27a28d32015-02-04 13:56:07 +0900568 break;
569 case MAX77843_MUIC_CHG_NONE:
570 break;
571 default:
572 dev_err(info->dev,
573 "failed to detect %s accessory (chg_type:0x%x)\n",
574 attached ? "attached" : "detached", chg_type);
575
Krzysztof Kozlowski309a3e02015-07-15 21:59:53 +0900576 max77843_muic_set_path(info, MAX77843_MUIC_CONTROL1_SW_OPEN,
577 attached);
Jaewon Kim27a28d32015-02-04 13:56:07 +0900578 return -EINVAL;
579 }
580
581 return 0;
582}
583
584static void max77843_muic_irq_work(struct work_struct *work)
585{
586 struct max77843_muic_info *info = container_of(work,
587 struct max77843_muic_info, irq_work);
Krzysztof Kozlowskibc1aadc2015-07-15 21:59:51 +0900588 struct max77693_dev *max77843 = info->max77843;
Jaewon Kim27a28d32015-02-04 13:56:07 +0900589 int ret = 0;
590
591 mutex_lock(&info->mutex);
592
593 ret = regmap_bulk_read(max77843->regmap_muic,
594 MAX77843_MUIC_REG_STATUS1, info->status,
595 MAX77843_MUIC_STATUS_NUM);
596 if (ret) {
597 dev_err(info->dev, "Cannot read STATUS registers\n");
598 mutex_unlock(&info->mutex);
599 return;
600 }
601
602 if (info->irq_adc) {
603 ret = max77843_muic_adc_handler(info);
604 if (ret)
605 dev_err(info->dev, "Unknown cable type\n");
606 info->irq_adc = false;
607 }
608
609 if (info->irq_chg) {
610 ret = max77843_muic_chg_handler(info);
611 if (ret)
612 dev_err(info->dev, "Unknown charger type\n");
613 info->irq_chg = false;
614 }
615
616 mutex_unlock(&info->mutex);
617}
618
619static irqreturn_t max77843_muic_irq_handler(int irq, void *data)
620{
621 struct max77843_muic_info *info = data;
622 int i, irq_type = -1;
623
624 for (i = 0; i < ARRAY_SIZE(max77843_muic_irqs); i++)
625 if (irq == max77843_muic_irqs[i].virq)
626 irq_type = max77843_muic_irqs[i].irq;
627
628 switch (irq_type) {
629 case MAX77843_MUIC_IRQ_INT1_ADC:
630 case MAX77843_MUIC_IRQ_INT1_ADCERROR:
631 case MAX77843_MUIC_IRQ_INT1_ADC1K:
632 info->irq_adc = true;
633 break;
634 case MAX77843_MUIC_IRQ_INT2_CHGTYP:
635 case MAX77843_MUIC_IRQ_INT2_CHGDETRUN:
636 case MAX77843_MUIC_IRQ_INT2_DCDTMR:
637 case MAX77843_MUIC_IRQ_INT2_DXOVP:
638 case MAX77843_MUIC_IRQ_INT2_VBVOLT:
639 info->irq_chg = true;
640 break;
641 case MAX77843_MUIC_IRQ_INT3_VBADC:
642 case MAX77843_MUIC_IRQ_INT3_VDNMON:
643 case MAX77843_MUIC_IRQ_INT3_DNRES:
644 case MAX77843_MUIC_IRQ_INT3_MPNACK:
645 case MAX77843_MUIC_IRQ_INT3_MRXBUFOW:
646 case MAX77843_MUIC_IRQ_INT3_MRXTRF:
647 case MAX77843_MUIC_IRQ_INT3_MRXPERR:
648 case MAX77843_MUIC_IRQ_INT3_MRXRDY:
649 break;
650 default:
651 dev_err(info->dev, "Cannot recognize IRQ(%d)\n", irq_type);
652 break;
653 }
654
655 schedule_work(&info->irq_work);
656
657 return IRQ_HANDLED;
658}
659
660static void max77843_muic_detect_cable_wq(struct work_struct *work)
661{
662 struct max77843_muic_info *info = container_of(to_delayed_work(work),
663 struct max77843_muic_info, wq_detcable);
Krzysztof Kozlowskibc1aadc2015-07-15 21:59:51 +0900664 struct max77693_dev *max77843 = info->max77843;
Jaewon Kim27a28d32015-02-04 13:56:07 +0900665 int chg_type, adc, ret;
666 bool attached;
667
668 mutex_lock(&info->mutex);
669
670 ret = regmap_bulk_read(max77843->regmap_muic,
671 MAX77843_MUIC_REG_STATUS1, info->status,
672 MAX77843_MUIC_STATUS_NUM);
673 if (ret) {
674 dev_err(info->dev, "Cannot read STATUS registers\n");
675 goto err_cable_wq;
676 }
677
678 adc = max77843_muic_get_cable_type(info,
679 MAX77843_CABLE_GROUP_ADC, &attached);
680 if (attached && adc != MAX77843_MUIC_ADC_OPEN) {
681 ret = max77843_muic_adc_handler(info);
682 if (ret < 0) {
683 dev_err(info->dev, "Cannot detect accessory\n");
684 goto err_cable_wq;
685 }
686 }
687
688 chg_type = max77843_muic_get_cable_type(info,
689 MAX77843_CABLE_GROUP_CHG, &attached);
690 if (attached && chg_type != MAX77843_MUIC_CHG_NONE) {
691 ret = max77843_muic_chg_handler(info);
692 if (ret < 0) {
693 dev_err(info->dev, "Cannot detect charger accessory\n");
694 goto err_cable_wq;
695 }
696 }
697
698err_cable_wq:
699 mutex_unlock(&info->mutex);
700}
701
702static int max77843_muic_set_debounce_time(struct max77843_muic_info *info,
703 enum max77843_muic_adc_debounce_time time)
704{
Krzysztof Kozlowskibc1aadc2015-07-15 21:59:51 +0900705 struct max77693_dev *max77843 = info->max77843;
Dan Carpenterd927c592015-03-15 13:49:47 +0300706 int ret;
Jaewon Kim27a28d32015-02-04 13:56:07 +0900707
708 switch (time) {
709 case MAX77843_DEBOUNCE_TIME_5MS:
710 case MAX77843_DEBOUNCE_TIME_10MS:
711 case MAX77843_DEBOUNCE_TIME_25MS:
712 case MAX77843_DEBOUNCE_TIME_38_62MS:
713 ret = regmap_update_bits(max77843->regmap_muic,
714 MAX77843_MUIC_REG_CONTROL4,
715 MAX77843_MUIC_CONTROL4_ADCDBSET_MASK,
Krzysztof Kozlowski309a3e02015-07-15 21:59:53 +0900716 time << MAX77843_MUIC_CONTROL4_ADCDBSET_SHIFT);
Jaewon Kim27a28d32015-02-04 13:56:07 +0900717 if (ret < 0) {
718 dev_err(info->dev, "Cannot write MUIC regmap\n");
719 return ret;
720 }
721 break;
722 default:
723 dev_err(info->dev, "Invalid ADC debounce time\n");
724 return -EINVAL;
725 }
726
727 return 0;
728}
729
Krzysztof Kozlowskibc1aadc2015-07-15 21:59:51 +0900730static int max77843_init_muic_regmap(struct max77693_dev *max77843)
Jaewon Kim27a28d32015-02-04 13:56:07 +0900731{
732 int ret;
733
734 max77843->i2c_muic = i2c_new_dummy(max77843->i2c->adapter,
735 I2C_ADDR_MUIC);
736 if (!max77843->i2c_muic) {
737 dev_err(&max77843->i2c->dev,
738 "Cannot allocate I2C device for MUIC\n");
Dan Carpenterb9b518f2015-03-15 13:56:04 +0300739 return -ENOMEM;
Jaewon Kim27a28d32015-02-04 13:56:07 +0900740 }
741
742 i2c_set_clientdata(max77843->i2c_muic, max77843);
743
744 max77843->regmap_muic = devm_regmap_init_i2c(max77843->i2c_muic,
745 &max77843_muic_regmap_config);
746 if (IS_ERR(max77843->regmap_muic)) {
747 ret = PTR_ERR(max77843->regmap_muic);
748 goto err_muic_i2c;
749 }
750
751 ret = regmap_add_irq_chip(max77843->regmap_muic, max77843->irq,
752 IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_SHARED,
753 0, &max77843_muic_irq_chip, &max77843->irq_data_muic);
754 if (ret < 0) {
755 dev_err(&max77843->i2c->dev, "Cannot add MUIC IRQ chip\n");
756 goto err_muic_i2c;
757 }
758
759 return 0;
760
761err_muic_i2c:
762 i2c_unregister_device(max77843->i2c_muic);
763
764 return ret;
765}
766
767static int max77843_muic_probe(struct platform_device *pdev)
768{
Krzysztof Kozlowskibc1aadc2015-07-15 21:59:51 +0900769 struct max77693_dev *max77843 = dev_get_drvdata(pdev->dev.parent);
Jaewon Kim27a28d32015-02-04 13:56:07 +0900770 struct max77843_muic_info *info;
771 unsigned int id;
772 int i, ret;
773
774 info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
775 if (!info)
776 return -ENOMEM;
777
778 info->dev = &pdev->dev;
779 info->max77843 = max77843;
780
781 platform_set_drvdata(pdev, info);
782 mutex_init(&info->mutex);
783
784 /* Initialize i2c and regmap */
785 ret = max77843_init_muic_regmap(max77843);
786 if (ret) {
787 dev_err(&pdev->dev, "Failed to init MUIC regmap\n");
788 return ret;
789 }
790
791 /* Turn off auto detection configuration */
792 ret = regmap_update_bits(max77843->regmap_muic,
793 MAX77843_MUIC_REG_CONTROL4,
794 MAX77843_MUIC_CONTROL4_USBAUTO_MASK |
795 MAX77843_MUIC_CONTROL4_FCTAUTO_MASK,
796 CONTROL4_AUTO_DISABLE);
797
798 /* Initialize extcon device */
799 info->edev = devm_extcon_dev_allocate(&pdev->dev,
800 max77843_extcon_cable);
801 if (IS_ERR(info->edev)) {
802 dev_err(&pdev->dev, "Failed to allocate memory for extcon\n");
803 ret = -ENODEV;
804 goto err_muic_irq;
805 }
806
807 ret = devm_extcon_dev_register(&pdev->dev, info->edev);
808 if (ret) {
809 dev_err(&pdev->dev, "Failed to register extcon device\n");
810 goto err_muic_irq;
811 }
812
813 /* Set ADC debounce time */
814 max77843_muic_set_debounce_time(info, MAX77843_DEBOUNCE_TIME_25MS);
815
816 /* Set initial path for UART */
Krzysztof Kozlowski309a3e02015-07-15 21:59:53 +0900817 max77843_muic_set_path(info, MAX77843_MUIC_CONTROL1_SW_UART, true);
Jaewon Kim27a28d32015-02-04 13:56:07 +0900818
819 /* Check revision number of MUIC device */
820 ret = regmap_read(max77843->regmap_muic, MAX77843_MUIC_REG_ID, &id);
821 if (ret < 0) {
822 dev_err(&pdev->dev, "Failed to read revision number\n");
823 goto err_muic_irq;
824 }
825 dev_info(info->dev, "MUIC device ID : 0x%x\n", id);
826
827 /* Support virtual irq domain for max77843 MUIC device */
828 INIT_WORK(&info->irq_work, max77843_muic_irq_work);
829
Jaewon Kim135d9f72015-06-05 13:32:27 +0900830 /* Clear IRQ bits before request IRQs */
831 ret = regmap_bulk_read(max77843->regmap_muic,
832 MAX77843_MUIC_REG_INT1, info->status,
Dan Carpenterc4924e92016-02-04 14:36:09 +0300833 MAX77843_MUIC_STATUS_NUM);
Jaewon Kim135d9f72015-06-05 13:32:27 +0900834 if (ret) {
835 dev_err(&pdev->dev, "Failed to Clear IRQ bits\n");
836 goto err_muic_irq;
837 }
838
Jaewon Kim27a28d32015-02-04 13:56:07 +0900839 for (i = 0; i < ARRAY_SIZE(max77843_muic_irqs); i++) {
840 struct max77843_muic_irq *muic_irq = &max77843_muic_irqs[i];
Andrzej Hajdac05c0d52015-12-14 11:06:03 +0100841 int virq = 0;
Jaewon Kim27a28d32015-02-04 13:56:07 +0900842
843 virq = regmap_irq_get_virq(max77843->irq_data_muic,
844 muic_irq->irq);
845 if (virq <= 0) {
846 ret = -EINVAL;
847 goto err_muic_irq;
848 }
849 muic_irq->virq = virq;
850
851 ret = devm_request_threaded_irq(&pdev->dev, virq, NULL,
852 max77843_muic_irq_handler, IRQF_NO_SUSPEND,
853 muic_irq->name, info);
854 if (ret) {
855 dev_err(&pdev->dev,
856 "Failed to request irq (IRQ: %d, error: %d)\n",
857 muic_irq->irq, ret);
858 goto err_muic_irq;
859 }
860 }
861
862 /* Detect accessory after completing the initialization of platform */
863 INIT_DELAYED_WORK(&info->wq_detcable, max77843_muic_detect_cable_wq);
864 queue_delayed_work(system_power_efficient_wq,
865 &info->wq_detcable, msecs_to_jiffies(DELAY_MS_DEFAULT));
866
867 return 0;
868
869err_muic_irq:
870 regmap_del_irq_chip(max77843->irq, max77843->irq_data_muic);
871 i2c_unregister_device(max77843->i2c_muic);
872
873 return ret;
874}
875
876static int max77843_muic_remove(struct platform_device *pdev)
877{
878 struct max77843_muic_info *info = platform_get_drvdata(pdev);
Krzysztof Kozlowskibc1aadc2015-07-15 21:59:51 +0900879 struct max77693_dev *max77843 = info->max77843;
Jaewon Kim27a28d32015-02-04 13:56:07 +0900880
881 cancel_work_sync(&info->irq_work);
882 regmap_del_irq_chip(max77843->irq, max77843->irq_data_muic);
883 i2c_unregister_device(max77843->i2c_muic);
884
885 return 0;
886}
887
888static const struct platform_device_id max77843_muic_id[] = {
889 { "max77843-muic", },
890 { /* sentinel */ },
891};
892MODULE_DEVICE_TABLE(platform, max77843_muic_id);
893
894static struct platform_driver max77843_muic_driver = {
895 .driver = {
896 .name = "max77843-muic",
897 },
898 .probe = max77843_muic_probe,
899 .remove = max77843_muic_remove,
900 .id_table = max77843_muic_id,
901};
902
903static int __init max77843_muic_init(void)
904{
905 return platform_driver_register(&max77843_muic_driver);
906}
907subsys_initcall(max77843_muic_init);
908
909MODULE_DESCRIPTION("Maxim MAX77843 Extcon driver");
910MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>");
911MODULE_LICENSE("GPL");