Commit | Line | Data |
---|---|---|
99f09beb | 1 | /* |
b76668ba | 2 | * extcon-max8997.c - MAX8997 extcon driver to support MAX8997 MUIC |
99f09beb | 3 | * |
2ca36f4a | 4 | * Copyright (C) 2012 Samsung Electronics |
99f09beb DK |
5 | * Donggeun Kim <dg77.kim@samsung.com> |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
99f09beb DK |
16 | */ |
17 | ||
18 | #include <linux/kernel.h> | |
19 | #include <linux/module.h> | |
20 | #include <linux/i2c.h> | |
21 | #include <linux/slab.h> | |
22 | #include <linux/interrupt.h> | |
23 | #include <linux/err.h> | |
24 | #include <linux/platform_device.h> | |
25 | #include <linux/kobject.h> | |
26 | #include <linux/mfd/max8997.h> | |
27 | #include <linux/mfd/max8997-private.h> | |
b76668ba | 28 | #include <linux/extcon.h> |
dca1a71e | 29 | #include <linux/irqdomain.h> |
b76668ba CC |
30 | |
31 | #define DEV_NAME "max8997-muic" | |
99f09beb | 32 | |
027fcd50 CC |
33 | enum max8997_muic_adc_debounce_time { |
34 | ADC_DEBOUNCE_TIME_0_5MS = 0, /* 0.5ms */ | |
35 | ADC_DEBOUNCE_TIME_10MS, /* 10ms */ | |
36 | ADC_DEBOUNCE_TIME_25MS, /* 25ms */ | |
37 | ADC_DEBOUNCE_TIME_38_62MS, /* 38.62ms */ | |
38 | }; | |
39 | ||
99f09beb DK |
40 | struct max8997_muic_irq { |
41 | unsigned int irq; | |
42 | const char *name; | |
dca1a71e | 43 | unsigned int virq; |
99f09beb DK |
44 | }; |
45 | ||
46 | static struct max8997_muic_irq muic_irqs[] = { | |
47 | { MAX8997_MUICIRQ_ADCError, "muic-ADC_error" }, | |
48 | { MAX8997_MUICIRQ_ADCLow, "muic-ADC_low" }, | |
49 | { MAX8997_MUICIRQ_ADC, "muic-ADC" }, | |
50 | { MAX8997_MUICIRQ_VBVolt, "muic-VB_voltage" }, | |
51 | { MAX8997_MUICIRQ_DBChg, "muic-DB_charger" }, | |
52 | { MAX8997_MUICIRQ_DCDTmr, "muic-DCD_timer" }, | |
53 | { MAX8997_MUICIRQ_ChgDetRun, "muic-CDR_status" }, | |
54 | { MAX8997_MUICIRQ_ChgTyp, "muic-charger_type" }, | |
55 | { MAX8997_MUICIRQ_OVP, "muic-over_voltage" }, | |
56 | }; | |
57 | ||
58 | struct max8997_muic_info { | |
59 | struct device *dev; | |
99f09beb DK |
60 | struct i2c_client *muic; |
61 | struct max8997_muic_platform_data *muic_pdata; | |
62 | ||
63 | int irq; | |
64 | struct work_struct irq_work; | |
65 | ||
66 | enum max8997_muic_charger_type pre_charger_type; | |
67 | int pre_adc; | |
68 | ||
69 | struct mutex mutex; | |
b76668ba CC |
70 | |
71 | struct extcon_dev *edev; | |
72 | }; | |
73 | ||
e3e5bc02 CC |
74 | enum { |
75 | EXTCON_CABLE_USB = 0, | |
76 | EXTCON_CABLE_USB_HOST, | |
77 | EXTCON_CABLE_TA, | |
78 | EXTCON_CABLE_FAST_CHARGER, | |
79 | EXTCON_CABLE_SLOW_CHARGER, | |
80 | EXTCON_CABLE_CHARGE_DOWNSTREAM, | |
81 | EXTCON_CABLE_MHL, | |
82 | EXTCON_CABLE_DOCK_DESK, | |
83 | EXTCON_CABLE_DOCK_CARD, | |
84 | EXTCON_CABLE_JIG, | |
85 | ||
86 | _EXTCON_CABLE_NUM, | |
87 | }; | |
88 | ||
cae93db3 | 89 | static const char *max8997_extcon_cable[] = { |
e3e5bc02 CC |
90 | [EXTCON_CABLE_USB] = "USB", |
91 | [EXTCON_CABLE_USB_HOST] = "USB-Host", | |
92 | [EXTCON_CABLE_TA] = "TA", | |
93 | [EXTCON_CABLE_FAST_CHARGER] = "Fast-charger", | |
94 | [EXTCON_CABLE_SLOW_CHARGER] = "Slow-charger", | |
95 | [EXTCON_CABLE_CHARGE_DOWNSTREAM] = "Charge-downstream", | |
96 | [EXTCON_CABLE_MHL] = "MHL", | |
97 | [EXTCON_CABLE_DOCK_DESK] = "Dock-Desk", | |
98 | [EXTCON_CABLE_DOCK_CARD] = "Dock-Card", | |
99 | [EXTCON_CABLE_JIG] = "JIG", | |
b76668ba CC |
100 | |
101 | NULL, | |
99f09beb DK |
102 | }; |
103 | ||
027fcd50 CC |
104 | /* |
105 | * max8997_muic_set_debounce_time - Set the debounce time of ADC | |
106 | * @info: the instance including private data of max8997 MUIC | |
107 | * @time: the debounce time of ADC | |
108 | */ | |
109 | static int max8997_muic_set_debounce_time(struct max8997_muic_info *info, | |
110 | enum max8997_muic_adc_debounce_time time) | |
111 | { | |
112 | int ret; | |
113 | ||
114 | switch (time) { | |
115 | case ADC_DEBOUNCE_TIME_0_5MS: | |
116 | case ADC_DEBOUNCE_TIME_10MS: | |
117 | case ADC_DEBOUNCE_TIME_25MS: | |
118 | case ADC_DEBOUNCE_TIME_38_62MS: | |
119 | ret = max8997_update_reg(info->muic, | |
120 | MAX8997_MUIC_REG_CONTROL3, | |
121 | time << CONTROL3_ADCDBSET_SHIFT, | |
122 | CONTROL3_ADCDBSET_MASK); | |
123 | if (ret) { | |
124 | dev_err(info->dev, "failed to set ADC debounce time\n"); | |
125 | return -EAGAIN; | |
126 | } | |
127 | break; | |
128 | default: | |
129 | dev_err(info->dev, "invalid ADC debounce time\n"); | |
130 | return -EINVAL; | |
131 | } | |
132 | ||
133 | return 0; | |
134 | }; | |
135 | ||
07c70503 CC |
136 | /* |
137 | * max8997_muic_set_path - Set hardware line according to attached cable | |
138 | * @info: the instance including private data of max8997 MUIC | |
139 | * @value: the path according to attached cable | |
140 | * @attached: the state of cable (true:attached, false:detached) | |
141 | * | |
142 | * The max8997 MUIC device share outside H/W line among a varity of cables, | |
143 | * so this function set internal path of H/W line according to the type of | |
144 | * attached cable. | |
145 | */ | |
146 | static int max8997_muic_set_path(struct max8997_muic_info *info, | |
147 | u8 val, bool attached) | |
148 | { | |
149 | int ret = 0; | |
150 | u8 ctrl1, ctrl2 = 0; | |
151 | ||
152 | if (attached) | |
153 | ctrl1 = val; | |
154 | else | |
155 | ctrl1 = CONTROL1_SW_OPEN; | |
156 | ||
157 | ret = max8997_update_reg(info->muic, | |
158 | MAX8997_MUIC_REG_CONTROL1, ctrl1, COMP_SW_MASK); | |
159 | if (ret < 0) { | |
160 | dev_err(info->dev, "failed to update MUIC register\n"); | |
161 | return -EAGAIN; | |
162 | } | |
163 | ||
164 | if (attached) | |
165 | ctrl2 |= CONTROL2_CPEN_MASK; /* LowPwr=0, CPEn=1 */ | |
166 | else | |
167 | ctrl2 |= CONTROL2_LOWPWR_MASK; /* LowPwr=1, CPEn=0 */ | |
168 | ||
169 | ret = max8997_update_reg(info->muic, | |
170 | MAX8997_MUIC_REG_CONTROL2, ctrl2, | |
171 | CONTROL2_LOWPWR_MASK | CONTROL2_CPEN_MASK); | |
172 | if (ret < 0) { | |
173 | dev_err(info->dev, "failed to update MUIC register\n"); | |
174 | return -EAGAIN; | |
175 | } | |
176 | ||
177 | dev_info(info->dev, | |
178 | "CONTROL1 : 0x%02x, CONTROL2 : 0x%02x, state : %s\n", | |
179 | ctrl1, ctrl2, attached ? "attached" : "detached"); | |
180 | ||
181 | return 0; | |
182 | } | |
183 | ||
99f09beb DK |
184 | static int max8997_muic_handle_usb(struct max8997_muic_info *info, |
185 | enum max8997_muic_usb_type usb_type, bool attached) | |
186 | { | |
99f09beb DK |
187 | int ret = 0; |
188 | ||
189 | if (usb_type == MAX8997_USB_HOST) { | |
07c70503 | 190 | ret = max8997_muic_set_path(info, CONTROL1_SW_USB, attached); |
99f09beb DK |
191 | if (ret) { |
192 | dev_err(info->dev, "failed to update muic register\n"); | |
193 | goto out; | |
194 | } | |
195 | } | |
196 | ||
b76668ba CC |
197 | switch (usb_type) { |
198 | case MAX8997_USB_HOST: | |
199 | extcon_set_cable_state(info->edev, "USB-Host", attached); | |
200 | break; | |
201 | case MAX8997_USB_DEVICE: | |
202 | extcon_set_cable_state(info->edev, "USB", attached); | |
203 | break; | |
204 | default: | |
205 | ret = -EINVAL; | |
206 | break; | |
207 | } | |
208 | ||
99f09beb DK |
209 | out: |
210 | return ret; | |
211 | } | |
212 | ||
99f09beb DK |
213 | static int max8997_muic_handle_dock(struct max8997_muic_info *info, |
214 | int adc, bool attached) | |
215 | { | |
99f09beb DK |
216 | int ret = 0; |
217 | ||
07c70503 | 218 | ret = max8997_muic_set_path(info, CONTROL1_SW_AUDIO, attached); |
99f09beb DK |
219 | if (ret) { |
220 | dev_err(info->dev, "failed to update muic register\n"); | |
221 | goto out; | |
222 | } | |
223 | ||
224 | switch (adc) { | |
225 | case MAX8997_ADC_DESKDOCK: | |
b76668ba | 226 | extcon_set_cable_state(info->edev, "Dock-desk", attached); |
99f09beb DK |
227 | break; |
228 | case MAX8997_ADC_CARDOCK: | |
b76668ba | 229 | extcon_set_cable_state(info->edev, "Dock-card", attached); |
99f09beb DK |
230 | break; |
231 | default: | |
b76668ba | 232 | ret = -EINVAL; |
99f09beb DK |
233 | break; |
234 | } | |
235 | out: | |
236 | return ret; | |
237 | } | |
238 | ||
239 | static int max8997_muic_handle_jig_uart(struct max8997_muic_info *info, | |
240 | bool attached) | |
241 | { | |
99f09beb DK |
242 | int ret = 0; |
243 | ||
244 | /* switch to UART */ | |
07c70503 | 245 | ret = max8997_muic_set_path(info, CONTROL1_SW_UART, attached); |
99f09beb DK |
246 | if (ret) { |
247 | dev_err(info->dev, "failed to update muic register\n"); | |
248 | goto out; | |
249 | } | |
250 | ||
b76668ba | 251 | extcon_set_cable_state(info->edev, "JIG", attached); |
99f09beb DK |
252 | out: |
253 | return ret; | |
254 | } | |
255 | ||
256 | static int max8997_muic_handle_adc_detach(struct max8997_muic_info *info) | |
257 | { | |
258 | int ret = 0; | |
259 | ||
260 | switch (info->pre_adc) { | |
261 | case MAX8997_ADC_GROUND: | |
262 | ret = max8997_muic_handle_usb(info, MAX8997_USB_HOST, false); | |
263 | break; | |
264 | case MAX8997_ADC_MHL: | |
b76668ba | 265 | extcon_set_cable_state(info->edev, "MHL", false); |
99f09beb DK |
266 | break; |
267 | case MAX8997_ADC_JIG_USB_1: | |
268 | case MAX8997_ADC_JIG_USB_2: | |
269 | ret = max8997_muic_handle_usb(info, MAX8997_USB_DEVICE, false); | |
270 | break; | |
271 | case MAX8997_ADC_DESKDOCK: | |
272 | case MAX8997_ADC_CARDOCK: | |
273 | ret = max8997_muic_handle_dock(info, info->pre_adc, false); | |
274 | break; | |
275 | case MAX8997_ADC_JIG_UART: | |
276 | ret = max8997_muic_handle_jig_uart(info, false); | |
277 | break; | |
278 | default: | |
279 | break; | |
280 | } | |
281 | ||
282 | return ret; | |
283 | } | |
284 | ||
285 | static int max8997_muic_handle_adc(struct max8997_muic_info *info, int adc) | |
286 | { | |
287 | int ret = 0; | |
288 | ||
289 | switch (adc) { | |
290 | case MAX8997_ADC_GROUND: | |
291 | ret = max8997_muic_handle_usb(info, MAX8997_USB_HOST, true); | |
292 | break; | |
293 | case MAX8997_ADC_MHL: | |
b76668ba | 294 | extcon_set_cable_state(info->edev, "MHL", true); |
99f09beb DK |
295 | break; |
296 | case MAX8997_ADC_JIG_USB_1: | |
297 | case MAX8997_ADC_JIG_USB_2: | |
298 | ret = max8997_muic_handle_usb(info, MAX8997_USB_DEVICE, true); | |
299 | break; | |
300 | case MAX8997_ADC_DESKDOCK: | |
301 | case MAX8997_ADC_CARDOCK: | |
302 | ret = max8997_muic_handle_dock(info, adc, true); | |
303 | break; | |
304 | case MAX8997_ADC_JIG_UART: | |
305 | ret = max8997_muic_handle_jig_uart(info, true); | |
306 | break; | |
307 | case MAX8997_ADC_OPEN: | |
308 | ret = max8997_muic_handle_adc_detach(info); | |
309 | break; | |
310 | default: | |
b76668ba CC |
311 | ret = -EINVAL; |
312 | goto out; | |
99f09beb DK |
313 | } |
314 | ||
315 | info->pre_adc = adc; | |
b76668ba CC |
316 | out: |
317 | return ret; | |
318 | } | |
319 | ||
320 | static int max8997_muic_handle_charger_type_detach( | |
321 | struct max8997_muic_info *info) | |
322 | { | |
b76668ba CC |
323 | switch (info->pre_charger_type) { |
324 | case MAX8997_CHARGER_TYPE_USB: | |
325 | extcon_set_cable_state(info->edev, "USB", false); | |
326 | break; | |
327 | case MAX8997_CHARGER_TYPE_DOWNSTREAM_PORT: | |
328 | extcon_set_cable_state(info->edev, "Charge-downstream", false); | |
329 | break; | |
330 | case MAX8997_CHARGER_TYPE_DEDICATED_CHG: | |
331 | extcon_set_cable_state(info->edev, "TA", false); | |
332 | break; | |
333 | case MAX8997_CHARGER_TYPE_500MA: | |
334 | extcon_set_cable_state(info->edev, "Slow-charger", false); | |
335 | break; | |
336 | case MAX8997_CHARGER_TYPE_1A: | |
337 | extcon_set_cable_state(info->edev, "Fast-charger", false); | |
338 | break; | |
339 | default: | |
3cafbd4e | 340 | return -EINVAL; |
b76668ba | 341 | } |
99f09beb | 342 | |
3cafbd4e | 343 | return 0; |
99f09beb DK |
344 | } |
345 | ||
346 | static int max8997_muic_handle_charger_type(struct max8997_muic_info *info, | |
347 | enum max8997_muic_charger_type charger_type) | |
348 | { | |
99f09beb DK |
349 | u8 adc; |
350 | int ret; | |
351 | ||
352 | ret = max8997_read_reg(info->muic, MAX8997_MUIC_REG_STATUS1, &adc); | |
353 | if (ret) { | |
354 | dev_err(info->dev, "failed to read muic register\n"); | |
355 | goto out; | |
356 | } | |
357 | ||
358 | switch (charger_type) { | |
359 | case MAX8997_CHARGER_TYPE_NONE: | |
b76668ba | 360 | ret = max8997_muic_handle_charger_type_detach(info); |
99f09beb DK |
361 | break; |
362 | case MAX8997_CHARGER_TYPE_USB: | |
363 | if ((adc & STATUS1_ADC_MASK) == MAX8997_ADC_OPEN) { | |
364 | max8997_muic_handle_usb(info, | |
365 | MAX8997_USB_DEVICE, true); | |
366 | } | |
99f09beb DK |
367 | break; |
368 | case MAX8997_CHARGER_TYPE_DOWNSTREAM_PORT: | |
b76668ba CC |
369 | extcon_set_cable_state(info->edev, "Charge-downstream", true); |
370 | break; | |
99f09beb | 371 | case MAX8997_CHARGER_TYPE_DEDICATED_CHG: |
b76668ba CC |
372 | extcon_set_cable_state(info->edev, "TA", true); |
373 | break; | |
99f09beb | 374 | case MAX8997_CHARGER_TYPE_500MA: |
b76668ba CC |
375 | extcon_set_cable_state(info->edev, "Slow-charger", true); |
376 | break; | |
99f09beb | 377 | case MAX8997_CHARGER_TYPE_1A: |
b76668ba | 378 | extcon_set_cable_state(info->edev, "Fast-charger", true); |
99f09beb DK |
379 | break; |
380 | default: | |
b76668ba CC |
381 | ret = -EINVAL; |
382 | goto out; | |
99f09beb DK |
383 | } |
384 | ||
385 | info->pre_charger_type = charger_type; | |
386 | out: | |
387 | return ret; | |
388 | } | |
389 | ||
390 | static void max8997_muic_irq_work(struct work_struct *work) | |
391 | { | |
392 | struct max8997_muic_info *info = container_of(work, | |
393 | struct max8997_muic_info, irq_work); | |
b76668ba | 394 | u8 status[2]; |
71e58782 | 395 | u8 adc, chg_type; |
dca1a71e CC |
396 | int irq_type = 0; |
397 | int i, ret; | |
99f09beb DK |
398 | |
399 | mutex_lock(&info->mutex); | |
400 | ||
401 | ret = max8997_bulk_read(info->muic, MAX8997_MUIC_REG_STATUS1, | |
b76668ba | 402 | 2, status); |
99f09beb DK |
403 | if (ret) { |
404 | dev_err(info->dev, "failed to read muic register\n"); | |
405 | mutex_unlock(&info->mutex); | |
406 | return; | |
407 | } | |
408 | ||
409 | dev_dbg(info->dev, "%s: STATUS1:0x%x, 2:0x%x\n", __func__, | |
410 | status[0], status[1]); | |
411 | ||
dca1a71e CC |
412 | for (i = 0 ; i < ARRAY_SIZE(muic_irqs) ; i++) |
413 | if (info->irq == muic_irqs[i].virq) | |
414 | irq_type = muic_irqs[i].irq; | |
415 | ||
99f09beb | 416 | switch (irq_type) { |
99f09beb DK |
417 | case MAX8997_MUICIRQ_ADC: |
418 | adc = status[0] & STATUS1_ADC_MASK; | |
419 | adc >>= STATUS1_ADC_SHIFT; | |
420 | ||
421 | max8997_muic_handle_adc(info, adc); | |
99f09beb DK |
422 | break; |
423 | case MAX8997_MUICIRQ_ChgTyp: | |
424 | chg_type = status[1] & STATUS2_CHGTYP_MASK; | |
425 | chg_type >>= STATUS2_CHGTYP_SHIFT; | |
426 | ||
427 | max8997_muic_handle_charger_type(info, chg_type); | |
99f09beb DK |
428 | break; |
429 | default: | |
b76668ba CC |
430 | dev_info(info->dev, "misc interrupt: irq %d occurred\n", |
431 | irq_type); | |
99f09beb DK |
432 | break; |
433 | } | |
434 | ||
99f09beb DK |
435 | mutex_unlock(&info->mutex); |
436 | ||
437 | return; | |
438 | } | |
439 | ||
440 | static irqreturn_t max8997_muic_irq_handler(int irq, void *data) | |
441 | { | |
442 | struct max8997_muic_info *info = data; | |
443 | ||
444 | dev_dbg(info->dev, "irq:%d\n", irq); | |
445 | info->irq = irq; | |
446 | ||
447 | schedule_work(&info->irq_work); | |
448 | ||
449 | return IRQ_HANDLED; | |
450 | } | |
451 | ||
452 | static void max8997_muic_detect_dev(struct max8997_muic_info *info) | |
453 | { | |
454 | int ret; | |
455 | u8 status[2], adc, chg_type; | |
456 | ||
457 | ret = max8997_bulk_read(info->muic, MAX8997_MUIC_REG_STATUS1, | |
458 | 2, status); | |
459 | if (ret) { | |
460 | dev_err(info->dev, "failed to read muic register\n"); | |
461 | return; | |
462 | } | |
463 | ||
464 | dev_info(info->dev, "STATUS1:0x%x, STATUS2:0x%x\n", | |
465 | status[0], status[1]); | |
466 | ||
467 | adc = status[0] & STATUS1_ADC_MASK; | |
468 | adc >>= STATUS1_ADC_SHIFT; | |
469 | ||
470 | chg_type = status[1] & STATUS2_CHGTYP_MASK; | |
471 | chg_type >>= STATUS2_CHGTYP_SHIFT; | |
472 | ||
473 | max8997_muic_handle_adc(info, adc); | |
474 | max8997_muic_handle_charger_type(info, chg_type); | |
475 | } | |
476 | ||
44f34fd4 | 477 | static int max8997_muic_probe(struct platform_device *pdev) |
99f09beb | 478 | { |
b76668ba CC |
479 | struct max8997_dev *max8997 = dev_get_drvdata(pdev->dev.parent); |
480 | struct max8997_platform_data *pdata = dev_get_platdata(max8997->dev); | |
99f09beb DK |
481 | struct max8997_muic_info *info; |
482 | int ret, i; | |
483 | ||
0b672e9b SK |
484 | info = devm_kzalloc(&pdev->dev, sizeof(struct max8997_muic_info), |
485 | GFP_KERNEL); | |
99f09beb DK |
486 | if (!info) { |
487 | dev_err(&pdev->dev, "failed to allocate memory\n"); | |
0b672e9b | 488 | return -ENOMEM; |
99f09beb DK |
489 | } |
490 | ||
99f09beb | 491 | info->dev = &pdev->dev; |
b76668ba | 492 | info->muic = max8997->muic; |
99f09beb DK |
493 | |
494 | platform_set_drvdata(pdev, info); | |
495 | mutex_init(&info->mutex); | |
496 | ||
99f09beb DK |
497 | INIT_WORK(&info->irq_work, max8997_muic_irq_work); |
498 | ||
499 | for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) { | |
500 | struct max8997_muic_irq *muic_irq = &muic_irqs[i]; | |
68c9274d | 501 | unsigned int virq = 0; |
dca1a71e CC |
502 | |
503 | virq = irq_create_mapping(max8997->irq_domain, muic_irq->irq); | |
68c9274d SK |
504 | if (!virq) { |
505 | ret = -EINVAL; | |
dca1a71e | 506 | goto err_irq; |
68c9274d | 507 | } |
dca1a71e | 508 | muic_irq->virq = virq; |
99f09beb | 509 | |
ae3b3215 CC |
510 | ret = request_threaded_irq(virq, NULL, |
511 | max8997_muic_irq_handler, | |
512 | IRQF_NO_SUSPEND, | |
513 | muic_irq->name, info); | |
99f09beb DK |
514 | if (ret) { |
515 | dev_err(&pdev->dev, | |
516 | "failed: irq request (IRQ: %d," | |
517 | " error :%d)\n", | |
518 | muic_irq->irq, ret); | |
99f09beb DK |
519 | goto err_irq; |
520 | } | |
521 | } | |
522 | ||
b76668ba | 523 | /* External connector */ |
0b672e9b SK |
524 | info->edev = devm_kzalloc(&pdev->dev, sizeof(struct extcon_dev), |
525 | GFP_KERNEL); | |
b76668ba CC |
526 | if (!info->edev) { |
527 | dev_err(&pdev->dev, "failed to allocate memory for extcon\n"); | |
528 | ret = -ENOMEM; | |
529 | goto err_irq; | |
530 | } | |
531 | info->edev->name = DEV_NAME; | |
532 | info->edev->supported_cable = max8997_extcon_cable; | |
533 | ret = extcon_dev_register(info->edev, NULL); | |
534 | if (ret) { | |
535 | dev_err(&pdev->dev, "failed to register extcon device\n"); | |
0b672e9b | 536 | goto err_irq; |
b76668ba CC |
537 | } |
538 | ||
99f09beb | 539 | /* Initialize registers according to platform data */ |
b76668ba CC |
540 | if (pdata->muic_pdata) { |
541 | struct max8997_muic_platform_data *mdata = info->muic_pdata; | |
542 | ||
543 | for (i = 0; i < mdata->num_init_data; i++) { | |
544 | max8997_write_reg(info->muic, mdata->init_data[i].addr, | |
545 | mdata->init_data[i].data); | |
546 | } | |
547 | } | |
99f09beb | 548 | |
027fcd50 CC |
549 | /* Set ADC debounce time */ |
550 | max8997_muic_set_debounce_time(info, ADC_DEBOUNCE_TIME_25MS); | |
551 | ||
99f09beb DK |
552 | /* Initial device detection */ |
553 | max8997_muic_detect_dev(info); | |
554 | ||
555 | return ret; | |
556 | ||
557 | err_irq: | |
3241d56e | 558 | while (--i >= 0) |
dca1a71e | 559 | free_irq(muic_irqs[i].virq, info); |
99f09beb DK |
560 | return ret; |
561 | } | |
562 | ||
93ed0327 | 563 | static int max8997_muic_remove(struct platform_device *pdev) |
99f09beb DK |
564 | { |
565 | struct max8997_muic_info *info = platform_get_drvdata(pdev); | |
99f09beb DK |
566 | int i; |
567 | ||
99f09beb | 568 | for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) |
dca1a71e | 569 | free_irq(muic_irqs[i].virq, info); |
71e58782 | 570 | cancel_work_sync(&info->irq_work); |
99f09beb | 571 | |
b76668ba CC |
572 | extcon_dev_unregister(info->edev); |
573 | ||
99f09beb DK |
574 | return 0; |
575 | } | |
576 | ||
577 | static struct platform_driver max8997_muic_driver = { | |
578 | .driver = { | |
b76668ba | 579 | .name = DEV_NAME, |
99f09beb DK |
580 | .owner = THIS_MODULE, |
581 | }, | |
582 | .probe = max8997_muic_probe, | |
5f7e2228 | 583 | .remove = max8997_muic_remove, |
99f09beb DK |
584 | }; |
585 | ||
b00e126f | 586 | module_platform_driver(max8997_muic_driver); |
99f09beb | 587 | |
b76668ba | 588 | MODULE_DESCRIPTION("Maxim MAX8997 Extcon driver"); |
99f09beb DK |
589 | MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); |
590 | MODULE_LICENSE("GPL"); |