]>
Commit | Line | Data |
---|---|---|
99f09beb | 1 | /* |
b76668ba | 2 | * extcon-max8997.c - MAX8997 extcon driver to support MAX8997 MUIC |
99f09beb | 3 | * |
b76668ba | 4 | * Copyright (C) 2012 Samsung Electrnoics |
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 DK |
32 | |
33 | /* MAX8997-MUIC STATUS1 register */ | |
34 | #define STATUS1_ADC_SHIFT 0 | |
35 | #define STATUS1_ADCLOW_SHIFT 5 | |
36 | #define STATUS1_ADCERR_SHIFT 6 | |
37 | #define STATUS1_ADC_MASK (0x1f << STATUS1_ADC_SHIFT) | |
38 | #define STATUS1_ADCLOW_MASK (0x1 << STATUS1_ADCLOW_SHIFT) | |
39 | #define STATUS1_ADCERR_MASK (0x1 << STATUS1_ADCERR_SHIFT) | |
40 | ||
41 | /* MAX8997-MUIC STATUS2 register */ | |
42 | #define STATUS2_CHGTYP_SHIFT 0 | |
43 | #define STATUS2_CHGDETRUN_SHIFT 3 | |
44 | #define STATUS2_DCDTMR_SHIFT 4 | |
45 | #define STATUS2_DBCHG_SHIFT 5 | |
46 | #define STATUS2_VBVOLT_SHIFT 6 | |
47 | #define STATUS2_CHGTYP_MASK (0x7 << STATUS2_CHGTYP_SHIFT) | |
48 | #define STATUS2_CHGDETRUN_MASK (0x1 << STATUS2_CHGDETRUN_SHIFT) | |
49 | #define STATUS2_DCDTMR_MASK (0x1 << STATUS2_DCDTMR_SHIFT) | |
50 | #define STATUS2_DBCHG_MASK (0x1 << STATUS2_DBCHG_SHIFT) | |
51 | #define STATUS2_VBVOLT_MASK (0x1 << STATUS2_VBVOLT_SHIFT) | |
52 | ||
53 | /* MAX8997-MUIC STATUS3 register */ | |
54 | #define STATUS3_OVP_SHIFT 2 | |
55 | #define STATUS3_OVP_MASK (0x1 << STATUS3_OVP_SHIFT) | |
56 | ||
57 | /* MAX8997-MUIC CONTROL1 register */ | |
58 | #define COMN1SW_SHIFT 0 | |
59 | #define COMP2SW_SHIFT 3 | |
60 | #define COMN1SW_MASK (0x7 << COMN1SW_SHIFT) | |
61 | #define COMP2SW_MASK (0x7 << COMP2SW_SHIFT) | |
62 | #define SW_MASK (COMP2SW_MASK | COMN1SW_MASK) | |
63 | ||
64 | #define MAX8997_SW_USB ((1 << COMP2SW_SHIFT) | (1 << COMN1SW_SHIFT)) | |
65 | #define MAX8997_SW_AUDIO ((2 << COMP2SW_SHIFT) | (2 << COMN1SW_SHIFT)) | |
66 | #define MAX8997_SW_UART ((3 << COMP2SW_SHIFT) | (3 << COMN1SW_SHIFT)) | |
67 | #define MAX8997_SW_OPEN ((0 << COMP2SW_SHIFT) | (0 << COMN1SW_SHIFT)) | |
68 | ||
69 | #define MAX8997_ADC_GROUND 0x00 | |
70 | #define MAX8997_ADC_MHL 0x01 | |
71 | #define MAX8997_ADC_JIG_USB_1 0x18 | |
72 | #define MAX8997_ADC_JIG_USB_2 0x19 | |
73 | #define MAX8997_ADC_DESKDOCK 0x1a | |
74 | #define MAX8997_ADC_JIG_UART 0x1c | |
75 | #define MAX8997_ADC_CARDOCK 0x1d | |
76 | #define MAX8997_ADC_OPEN 0x1f | |
77 | ||
78 | struct max8997_muic_irq { | |
79 | unsigned int irq; | |
80 | const char *name; | |
dca1a71e | 81 | unsigned int virq; |
99f09beb DK |
82 | }; |
83 | ||
84 | static struct max8997_muic_irq muic_irqs[] = { | |
85 | { MAX8997_MUICIRQ_ADCError, "muic-ADC_error" }, | |
86 | { MAX8997_MUICIRQ_ADCLow, "muic-ADC_low" }, | |
87 | { MAX8997_MUICIRQ_ADC, "muic-ADC" }, | |
88 | { MAX8997_MUICIRQ_VBVolt, "muic-VB_voltage" }, | |
89 | { MAX8997_MUICIRQ_DBChg, "muic-DB_charger" }, | |
90 | { MAX8997_MUICIRQ_DCDTmr, "muic-DCD_timer" }, | |
91 | { MAX8997_MUICIRQ_ChgDetRun, "muic-CDR_status" }, | |
92 | { MAX8997_MUICIRQ_ChgTyp, "muic-charger_type" }, | |
93 | { MAX8997_MUICIRQ_OVP, "muic-over_voltage" }, | |
94 | }; | |
95 | ||
96 | struct max8997_muic_info { | |
97 | struct device *dev; | |
99f09beb DK |
98 | struct i2c_client *muic; |
99 | struct max8997_muic_platform_data *muic_pdata; | |
100 | ||
101 | int irq; | |
102 | struct work_struct irq_work; | |
103 | ||
104 | enum max8997_muic_charger_type pre_charger_type; | |
105 | int pre_adc; | |
106 | ||
107 | struct mutex mutex; | |
b76668ba CC |
108 | |
109 | struct extcon_dev *edev; | |
110 | }; | |
111 | ||
112 | const char *max8997_extcon_cable[] = { | |
113 | [0] = "USB", | |
114 | [1] = "USB-Host", | |
115 | [2] = "TA", | |
116 | [3] = "Fast-charger", | |
117 | [4] = "Slow-charger", | |
118 | [5] = "Charge-downstream", | |
119 | [6] = "MHL", | |
120 | [7] = "Dock-desk", | |
155cb06c AL |
121 | [8] = "Dock-card", |
122 | [9] = "JIG", | |
b76668ba CC |
123 | |
124 | NULL, | |
99f09beb DK |
125 | }; |
126 | ||
127 | static int max8997_muic_handle_usb(struct max8997_muic_info *info, | |
128 | enum max8997_muic_usb_type usb_type, bool attached) | |
129 | { | |
99f09beb DK |
130 | int ret = 0; |
131 | ||
132 | if (usb_type == MAX8997_USB_HOST) { | |
133 | /* switch to USB */ | |
134 | ret = max8997_update_reg(info->muic, MAX8997_MUIC_REG_CONTROL1, | |
135 | attached ? MAX8997_SW_USB : MAX8997_SW_OPEN, | |
136 | SW_MASK); | |
137 | if (ret) { | |
138 | dev_err(info->dev, "failed to update muic register\n"); | |
139 | goto out; | |
140 | } | |
141 | } | |
142 | ||
b76668ba CC |
143 | switch (usb_type) { |
144 | case MAX8997_USB_HOST: | |
145 | extcon_set_cable_state(info->edev, "USB-Host", attached); | |
146 | break; | |
147 | case MAX8997_USB_DEVICE: | |
148 | extcon_set_cable_state(info->edev, "USB", attached); | |
149 | break; | |
150 | default: | |
151 | ret = -EINVAL; | |
152 | break; | |
153 | } | |
154 | ||
99f09beb DK |
155 | out: |
156 | return ret; | |
157 | } | |
158 | ||
99f09beb DK |
159 | static int max8997_muic_handle_dock(struct max8997_muic_info *info, |
160 | int adc, bool attached) | |
161 | { | |
99f09beb DK |
162 | int ret = 0; |
163 | ||
164 | /* switch to AUDIO */ | |
165 | ret = max8997_update_reg(info->muic, MAX8997_MUIC_REG_CONTROL1, | |
166 | attached ? MAX8997_SW_AUDIO : MAX8997_SW_OPEN, | |
167 | SW_MASK); | |
168 | if (ret) { | |
169 | dev_err(info->dev, "failed to update muic register\n"); | |
170 | goto out; | |
171 | } | |
172 | ||
173 | switch (adc) { | |
174 | case MAX8997_ADC_DESKDOCK: | |
b76668ba | 175 | extcon_set_cable_state(info->edev, "Dock-desk", attached); |
99f09beb DK |
176 | break; |
177 | case MAX8997_ADC_CARDOCK: | |
b76668ba | 178 | extcon_set_cable_state(info->edev, "Dock-card", attached); |
99f09beb DK |
179 | break; |
180 | default: | |
b76668ba | 181 | ret = -EINVAL; |
99f09beb DK |
182 | break; |
183 | } | |
184 | out: | |
185 | return ret; | |
186 | } | |
187 | ||
188 | static int max8997_muic_handle_jig_uart(struct max8997_muic_info *info, | |
189 | bool attached) | |
190 | { | |
99f09beb DK |
191 | int ret = 0; |
192 | ||
193 | /* switch to UART */ | |
194 | ret = max8997_update_reg(info->muic, MAX8997_MUIC_REG_CONTROL1, | |
195 | attached ? MAX8997_SW_UART : MAX8997_SW_OPEN, | |
196 | SW_MASK); | |
197 | if (ret) { | |
198 | dev_err(info->dev, "failed to update muic register\n"); | |
199 | goto out; | |
200 | } | |
201 | ||
b76668ba | 202 | extcon_set_cable_state(info->edev, "JIG", attached); |
99f09beb DK |
203 | out: |
204 | return ret; | |
205 | } | |
206 | ||
207 | static int max8997_muic_handle_adc_detach(struct max8997_muic_info *info) | |
208 | { | |
209 | int ret = 0; | |
210 | ||
211 | switch (info->pre_adc) { | |
212 | case MAX8997_ADC_GROUND: | |
213 | ret = max8997_muic_handle_usb(info, MAX8997_USB_HOST, false); | |
214 | break; | |
215 | case MAX8997_ADC_MHL: | |
b76668ba | 216 | extcon_set_cable_state(info->edev, "MHL", false); |
99f09beb DK |
217 | break; |
218 | case MAX8997_ADC_JIG_USB_1: | |
219 | case MAX8997_ADC_JIG_USB_2: | |
220 | ret = max8997_muic_handle_usb(info, MAX8997_USB_DEVICE, false); | |
221 | break; | |
222 | case MAX8997_ADC_DESKDOCK: | |
223 | case MAX8997_ADC_CARDOCK: | |
224 | ret = max8997_muic_handle_dock(info, info->pre_adc, false); | |
225 | break; | |
226 | case MAX8997_ADC_JIG_UART: | |
227 | ret = max8997_muic_handle_jig_uart(info, false); | |
228 | break; | |
229 | default: | |
230 | break; | |
231 | } | |
232 | ||
233 | return ret; | |
234 | } | |
235 | ||
236 | static int max8997_muic_handle_adc(struct max8997_muic_info *info, int adc) | |
237 | { | |
238 | int ret = 0; | |
239 | ||
240 | switch (adc) { | |
241 | case MAX8997_ADC_GROUND: | |
242 | ret = max8997_muic_handle_usb(info, MAX8997_USB_HOST, true); | |
243 | break; | |
244 | case MAX8997_ADC_MHL: | |
b76668ba | 245 | extcon_set_cable_state(info->edev, "MHL", true); |
99f09beb DK |
246 | break; |
247 | case MAX8997_ADC_JIG_USB_1: | |
248 | case MAX8997_ADC_JIG_USB_2: | |
249 | ret = max8997_muic_handle_usb(info, MAX8997_USB_DEVICE, true); | |
250 | break; | |
251 | case MAX8997_ADC_DESKDOCK: | |
252 | case MAX8997_ADC_CARDOCK: | |
253 | ret = max8997_muic_handle_dock(info, adc, true); | |
254 | break; | |
255 | case MAX8997_ADC_JIG_UART: | |
256 | ret = max8997_muic_handle_jig_uart(info, true); | |
257 | break; | |
258 | case MAX8997_ADC_OPEN: | |
259 | ret = max8997_muic_handle_adc_detach(info); | |
260 | break; | |
261 | default: | |
b76668ba CC |
262 | ret = -EINVAL; |
263 | goto out; | |
99f09beb DK |
264 | } |
265 | ||
266 | info->pre_adc = adc; | |
b76668ba CC |
267 | out: |
268 | return ret; | |
269 | } | |
270 | ||
271 | static int max8997_muic_handle_charger_type_detach( | |
272 | struct max8997_muic_info *info) | |
273 | { | |
274 | int ret = 0; | |
275 | ||
276 | switch (info->pre_charger_type) { | |
277 | case MAX8997_CHARGER_TYPE_USB: | |
278 | extcon_set_cable_state(info->edev, "USB", false); | |
279 | break; | |
280 | case MAX8997_CHARGER_TYPE_DOWNSTREAM_PORT: | |
281 | extcon_set_cable_state(info->edev, "Charge-downstream", false); | |
282 | break; | |
283 | case MAX8997_CHARGER_TYPE_DEDICATED_CHG: | |
284 | extcon_set_cable_state(info->edev, "TA", false); | |
285 | break; | |
286 | case MAX8997_CHARGER_TYPE_500MA: | |
287 | extcon_set_cable_state(info->edev, "Slow-charger", false); | |
288 | break; | |
289 | case MAX8997_CHARGER_TYPE_1A: | |
290 | extcon_set_cable_state(info->edev, "Fast-charger", false); | |
291 | break; | |
292 | default: | |
293 | ret = -EINVAL; | |
294 | break; | |
295 | } | |
99f09beb DK |
296 | |
297 | return ret; | |
298 | } | |
299 | ||
300 | static int max8997_muic_handle_charger_type(struct max8997_muic_info *info, | |
301 | enum max8997_muic_charger_type charger_type) | |
302 | { | |
99f09beb DK |
303 | u8 adc; |
304 | int ret; | |
305 | ||
306 | ret = max8997_read_reg(info->muic, MAX8997_MUIC_REG_STATUS1, &adc); | |
307 | if (ret) { | |
308 | dev_err(info->dev, "failed to read muic register\n"); | |
309 | goto out; | |
310 | } | |
311 | ||
312 | switch (charger_type) { | |
313 | case MAX8997_CHARGER_TYPE_NONE: | |
b76668ba | 314 | ret = max8997_muic_handle_charger_type_detach(info); |
99f09beb DK |
315 | break; |
316 | case MAX8997_CHARGER_TYPE_USB: | |
317 | if ((adc & STATUS1_ADC_MASK) == MAX8997_ADC_OPEN) { | |
318 | max8997_muic_handle_usb(info, | |
319 | MAX8997_USB_DEVICE, true); | |
320 | } | |
99f09beb DK |
321 | break; |
322 | case MAX8997_CHARGER_TYPE_DOWNSTREAM_PORT: | |
b76668ba CC |
323 | extcon_set_cable_state(info->edev, "Charge-downstream", true); |
324 | break; | |
99f09beb | 325 | case MAX8997_CHARGER_TYPE_DEDICATED_CHG: |
b76668ba CC |
326 | extcon_set_cable_state(info->edev, "TA", true); |
327 | break; | |
99f09beb | 328 | case MAX8997_CHARGER_TYPE_500MA: |
b76668ba CC |
329 | extcon_set_cable_state(info->edev, "Slow-charger", true); |
330 | break; | |
99f09beb | 331 | case MAX8997_CHARGER_TYPE_1A: |
b76668ba | 332 | extcon_set_cable_state(info->edev, "Fast-charger", true); |
99f09beb DK |
333 | break; |
334 | default: | |
b76668ba CC |
335 | ret = -EINVAL; |
336 | goto out; | |
99f09beb DK |
337 | } |
338 | ||
339 | info->pre_charger_type = charger_type; | |
340 | out: | |
341 | return ret; | |
342 | } | |
343 | ||
344 | static void max8997_muic_irq_work(struct work_struct *work) | |
345 | { | |
346 | struct max8997_muic_info *info = container_of(work, | |
347 | struct max8997_muic_info, irq_work); | |
b76668ba | 348 | u8 status[2]; |
71e58782 | 349 | u8 adc, chg_type; |
dca1a71e CC |
350 | int irq_type = 0; |
351 | int i, ret; | |
99f09beb DK |
352 | |
353 | mutex_lock(&info->mutex); | |
354 | ||
355 | ret = max8997_bulk_read(info->muic, MAX8997_MUIC_REG_STATUS1, | |
b76668ba | 356 | 2, status); |
99f09beb DK |
357 | if (ret) { |
358 | dev_err(info->dev, "failed to read muic register\n"); | |
359 | mutex_unlock(&info->mutex); | |
360 | return; | |
361 | } | |
362 | ||
363 | dev_dbg(info->dev, "%s: STATUS1:0x%x, 2:0x%x\n", __func__, | |
364 | status[0], status[1]); | |
365 | ||
dca1a71e CC |
366 | for (i = 0 ; i < ARRAY_SIZE(muic_irqs) ; i++) |
367 | if (info->irq == muic_irqs[i].virq) | |
368 | irq_type = muic_irqs[i].irq; | |
369 | ||
99f09beb | 370 | switch (irq_type) { |
99f09beb DK |
371 | case MAX8997_MUICIRQ_ADC: |
372 | adc = status[0] & STATUS1_ADC_MASK; | |
373 | adc >>= STATUS1_ADC_SHIFT; | |
374 | ||
375 | max8997_muic_handle_adc(info, adc); | |
99f09beb DK |
376 | break; |
377 | case MAX8997_MUICIRQ_ChgTyp: | |
378 | chg_type = status[1] & STATUS2_CHGTYP_MASK; | |
379 | chg_type >>= STATUS2_CHGTYP_SHIFT; | |
380 | ||
381 | max8997_muic_handle_charger_type(info, chg_type); | |
99f09beb DK |
382 | break; |
383 | default: | |
b76668ba CC |
384 | dev_info(info->dev, "misc interrupt: irq %d occurred\n", |
385 | irq_type); | |
99f09beb DK |
386 | break; |
387 | } | |
388 | ||
99f09beb DK |
389 | mutex_unlock(&info->mutex); |
390 | ||
391 | return; | |
392 | } | |
393 | ||
394 | static irqreturn_t max8997_muic_irq_handler(int irq, void *data) | |
395 | { | |
396 | struct max8997_muic_info *info = data; | |
397 | ||
398 | dev_dbg(info->dev, "irq:%d\n", irq); | |
399 | info->irq = irq; | |
400 | ||
401 | schedule_work(&info->irq_work); | |
402 | ||
403 | return IRQ_HANDLED; | |
404 | } | |
405 | ||
406 | static void max8997_muic_detect_dev(struct max8997_muic_info *info) | |
407 | { | |
408 | int ret; | |
409 | u8 status[2], adc, chg_type; | |
410 | ||
411 | ret = max8997_bulk_read(info->muic, MAX8997_MUIC_REG_STATUS1, | |
412 | 2, status); | |
413 | if (ret) { | |
414 | dev_err(info->dev, "failed to read muic register\n"); | |
415 | return; | |
416 | } | |
417 | ||
418 | dev_info(info->dev, "STATUS1:0x%x, STATUS2:0x%x\n", | |
419 | status[0], status[1]); | |
420 | ||
421 | adc = status[0] & STATUS1_ADC_MASK; | |
422 | adc >>= STATUS1_ADC_SHIFT; | |
423 | ||
424 | chg_type = status[1] & STATUS2_CHGTYP_MASK; | |
425 | chg_type >>= STATUS2_CHGTYP_SHIFT; | |
426 | ||
427 | max8997_muic_handle_adc(info, adc); | |
428 | max8997_muic_handle_charger_type(info, chg_type); | |
429 | } | |
430 | ||
99f09beb DK |
431 | static int __devinit max8997_muic_probe(struct platform_device *pdev) |
432 | { | |
b76668ba CC |
433 | struct max8997_dev *max8997 = dev_get_drvdata(pdev->dev.parent); |
434 | struct max8997_platform_data *pdata = dev_get_platdata(max8997->dev); | |
99f09beb DK |
435 | struct max8997_muic_info *info; |
436 | int ret, i; | |
437 | ||
438 | info = kzalloc(sizeof(struct max8997_muic_info), GFP_KERNEL); | |
439 | if (!info) { | |
440 | dev_err(&pdev->dev, "failed to allocate memory\n"); | |
441 | ret = -ENOMEM; | |
442 | goto err_kfree; | |
443 | } | |
444 | ||
99f09beb | 445 | info->dev = &pdev->dev; |
b76668ba | 446 | info->muic = max8997->muic; |
99f09beb DK |
447 | |
448 | platform_set_drvdata(pdev, info); | |
449 | mutex_init(&info->mutex); | |
450 | ||
99f09beb DK |
451 | INIT_WORK(&info->irq_work, max8997_muic_irq_work); |
452 | ||
453 | for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) { | |
454 | struct max8997_muic_irq *muic_irq = &muic_irqs[i]; | |
dca1a71e CC |
455 | int virq = 0; |
456 | ||
457 | virq = irq_create_mapping(max8997->irq_domain, muic_irq->irq); | |
458 | if (!virq) | |
459 | goto err_irq; | |
460 | muic_irq->virq = virq; | |
99f09beb | 461 | |
dca1a71e CC |
462 | ret = request_threaded_irq(virq, NULL,max8997_muic_irq_handler, |
463 | 0, muic_irq->name, info); | |
99f09beb DK |
464 | if (ret) { |
465 | dev_err(&pdev->dev, | |
466 | "failed: irq request (IRQ: %d," | |
467 | " error :%d)\n", | |
468 | muic_irq->irq, ret); | |
99f09beb DK |
469 | goto err_irq; |
470 | } | |
471 | } | |
472 | ||
b76668ba CC |
473 | /* External connector */ |
474 | info->edev = kzalloc(sizeof(struct extcon_dev), GFP_KERNEL); | |
475 | if (!info->edev) { | |
476 | dev_err(&pdev->dev, "failed to allocate memory for extcon\n"); | |
477 | ret = -ENOMEM; | |
478 | goto err_irq; | |
479 | } | |
480 | info->edev->name = DEV_NAME; | |
481 | info->edev->supported_cable = max8997_extcon_cable; | |
482 | ret = extcon_dev_register(info->edev, NULL); | |
483 | if (ret) { | |
484 | dev_err(&pdev->dev, "failed to register extcon device\n"); | |
485 | goto err_extcon; | |
486 | } | |
487 | ||
99f09beb | 488 | /* Initialize registers according to platform data */ |
b76668ba CC |
489 | if (pdata->muic_pdata) { |
490 | struct max8997_muic_platform_data *mdata = info->muic_pdata; | |
491 | ||
492 | for (i = 0; i < mdata->num_init_data; i++) { | |
493 | max8997_write_reg(info->muic, mdata->init_data[i].addr, | |
494 | mdata->init_data[i].data); | |
495 | } | |
496 | } | |
99f09beb DK |
497 | |
498 | /* Initial device detection */ | |
499 | max8997_muic_detect_dev(info); | |
500 | ||
501 | return ret; | |
502 | ||
b76668ba CC |
503 | err_extcon: |
504 | kfree(info->edev); | |
99f09beb | 505 | err_irq: |
3241d56e | 506 | while (--i >= 0) |
dca1a71e | 507 | free_irq(muic_irqs[i].virq, info); |
99f09beb DK |
508 | kfree(info); |
509 | err_kfree: | |
510 | return ret; | |
511 | } | |
512 | ||
513 | static int __devexit max8997_muic_remove(struct platform_device *pdev) | |
514 | { | |
515 | struct max8997_muic_info *info = platform_get_drvdata(pdev); | |
99f09beb DK |
516 | int i; |
517 | ||
99f09beb | 518 | for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) |
dca1a71e | 519 | free_irq(muic_irqs[i].virq, info); |
71e58782 | 520 | cancel_work_sync(&info->irq_work); |
99f09beb | 521 | |
b76668ba CC |
522 | extcon_dev_unregister(info->edev); |
523 | ||
96c9f05b | 524 | kfree(info->edev); |
99f09beb DK |
525 | kfree(info); |
526 | ||
527 | return 0; | |
528 | } | |
529 | ||
530 | static struct platform_driver max8997_muic_driver = { | |
531 | .driver = { | |
b76668ba | 532 | .name = DEV_NAME, |
99f09beb DK |
533 | .owner = THIS_MODULE, |
534 | }, | |
535 | .probe = max8997_muic_probe, | |
536 | .remove = __devexit_p(max8997_muic_remove), | |
537 | }; | |
538 | ||
b00e126f | 539 | module_platform_driver(max8997_muic_driver); |
99f09beb | 540 | |
b76668ba | 541 | MODULE_DESCRIPTION("Maxim MAX8997 Extcon driver"); |
99f09beb DK |
542 | MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); |
543 | MODULE_LICENSE("GPL"); |