xref: /linux/drivers/extcon/extcon-max77843.c (revision 27a28d32b4f22a4ae687837aeda6afb42116cca4)
1*27a28d32SJaewon Kim /*
2*27a28d32SJaewon Kim  * extcon-max77843.c - Maxim MAX77843 extcon driver to support
3*27a28d32SJaewon Kim  *			MUIC(Micro USB Interface Controller)
4*27a28d32SJaewon Kim  *
5*27a28d32SJaewon Kim  * Copyright (C) 2015 Samsung Electronics
6*27a28d32SJaewon Kim  * Author: Jaewon Kim <jaewon02.kim@samsung.com>
7*27a28d32SJaewon Kim  *
8*27a28d32SJaewon Kim  * This program is free software; you can redistribute it and/or modify
9*27a28d32SJaewon Kim  * it under the terms of the GNU General Public License as published by
10*27a28d32SJaewon Kim  * the Free Software Foundation; either version 2 of the License, or
11*27a28d32SJaewon Kim  * (at your option) any later version.
12*27a28d32SJaewon Kim  */
13*27a28d32SJaewon Kim 
14*27a28d32SJaewon Kim #include <linux/extcon.h>
15*27a28d32SJaewon Kim #include <linux/i2c.h>
16*27a28d32SJaewon Kim #include <linux/interrupt.h>
17*27a28d32SJaewon Kim #include <linux/kernel.h>
18*27a28d32SJaewon Kim #include <linux/mfd/max77843-private.h>
19*27a28d32SJaewon Kim #include <linux/module.h>
20*27a28d32SJaewon Kim #include <linux/platform_device.h>
21*27a28d32SJaewon Kim #include <linux/workqueue.h>
22*27a28d32SJaewon Kim 
23*27a28d32SJaewon Kim #define DELAY_MS_DEFAULT		15000	/* unit: millisecond */
24*27a28d32SJaewon Kim 
25*27a28d32SJaewon Kim enum max77843_muic_status {
26*27a28d32SJaewon Kim 	MAX77843_MUIC_STATUS1 = 0,
27*27a28d32SJaewon Kim 	MAX77843_MUIC_STATUS2,
28*27a28d32SJaewon Kim 	MAX77843_MUIC_STATUS3,
29*27a28d32SJaewon Kim 
30*27a28d32SJaewon Kim 	MAX77843_MUIC_STATUS_NUM,
31*27a28d32SJaewon Kim };
32*27a28d32SJaewon Kim 
33*27a28d32SJaewon Kim struct max77843_muic_info {
34*27a28d32SJaewon Kim 	struct device *dev;
35*27a28d32SJaewon Kim 	struct max77843 *max77843;
36*27a28d32SJaewon Kim 	struct extcon_dev *edev;
37*27a28d32SJaewon Kim 
38*27a28d32SJaewon Kim 	struct mutex mutex;
39*27a28d32SJaewon Kim 	struct work_struct irq_work;
40*27a28d32SJaewon Kim 	struct delayed_work wq_detcable;
41*27a28d32SJaewon Kim 
42*27a28d32SJaewon Kim 	u8 status[MAX77843_MUIC_STATUS_NUM];
43*27a28d32SJaewon Kim 	int prev_cable_type;
44*27a28d32SJaewon Kim 	int prev_chg_type;
45*27a28d32SJaewon Kim 	int prev_gnd_type;
46*27a28d32SJaewon Kim 
47*27a28d32SJaewon Kim 	bool irq_adc;
48*27a28d32SJaewon Kim 	bool irq_chg;
49*27a28d32SJaewon Kim };
50*27a28d32SJaewon Kim 
51*27a28d32SJaewon Kim enum max77843_muic_cable_group {
52*27a28d32SJaewon Kim 	MAX77843_CABLE_GROUP_ADC = 0,
53*27a28d32SJaewon Kim 	MAX77843_CABLE_GROUP_ADC_GND,
54*27a28d32SJaewon Kim 	MAX77843_CABLE_GROUP_CHG,
55*27a28d32SJaewon Kim };
56*27a28d32SJaewon Kim 
57*27a28d32SJaewon Kim enum max77843_muic_adc_debounce_time {
58*27a28d32SJaewon Kim 	MAX77843_DEBOUNCE_TIME_5MS = 0,
59*27a28d32SJaewon Kim 	MAX77843_DEBOUNCE_TIME_10MS,
60*27a28d32SJaewon Kim 	MAX77843_DEBOUNCE_TIME_25MS,
61*27a28d32SJaewon Kim 	MAX77843_DEBOUNCE_TIME_38_62MS,
62*27a28d32SJaewon Kim };
63*27a28d32SJaewon Kim 
64*27a28d32SJaewon Kim /* Define accessory cable type */
65*27a28d32SJaewon Kim enum max77843_muic_accessory_type {
66*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_GROUND = 0,
67*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_SEND_END_BUTTON,
68*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_REMOTE_S1_BUTTON,
69*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_REMOTE_S2_BUTTON,
70*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_REMOTE_S3_BUTTON,
71*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_REMOTE_S4_BUTTON,
72*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_REMOTE_S5_BUTTON,
73*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_REMOTE_S6_BUTTON,
74*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_REMOTE_S7_BUTTON,
75*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_REMOTE_S8_BUTTON,
76*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_REMOTE_S9_BUTTON,
77*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_REMOTE_S10_BUTTON,
78*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_REMOTE_S11_BUTTON,
79*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_REMOTE_S12_BUTTON,
80*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_RESERVED_ACC_1,
81*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_RESERVED_ACC_2,
82*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_RESERVED_ACC_3,
83*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_RESERVED_ACC_4,
84*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_RESERVED_ACC_5,
85*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_AUDIO_DEVICE_TYPE2,
86*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_PHONE_POWERED_DEV,
87*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_TTY_CONVERTER,
88*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_UART_CABLE,
89*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_CEA936A_TYPE1_CHG,
90*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_FACTORY_MODE_USB_OFF,
91*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_FACTORY_MODE_USB_ON,
92*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_AV_CABLE_NOLOAD,
93*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_CEA936A_TYPE2_CHG,
94*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_FACTORY_MODE_UART_OFF,
95*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_FACTORY_MODE_UART_ON,
96*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_AUDIO_DEVICE_TYPE1,
97*27a28d32SJaewon Kim 	MAX77843_MUIC_ADC_OPEN,
98*27a28d32SJaewon Kim 
99*27a28d32SJaewon Kim 	/* The blow accessories should check
100*27a28d32SJaewon Kim 	   not only ADC value but also ADC1K and VBVolt value. */
101*27a28d32SJaewon Kim 						/* Offset|ADC1K|VBVolt| */
102*27a28d32SJaewon Kim 	MAX77843_MUIC_GND_USB_HOST = 0x100,	/*    0x1|    0|     0| */
103*27a28d32SJaewon Kim 	MAX77843_MUIC_GND_USB_HOST_VB = 0x101,	/*    0x1|    0|     1| */
104*27a28d32SJaewon Kim 	MAX77843_MUIC_GND_MHL = 0x102,		/*    0x1|    1|     0| */
105*27a28d32SJaewon Kim 	MAX77843_MUIC_GND_MHL_VB = 0x103,	/*    0x1|    1|     1| */
106*27a28d32SJaewon Kim };
107*27a28d32SJaewon Kim 
108*27a28d32SJaewon Kim /* Define charger cable type */
109*27a28d32SJaewon Kim enum max77843_muic_charger_type {
110*27a28d32SJaewon Kim 	MAX77843_MUIC_CHG_NONE = 0,
111*27a28d32SJaewon Kim 	MAX77843_MUIC_CHG_USB,
112*27a28d32SJaewon Kim 	MAX77843_MUIC_CHG_DOWNSTREAM,
113*27a28d32SJaewon Kim 	MAX77843_MUIC_CHG_DEDICATED,
114*27a28d32SJaewon Kim 	MAX77843_MUIC_CHG_SPECIAL_500MA,
115*27a28d32SJaewon Kim 	MAX77843_MUIC_CHG_SPECIAL_1A,
116*27a28d32SJaewon Kim 	MAX77843_MUIC_CHG_SPECIAL_BIAS,
117*27a28d32SJaewon Kim 	MAX77843_MUIC_CHG_RESERVED,
118*27a28d32SJaewon Kim 	MAX77843_MUIC_CHG_GND,
119*27a28d32SJaewon Kim };
120*27a28d32SJaewon Kim 
121*27a28d32SJaewon Kim enum {
122*27a28d32SJaewon Kim 	MAX77843_CABLE_USB = 0,
123*27a28d32SJaewon Kim 	MAX77843_CABLE_USB_HOST,
124*27a28d32SJaewon Kim 	MAX77843_CABLE_TA,
125*27a28d32SJaewon Kim 	MAX77843_CABLE_CHARGE_DOWNSTREAM,
126*27a28d32SJaewon Kim 	MAX77843_CABLE_FAST_CHARGER,
127*27a28d32SJaewon Kim 	MAX77843_CABLE_SLOW_CHARGER,
128*27a28d32SJaewon Kim 	MAX77843_CABLE_MHL,
129*27a28d32SJaewon Kim 	MAX77843_CABLE_MHL_TA,
130*27a28d32SJaewon Kim 	MAX77843_CABLE_JIG_USB_ON,
131*27a28d32SJaewon Kim 	MAX77843_CABLE_JIG_USB_OFF,
132*27a28d32SJaewon Kim 	MAX77843_CABLE_JIG_UART_ON,
133*27a28d32SJaewon Kim 	MAX77843_CABLE_JIG_UART_OFF,
134*27a28d32SJaewon Kim 
135*27a28d32SJaewon Kim 	MAX77843_CABLE_NUM,
136*27a28d32SJaewon Kim };
137*27a28d32SJaewon Kim 
138*27a28d32SJaewon Kim static const char *max77843_extcon_cable[] = {
139*27a28d32SJaewon Kim 	[MAX77843_CABLE_USB]			= "USB",
140*27a28d32SJaewon Kim 	[MAX77843_CABLE_USB_HOST]		= "USB-HOST",
141*27a28d32SJaewon Kim 	[MAX77843_CABLE_TA]			= "TA",
142*27a28d32SJaewon Kim 	[MAX77843_CABLE_CHARGE_DOWNSTREAM]	= "CHARGER-DOWNSTREAM",
143*27a28d32SJaewon Kim 	[MAX77843_CABLE_FAST_CHARGER]		= "FAST-CHARGER",
144*27a28d32SJaewon Kim 	[MAX77843_CABLE_SLOW_CHARGER]		= "SLOW-CHARGER",
145*27a28d32SJaewon Kim 	[MAX77843_CABLE_MHL]			= "MHL",
146*27a28d32SJaewon Kim 	[MAX77843_CABLE_MHL_TA]			= "MHL-TA",
147*27a28d32SJaewon Kim 	[MAX77843_CABLE_JIG_USB_ON]		= "JIG-USB-ON",
148*27a28d32SJaewon Kim 	[MAX77843_CABLE_JIG_USB_OFF]		= "JIG-USB-OFF",
149*27a28d32SJaewon Kim 	[MAX77843_CABLE_JIG_UART_ON]		= "JIG-UART-ON",
150*27a28d32SJaewon Kim 	[MAX77843_CABLE_JIG_UART_OFF]		= "JIG-UART-OFF",
151*27a28d32SJaewon Kim };
152*27a28d32SJaewon Kim 
153*27a28d32SJaewon Kim struct max77843_muic_irq {
154*27a28d32SJaewon Kim 	unsigned int irq;
155*27a28d32SJaewon Kim 	const char *name;
156*27a28d32SJaewon Kim 	unsigned int virq;
157*27a28d32SJaewon Kim };
158*27a28d32SJaewon Kim 
159*27a28d32SJaewon Kim static struct max77843_muic_irq max77843_muic_irqs[] = {
160*27a28d32SJaewon Kim 	{ MAX77843_MUIC_IRQ_INT1_ADC,		"MUIC-ADC" },
161*27a28d32SJaewon Kim 	{ MAX77843_MUIC_IRQ_INT1_ADCERROR,	"MUIC-ADC_ERROR" },
162*27a28d32SJaewon Kim 	{ MAX77843_MUIC_IRQ_INT1_ADC1K,		"MUIC-ADC1K" },
163*27a28d32SJaewon Kim 	{ MAX77843_MUIC_IRQ_INT2_CHGTYP,	"MUIC-CHGTYP" },
164*27a28d32SJaewon Kim 	{ MAX77843_MUIC_IRQ_INT2_CHGDETRUN,	"MUIC-CHGDETRUN" },
165*27a28d32SJaewon Kim 	{ MAX77843_MUIC_IRQ_INT2_DCDTMR,	"MUIC-DCDTMR" },
166*27a28d32SJaewon Kim 	{ MAX77843_MUIC_IRQ_INT2_DXOVP,		"MUIC-DXOVP" },
167*27a28d32SJaewon Kim 	{ MAX77843_MUIC_IRQ_INT2_VBVOLT,	"MUIC-VBVOLT" },
168*27a28d32SJaewon Kim 	{ MAX77843_MUIC_IRQ_INT3_VBADC,		"MUIC-VBADC" },
169*27a28d32SJaewon Kim 	{ MAX77843_MUIC_IRQ_INT3_VDNMON,	"MUIC-VDNMON" },
170*27a28d32SJaewon Kim 	{ MAX77843_MUIC_IRQ_INT3_DNRES,		"MUIC-DNRES" },
171*27a28d32SJaewon Kim 	{ MAX77843_MUIC_IRQ_INT3_MPNACK,	"MUIC-MPNACK"},
172*27a28d32SJaewon Kim 	{ MAX77843_MUIC_IRQ_INT3_MRXBUFOW,	"MUIC-MRXBUFOW"},
173*27a28d32SJaewon Kim 	{ MAX77843_MUIC_IRQ_INT3_MRXTRF,	"MUIC-MRXTRF"},
174*27a28d32SJaewon Kim 	{ MAX77843_MUIC_IRQ_INT3_MRXPERR,	"MUIC-MRXPERR"},
175*27a28d32SJaewon Kim 	{ MAX77843_MUIC_IRQ_INT3_MRXRDY,	"MUIC-MRXRDY"},
176*27a28d32SJaewon Kim };
177*27a28d32SJaewon Kim 
178*27a28d32SJaewon Kim static const struct regmap_config max77843_muic_regmap_config = {
179*27a28d32SJaewon Kim 	.reg_bits       = 8,
180*27a28d32SJaewon Kim 	.val_bits       = 8,
181*27a28d32SJaewon Kim 	.max_register   = MAX77843_MUIC_REG_END,
182*27a28d32SJaewon Kim };
183*27a28d32SJaewon Kim 
184*27a28d32SJaewon Kim static const struct regmap_irq max77843_muic_irq[] = {
185*27a28d32SJaewon Kim 	/* INT1 interrupt */
186*27a28d32SJaewon Kim 	{ .reg_offset = 0, .mask = MAX77843_MUIC_ADC, },
187*27a28d32SJaewon Kim 	{ .reg_offset = 0, .mask = MAX77843_MUIC_ADCERROR, },
188*27a28d32SJaewon Kim 	{ .reg_offset = 0, .mask = MAX77843_MUIC_ADC1K, },
189*27a28d32SJaewon Kim 
190*27a28d32SJaewon Kim 	/* INT2 interrupt */
191*27a28d32SJaewon Kim 	{ .reg_offset = 1, .mask = MAX77843_MUIC_CHGTYP, },
192*27a28d32SJaewon Kim 	{ .reg_offset = 1, .mask = MAX77843_MUIC_CHGDETRUN, },
193*27a28d32SJaewon Kim 	{ .reg_offset = 1, .mask = MAX77843_MUIC_DCDTMR, },
194*27a28d32SJaewon Kim 	{ .reg_offset = 1, .mask = MAX77843_MUIC_DXOVP, },
195*27a28d32SJaewon Kim 	{ .reg_offset = 1, .mask = MAX77843_MUIC_VBVOLT, },
196*27a28d32SJaewon Kim 
197*27a28d32SJaewon Kim 	/* INT3 interrupt */
198*27a28d32SJaewon Kim 	{ .reg_offset = 2, .mask = MAX77843_MUIC_VBADC, },
199*27a28d32SJaewon Kim 	{ .reg_offset = 2, .mask = MAX77843_MUIC_VDNMON, },
200*27a28d32SJaewon Kim 	{ .reg_offset = 2, .mask = MAX77843_MUIC_DNRES, },
201*27a28d32SJaewon Kim 	{ .reg_offset = 2, .mask = MAX77843_MUIC_MPNACK, },
202*27a28d32SJaewon Kim 	{ .reg_offset = 2, .mask = MAX77843_MUIC_MRXBUFOW, },
203*27a28d32SJaewon Kim 	{ .reg_offset = 2, .mask = MAX77843_MUIC_MRXTRF, },
204*27a28d32SJaewon Kim 	{ .reg_offset = 2, .mask = MAX77843_MUIC_MRXPERR, },
205*27a28d32SJaewon Kim 	{ .reg_offset = 2, .mask = MAX77843_MUIC_MRXRDY, },
206*27a28d32SJaewon Kim };
207*27a28d32SJaewon Kim 
208*27a28d32SJaewon Kim static const struct regmap_irq_chip max77843_muic_irq_chip = {
209*27a28d32SJaewon Kim 	.name           = "max77843-muic",
210*27a28d32SJaewon Kim 	.status_base    = MAX77843_MUIC_REG_INT1,
211*27a28d32SJaewon Kim 	.mask_base      = MAX77843_MUIC_REG_INTMASK1,
212*27a28d32SJaewon Kim 	.mask_invert    = true,
213*27a28d32SJaewon Kim 	.num_regs       = 3,
214*27a28d32SJaewon Kim 	.irqs           = max77843_muic_irq,
215*27a28d32SJaewon Kim 	.num_irqs       = ARRAY_SIZE(max77843_muic_irq),
216*27a28d32SJaewon Kim };
217*27a28d32SJaewon Kim 
218*27a28d32SJaewon Kim static int max77843_muic_set_path(struct max77843_muic_info *info,
219*27a28d32SJaewon Kim 		u8 val, bool attached)
220*27a28d32SJaewon Kim {
221*27a28d32SJaewon Kim 	struct max77843 *max77843 = info->max77843;
222*27a28d32SJaewon Kim 	int ret = 0;
223*27a28d32SJaewon Kim 	unsigned int ctrl1, ctrl2;
224*27a28d32SJaewon Kim 
225*27a28d32SJaewon Kim 	if (attached)
226*27a28d32SJaewon Kim 		ctrl1 = val;
227*27a28d32SJaewon Kim 	else
228*27a28d32SJaewon Kim 		ctrl1 = CONTROL1_SW_OPEN;
229*27a28d32SJaewon Kim 
230*27a28d32SJaewon Kim 	ret = regmap_update_bits(max77843->regmap_muic,
231*27a28d32SJaewon Kim 			MAX77843_MUIC_REG_CONTROL1,
232*27a28d32SJaewon Kim 			CONTROL1_COM_SW, ctrl1);
233*27a28d32SJaewon Kim 	if (ret < 0) {
234*27a28d32SJaewon Kim 		dev_err(info->dev, "Cannot switch MUIC port\n");
235*27a28d32SJaewon Kim 		return ret;
236*27a28d32SJaewon Kim 	}
237*27a28d32SJaewon Kim 
238*27a28d32SJaewon Kim 	if (attached)
239*27a28d32SJaewon Kim 		ctrl2 = MAX77843_MUIC_CONTROL2_CPEN_MASK;
240*27a28d32SJaewon Kim 	else
241*27a28d32SJaewon Kim 		ctrl2 = MAX77843_MUIC_CONTROL2_LOWPWR_MASK;
242*27a28d32SJaewon Kim 
243*27a28d32SJaewon Kim 	ret = regmap_update_bits(max77843->regmap_muic,
244*27a28d32SJaewon Kim 			MAX77843_MUIC_REG_CONTROL2,
245*27a28d32SJaewon Kim 			MAX77843_MUIC_CONTROL2_LOWPWR_MASK |
246*27a28d32SJaewon Kim 			MAX77843_MUIC_CONTROL2_CPEN_MASK, ctrl2);
247*27a28d32SJaewon Kim 	if (ret < 0) {
248*27a28d32SJaewon Kim 		dev_err(info->dev, "Cannot update lowpower mode\n");
249*27a28d32SJaewon Kim 		return ret;
250*27a28d32SJaewon Kim 	}
251*27a28d32SJaewon Kim 
252*27a28d32SJaewon Kim 	dev_dbg(info->dev,
253*27a28d32SJaewon Kim 		"CONTROL1 : 0x%02x, CONTROL2 : 0x%02x, state : %s\n",
254*27a28d32SJaewon Kim 		ctrl1, ctrl2, attached ? "attached" : "detached");
255*27a28d32SJaewon Kim 
256*27a28d32SJaewon Kim 	return 0;
257*27a28d32SJaewon Kim }
258*27a28d32SJaewon Kim 
259*27a28d32SJaewon Kim static int max77843_muic_get_cable_type(struct max77843_muic_info *info,
260*27a28d32SJaewon Kim 		enum max77843_muic_cable_group group, bool *attached)
261*27a28d32SJaewon Kim {
262*27a28d32SJaewon Kim 	int adc, chg_type, cable_type, gnd_type;
263*27a28d32SJaewon Kim 
264*27a28d32SJaewon Kim 	adc = info->status[MAX77843_MUIC_STATUS1] &
265*27a28d32SJaewon Kim 			MAX77843_MUIC_STATUS1_ADC_MASK;
266*27a28d32SJaewon Kim 	adc >>= STATUS1_ADC_SHIFT;
267*27a28d32SJaewon Kim 
268*27a28d32SJaewon Kim 	switch (group) {
269*27a28d32SJaewon Kim 	case MAX77843_CABLE_GROUP_ADC:
270*27a28d32SJaewon Kim 		if (adc == MAX77843_MUIC_ADC_OPEN) {
271*27a28d32SJaewon Kim 			*attached = false;
272*27a28d32SJaewon Kim 			cable_type = info->prev_cable_type;
273*27a28d32SJaewon Kim 			info->prev_cable_type = MAX77843_MUIC_ADC_OPEN;
274*27a28d32SJaewon Kim 		} else {
275*27a28d32SJaewon Kim 			*attached = true;
276*27a28d32SJaewon Kim 			cable_type = info->prev_cable_type = adc;
277*27a28d32SJaewon Kim 		}
278*27a28d32SJaewon Kim 		break;
279*27a28d32SJaewon Kim 	case MAX77843_CABLE_GROUP_CHG:
280*27a28d32SJaewon Kim 		chg_type = info->status[MAX77843_MUIC_STATUS2] &
281*27a28d32SJaewon Kim 				MAX77843_MUIC_STATUS2_CHGTYP_MASK;
282*27a28d32SJaewon Kim 
283*27a28d32SJaewon Kim 		/* Check GROUND accessory with charger cable */
284*27a28d32SJaewon Kim 		if (adc == MAX77843_MUIC_ADC_GROUND) {
285*27a28d32SJaewon Kim 			if (chg_type == MAX77843_MUIC_CHG_NONE) {
286*27a28d32SJaewon Kim 				/* The following state when charger cable is
287*27a28d32SJaewon Kim 				 * disconnected but the GROUND accessory still
288*27a28d32SJaewon Kim 				 * connected */
289*27a28d32SJaewon Kim 				*attached = false;
290*27a28d32SJaewon Kim 				cable_type = info->prev_chg_type;
291*27a28d32SJaewon Kim 				info->prev_chg_type = MAX77843_MUIC_CHG_NONE;
292*27a28d32SJaewon Kim 			} else {
293*27a28d32SJaewon Kim 
294*27a28d32SJaewon Kim 				/* The following state when charger cable is
295*27a28d32SJaewon Kim 				 * connected on the GROUND accessory */
296*27a28d32SJaewon Kim 				*attached = true;
297*27a28d32SJaewon Kim 				cable_type = MAX77843_MUIC_CHG_GND;
298*27a28d32SJaewon Kim 				info->prev_chg_type = MAX77843_MUIC_CHG_GND;
299*27a28d32SJaewon Kim 			}
300*27a28d32SJaewon Kim 			break;
301*27a28d32SJaewon Kim 		}
302*27a28d32SJaewon Kim 
303*27a28d32SJaewon Kim 		if (chg_type == MAX77843_MUIC_CHG_NONE) {
304*27a28d32SJaewon Kim 			*attached = false;
305*27a28d32SJaewon Kim 			cable_type = info->prev_chg_type;
306*27a28d32SJaewon Kim 			info->prev_chg_type = MAX77843_MUIC_CHG_NONE;
307*27a28d32SJaewon Kim 		} else {
308*27a28d32SJaewon Kim 			*attached = true;
309*27a28d32SJaewon Kim 			cable_type = info->prev_chg_type = chg_type;
310*27a28d32SJaewon Kim 		}
311*27a28d32SJaewon Kim 		break;
312*27a28d32SJaewon Kim 	case MAX77843_CABLE_GROUP_ADC_GND:
313*27a28d32SJaewon Kim 		if (adc == MAX77843_MUIC_ADC_OPEN) {
314*27a28d32SJaewon Kim 			*attached = false;
315*27a28d32SJaewon Kim 			cable_type = info->prev_gnd_type;
316*27a28d32SJaewon Kim 			info->prev_gnd_type = MAX77843_MUIC_ADC_OPEN;
317*27a28d32SJaewon Kim 		} else {
318*27a28d32SJaewon Kim 			*attached = true;
319*27a28d32SJaewon Kim 
320*27a28d32SJaewon Kim 			/* Offset|ADC1K|VBVolt|
321*27a28d32SJaewon Kim 			 *    0x1|    0|     0| USB-HOST
322*27a28d32SJaewon Kim 			 *    0x1|    0|     1| USB-HOST with VB
323*27a28d32SJaewon Kim 			 *    0x1|    1|     0| MHL
324*27a28d32SJaewon Kim 			 *    0x1|    1|     1| MHL with VB */
325*27a28d32SJaewon Kim 			/* Get ADC1K register bit */
326*27a28d32SJaewon Kim 			gnd_type = (info->status[MAX77843_MUIC_STATUS1] &
327*27a28d32SJaewon Kim 					MAX77843_MUIC_STATUS1_ADC1K_MASK);
328*27a28d32SJaewon Kim 
329*27a28d32SJaewon Kim 			/* Get VBVolt register bit */
330*27a28d32SJaewon Kim 			gnd_type |= (info->status[MAX77843_MUIC_STATUS2] &
331*27a28d32SJaewon Kim 					MAX77843_MUIC_STATUS2_VBVOLT_MASK);
332*27a28d32SJaewon Kim 			gnd_type >>= STATUS2_VBVOLT_SHIFT;
333*27a28d32SJaewon Kim 
334*27a28d32SJaewon Kim 			/* Offset of GND cable */
335*27a28d32SJaewon Kim 			gnd_type |= MAX77843_MUIC_GND_USB_HOST;
336*27a28d32SJaewon Kim 			cable_type = info->prev_gnd_type = gnd_type;
337*27a28d32SJaewon Kim 		}
338*27a28d32SJaewon Kim 		break;
339*27a28d32SJaewon Kim 	default:
340*27a28d32SJaewon Kim 		dev_err(info->dev, "Unknown cable group (%d)\n", group);
341*27a28d32SJaewon Kim 		cable_type = -EINVAL;
342*27a28d32SJaewon Kim 		break;
343*27a28d32SJaewon Kim 	}
344*27a28d32SJaewon Kim 
345*27a28d32SJaewon Kim 	return cable_type;
346*27a28d32SJaewon Kim }
347*27a28d32SJaewon Kim 
348*27a28d32SJaewon Kim static int max77843_muic_adc_gnd_handler(struct max77843_muic_info *info)
349*27a28d32SJaewon Kim {
350*27a28d32SJaewon Kim 	int ret, gnd_cable_type;
351*27a28d32SJaewon Kim 	bool attached;
352*27a28d32SJaewon Kim 
353*27a28d32SJaewon Kim 	gnd_cable_type = max77843_muic_get_cable_type(info,
354*27a28d32SJaewon Kim 			MAX77843_CABLE_GROUP_ADC_GND, &attached);
355*27a28d32SJaewon Kim 	dev_dbg(info->dev, "external connector is %s (gnd:0x%02x)\n",
356*27a28d32SJaewon Kim 			attached ? "attached" : "detached", gnd_cable_type);
357*27a28d32SJaewon Kim 
358*27a28d32SJaewon Kim 	switch (gnd_cable_type) {
359*27a28d32SJaewon Kim 	case MAX77843_MUIC_GND_USB_HOST:
360*27a28d32SJaewon Kim 	case MAX77843_MUIC_GND_USB_HOST_VB:
361*27a28d32SJaewon Kim 		ret = max77843_muic_set_path(info, CONTROL1_SW_USB, attached);
362*27a28d32SJaewon Kim 		if (ret < 0)
363*27a28d32SJaewon Kim 			return ret;
364*27a28d32SJaewon Kim 
365*27a28d32SJaewon Kim 		extcon_set_cable_state(info->edev, "USB-HOST", attached);
366*27a28d32SJaewon Kim 		break;
367*27a28d32SJaewon Kim 	case MAX77843_MUIC_GND_MHL_VB:
368*27a28d32SJaewon Kim 	case MAX77843_MUIC_GND_MHL:
369*27a28d32SJaewon Kim 		ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached);
370*27a28d32SJaewon Kim 		if (ret < 0)
371*27a28d32SJaewon Kim 			return ret;
372*27a28d32SJaewon Kim 
373*27a28d32SJaewon Kim 		extcon_set_cable_state(info->edev, "MHL", attached);
374*27a28d32SJaewon Kim 		break;
375*27a28d32SJaewon Kim 	default:
376*27a28d32SJaewon Kim 		dev_err(info->dev, "failed to detect %s accessory(gnd:0x%x)\n",
377*27a28d32SJaewon Kim 			attached ? "attached" : "detached", gnd_cable_type);
378*27a28d32SJaewon Kim 		return -EINVAL;
379*27a28d32SJaewon Kim 	}
380*27a28d32SJaewon Kim 
381*27a28d32SJaewon Kim 	return 0;
382*27a28d32SJaewon Kim }
383*27a28d32SJaewon Kim 
384*27a28d32SJaewon Kim static int max77843_muic_jig_handler(struct max77843_muic_info *info,
385*27a28d32SJaewon Kim 		int cable_type, bool attached)
386*27a28d32SJaewon Kim {
387*27a28d32SJaewon Kim 	int ret;
388*27a28d32SJaewon Kim 
389*27a28d32SJaewon Kim 	dev_dbg(info->dev, "external connector is %s (adc:0x%02x)\n",
390*27a28d32SJaewon Kim 			attached ? "attached" : "detached", cable_type);
391*27a28d32SJaewon Kim 
392*27a28d32SJaewon Kim 	switch (cable_type) {
393*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_FACTORY_MODE_USB_OFF:
394*27a28d32SJaewon Kim 		ret = max77843_muic_set_path(info, CONTROL1_SW_USB, attached);
395*27a28d32SJaewon Kim 		if (ret < 0)
396*27a28d32SJaewon Kim 			return ret;
397*27a28d32SJaewon Kim 		extcon_set_cable_state(info->edev, "JIG-USB-OFF", attached);
398*27a28d32SJaewon Kim 		break;
399*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_FACTORY_MODE_USB_ON:
400*27a28d32SJaewon Kim 		ret = max77843_muic_set_path(info, CONTROL1_SW_USB, attached);
401*27a28d32SJaewon Kim 		if (ret < 0)
402*27a28d32SJaewon Kim 			return ret;
403*27a28d32SJaewon Kim 		extcon_set_cable_state(info->edev, "JIG-USB-ON", attached);
404*27a28d32SJaewon Kim 		break;
405*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_FACTORY_MODE_UART_OFF:
406*27a28d32SJaewon Kim 		ret = max77843_muic_set_path(info, CONTROL1_SW_UART, attached);
407*27a28d32SJaewon Kim 		if (ret < 0)
408*27a28d32SJaewon Kim 			return ret;
409*27a28d32SJaewon Kim 		extcon_set_cable_state(info->edev, "JIG-UART-OFF", attached);
410*27a28d32SJaewon Kim 		break;
411*27a28d32SJaewon Kim 	default:
412*27a28d32SJaewon Kim 		ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached);
413*27a28d32SJaewon Kim 		if (ret < 0)
414*27a28d32SJaewon Kim 			return ret;
415*27a28d32SJaewon Kim 		break;
416*27a28d32SJaewon Kim 	}
417*27a28d32SJaewon Kim 
418*27a28d32SJaewon Kim 	return 0;
419*27a28d32SJaewon Kim }
420*27a28d32SJaewon Kim 
421*27a28d32SJaewon Kim static int max77843_muic_adc_handler(struct max77843_muic_info *info)
422*27a28d32SJaewon Kim {
423*27a28d32SJaewon Kim 	int ret, cable_type;
424*27a28d32SJaewon Kim 	bool attached;
425*27a28d32SJaewon Kim 
426*27a28d32SJaewon Kim 	cable_type = max77843_muic_get_cable_type(info,
427*27a28d32SJaewon Kim 			MAX77843_CABLE_GROUP_ADC, &attached);
428*27a28d32SJaewon Kim 
429*27a28d32SJaewon Kim 	dev_dbg(info->dev,
430*27a28d32SJaewon Kim 		"external connector is %s (adc:0x%02x, prev_adc:0x%x)\n",
431*27a28d32SJaewon Kim 		attached ? "attached" : "detached", cable_type,
432*27a28d32SJaewon Kim 		info->prev_cable_type);
433*27a28d32SJaewon Kim 
434*27a28d32SJaewon Kim 	switch (cable_type) {
435*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_GROUND:
436*27a28d32SJaewon Kim 		ret = max77843_muic_adc_gnd_handler(info);
437*27a28d32SJaewon Kim 		if (ret < 0)
438*27a28d32SJaewon Kim 			return ret;
439*27a28d32SJaewon Kim 		break;
440*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_FACTORY_MODE_USB_OFF:
441*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_FACTORY_MODE_USB_ON:
442*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_FACTORY_MODE_UART_OFF:
443*27a28d32SJaewon Kim 		ret = max77843_muic_jig_handler(info, cable_type, attached);
444*27a28d32SJaewon Kim 		if (ret < 0)
445*27a28d32SJaewon Kim 			return ret;
446*27a28d32SJaewon Kim 		break;
447*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_SEND_END_BUTTON:
448*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_REMOTE_S1_BUTTON:
449*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_REMOTE_S2_BUTTON:
450*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_REMOTE_S3_BUTTON:
451*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_REMOTE_S4_BUTTON:
452*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_REMOTE_S5_BUTTON:
453*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_REMOTE_S6_BUTTON:
454*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_REMOTE_S7_BUTTON:
455*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_REMOTE_S8_BUTTON:
456*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_REMOTE_S9_BUTTON:
457*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_REMOTE_S10_BUTTON:
458*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_REMOTE_S11_BUTTON:
459*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_REMOTE_S12_BUTTON:
460*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_RESERVED_ACC_1:
461*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_RESERVED_ACC_2:
462*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_RESERVED_ACC_3:
463*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_RESERVED_ACC_4:
464*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_RESERVED_ACC_5:
465*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_AUDIO_DEVICE_TYPE2:
466*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_PHONE_POWERED_DEV:
467*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_TTY_CONVERTER:
468*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_UART_CABLE:
469*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_CEA936A_TYPE1_CHG:
470*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_AV_CABLE_NOLOAD:
471*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_CEA936A_TYPE2_CHG:
472*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_FACTORY_MODE_UART_ON:
473*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_AUDIO_DEVICE_TYPE1:
474*27a28d32SJaewon Kim 	case MAX77843_MUIC_ADC_OPEN:
475*27a28d32SJaewon Kim 		dev_err(info->dev,
476*27a28d32SJaewon Kim 			"accessory is %s but it isn't used (adc:0x%x)\n",
477*27a28d32SJaewon Kim 			attached ? "attached" : "detached", cable_type);
478*27a28d32SJaewon Kim 		return -EAGAIN;
479*27a28d32SJaewon Kim 	default:
480*27a28d32SJaewon Kim 		dev_err(info->dev,
481*27a28d32SJaewon Kim 			"failed to detect %s accessory (adc:0x%x)\n",
482*27a28d32SJaewon Kim 			attached ? "attached" : "detached", cable_type);
483*27a28d32SJaewon Kim 		return -EINVAL;
484*27a28d32SJaewon Kim 	}
485*27a28d32SJaewon Kim 
486*27a28d32SJaewon Kim 	return 0;
487*27a28d32SJaewon Kim }
488*27a28d32SJaewon Kim 
489*27a28d32SJaewon Kim static int max77843_muic_chg_handler(struct max77843_muic_info *info)
490*27a28d32SJaewon Kim {
491*27a28d32SJaewon Kim 	int ret, chg_type, gnd_type;
492*27a28d32SJaewon Kim 	bool attached;
493*27a28d32SJaewon Kim 
494*27a28d32SJaewon Kim 	chg_type = max77843_muic_get_cable_type(info,
495*27a28d32SJaewon Kim 			MAX77843_CABLE_GROUP_CHG, &attached);
496*27a28d32SJaewon Kim 
497*27a28d32SJaewon Kim 	dev_dbg(info->dev,
498*27a28d32SJaewon Kim 		"external connector is %s(chg_type:0x%x, prev_chg_type:0x%x)\n",
499*27a28d32SJaewon Kim 		attached ? "attached" : "detached",
500*27a28d32SJaewon Kim 		chg_type, info->prev_chg_type);
501*27a28d32SJaewon Kim 
502*27a28d32SJaewon Kim 	switch (chg_type) {
503*27a28d32SJaewon Kim 	case MAX77843_MUIC_CHG_USB:
504*27a28d32SJaewon Kim 		ret = max77843_muic_set_path(info, CONTROL1_SW_USB, attached);
505*27a28d32SJaewon Kim 		if (ret < 0)
506*27a28d32SJaewon Kim 			return ret;
507*27a28d32SJaewon Kim 
508*27a28d32SJaewon Kim 		extcon_set_cable_state(info->edev, "USB", attached);
509*27a28d32SJaewon Kim 		break;
510*27a28d32SJaewon Kim 	case MAX77843_MUIC_CHG_DOWNSTREAM:
511*27a28d32SJaewon Kim 		ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached);
512*27a28d32SJaewon Kim 		if (ret < 0)
513*27a28d32SJaewon Kim 			return ret;
514*27a28d32SJaewon Kim 
515*27a28d32SJaewon Kim 		extcon_set_cable_state(info->edev,
516*27a28d32SJaewon Kim 				"CHARGER-DOWNSTREAM", attached);
517*27a28d32SJaewon Kim 		break;
518*27a28d32SJaewon Kim 	case MAX77843_MUIC_CHG_DEDICATED:
519*27a28d32SJaewon Kim 		ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached);
520*27a28d32SJaewon Kim 		if (ret < 0)
521*27a28d32SJaewon Kim 			return ret;
522*27a28d32SJaewon Kim 
523*27a28d32SJaewon Kim 		extcon_set_cable_state(info->edev, "TA", attached);
524*27a28d32SJaewon Kim 		break;
525*27a28d32SJaewon Kim 	case MAX77843_MUIC_CHG_SPECIAL_500MA:
526*27a28d32SJaewon Kim 		ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached);
527*27a28d32SJaewon Kim 		if (ret < 0)
528*27a28d32SJaewon Kim 			return ret;
529*27a28d32SJaewon Kim 
530*27a28d32SJaewon Kim 		extcon_set_cable_state(info->edev, "SLOW-CHAREGER", attached);
531*27a28d32SJaewon Kim 		break;
532*27a28d32SJaewon Kim 	case MAX77843_MUIC_CHG_SPECIAL_1A:
533*27a28d32SJaewon Kim 		ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached);
534*27a28d32SJaewon Kim 		if (ret < 0)
535*27a28d32SJaewon Kim 			return ret;
536*27a28d32SJaewon Kim 
537*27a28d32SJaewon Kim 		extcon_set_cable_state(info->edev, "FAST-CHARGER", attached);
538*27a28d32SJaewon Kim 		break;
539*27a28d32SJaewon Kim 	case MAX77843_MUIC_CHG_GND:
540*27a28d32SJaewon Kim 		gnd_type = max77843_muic_get_cable_type(info,
541*27a28d32SJaewon Kim 				MAX77843_CABLE_GROUP_ADC_GND, &attached);
542*27a28d32SJaewon Kim 
543*27a28d32SJaewon Kim 		/* Charger cable on MHL accessory is attach or detach */
544*27a28d32SJaewon Kim 		if (gnd_type == MAX77843_MUIC_GND_MHL_VB)
545*27a28d32SJaewon Kim 			extcon_set_cable_state(info->edev, "MHL-TA", true);
546*27a28d32SJaewon Kim 		else if (gnd_type == MAX77843_MUIC_GND_MHL)
547*27a28d32SJaewon Kim 			extcon_set_cable_state(info->edev, "MHL-TA", false);
548*27a28d32SJaewon Kim 		break;
549*27a28d32SJaewon Kim 	case MAX77843_MUIC_CHG_NONE:
550*27a28d32SJaewon Kim 		break;
551*27a28d32SJaewon Kim 	default:
552*27a28d32SJaewon Kim 		dev_err(info->dev,
553*27a28d32SJaewon Kim 			"failed to detect %s accessory (chg_type:0x%x)\n",
554*27a28d32SJaewon Kim 			attached ? "attached" : "detached", chg_type);
555*27a28d32SJaewon Kim 
556*27a28d32SJaewon Kim 		max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached);
557*27a28d32SJaewon Kim 		return -EINVAL;
558*27a28d32SJaewon Kim 	}
559*27a28d32SJaewon Kim 
560*27a28d32SJaewon Kim 	return 0;
561*27a28d32SJaewon Kim }
562*27a28d32SJaewon Kim 
563*27a28d32SJaewon Kim static void max77843_muic_irq_work(struct work_struct *work)
564*27a28d32SJaewon Kim {
565*27a28d32SJaewon Kim 	struct max77843_muic_info *info = container_of(work,
566*27a28d32SJaewon Kim 			struct max77843_muic_info, irq_work);
567*27a28d32SJaewon Kim 	struct max77843 *max77843 = info->max77843;
568*27a28d32SJaewon Kim 	int ret = 0;
569*27a28d32SJaewon Kim 
570*27a28d32SJaewon Kim 	mutex_lock(&info->mutex);
571*27a28d32SJaewon Kim 
572*27a28d32SJaewon Kim 	ret = regmap_bulk_read(max77843->regmap_muic,
573*27a28d32SJaewon Kim 			MAX77843_MUIC_REG_STATUS1, info->status,
574*27a28d32SJaewon Kim 			MAX77843_MUIC_STATUS_NUM);
575*27a28d32SJaewon Kim 	if (ret) {
576*27a28d32SJaewon Kim 		dev_err(info->dev, "Cannot read STATUS registers\n");
577*27a28d32SJaewon Kim 		mutex_unlock(&info->mutex);
578*27a28d32SJaewon Kim 		return;
579*27a28d32SJaewon Kim 	}
580*27a28d32SJaewon Kim 
581*27a28d32SJaewon Kim 	if (info->irq_adc) {
582*27a28d32SJaewon Kim 		ret = max77843_muic_adc_handler(info);
583*27a28d32SJaewon Kim 		if (ret)
584*27a28d32SJaewon Kim 			dev_err(info->dev, "Unknown cable type\n");
585*27a28d32SJaewon Kim 		info->irq_adc = false;
586*27a28d32SJaewon Kim 	}
587*27a28d32SJaewon Kim 
588*27a28d32SJaewon Kim 	if (info->irq_chg) {
589*27a28d32SJaewon Kim 		ret = max77843_muic_chg_handler(info);
590*27a28d32SJaewon Kim 		if (ret)
591*27a28d32SJaewon Kim 			dev_err(info->dev, "Unknown charger type\n");
592*27a28d32SJaewon Kim 		info->irq_chg = false;
593*27a28d32SJaewon Kim 	}
594*27a28d32SJaewon Kim 
595*27a28d32SJaewon Kim 	mutex_unlock(&info->mutex);
596*27a28d32SJaewon Kim }
597*27a28d32SJaewon Kim 
598*27a28d32SJaewon Kim static irqreturn_t max77843_muic_irq_handler(int irq, void *data)
599*27a28d32SJaewon Kim {
600*27a28d32SJaewon Kim 	struct max77843_muic_info *info = data;
601*27a28d32SJaewon Kim 	int i, irq_type = -1;
602*27a28d32SJaewon Kim 
603*27a28d32SJaewon Kim 	for (i = 0; i < ARRAY_SIZE(max77843_muic_irqs); i++)
604*27a28d32SJaewon Kim 		if (irq == max77843_muic_irqs[i].virq)
605*27a28d32SJaewon Kim 			irq_type = max77843_muic_irqs[i].irq;
606*27a28d32SJaewon Kim 
607*27a28d32SJaewon Kim 	switch (irq_type) {
608*27a28d32SJaewon Kim 	case MAX77843_MUIC_IRQ_INT1_ADC:
609*27a28d32SJaewon Kim 	case MAX77843_MUIC_IRQ_INT1_ADCERROR:
610*27a28d32SJaewon Kim 	case MAX77843_MUIC_IRQ_INT1_ADC1K:
611*27a28d32SJaewon Kim 		info->irq_adc = true;
612*27a28d32SJaewon Kim 		break;
613*27a28d32SJaewon Kim 	case MAX77843_MUIC_IRQ_INT2_CHGTYP:
614*27a28d32SJaewon Kim 	case MAX77843_MUIC_IRQ_INT2_CHGDETRUN:
615*27a28d32SJaewon Kim 	case MAX77843_MUIC_IRQ_INT2_DCDTMR:
616*27a28d32SJaewon Kim 	case MAX77843_MUIC_IRQ_INT2_DXOVP:
617*27a28d32SJaewon Kim 	case MAX77843_MUIC_IRQ_INT2_VBVOLT:
618*27a28d32SJaewon Kim 		info->irq_chg = true;
619*27a28d32SJaewon Kim 		break;
620*27a28d32SJaewon Kim 	case MAX77843_MUIC_IRQ_INT3_VBADC:
621*27a28d32SJaewon Kim 	case MAX77843_MUIC_IRQ_INT3_VDNMON:
622*27a28d32SJaewon Kim 	case MAX77843_MUIC_IRQ_INT3_DNRES:
623*27a28d32SJaewon Kim 	case MAX77843_MUIC_IRQ_INT3_MPNACK:
624*27a28d32SJaewon Kim 	case MAX77843_MUIC_IRQ_INT3_MRXBUFOW:
625*27a28d32SJaewon Kim 	case MAX77843_MUIC_IRQ_INT3_MRXTRF:
626*27a28d32SJaewon Kim 	case MAX77843_MUIC_IRQ_INT3_MRXPERR:
627*27a28d32SJaewon Kim 	case MAX77843_MUIC_IRQ_INT3_MRXRDY:
628*27a28d32SJaewon Kim 		break;
629*27a28d32SJaewon Kim 	default:
630*27a28d32SJaewon Kim 		dev_err(info->dev, "Cannot recognize IRQ(%d)\n", irq_type);
631*27a28d32SJaewon Kim 		break;
632*27a28d32SJaewon Kim 	}
633*27a28d32SJaewon Kim 
634*27a28d32SJaewon Kim 	schedule_work(&info->irq_work);
635*27a28d32SJaewon Kim 
636*27a28d32SJaewon Kim 	return IRQ_HANDLED;
637*27a28d32SJaewon Kim }
638*27a28d32SJaewon Kim 
639*27a28d32SJaewon Kim static void max77843_muic_detect_cable_wq(struct work_struct *work)
640*27a28d32SJaewon Kim {
641*27a28d32SJaewon Kim 	struct max77843_muic_info *info = container_of(to_delayed_work(work),
642*27a28d32SJaewon Kim 			struct max77843_muic_info, wq_detcable);
643*27a28d32SJaewon Kim 	struct max77843 *max77843 = info->max77843;
644*27a28d32SJaewon Kim 	int chg_type, adc, ret;
645*27a28d32SJaewon Kim 	bool attached;
646*27a28d32SJaewon Kim 
647*27a28d32SJaewon Kim 	mutex_lock(&info->mutex);
648*27a28d32SJaewon Kim 
649*27a28d32SJaewon Kim 	ret = regmap_bulk_read(max77843->regmap_muic,
650*27a28d32SJaewon Kim 			MAX77843_MUIC_REG_STATUS1, info->status,
651*27a28d32SJaewon Kim 			MAX77843_MUIC_STATUS_NUM);
652*27a28d32SJaewon Kim 	if (ret) {
653*27a28d32SJaewon Kim 		dev_err(info->dev, "Cannot read STATUS registers\n");
654*27a28d32SJaewon Kim 		goto err_cable_wq;
655*27a28d32SJaewon Kim 	}
656*27a28d32SJaewon Kim 
657*27a28d32SJaewon Kim 	adc = max77843_muic_get_cable_type(info,
658*27a28d32SJaewon Kim 			MAX77843_CABLE_GROUP_ADC, &attached);
659*27a28d32SJaewon Kim 	if (attached && adc != MAX77843_MUIC_ADC_OPEN) {
660*27a28d32SJaewon Kim 		ret = max77843_muic_adc_handler(info);
661*27a28d32SJaewon Kim 		if (ret < 0) {
662*27a28d32SJaewon Kim 			dev_err(info->dev, "Cannot detect accessory\n");
663*27a28d32SJaewon Kim 			goto err_cable_wq;
664*27a28d32SJaewon Kim 		}
665*27a28d32SJaewon Kim 	}
666*27a28d32SJaewon Kim 
667*27a28d32SJaewon Kim 	chg_type = max77843_muic_get_cable_type(info,
668*27a28d32SJaewon Kim 			MAX77843_CABLE_GROUP_CHG, &attached);
669*27a28d32SJaewon Kim 	if (attached && chg_type != MAX77843_MUIC_CHG_NONE) {
670*27a28d32SJaewon Kim 		ret = max77843_muic_chg_handler(info);
671*27a28d32SJaewon Kim 		if (ret < 0) {
672*27a28d32SJaewon Kim 			dev_err(info->dev, "Cannot detect charger accessory\n");
673*27a28d32SJaewon Kim 			goto err_cable_wq;
674*27a28d32SJaewon Kim 		}
675*27a28d32SJaewon Kim 	}
676*27a28d32SJaewon Kim 
677*27a28d32SJaewon Kim err_cable_wq:
678*27a28d32SJaewon Kim 	mutex_unlock(&info->mutex);
679*27a28d32SJaewon Kim }
680*27a28d32SJaewon Kim 
681*27a28d32SJaewon Kim static int max77843_muic_set_debounce_time(struct max77843_muic_info *info,
682*27a28d32SJaewon Kim 		enum max77843_muic_adc_debounce_time time)
683*27a28d32SJaewon Kim {
684*27a28d32SJaewon Kim 	struct max77843 *max77843 = info->max77843;
685*27a28d32SJaewon Kim 	unsigned int ret;
686*27a28d32SJaewon Kim 
687*27a28d32SJaewon Kim 	switch (time) {
688*27a28d32SJaewon Kim 	case MAX77843_DEBOUNCE_TIME_5MS:
689*27a28d32SJaewon Kim 	case MAX77843_DEBOUNCE_TIME_10MS:
690*27a28d32SJaewon Kim 	case MAX77843_DEBOUNCE_TIME_25MS:
691*27a28d32SJaewon Kim 	case MAX77843_DEBOUNCE_TIME_38_62MS:
692*27a28d32SJaewon Kim 		ret = regmap_update_bits(max77843->regmap_muic,
693*27a28d32SJaewon Kim 				MAX77843_MUIC_REG_CONTROL4,
694*27a28d32SJaewon Kim 				MAX77843_MUIC_CONTROL4_ADCDBSET_MASK,
695*27a28d32SJaewon Kim 				time << CONTROL4_ADCDBSET_SHIFT);
696*27a28d32SJaewon Kim 		if (ret < 0) {
697*27a28d32SJaewon Kim 			dev_err(info->dev, "Cannot write MUIC regmap\n");
698*27a28d32SJaewon Kim 			return ret;
699*27a28d32SJaewon Kim 		}
700*27a28d32SJaewon Kim 		break;
701*27a28d32SJaewon Kim 	default:
702*27a28d32SJaewon Kim 		dev_err(info->dev, "Invalid ADC debounce time\n");
703*27a28d32SJaewon Kim 		return -EINVAL;
704*27a28d32SJaewon Kim 	}
705*27a28d32SJaewon Kim 
706*27a28d32SJaewon Kim 	return 0;
707*27a28d32SJaewon Kim }
708*27a28d32SJaewon Kim 
709*27a28d32SJaewon Kim static int max77843_init_muic_regmap(struct max77843 *max77843)
710*27a28d32SJaewon Kim {
711*27a28d32SJaewon Kim 	int ret;
712*27a28d32SJaewon Kim 
713*27a28d32SJaewon Kim 	max77843->i2c_muic = i2c_new_dummy(max77843->i2c->adapter,
714*27a28d32SJaewon Kim 			I2C_ADDR_MUIC);
715*27a28d32SJaewon Kim 	if (!max77843->i2c_muic) {
716*27a28d32SJaewon Kim 		dev_err(&max77843->i2c->dev,
717*27a28d32SJaewon Kim 				"Cannot allocate I2C device for MUIC\n");
718*27a28d32SJaewon Kim 		return PTR_ERR(max77843->i2c_muic);
719*27a28d32SJaewon Kim 	}
720*27a28d32SJaewon Kim 
721*27a28d32SJaewon Kim 	i2c_set_clientdata(max77843->i2c_muic, max77843);
722*27a28d32SJaewon Kim 
723*27a28d32SJaewon Kim 	max77843->regmap_muic = devm_regmap_init_i2c(max77843->i2c_muic,
724*27a28d32SJaewon Kim 			&max77843_muic_regmap_config);
725*27a28d32SJaewon Kim 	if (IS_ERR(max77843->regmap_muic)) {
726*27a28d32SJaewon Kim 		ret = PTR_ERR(max77843->regmap_muic);
727*27a28d32SJaewon Kim 		goto err_muic_i2c;
728*27a28d32SJaewon Kim 	}
729*27a28d32SJaewon Kim 
730*27a28d32SJaewon Kim 	ret = regmap_add_irq_chip(max77843->regmap_muic, max77843->irq,
731*27a28d32SJaewon Kim 			IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_SHARED,
732*27a28d32SJaewon Kim 			0, &max77843_muic_irq_chip, &max77843->irq_data_muic);
733*27a28d32SJaewon Kim 	if (ret < 0) {
734*27a28d32SJaewon Kim 		dev_err(&max77843->i2c->dev, "Cannot add MUIC IRQ chip\n");
735*27a28d32SJaewon Kim 		goto err_muic_i2c;
736*27a28d32SJaewon Kim 	}
737*27a28d32SJaewon Kim 
738*27a28d32SJaewon Kim 	return 0;
739*27a28d32SJaewon Kim 
740*27a28d32SJaewon Kim err_muic_i2c:
741*27a28d32SJaewon Kim 	i2c_unregister_device(max77843->i2c_muic);
742*27a28d32SJaewon Kim 
743*27a28d32SJaewon Kim 	return ret;
744*27a28d32SJaewon Kim }
745*27a28d32SJaewon Kim 
746*27a28d32SJaewon Kim static int max77843_muic_probe(struct platform_device *pdev)
747*27a28d32SJaewon Kim {
748*27a28d32SJaewon Kim 	struct max77843 *max77843 = dev_get_drvdata(pdev->dev.parent);
749*27a28d32SJaewon Kim 	struct max77843_muic_info *info;
750*27a28d32SJaewon Kim 	unsigned int id;
751*27a28d32SJaewon Kim 	int i, ret;
752*27a28d32SJaewon Kim 
753*27a28d32SJaewon Kim 	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
754*27a28d32SJaewon Kim 	if (!info)
755*27a28d32SJaewon Kim 		return -ENOMEM;
756*27a28d32SJaewon Kim 
757*27a28d32SJaewon Kim 	info->dev = &pdev->dev;
758*27a28d32SJaewon Kim 	info->max77843 = max77843;
759*27a28d32SJaewon Kim 
760*27a28d32SJaewon Kim 	platform_set_drvdata(pdev, info);
761*27a28d32SJaewon Kim 	mutex_init(&info->mutex);
762*27a28d32SJaewon Kim 
763*27a28d32SJaewon Kim 	/* Initialize i2c and regmap */
764*27a28d32SJaewon Kim 	ret = max77843_init_muic_regmap(max77843);
765*27a28d32SJaewon Kim 	if (ret) {
766*27a28d32SJaewon Kim 		dev_err(&pdev->dev, "Failed to init MUIC regmap\n");
767*27a28d32SJaewon Kim 		return ret;
768*27a28d32SJaewon Kim 	}
769*27a28d32SJaewon Kim 
770*27a28d32SJaewon Kim 	/* Turn off auto detection configuration */
771*27a28d32SJaewon Kim 	ret = regmap_update_bits(max77843->regmap_muic,
772*27a28d32SJaewon Kim 			MAX77843_MUIC_REG_CONTROL4,
773*27a28d32SJaewon Kim 			MAX77843_MUIC_CONTROL4_USBAUTO_MASK |
774*27a28d32SJaewon Kim 			MAX77843_MUIC_CONTROL4_FCTAUTO_MASK,
775*27a28d32SJaewon Kim 			CONTROL4_AUTO_DISABLE);
776*27a28d32SJaewon Kim 
777*27a28d32SJaewon Kim 	/* Initialize extcon device */
778*27a28d32SJaewon Kim 	info->edev = devm_extcon_dev_allocate(&pdev->dev,
779*27a28d32SJaewon Kim 			max77843_extcon_cable);
780*27a28d32SJaewon Kim 	if (IS_ERR(info->edev)) {
781*27a28d32SJaewon Kim 		dev_err(&pdev->dev, "Failed to allocate memory for extcon\n");
782*27a28d32SJaewon Kim 		ret = -ENODEV;
783*27a28d32SJaewon Kim 		goto err_muic_irq;
784*27a28d32SJaewon Kim 	}
785*27a28d32SJaewon Kim 
786*27a28d32SJaewon Kim 	ret = devm_extcon_dev_register(&pdev->dev, info->edev);
787*27a28d32SJaewon Kim 	if (ret) {
788*27a28d32SJaewon Kim 		dev_err(&pdev->dev, "Failed to register extcon device\n");
789*27a28d32SJaewon Kim 		goto err_muic_irq;
790*27a28d32SJaewon Kim 	}
791*27a28d32SJaewon Kim 
792*27a28d32SJaewon Kim 	/* Set ADC debounce time */
793*27a28d32SJaewon Kim 	max77843_muic_set_debounce_time(info, MAX77843_DEBOUNCE_TIME_25MS);
794*27a28d32SJaewon Kim 
795*27a28d32SJaewon Kim 	/* Set initial path for UART */
796*27a28d32SJaewon Kim 	max77843_muic_set_path(info, CONTROL1_SW_UART, true);
797*27a28d32SJaewon Kim 
798*27a28d32SJaewon Kim 	/* Check revision number of MUIC device */
799*27a28d32SJaewon Kim 	ret = regmap_read(max77843->regmap_muic, MAX77843_MUIC_REG_ID, &id);
800*27a28d32SJaewon Kim 	if (ret < 0) {
801*27a28d32SJaewon Kim 		dev_err(&pdev->dev, "Failed to read revision number\n");
802*27a28d32SJaewon Kim 		goto err_muic_irq;
803*27a28d32SJaewon Kim 	}
804*27a28d32SJaewon Kim 	dev_info(info->dev, "MUIC device ID : 0x%x\n", id);
805*27a28d32SJaewon Kim 
806*27a28d32SJaewon Kim 	/* Support virtual irq domain for max77843 MUIC device */
807*27a28d32SJaewon Kim 	INIT_WORK(&info->irq_work, max77843_muic_irq_work);
808*27a28d32SJaewon Kim 
809*27a28d32SJaewon Kim 	for (i = 0; i < ARRAY_SIZE(max77843_muic_irqs); i++) {
810*27a28d32SJaewon Kim 		struct max77843_muic_irq *muic_irq = &max77843_muic_irqs[i];
811*27a28d32SJaewon Kim 		unsigned int virq = 0;
812*27a28d32SJaewon Kim 
813*27a28d32SJaewon Kim 		virq = regmap_irq_get_virq(max77843->irq_data_muic,
814*27a28d32SJaewon Kim 				muic_irq->irq);
815*27a28d32SJaewon Kim 		if (virq <= 0) {
816*27a28d32SJaewon Kim 			ret = -EINVAL;
817*27a28d32SJaewon Kim 			goto err_muic_irq;
818*27a28d32SJaewon Kim 		}
819*27a28d32SJaewon Kim 		muic_irq->virq = virq;
820*27a28d32SJaewon Kim 
821*27a28d32SJaewon Kim 		ret = devm_request_threaded_irq(&pdev->dev, virq, NULL,
822*27a28d32SJaewon Kim 				max77843_muic_irq_handler, IRQF_NO_SUSPEND,
823*27a28d32SJaewon Kim 				muic_irq->name, info);
824*27a28d32SJaewon Kim 		if (ret) {
825*27a28d32SJaewon Kim 			dev_err(&pdev->dev,
826*27a28d32SJaewon Kim 				"Failed to request irq (IRQ: %d, error: %d)\n",
827*27a28d32SJaewon Kim 				muic_irq->irq, ret);
828*27a28d32SJaewon Kim 			goto err_muic_irq;
829*27a28d32SJaewon Kim 		}
830*27a28d32SJaewon Kim 	}
831*27a28d32SJaewon Kim 
832*27a28d32SJaewon Kim 	/* Detect accessory after completing the initialization of platform */
833*27a28d32SJaewon Kim 	INIT_DELAYED_WORK(&info->wq_detcable, max77843_muic_detect_cable_wq);
834*27a28d32SJaewon Kim 	queue_delayed_work(system_power_efficient_wq,
835*27a28d32SJaewon Kim 			&info->wq_detcable, msecs_to_jiffies(DELAY_MS_DEFAULT));
836*27a28d32SJaewon Kim 
837*27a28d32SJaewon Kim 	return 0;
838*27a28d32SJaewon Kim 
839*27a28d32SJaewon Kim err_muic_irq:
840*27a28d32SJaewon Kim 	regmap_del_irq_chip(max77843->irq, max77843->irq_data_muic);
841*27a28d32SJaewon Kim 	i2c_unregister_device(max77843->i2c_muic);
842*27a28d32SJaewon Kim 
843*27a28d32SJaewon Kim 	return ret;
844*27a28d32SJaewon Kim }
845*27a28d32SJaewon Kim 
846*27a28d32SJaewon Kim static int max77843_muic_remove(struct platform_device *pdev)
847*27a28d32SJaewon Kim {
848*27a28d32SJaewon Kim 	struct max77843_muic_info *info = platform_get_drvdata(pdev);
849*27a28d32SJaewon Kim 	struct max77843 *max77843 = info->max77843;
850*27a28d32SJaewon Kim 
851*27a28d32SJaewon Kim 	cancel_work_sync(&info->irq_work);
852*27a28d32SJaewon Kim 	regmap_del_irq_chip(max77843->irq, max77843->irq_data_muic);
853*27a28d32SJaewon Kim 	i2c_unregister_device(max77843->i2c_muic);
854*27a28d32SJaewon Kim 
855*27a28d32SJaewon Kim 	return 0;
856*27a28d32SJaewon Kim }
857*27a28d32SJaewon Kim 
858*27a28d32SJaewon Kim static const struct platform_device_id max77843_muic_id[] = {
859*27a28d32SJaewon Kim 	{ "max77843-muic", },
860*27a28d32SJaewon Kim 	{ /* sentinel */ },
861*27a28d32SJaewon Kim };
862*27a28d32SJaewon Kim MODULE_DEVICE_TABLE(platform, max77843_muic_id);
863*27a28d32SJaewon Kim 
864*27a28d32SJaewon Kim static struct platform_driver max77843_muic_driver = {
865*27a28d32SJaewon Kim 	.driver		= {
866*27a28d32SJaewon Kim 		.name		= "max77843-muic",
867*27a28d32SJaewon Kim 	},
868*27a28d32SJaewon Kim 	.probe		= max77843_muic_probe,
869*27a28d32SJaewon Kim 	.remove		= max77843_muic_remove,
870*27a28d32SJaewon Kim 	.id_table	= max77843_muic_id,
871*27a28d32SJaewon Kim };
872*27a28d32SJaewon Kim 
873*27a28d32SJaewon Kim static int __init max77843_muic_init(void)
874*27a28d32SJaewon Kim {
875*27a28d32SJaewon Kim 	return platform_driver_register(&max77843_muic_driver);
876*27a28d32SJaewon Kim }
877*27a28d32SJaewon Kim subsys_initcall(max77843_muic_init);
878*27a28d32SJaewon Kim 
879*27a28d32SJaewon Kim MODULE_DESCRIPTION("Maxim MAX77843 Extcon driver");
880*27a28d32SJaewon Kim MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>");
881*27a28d32SJaewon Kim MODULE_LICENSE("GPL");
882