]>
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 | { | |
b76668ba CC |
274 | switch (info->pre_charger_type) { |
275 | case MAX8997_CHARGER_TYPE_USB: | |
276 | extcon_set_cable_state(info->edev, "USB", false); | |
277 | break; | |
278 | case MAX8997_CHARGER_TYPE_DOWNSTREAM_PORT: | |
279 | extcon_set_cable_state(info->edev, "Charge-downstream", false); | |
280 | break; | |
281 | case MAX8997_CHARGER_TYPE_DEDICATED_CHG: | |
282 | extcon_set_cable_state(info->edev, "TA", false); | |
283 | break; | |
284 | case MAX8997_CHARGER_TYPE_500MA: | |
285 | extcon_set_cable_state(info->edev, "Slow-charger", false); | |
286 | break; | |
287 | case MAX8997_CHARGER_TYPE_1A: | |
288 | extcon_set_cable_state(info->edev, "Fast-charger", false); | |
289 | break; | |
290 | default: | |
3cafbd4e | 291 | return -EINVAL; |
b76668ba CC |
292 | break; |
293 | } | |
99f09beb | 294 | |
3cafbd4e | 295 | return 0; |
99f09beb DK |
296 | } |
297 | ||
298 | static int max8997_muic_handle_charger_type(struct max8997_muic_info *info, | |
299 | enum max8997_muic_charger_type charger_type) | |
300 | { | |
99f09beb DK |
301 | u8 adc; |
302 | int ret; | |
303 | ||
304 | ret = max8997_read_reg(info->muic, MAX8997_MUIC_REG_STATUS1, &adc); | |
305 | if (ret) { | |
306 | dev_err(info->dev, "failed to read muic register\n"); | |
307 | goto out; | |
308 | } | |
309 | ||
310 | switch (charger_type) { | |
311 | case MAX8997_CHARGER_TYPE_NONE: | |
b76668ba | 312 | ret = max8997_muic_handle_charger_type_detach(info); |
99f09beb DK |
313 | break; |
314 | case MAX8997_CHARGER_TYPE_USB: | |
315 | if ((adc & STATUS1_ADC_MASK) == MAX8997_ADC_OPEN) { | |
316 | max8997_muic_handle_usb(info, | |
317 | MAX8997_USB_DEVICE, true); | |
318 | } | |
99f09beb DK |
319 | break; |
320 | case MAX8997_CHARGER_TYPE_DOWNSTREAM_PORT: | |
b76668ba CC |
321 | extcon_set_cable_state(info->edev, "Charge-downstream", true); |
322 | break; | |
99f09beb | 323 | case MAX8997_CHARGER_TYPE_DEDICATED_CHG: |
b76668ba CC |
324 | extcon_set_cable_state(info->edev, "TA", true); |
325 | break; | |
99f09beb | 326 | case MAX8997_CHARGER_TYPE_500MA: |
b76668ba CC |
327 | extcon_set_cable_state(info->edev, "Slow-charger", true); |
328 | break; | |
99f09beb | 329 | case MAX8997_CHARGER_TYPE_1A: |
b76668ba | 330 | extcon_set_cable_state(info->edev, "Fast-charger", true); |
99f09beb DK |
331 | break; |
332 | default: | |
b76668ba CC |
333 | ret = -EINVAL; |
334 | goto out; | |
99f09beb DK |
335 | } |
336 | ||
337 | info->pre_charger_type = charger_type; | |
338 | out: | |
339 | return ret; | |
340 | } | |
341 | ||
342 | static void max8997_muic_irq_work(struct work_struct *work) | |
343 | { | |
344 | struct max8997_muic_info *info = container_of(work, | |
345 | struct max8997_muic_info, irq_work); | |
b76668ba | 346 | u8 status[2]; |
71e58782 | 347 | u8 adc, chg_type; |
dca1a71e CC |
348 | int irq_type = 0; |
349 | int i, ret; | |
99f09beb DK |
350 | |
351 | mutex_lock(&info->mutex); | |
352 | ||
353 | ret = max8997_bulk_read(info->muic, MAX8997_MUIC_REG_STATUS1, | |
b76668ba | 354 | 2, status); |
99f09beb DK |
355 | if (ret) { |
356 | dev_err(info->dev, "failed to read muic register\n"); | |
357 | mutex_unlock(&info->mutex); | |
358 | return; | |
359 | } | |
360 | ||
361 | dev_dbg(info->dev, "%s: STATUS1:0x%x, 2:0x%x\n", __func__, | |
362 | status[0], status[1]); | |
363 | ||
dca1a71e CC |
364 | for (i = 0 ; i < ARRAY_SIZE(muic_irqs) ; i++) |
365 | if (info->irq == muic_irqs[i].virq) | |
366 | irq_type = muic_irqs[i].irq; | |
367 | ||
99f09beb | 368 | switch (irq_type) { |
99f09beb DK |
369 | case MAX8997_MUICIRQ_ADC: |
370 | adc = status[0] & STATUS1_ADC_MASK; | |
371 | adc >>= STATUS1_ADC_SHIFT; | |
372 | ||
373 | max8997_muic_handle_adc(info, adc); | |
99f09beb DK |
374 | break; |
375 | case MAX8997_MUICIRQ_ChgTyp: | |
376 | chg_type = status[1] & STATUS2_CHGTYP_MASK; | |
377 | chg_type >>= STATUS2_CHGTYP_SHIFT; | |
378 | ||
379 | max8997_muic_handle_charger_type(info, chg_type); | |
99f09beb DK |
380 | break; |
381 | default: | |
b76668ba CC |
382 | dev_info(info->dev, "misc interrupt: irq %d occurred\n", |
383 | irq_type); | |
99f09beb DK |
384 | break; |
385 | } | |
386 | ||
99f09beb DK |
387 | mutex_unlock(&info->mutex); |
388 | ||
389 | return; | |
390 | } | |
391 | ||
392 | static irqreturn_t max8997_muic_irq_handler(int irq, void *data) | |
393 | { | |
394 | struct max8997_muic_info *info = data; | |
395 | ||
396 | dev_dbg(info->dev, "irq:%d\n", irq); | |
397 | info->irq = irq; | |
398 | ||
399 | schedule_work(&info->irq_work); | |
400 | ||
401 | return IRQ_HANDLED; | |
402 | } | |
403 | ||
404 | static void max8997_muic_detect_dev(struct max8997_muic_info *info) | |
405 | { | |
406 | int ret; | |
407 | u8 status[2], adc, chg_type; | |
408 | ||
409 | ret = max8997_bulk_read(info->muic, MAX8997_MUIC_REG_STATUS1, | |
410 | 2, status); | |
411 | if (ret) { | |
412 | dev_err(info->dev, "failed to read muic register\n"); | |
413 | return; | |
414 | } | |
415 | ||
416 | dev_info(info->dev, "STATUS1:0x%x, STATUS2:0x%x\n", | |
417 | status[0], status[1]); | |
418 | ||
419 | adc = status[0] & STATUS1_ADC_MASK; | |
420 | adc >>= STATUS1_ADC_SHIFT; | |
421 | ||
422 | chg_type = status[1] & STATUS2_CHGTYP_MASK; | |
423 | chg_type >>= STATUS2_CHGTYP_SHIFT; | |
424 | ||
425 | max8997_muic_handle_adc(info, adc); | |
426 | max8997_muic_handle_charger_type(info, chg_type); | |
427 | } | |
428 | ||
44f34fd4 | 429 | static int max8997_muic_probe(struct platform_device *pdev) |
99f09beb | 430 | { |
b76668ba CC |
431 | struct max8997_dev *max8997 = dev_get_drvdata(pdev->dev.parent); |
432 | struct max8997_platform_data *pdata = dev_get_platdata(max8997->dev); | |
99f09beb DK |
433 | struct max8997_muic_info *info; |
434 | int ret, i; | |
435 | ||
436 | info = kzalloc(sizeof(struct max8997_muic_info), GFP_KERNEL); | |
437 | if (!info) { | |
438 | dev_err(&pdev->dev, "failed to allocate memory\n"); | |
439 | ret = -ENOMEM; | |
440 | goto err_kfree; | |
441 | } | |
442 | ||
99f09beb | 443 | info->dev = &pdev->dev; |
b76668ba | 444 | info->muic = max8997->muic; |
99f09beb DK |
445 | |
446 | platform_set_drvdata(pdev, info); | |
447 | mutex_init(&info->mutex); | |
448 | ||
99f09beb DK |
449 | INIT_WORK(&info->irq_work, max8997_muic_irq_work); |
450 | ||
451 | for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) { | |
452 | struct max8997_muic_irq *muic_irq = &muic_irqs[i]; | |
dca1a71e CC |
453 | int virq = 0; |
454 | ||
455 | virq = irq_create_mapping(max8997->irq_domain, muic_irq->irq); | |
456 | if (!virq) | |
457 | goto err_irq; | |
458 | muic_irq->virq = virq; | |
99f09beb | 459 | |
dca1a71e CC |
460 | ret = request_threaded_irq(virq, NULL,max8997_muic_irq_handler, |
461 | 0, muic_irq->name, info); | |
99f09beb DK |
462 | if (ret) { |
463 | dev_err(&pdev->dev, | |
464 | "failed: irq request (IRQ: %d," | |
465 | " error :%d)\n", | |
466 | muic_irq->irq, ret); | |
99f09beb DK |
467 | goto err_irq; |
468 | } | |
469 | } | |
470 | ||
b76668ba CC |
471 | /* External connector */ |
472 | info->edev = kzalloc(sizeof(struct extcon_dev), GFP_KERNEL); | |
473 | if (!info->edev) { | |
474 | dev_err(&pdev->dev, "failed to allocate memory for extcon\n"); | |
475 | ret = -ENOMEM; | |
476 | goto err_irq; | |
477 | } | |
478 | info->edev->name = DEV_NAME; | |
479 | info->edev->supported_cable = max8997_extcon_cable; | |
480 | ret = extcon_dev_register(info->edev, NULL); | |
481 | if (ret) { | |
482 | dev_err(&pdev->dev, "failed to register extcon device\n"); | |
483 | goto err_extcon; | |
484 | } | |
485 | ||
99f09beb | 486 | /* Initialize registers according to platform data */ |
b76668ba CC |
487 | if (pdata->muic_pdata) { |
488 | struct max8997_muic_platform_data *mdata = info->muic_pdata; | |
489 | ||
490 | for (i = 0; i < mdata->num_init_data; i++) { | |
491 | max8997_write_reg(info->muic, mdata->init_data[i].addr, | |
492 | mdata->init_data[i].data); | |
493 | } | |
494 | } | |
99f09beb DK |
495 | |
496 | /* Initial device detection */ | |
497 | max8997_muic_detect_dev(info); | |
498 | ||
499 | return ret; | |
500 | ||
b76668ba CC |
501 | err_extcon: |
502 | kfree(info->edev); | |
99f09beb | 503 | err_irq: |
3241d56e | 504 | while (--i >= 0) |
dca1a71e | 505 | free_irq(muic_irqs[i].virq, info); |
99f09beb DK |
506 | kfree(info); |
507 | err_kfree: | |
508 | return ret; | |
509 | } | |
510 | ||
93ed0327 | 511 | static int max8997_muic_remove(struct platform_device *pdev) |
99f09beb DK |
512 | { |
513 | struct max8997_muic_info *info = platform_get_drvdata(pdev); | |
99f09beb DK |
514 | int i; |
515 | ||
99f09beb | 516 | for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) |
dca1a71e | 517 | free_irq(muic_irqs[i].virq, info); |
71e58782 | 518 | cancel_work_sync(&info->irq_work); |
99f09beb | 519 | |
b76668ba CC |
520 | extcon_dev_unregister(info->edev); |
521 | ||
96c9f05b | 522 | kfree(info->edev); |
99f09beb DK |
523 | kfree(info); |
524 | ||
525 | return 0; | |
526 | } | |
527 | ||
528 | static struct platform_driver max8997_muic_driver = { | |
529 | .driver = { | |
b76668ba | 530 | .name = DEV_NAME, |
99f09beb DK |
531 | .owner = THIS_MODULE, |
532 | }, | |
533 | .probe = max8997_muic_probe, | |
5f7e2228 | 534 | .remove = max8997_muic_remove, |
99f09beb DK |
535 | }; |
536 | ||
b00e126f | 537 | module_platform_driver(max8997_muic_driver); |
99f09beb | 538 | |
b76668ba | 539 | MODULE_DESCRIPTION("Maxim MAX8997 Extcon driver"); |
99f09beb DK |
540 | MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); |
541 | MODULE_LICENSE("GPL"); |