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