1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * DA9150 Charger Driver |
4 | * |
5 | * Copyright (c) 2014 Dialog Semiconductor |
6 | * |
7 | * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com> |
8 | */ |
9 | |
10 | #include <linux/kernel.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/module.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/interrupt.h> |
15 | #include <linux/power_supply.h> |
16 | #include <linux/notifier.h> |
17 | #include <linux/usb/phy.h> |
18 | #include <linux/iio/consumer.h> |
19 | #include <linux/mfd/da9150/core.h> |
20 | #include <linux/mfd/da9150/registers.h> |
21 | |
22 | /* Private data */ |
23 | struct da9150_charger { |
24 | struct da9150 *da9150; |
25 | struct device *dev; |
26 | |
27 | struct power_supply *usb; |
28 | struct power_supply *battery; |
29 | struct power_supply *supply_online; |
30 | |
31 | struct usb_phy *usb_phy; |
32 | struct notifier_block otg_nb; |
33 | struct work_struct otg_work; |
34 | unsigned long usb_event; |
35 | |
36 | struct iio_channel *ibus_chan; |
37 | struct iio_channel *vbus_chan; |
38 | struct iio_channel *tjunc_chan; |
39 | struct iio_channel *vbat_chan; |
40 | }; |
41 | |
42 | static inline int da9150_charger_supply_online(struct da9150_charger *charger, |
43 | struct power_supply *psy, |
44 | union power_supply_propval *val) |
45 | { |
46 | val->intval = (psy == charger->supply_online) ? 1 : 0; |
47 | |
48 | return 0; |
49 | } |
50 | |
51 | /* Charger Properties */ |
52 | static int da9150_charger_vbus_voltage_now(struct da9150_charger *charger, |
53 | union power_supply_propval *val) |
54 | { |
55 | int v_val, ret; |
56 | |
57 | /* Read processed value - mV units */ |
58 | ret = iio_read_channel_processed(chan: charger->vbus_chan, val: &v_val); |
59 | if (ret < 0) |
60 | return ret; |
61 | |
62 | /* Convert voltage to expected uV units */ |
63 | val->intval = v_val * 1000; |
64 | |
65 | return 0; |
66 | } |
67 | |
68 | static int da9150_charger_ibus_current_avg(struct da9150_charger *charger, |
69 | union power_supply_propval *val) |
70 | { |
71 | int i_val, ret; |
72 | |
73 | /* Read processed value - mA units */ |
74 | ret = iio_read_channel_processed(chan: charger->ibus_chan, val: &i_val); |
75 | if (ret < 0) |
76 | return ret; |
77 | |
78 | /* Convert current to expected uA units */ |
79 | val->intval = i_val * 1000; |
80 | |
81 | return 0; |
82 | } |
83 | |
84 | static int da9150_charger_tjunc_temp(struct da9150_charger *charger, |
85 | union power_supply_propval *val) |
86 | { |
87 | int t_val, ret; |
88 | |
89 | /* Read processed value - 0.001 degrees C units */ |
90 | ret = iio_read_channel_processed(chan: charger->tjunc_chan, val: &t_val); |
91 | if (ret < 0) |
92 | return ret; |
93 | |
94 | /* Convert temp to expect 0.1 degrees C units */ |
95 | val->intval = t_val / 100; |
96 | |
97 | return 0; |
98 | } |
99 | |
100 | static enum power_supply_property da9150_charger_props[] = { |
101 | POWER_SUPPLY_PROP_ONLINE, |
102 | POWER_SUPPLY_PROP_VOLTAGE_NOW, |
103 | POWER_SUPPLY_PROP_CURRENT_AVG, |
104 | POWER_SUPPLY_PROP_TEMP, |
105 | }; |
106 | |
107 | static int da9150_charger_get_prop(struct power_supply *psy, |
108 | enum power_supply_property psp, |
109 | union power_supply_propval *val) |
110 | { |
111 | struct da9150_charger *charger = dev_get_drvdata(dev: psy->dev.parent); |
112 | int ret; |
113 | |
114 | switch (psp) { |
115 | case POWER_SUPPLY_PROP_ONLINE: |
116 | ret = da9150_charger_supply_online(charger, psy, val); |
117 | break; |
118 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
119 | ret = da9150_charger_vbus_voltage_now(charger, val); |
120 | break; |
121 | case POWER_SUPPLY_PROP_CURRENT_AVG: |
122 | ret = da9150_charger_ibus_current_avg(charger, val); |
123 | break; |
124 | case POWER_SUPPLY_PROP_TEMP: |
125 | ret = da9150_charger_tjunc_temp(charger, val); |
126 | break; |
127 | default: |
128 | ret = -EINVAL; |
129 | break; |
130 | } |
131 | |
132 | return ret; |
133 | } |
134 | |
135 | /* Battery Properties */ |
136 | static int da9150_charger_battery_status(struct da9150_charger *charger, |
137 | union power_supply_propval *val) |
138 | { |
139 | u8 reg; |
140 | |
141 | /* Check to see if battery is discharging */ |
142 | reg = da9150_reg_read(da9150: charger->da9150, DA9150_STATUS_H); |
143 | |
144 | if (((reg & DA9150_VBUS_STAT_MASK) == DA9150_VBUS_STAT_OFF) || |
145 | ((reg & DA9150_VBUS_STAT_MASK) == DA9150_VBUS_STAT_WAIT)) { |
146 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; |
147 | |
148 | return 0; |
149 | } |
150 | |
151 | reg = da9150_reg_read(da9150: charger->da9150, DA9150_STATUS_J); |
152 | |
153 | /* Now check for other states */ |
154 | switch (reg & DA9150_CHG_STAT_MASK) { |
155 | case DA9150_CHG_STAT_ACT: |
156 | case DA9150_CHG_STAT_PRE: |
157 | case DA9150_CHG_STAT_CC: |
158 | case DA9150_CHG_STAT_CV: |
159 | val->intval = POWER_SUPPLY_STATUS_CHARGING; |
160 | break; |
161 | case DA9150_CHG_STAT_OFF: |
162 | case DA9150_CHG_STAT_SUSP: |
163 | case DA9150_CHG_STAT_TEMP: |
164 | case DA9150_CHG_STAT_TIME: |
165 | case DA9150_CHG_STAT_BAT: |
166 | val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; |
167 | break; |
168 | case DA9150_CHG_STAT_FULL: |
169 | val->intval = POWER_SUPPLY_STATUS_FULL; |
170 | break; |
171 | default: |
172 | val->intval = POWER_SUPPLY_STATUS_UNKNOWN; |
173 | break; |
174 | } |
175 | |
176 | return 0; |
177 | } |
178 | |
179 | static int da9150_charger_battery_health(struct da9150_charger *charger, |
180 | union power_supply_propval *val) |
181 | { |
182 | u8 reg; |
183 | |
184 | reg = da9150_reg_read(da9150: charger->da9150, DA9150_STATUS_J); |
185 | |
186 | /* Check if temperature limit reached */ |
187 | switch (reg & DA9150_CHG_TEMP_MASK) { |
188 | case DA9150_CHG_TEMP_UNDER: |
189 | val->intval = POWER_SUPPLY_HEALTH_COLD; |
190 | return 0; |
191 | case DA9150_CHG_TEMP_OVER: |
192 | val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; |
193 | return 0; |
194 | default: |
195 | break; |
196 | } |
197 | |
198 | /* Check for other health states */ |
199 | switch (reg & DA9150_CHG_STAT_MASK) { |
200 | case DA9150_CHG_STAT_ACT: |
201 | case DA9150_CHG_STAT_PRE: |
202 | val->intval = POWER_SUPPLY_HEALTH_DEAD; |
203 | break; |
204 | case DA9150_CHG_STAT_TIME: |
205 | val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; |
206 | break; |
207 | default: |
208 | val->intval = POWER_SUPPLY_HEALTH_GOOD; |
209 | break; |
210 | } |
211 | |
212 | return 0; |
213 | } |
214 | |
215 | static int da9150_charger_battery_present(struct da9150_charger *charger, |
216 | union power_supply_propval *val) |
217 | { |
218 | u8 reg; |
219 | |
220 | /* Check if battery present or removed */ |
221 | reg = da9150_reg_read(da9150: charger->da9150, DA9150_STATUS_J); |
222 | if ((reg & DA9150_CHG_STAT_MASK) == DA9150_CHG_STAT_BAT) |
223 | val->intval = 0; |
224 | else |
225 | val->intval = 1; |
226 | |
227 | return 0; |
228 | } |
229 | |
230 | static int da9150_charger_battery_charge_type(struct da9150_charger *charger, |
231 | union power_supply_propval *val) |
232 | { |
233 | u8 reg; |
234 | |
235 | reg = da9150_reg_read(da9150: charger->da9150, DA9150_STATUS_J); |
236 | |
237 | switch (reg & DA9150_CHG_STAT_MASK) { |
238 | case DA9150_CHG_STAT_CC: |
239 | val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; |
240 | break; |
241 | case DA9150_CHG_STAT_ACT: |
242 | case DA9150_CHG_STAT_PRE: |
243 | case DA9150_CHG_STAT_CV: |
244 | val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; |
245 | break; |
246 | default: |
247 | val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; |
248 | break; |
249 | } |
250 | |
251 | return 0; |
252 | } |
253 | |
254 | static int da9150_charger_battery_voltage_min(struct da9150_charger *charger, |
255 | union power_supply_propval *val) |
256 | { |
257 | u8 reg; |
258 | |
259 | reg = da9150_reg_read(da9150: charger->da9150, DA9150_PPR_CHGCTRL_C); |
260 | |
261 | /* Value starts at 2500 mV, 50 mV increments, presented in uV */ |
262 | val->intval = ((reg & DA9150_CHG_VFAULT_MASK) * 50000) + 2500000; |
263 | |
264 | return 0; |
265 | } |
266 | |
267 | static int da9150_charger_battery_voltage_now(struct da9150_charger *charger, |
268 | union power_supply_propval *val) |
269 | { |
270 | int v_val, ret; |
271 | |
272 | /* Read processed value - mV units */ |
273 | ret = iio_read_channel_processed(chan: charger->vbat_chan, val: &v_val); |
274 | if (ret < 0) |
275 | return ret; |
276 | |
277 | val->intval = v_val * 1000; |
278 | |
279 | return 0; |
280 | } |
281 | |
282 | static int da9150_charger_battery_current_max(struct da9150_charger *charger, |
283 | union power_supply_propval *val) |
284 | { |
285 | int reg; |
286 | |
287 | reg = da9150_reg_read(da9150: charger->da9150, DA9150_PPR_CHGCTRL_D); |
288 | |
289 | /* 25mA increments */ |
290 | val->intval = reg * 25000; |
291 | |
292 | return 0; |
293 | } |
294 | |
295 | static int da9150_charger_battery_voltage_max(struct da9150_charger *charger, |
296 | union power_supply_propval *val) |
297 | { |
298 | u8 reg; |
299 | |
300 | reg = da9150_reg_read(da9150: charger->da9150, DA9150_PPR_CHGCTRL_B); |
301 | |
302 | /* Value starts at 3650 mV, 25 mV increments, presented in uV */ |
303 | val->intval = ((reg & DA9150_CHG_VBAT_MASK) * 25000) + 3650000; |
304 | return 0; |
305 | } |
306 | |
307 | static enum power_supply_property da9150_charger_bat_props[] = { |
308 | POWER_SUPPLY_PROP_STATUS, |
309 | POWER_SUPPLY_PROP_ONLINE, |
310 | POWER_SUPPLY_PROP_HEALTH, |
311 | POWER_SUPPLY_PROP_PRESENT, |
312 | POWER_SUPPLY_PROP_CHARGE_TYPE, |
313 | POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, |
314 | POWER_SUPPLY_PROP_VOLTAGE_NOW, |
315 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, |
316 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, |
317 | }; |
318 | |
319 | static int da9150_charger_battery_get_prop(struct power_supply *psy, |
320 | enum power_supply_property psp, |
321 | union power_supply_propval *val) |
322 | { |
323 | struct da9150_charger *charger = dev_get_drvdata(dev: psy->dev.parent); |
324 | int ret; |
325 | |
326 | switch (psp) { |
327 | case POWER_SUPPLY_PROP_STATUS: |
328 | ret = da9150_charger_battery_status(charger, val); |
329 | break; |
330 | case POWER_SUPPLY_PROP_ONLINE: |
331 | ret = da9150_charger_supply_online(charger, psy, val); |
332 | break; |
333 | case POWER_SUPPLY_PROP_HEALTH: |
334 | ret = da9150_charger_battery_health(charger, val); |
335 | break; |
336 | case POWER_SUPPLY_PROP_PRESENT: |
337 | ret = da9150_charger_battery_present(charger, val); |
338 | break; |
339 | case POWER_SUPPLY_PROP_CHARGE_TYPE: |
340 | ret = da9150_charger_battery_charge_type(charger, val); |
341 | break; |
342 | case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: |
343 | ret = da9150_charger_battery_voltage_min(charger, val); |
344 | break; |
345 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
346 | ret = da9150_charger_battery_voltage_now(charger, val); |
347 | break; |
348 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: |
349 | ret = da9150_charger_battery_current_max(charger, val); |
350 | break; |
351 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: |
352 | ret = da9150_charger_battery_voltage_max(charger, val); |
353 | break; |
354 | default: |
355 | ret = -EINVAL; |
356 | break; |
357 | } |
358 | |
359 | return ret; |
360 | } |
361 | |
362 | static irqreturn_t da9150_charger_chg_irq(int irq, void *data) |
363 | { |
364 | struct da9150_charger *charger = data; |
365 | |
366 | power_supply_changed(psy: charger->battery); |
367 | |
368 | return IRQ_HANDLED; |
369 | } |
370 | |
371 | static irqreturn_t da9150_charger_tjunc_irq(int irq, void *data) |
372 | { |
373 | struct da9150_charger *charger = data; |
374 | |
375 | /* Nothing we can really do except report this. */ |
376 | dev_crit(charger->dev, "TJunc over temperature!!!\n" ); |
377 | power_supply_changed(psy: charger->usb); |
378 | |
379 | return IRQ_HANDLED; |
380 | } |
381 | |
382 | static irqreturn_t da9150_charger_vfault_irq(int irq, void *data) |
383 | { |
384 | struct da9150_charger *charger = data; |
385 | |
386 | /* Nothing we can really do except report this. */ |
387 | dev_crit(charger->dev, "VSYS under voltage!!!\n" ); |
388 | power_supply_changed(psy: charger->usb); |
389 | power_supply_changed(psy: charger->battery); |
390 | |
391 | return IRQ_HANDLED; |
392 | } |
393 | |
394 | static irqreturn_t da9150_charger_vbus_irq(int irq, void *data) |
395 | { |
396 | struct da9150_charger *charger = data; |
397 | u8 reg; |
398 | |
399 | reg = da9150_reg_read(da9150: charger->da9150, DA9150_STATUS_H); |
400 | |
401 | /* Charger plugged in or battery only */ |
402 | switch (reg & DA9150_VBUS_STAT_MASK) { |
403 | case DA9150_VBUS_STAT_OFF: |
404 | case DA9150_VBUS_STAT_WAIT: |
405 | charger->supply_online = charger->battery; |
406 | break; |
407 | case DA9150_VBUS_STAT_CHG: |
408 | charger->supply_online = charger->usb; |
409 | break; |
410 | default: |
411 | dev_warn(charger->dev, "Unknown VBUS state - reg = 0x%x\n" , |
412 | reg); |
413 | charger->supply_online = NULL; |
414 | break; |
415 | } |
416 | |
417 | power_supply_changed(psy: charger->usb); |
418 | power_supply_changed(psy: charger->battery); |
419 | |
420 | return IRQ_HANDLED; |
421 | } |
422 | |
423 | static void da9150_charger_otg_work(struct work_struct *data) |
424 | { |
425 | struct da9150_charger *charger = |
426 | container_of(data, struct da9150_charger, otg_work); |
427 | |
428 | switch (charger->usb_event) { |
429 | case USB_EVENT_ID: |
430 | /* Enable OTG Boost */ |
431 | da9150_set_bits(da9150: charger->da9150, DA9150_PPR_BKCTRL_A, |
432 | DA9150_VBUS_MODE_MASK, DA9150_VBUS_MODE_OTG); |
433 | break; |
434 | case USB_EVENT_NONE: |
435 | /* Revert to charge mode */ |
436 | power_supply_changed(psy: charger->usb); |
437 | power_supply_changed(psy: charger->battery); |
438 | da9150_set_bits(da9150: charger->da9150, DA9150_PPR_BKCTRL_A, |
439 | DA9150_VBUS_MODE_MASK, DA9150_VBUS_MODE_CHG); |
440 | break; |
441 | } |
442 | } |
443 | |
444 | static int da9150_charger_otg_ncb(struct notifier_block *nb, unsigned long val, |
445 | void *priv) |
446 | { |
447 | struct da9150_charger *charger = |
448 | container_of(nb, struct da9150_charger, otg_nb); |
449 | |
450 | dev_dbg(charger->dev, "DA9150 OTG notify %lu\n" , val); |
451 | |
452 | charger->usb_event = val; |
453 | schedule_work(work: &charger->otg_work); |
454 | |
455 | return NOTIFY_OK; |
456 | } |
457 | |
458 | static int da9150_charger_register_irq(struct platform_device *pdev, |
459 | irq_handler_t handler, |
460 | const char *irq_name) |
461 | { |
462 | struct device *dev = &pdev->dev; |
463 | struct da9150_charger *charger = platform_get_drvdata(pdev); |
464 | int irq, ret; |
465 | |
466 | irq = platform_get_irq_byname(pdev, irq_name); |
467 | if (irq < 0) |
468 | return irq; |
469 | |
470 | ret = request_threaded_irq(irq, NULL, thread_fn: handler, IRQF_ONESHOT, name: irq_name, |
471 | dev: charger); |
472 | if (ret) |
473 | dev_err(dev, "Failed to request IRQ %d: %d\n" , irq, ret); |
474 | |
475 | return ret; |
476 | } |
477 | |
478 | static void da9150_charger_unregister_irq(struct platform_device *pdev, |
479 | const char *irq_name) |
480 | { |
481 | struct da9150_charger *charger = platform_get_drvdata(pdev); |
482 | int irq; |
483 | |
484 | irq = platform_get_irq_byname(pdev, irq_name); |
485 | if (irq < 0) |
486 | return; |
487 | |
488 | free_irq(irq, charger); |
489 | } |
490 | |
491 | static const struct power_supply_desc usb_desc = { |
492 | .name = "da9150-usb" , |
493 | .type = POWER_SUPPLY_TYPE_USB, |
494 | .properties = da9150_charger_props, |
495 | .num_properties = ARRAY_SIZE(da9150_charger_props), |
496 | .get_property = da9150_charger_get_prop, |
497 | }; |
498 | |
499 | static const struct power_supply_desc battery_desc = { |
500 | .name = "da9150-battery" , |
501 | .type = POWER_SUPPLY_TYPE_BATTERY, |
502 | .properties = da9150_charger_bat_props, |
503 | .num_properties = ARRAY_SIZE(da9150_charger_bat_props), |
504 | .get_property = da9150_charger_battery_get_prop, |
505 | }; |
506 | |
507 | static int da9150_charger_probe(struct platform_device *pdev) |
508 | { |
509 | struct device *dev = &pdev->dev; |
510 | struct da9150 *da9150 = dev_get_drvdata(dev: dev->parent); |
511 | struct da9150_charger *charger; |
512 | u8 reg; |
513 | int ret; |
514 | |
515 | charger = devm_kzalloc(dev, size: sizeof(struct da9150_charger), GFP_KERNEL); |
516 | if (!charger) |
517 | return -ENOMEM; |
518 | |
519 | platform_set_drvdata(pdev, data: charger); |
520 | charger->da9150 = da9150; |
521 | charger->dev = dev; |
522 | |
523 | /* Acquire ADC channels */ |
524 | charger->ibus_chan = iio_channel_get(dev, consumer_channel: "CHAN_IBUS" ); |
525 | if (IS_ERR(ptr: charger->ibus_chan)) { |
526 | ret = PTR_ERR(ptr: charger->ibus_chan); |
527 | goto ibus_chan_fail; |
528 | } |
529 | |
530 | charger->vbus_chan = iio_channel_get(dev, consumer_channel: "CHAN_VBUS" ); |
531 | if (IS_ERR(ptr: charger->vbus_chan)) { |
532 | ret = PTR_ERR(ptr: charger->vbus_chan); |
533 | goto vbus_chan_fail; |
534 | } |
535 | |
536 | charger->tjunc_chan = iio_channel_get(dev, consumer_channel: "CHAN_TJUNC" ); |
537 | if (IS_ERR(ptr: charger->tjunc_chan)) { |
538 | ret = PTR_ERR(ptr: charger->tjunc_chan); |
539 | goto tjunc_chan_fail; |
540 | } |
541 | |
542 | charger->vbat_chan = iio_channel_get(dev, consumer_channel: "CHAN_VBAT" ); |
543 | if (IS_ERR(ptr: charger->vbat_chan)) { |
544 | ret = PTR_ERR(ptr: charger->vbat_chan); |
545 | goto vbat_chan_fail; |
546 | } |
547 | |
548 | /* Register power supplies */ |
549 | charger->usb = power_supply_register(parent: dev, desc: &usb_desc, NULL); |
550 | if (IS_ERR(ptr: charger->usb)) { |
551 | ret = PTR_ERR(ptr: charger->usb); |
552 | goto usb_fail; |
553 | } |
554 | |
555 | charger->battery = power_supply_register(parent: dev, desc: &battery_desc, NULL); |
556 | if (IS_ERR(ptr: charger->battery)) { |
557 | ret = PTR_ERR(ptr: charger->battery); |
558 | goto battery_fail; |
559 | } |
560 | |
561 | /* Get initial online supply */ |
562 | reg = da9150_reg_read(da9150, DA9150_STATUS_H); |
563 | |
564 | switch (reg & DA9150_VBUS_STAT_MASK) { |
565 | case DA9150_VBUS_STAT_OFF: |
566 | case DA9150_VBUS_STAT_WAIT: |
567 | charger->supply_online = charger->battery; |
568 | break; |
569 | case DA9150_VBUS_STAT_CHG: |
570 | charger->supply_online = charger->usb; |
571 | break; |
572 | default: |
573 | dev_warn(dev, "Unknown VBUS state - reg = 0x%x\n" , reg); |
574 | charger->supply_online = NULL; |
575 | break; |
576 | } |
577 | |
578 | /* Setup OTG reporting & configuration */ |
579 | charger->usb_phy = devm_usb_get_phy(dev, type: USB_PHY_TYPE_USB2); |
580 | if (!IS_ERR_OR_NULL(ptr: charger->usb_phy)) { |
581 | INIT_WORK(&charger->otg_work, da9150_charger_otg_work); |
582 | charger->otg_nb.notifier_call = da9150_charger_otg_ncb; |
583 | usb_register_notifier(x: charger->usb_phy, nb: &charger->otg_nb); |
584 | } |
585 | |
586 | /* Register IRQs */ |
587 | ret = da9150_charger_register_irq(pdev, handler: da9150_charger_chg_irq, |
588 | irq_name: "CHG_STATUS" ); |
589 | if (ret < 0) |
590 | goto chg_irq_fail; |
591 | |
592 | ret = da9150_charger_register_irq(pdev, handler: da9150_charger_tjunc_irq, |
593 | irq_name: "CHG_TJUNC" ); |
594 | if (ret < 0) |
595 | goto tjunc_irq_fail; |
596 | |
597 | ret = da9150_charger_register_irq(pdev, handler: da9150_charger_vfault_irq, |
598 | irq_name: "CHG_VFAULT" ); |
599 | if (ret < 0) |
600 | goto vfault_irq_fail; |
601 | |
602 | ret = da9150_charger_register_irq(pdev, handler: da9150_charger_vbus_irq, |
603 | irq_name: "CHG_VBUS" ); |
604 | if (ret < 0) |
605 | goto vbus_irq_fail; |
606 | |
607 | return 0; |
608 | |
609 | |
610 | vbus_irq_fail: |
611 | da9150_charger_unregister_irq(pdev, irq_name: "CHG_VFAULT" ); |
612 | vfault_irq_fail: |
613 | da9150_charger_unregister_irq(pdev, irq_name: "CHG_TJUNC" ); |
614 | tjunc_irq_fail: |
615 | da9150_charger_unregister_irq(pdev, irq_name: "CHG_STATUS" ); |
616 | chg_irq_fail: |
617 | if (!IS_ERR_OR_NULL(ptr: charger->usb_phy)) |
618 | usb_unregister_notifier(x: charger->usb_phy, nb: &charger->otg_nb); |
619 | battery_fail: |
620 | power_supply_unregister(psy: charger->usb); |
621 | |
622 | usb_fail: |
623 | iio_channel_release(chan: charger->vbat_chan); |
624 | |
625 | vbat_chan_fail: |
626 | iio_channel_release(chan: charger->tjunc_chan); |
627 | |
628 | tjunc_chan_fail: |
629 | iio_channel_release(chan: charger->vbus_chan); |
630 | |
631 | vbus_chan_fail: |
632 | iio_channel_release(chan: charger->ibus_chan); |
633 | |
634 | ibus_chan_fail: |
635 | return ret; |
636 | } |
637 | |
638 | static void da9150_charger_remove(struct platform_device *pdev) |
639 | { |
640 | struct da9150_charger *charger = platform_get_drvdata(pdev); |
641 | int irq; |
642 | |
643 | /* Make sure IRQs are released before unregistering power supplies */ |
644 | irq = platform_get_irq_byname(pdev, "CHG_VBUS" ); |
645 | free_irq(irq, charger); |
646 | |
647 | irq = platform_get_irq_byname(pdev, "CHG_VFAULT" ); |
648 | free_irq(irq, charger); |
649 | |
650 | irq = platform_get_irq_byname(pdev, "CHG_TJUNC" ); |
651 | free_irq(irq, charger); |
652 | |
653 | irq = platform_get_irq_byname(pdev, "CHG_STATUS" ); |
654 | free_irq(irq, charger); |
655 | |
656 | if (!IS_ERR_OR_NULL(ptr: charger->usb_phy)) |
657 | usb_unregister_notifier(x: charger->usb_phy, nb: &charger->otg_nb); |
658 | cancel_work_sync(work: &charger->otg_work); |
659 | |
660 | power_supply_unregister(psy: charger->battery); |
661 | power_supply_unregister(psy: charger->usb); |
662 | |
663 | /* Release ADC channels */ |
664 | iio_channel_release(chan: charger->ibus_chan); |
665 | iio_channel_release(chan: charger->vbus_chan); |
666 | iio_channel_release(chan: charger->tjunc_chan); |
667 | iio_channel_release(chan: charger->vbat_chan); |
668 | } |
669 | |
670 | static struct platform_driver da9150_charger_driver = { |
671 | .driver = { |
672 | .name = "da9150-charger" , |
673 | }, |
674 | .probe = da9150_charger_probe, |
675 | .remove_new = da9150_charger_remove, |
676 | }; |
677 | |
678 | module_platform_driver(da9150_charger_driver); |
679 | |
680 | MODULE_DESCRIPTION("Charger Driver for DA9150" ); |
681 | MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>" ); |
682 | MODULE_LICENSE("GPL" ); |
683 | |