1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Battery charger driver for Dialog Semiconductor DA9030 |
4 | * |
5 | * Copyright (C) 2008 Compulab, Ltd. |
6 | * Mike Rapoport <mike@compulab.co.il> |
7 | */ |
8 | |
9 | #include <linux/kernel.h> |
10 | #include <linux/slab.h> |
11 | #include <linux/init.h> |
12 | #include <linux/types.h> |
13 | #include <linux/device.h> |
14 | #include <linux/workqueue.h> |
15 | #include <linux/module.h> |
16 | #include <linux/platform_device.h> |
17 | #include <linux/power_supply.h> |
18 | #include <linux/mfd/da903x.h> |
19 | |
20 | #include <linux/debugfs.h> |
21 | #include <linux/seq_file.h> |
22 | #include <linux/notifier.h> |
23 | |
24 | #define DA9030_FAULT_LOG 0x0a |
25 | #define DA9030_FAULT_LOG_OVER_TEMP (1 << 7) |
26 | #define DA9030_FAULT_LOG_VBAT_OVER (1 << 4) |
27 | |
28 | #define DA9030_CHARGE_CONTROL 0x28 |
29 | #define DA9030_CHRG_CHARGER_ENABLE (1 << 7) |
30 | |
31 | #define DA9030_ADC_MAN_CONTROL 0x30 |
32 | #define DA9030_ADC_TBATREF_ENABLE (1 << 5) |
33 | #define DA9030_ADC_LDO_INT_ENABLE (1 << 4) |
34 | |
35 | #define DA9030_ADC_AUTO_CONTROL 0x31 |
36 | #define DA9030_ADC_TBAT_ENABLE (1 << 5) |
37 | #define DA9030_ADC_VBAT_IN_TXON (1 << 4) |
38 | #define DA9030_ADC_VCH_ENABLE (1 << 3) |
39 | #define DA9030_ADC_ICH_ENABLE (1 << 2) |
40 | #define DA9030_ADC_VBAT_ENABLE (1 << 1) |
41 | #define DA9030_ADC_AUTO_SLEEP_ENABLE (1 << 0) |
42 | |
43 | #define DA9030_VBATMON 0x32 |
44 | #define DA9030_VBATMONTXON 0x33 |
45 | #define DA9030_TBATHIGHP 0x34 |
46 | #define DA9030_TBATHIGHN 0x35 |
47 | #define DA9030_TBATLOW 0x36 |
48 | |
49 | #define DA9030_VBAT_RES 0x41 |
50 | #define DA9030_VBATMIN_RES 0x42 |
51 | #define DA9030_VBATMINTXON_RES 0x43 |
52 | #define DA9030_ICHMAX_RES 0x44 |
53 | #define DA9030_ICHMIN_RES 0x45 |
54 | #define DA9030_ICHAVERAGE_RES 0x46 |
55 | #define DA9030_VCHMAX_RES 0x47 |
56 | #define DA9030_VCHMIN_RES 0x48 |
57 | #define DA9030_TBAT_RES 0x49 |
58 | |
59 | struct da9030_adc_res { |
60 | uint8_t vbat_res; |
61 | uint8_t vbatmin_res; |
62 | uint8_t vbatmintxon; |
63 | uint8_t ichmax_res; |
64 | uint8_t ichmin_res; |
65 | uint8_t ichaverage_res; |
66 | uint8_t vchmax_res; |
67 | uint8_t vchmin_res; |
68 | uint8_t tbat_res; |
69 | uint8_t adc_in4_res; |
70 | uint8_t adc_in5_res; |
71 | }; |
72 | |
73 | struct da9030_battery_thresholds { |
74 | int tbat_low; |
75 | int tbat_high; |
76 | int tbat_restart; |
77 | |
78 | int vbat_low; |
79 | int vbat_crit; |
80 | int vbat_charge_start; |
81 | int vbat_charge_stop; |
82 | int vbat_charge_restart; |
83 | |
84 | int vcharge_min; |
85 | int vcharge_max; |
86 | }; |
87 | |
88 | struct da9030_charger { |
89 | struct power_supply *psy; |
90 | struct power_supply_desc psy_desc; |
91 | |
92 | struct device *master; |
93 | |
94 | struct da9030_adc_res adc; |
95 | struct delayed_work work; |
96 | unsigned int interval; |
97 | |
98 | struct power_supply_info *battery_info; |
99 | |
100 | struct da9030_battery_thresholds thresholds; |
101 | |
102 | unsigned int charge_milliamp; |
103 | unsigned int charge_millivolt; |
104 | |
105 | /* charger status */ |
106 | bool chdet; |
107 | uint8_t fault; |
108 | int mA; |
109 | int mV; |
110 | bool is_on; |
111 | |
112 | struct notifier_block nb; |
113 | |
114 | /* platform callbacks for battery low and critical events */ |
115 | void (*battery_low)(void); |
116 | void (*battery_critical)(void); |
117 | |
118 | struct dentry *debug_file; |
119 | }; |
120 | |
121 | static inline int da9030_reg_to_mV(int reg) |
122 | { |
123 | return ((reg * 2650) >> 8) + 2650; |
124 | } |
125 | |
126 | static inline int da9030_millivolt_to_reg(int mV) |
127 | { |
128 | return ((mV - 2650) << 8) / 2650; |
129 | } |
130 | |
131 | static inline int da9030_reg_to_mA(int reg) |
132 | { |
133 | return ((reg * 24000) >> 8) / 15; |
134 | } |
135 | |
136 | #ifdef CONFIG_DEBUG_FS |
137 | static int bat_debug_show(struct seq_file *s, void *data) |
138 | { |
139 | struct da9030_charger *charger = s->private; |
140 | |
141 | seq_printf(m: s, fmt: "charger is %s\n" , charger->is_on ? "on" : "off" ); |
142 | if (charger->chdet) { |
143 | seq_printf(m: s, fmt: "iset = %dmA, vset = %dmV\n" , |
144 | charger->mA, charger->mV); |
145 | } |
146 | |
147 | seq_printf(m: s, fmt: "vbat_res = %d (%dmV)\n" , |
148 | charger->adc.vbat_res, |
149 | da9030_reg_to_mV(reg: charger->adc.vbat_res)); |
150 | seq_printf(m: s, fmt: "vbatmin_res = %d (%dmV)\n" , |
151 | charger->adc.vbatmin_res, |
152 | da9030_reg_to_mV(reg: charger->adc.vbatmin_res)); |
153 | seq_printf(m: s, fmt: "vbatmintxon = %d (%dmV)\n" , |
154 | charger->adc.vbatmintxon, |
155 | da9030_reg_to_mV(reg: charger->adc.vbatmintxon)); |
156 | seq_printf(m: s, fmt: "ichmax_res = %d (%dmA)\n" , |
157 | charger->adc.ichmax_res, |
158 | da9030_reg_to_mV(reg: charger->adc.ichmax_res)); |
159 | seq_printf(m: s, fmt: "ichmin_res = %d (%dmA)\n" , |
160 | charger->adc.ichmin_res, |
161 | da9030_reg_to_mA(reg: charger->adc.ichmin_res)); |
162 | seq_printf(m: s, fmt: "ichaverage_res = %d (%dmA)\n" , |
163 | charger->adc.ichaverage_res, |
164 | da9030_reg_to_mA(reg: charger->adc.ichaverage_res)); |
165 | seq_printf(m: s, fmt: "vchmax_res = %d (%dmV)\n" , |
166 | charger->adc.vchmax_res, |
167 | da9030_reg_to_mA(reg: charger->adc.vchmax_res)); |
168 | seq_printf(m: s, fmt: "vchmin_res = %d (%dmV)\n" , |
169 | charger->adc.vchmin_res, |
170 | da9030_reg_to_mV(reg: charger->adc.vchmin_res)); |
171 | |
172 | return 0; |
173 | } |
174 | |
175 | DEFINE_SHOW_ATTRIBUTE(bat_debug); |
176 | |
177 | static struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger) |
178 | { |
179 | charger->debug_file = debugfs_create_file(name: "charger" , mode: 0666, NULL, |
180 | data: charger, fops: &bat_debug_fops); |
181 | return charger->debug_file; |
182 | } |
183 | |
184 | static void da9030_bat_remove_debugfs(struct da9030_charger *charger) |
185 | { |
186 | debugfs_remove(dentry: charger->debug_file); |
187 | } |
188 | #else |
189 | static inline struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger) |
190 | { |
191 | return NULL; |
192 | } |
193 | static inline void da9030_bat_remove_debugfs(struct da9030_charger *charger) |
194 | { |
195 | } |
196 | #endif |
197 | |
198 | static inline void da9030_read_adc(struct da9030_charger *charger, |
199 | struct da9030_adc_res *adc) |
200 | { |
201 | da903x_reads(dev: charger->master, DA9030_VBAT_RES, |
202 | len: sizeof(*adc), val: (uint8_t *)adc); |
203 | } |
204 | |
205 | static void da9030_charger_update_state(struct da9030_charger *charger) |
206 | { |
207 | uint8_t val; |
208 | |
209 | da903x_read(dev: charger->master, DA9030_CHARGE_CONTROL, val: &val); |
210 | charger->is_on = (val & DA9030_CHRG_CHARGER_ENABLE) ? 1 : 0; |
211 | charger->mA = ((val >> 3) & 0xf) * 100; |
212 | charger->mV = (val & 0x7) * 50 + 4000; |
213 | |
214 | da9030_read_adc(charger, adc: &charger->adc); |
215 | da903x_read(dev: charger->master, DA9030_FAULT_LOG, val: &charger->fault); |
216 | charger->chdet = da903x_query_status(dev: charger->master, |
217 | DA9030_STATUS_CHDET); |
218 | } |
219 | |
220 | static void da9030_set_charge(struct da9030_charger *charger, int on) |
221 | { |
222 | uint8_t val; |
223 | |
224 | if (on) { |
225 | val = DA9030_CHRG_CHARGER_ENABLE; |
226 | val |= (charger->charge_milliamp / 100) << 3; |
227 | val |= (charger->charge_millivolt - 4000) / 50; |
228 | charger->is_on = 1; |
229 | } else { |
230 | val = 0; |
231 | charger->is_on = 0; |
232 | } |
233 | |
234 | da903x_write(dev: charger->master, DA9030_CHARGE_CONTROL, val); |
235 | |
236 | power_supply_changed(psy: charger->psy); |
237 | } |
238 | |
239 | static void da9030_charger_check_state(struct da9030_charger *charger) |
240 | { |
241 | da9030_charger_update_state(charger); |
242 | |
243 | /* we wake or boot with external power on */ |
244 | if (!charger->is_on) { |
245 | if ((charger->chdet) && |
246 | (charger->adc.vbat_res < |
247 | charger->thresholds.vbat_charge_start)) { |
248 | da9030_set_charge(charger, on: 1); |
249 | } |
250 | } else { |
251 | /* Charger has been pulled out */ |
252 | if (!charger->chdet) { |
253 | da9030_set_charge(charger, on: 0); |
254 | return; |
255 | } |
256 | |
257 | if (charger->adc.vbat_res >= |
258 | charger->thresholds.vbat_charge_stop) { |
259 | da9030_set_charge(charger, on: 0); |
260 | da903x_write(dev: charger->master, DA9030_VBATMON, |
261 | val: charger->thresholds.vbat_charge_restart); |
262 | } else if (charger->adc.vbat_res > |
263 | charger->thresholds.vbat_low) { |
264 | /* we are charging and passed LOW_THRESH, |
265 | so upate DA9030 VBAT threshold |
266 | */ |
267 | da903x_write(dev: charger->master, DA9030_VBATMON, |
268 | val: charger->thresholds.vbat_low); |
269 | } |
270 | if (charger->adc.vchmax_res > charger->thresholds.vcharge_max || |
271 | charger->adc.vchmin_res < charger->thresholds.vcharge_min || |
272 | /* Tempreture readings are negative */ |
273 | charger->adc.tbat_res < charger->thresholds.tbat_high || |
274 | charger->adc.tbat_res > charger->thresholds.tbat_low) { |
275 | /* disable charger */ |
276 | da9030_set_charge(charger, on: 0); |
277 | } |
278 | } |
279 | } |
280 | |
281 | static void da9030_charging_monitor(struct work_struct *work) |
282 | { |
283 | struct da9030_charger *charger; |
284 | |
285 | charger = container_of(work, struct da9030_charger, work.work); |
286 | |
287 | da9030_charger_check_state(charger); |
288 | |
289 | /* reschedule for the next time */ |
290 | schedule_delayed_work(dwork: &charger->work, delay: charger->interval); |
291 | } |
292 | |
293 | static enum power_supply_property da9030_battery_props[] = { |
294 | POWER_SUPPLY_PROP_MODEL_NAME, |
295 | POWER_SUPPLY_PROP_STATUS, |
296 | POWER_SUPPLY_PROP_HEALTH, |
297 | POWER_SUPPLY_PROP_TECHNOLOGY, |
298 | POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, |
299 | POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, |
300 | POWER_SUPPLY_PROP_VOLTAGE_NOW, |
301 | POWER_SUPPLY_PROP_CURRENT_AVG, |
302 | }; |
303 | |
304 | static void da9030_battery_check_status(struct da9030_charger *charger, |
305 | union power_supply_propval *val) |
306 | { |
307 | if (charger->chdet) { |
308 | if (charger->is_on) |
309 | val->intval = POWER_SUPPLY_STATUS_CHARGING; |
310 | else |
311 | val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; |
312 | } else { |
313 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; |
314 | } |
315 | } |
316 | |
317 | static void da9030_battery_check_health(struct da9030_charger *charger, |
318 | union power_supply_propval *val) |
319 | { |
320 | if (charger->fault & DA9030_FAULT_LOG_OVER_TEMP) |
321 | val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; |
322 | else if (charger->fault & DA9030_FAULT_LOG_VBAT_OVER) |
323 | val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; |
324 | else |
325 | val->intval = POWER_SUPPLY_HEALTH_GOOD; |
326 | } |
327 | |
328 | static int da9030_battery_get_property(struct power_supply *psy, |
329 | enum power_supply_property psp, |
330 | union power_supply_propval *val) |
331 | { |
332 | struct da9030_charger *charger = power_supply_get_drvdata(psy); |
333 | |
334 | switch (psp) { |
335 | case POWER_SUPPLY_PROP_STATUS: |
336 | da9030_battery_check_status(charger, val); |
337 | break; |
338 | case POWER_SUPPLY_PROP_HEALTH: |
339 | da9030_battery_check_health(charger, val); |
340 | break; |
341 | case POWER_SUPPLY_PROP_TECHNOLOGY: |
342 | val->intval = charger->battery_info->technology; |
343 | break; |
344 | case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: |
345 | val->intval = charger->battery_info->voltage_max_design; |
346 | break; |
347 | case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: |
348 | val->intval = charger->battery_info->voltage_min_design; |
349 | break; |
350 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
351 | val->intval = da9030_reg_to_mV(reg: charger->adc.vbat_res) * 1000; |
352 | break; |
353 | case POWER_SUPPLY_PROP_CURRENT_AVG: |
354 | val->intval = |
355 | da9030_reg_to_mA(reg: charger->adc.ichaverage_res) * 1000; |
356 | break; |
357 | case POWER_SUPPLY_PROP_MODEL_NAME: |
358 | val->strval = charger->battery_info->name; |
359 | break; |
360 | default: |
361 | break; |
362 | } |
363 | |
364 | return 0; |
365 | } |
366 | |
367 | static void da9030_battery_vbat_event(struct da9030_charger *charger) |
368 | { |
369 | da9030_read_adc(charger, adc: &charger->adc); |
370 | |
371 | if (charger->is_on) |
372 | return; |
373 | |
374 | if (charger->adc.vbat_res < charger->thresholds.vbat_low) { |
375 | /* set VBAT threshold for critical */ |
376 | da903x_write(dev: charger->master, DA9030_VBATMON, |
377 | val: charger->thresholds.vbat_crit); |
378 | if (charger->battery_low) |
379 | charger->battery_low(); |
380 | } else if (charger->adc.vbat_res < |
381 | charger->thresholds.vbat_crit) { |
382 | /* notify the system of battery critical */ |
383 | if (charger->battery_critical) |
384 | charger->battery_critical(); |
385 | } |
386 | } |
387 | |
388 | static int da9030_battery_event(struct notifier_block *nb, unsigned long event, |
389 | void *data) |
390 | { |
391 | struct da9030_charger *charger = |
392 | container_of(nb, struct da9030_charger, nb); |
393 | |
394 | switch (event) { |
395 | case DA9030_EVENT_CHDET: |
396 | cancel_delayed_work_sync(dwork: &charger->work); |
397 | schedule_work(work: &charger->work.work); |
398 | break; |
399 | case DA9030_EVENT_VBATMON: |
400 | da9030_battery_vbat_event(charger); |
401 | break; |
402 | case DA9030_EVENT_CHIOVER: |
403 | case DA9030_EVENT_TBAT: |
404 | da9030_set_charge(charger, on: 0); |
405 | break; |
406 | } |
407 | |
408 | return 0; |
409 | } |
410 | |
411 | static void da9030_battery_convert_thresholds(struct da9030_charger *charger, |
412 | struct da9030_battery_info *pdata) |
413 | { |
414 | charger->thresholds.tbat_low = pdata->tbat_low; |
415 | charger->thresholds.tbat_high = pdata->tbat_high; |
416 | charger->thresholds.tbat_restart = pdata->tbat_restart; |
417 | |
418 | charger->thresholds.vbat_low = |
419 | da9030_millivolt_to_reg(mV: pdata->vbat_low); |
420 | charger->thresholds.vbat_crit = |
421 | da9030_millivolt_to_reg(mV: pdata->vbat_crit); |
422 | charger->thresholds.vbat_charge_start = |
423 | da9030_millivolt_to_reg(mV: pdata->vbat_charge_start); |
424 | charger->thresholds.vbat_charge_stop = |
425 | da9030_millivolt_to_reg(mV: pdata->vbat_charge_stop); |
426 | charger->thresholds.vbat_charge_restart = |
427 | da9030_millivolt_to_reg(mV: pdata->vbat_charge_restart); |
428 | |
429 | charger->thresholds.vcharge_min = |
430 | da9030_millivolt_to_reg(mV: pdata->vcharge_min); |
431 | charger->thresholds.vcharge_max = |
432 | da9030_millivolt_to_reg(mV: pdata->vcharge_max); |
433 | } |
434 | |
435 | static void da9030_battery_setup_psy(struct da9030_charger *charger) |
436 | { |
437 | struct power_supply_desc *psy_desc = &charger->psy_desc; |
438 | struct power_supply_info *info = charger->battery_info; |
439 | |
440 | psy_desc->name = info->name; |
441 | psy_desc->use_for_apm = info->use_for_apm; |
442 | psy_desc->type = POWER_SUPPLY_TYPE_BATTERY; |
443 | psy_desc->get_property = da9030_battery_get_property; |
444 | |
445 | psy_desc->properties = da9030_battery_props; |
446 | psy_desc->num_properties = ARRAY_SIZE(da9030_battery_props); |
447 | }; |
448 | |
449 | static int da9030_battery_charger_init(struct da9030_charger *charger) |
450 | { |
451 | char v[5]; |
452 | int ret; |
453 | |
454 | v[0] = v[1] = charger->thresholds.vbat_low; |
455 | v[2] = charger->thresholds.tbat_high; |
456 | v[3] = charger->thresholds.tbat_restart; |
457 | v[4] = charger->thresholds.tbat_low; |
458 | |
459 | ret = da903x_writes(dev: charger->master, DA9030_VBATMON, len: 5, val: v); |
460 | if (ret) |
461 | return ret; |
462 | |
463 | /* |
464 | * Enable reference voltage supply for ADC from the LDO_INTERNAL |
465 | * regulator. Must be set before ADC measurements can be made. |
466 | */ |
467 | ret = da903x_write(dev: charger->master, DA9030_ADC_MAN_CONTROL, |
468 | DA9030_ADC_LDO_INT_ENABLE | |
469 | DA9030_ADC_TBATREF_ENABLE); |
470 | if (ret) |
471 | return ret; |
472 | |
473 | /* enable auto ADC measuremnts */ |
474 | return da903x_write(dev: charger->master, DA9030_ADC_AUTO_CONTROL, |
475 | DA9030_ADC_TBAT_ENABLE | DA9030_ADC_VBAT_IN_TXON | |
476 | DA9030_ADC_VCH_ENABLE | DA9030_ADC_ICH_ENABLE | |
477 | DA9030_ADC_VBAT_ENABLE | |
478 | DA9030_ADC_AUTO_SLEEP_ENABLE); |
479 | } |
480 | |
481 | static int da9030_battery_probe(struct platform_device *pdev) |
482 | { |
483 | struct da9030_charger *charger; |
484 | struct power_supply_config psy_cfg = {}; |
485 | struct da9030_battery_info *pdata = pdev->dev.platform_data; |
486 | int ret; |
487 | |
488 | if (pdata == NULL) |
489 | return -EINVAL; |
490 | |
491 | if (pdata->charge_milliamp >= 1500 || |
492 | pdata->charge_millivolt < 4000 || |
493 | pdata->charge_millivolt > 4350) |
494 | return -EINVAL; |
495 | |
496 | charger = devm_kzalloc(dev: &pdev->dev, size: sizeof(*charger), GFP_KERNEL); |
497 | if (charger == NULL) |
498 | return -ENOMEM; |
499 | |
500 | charger->master = pdev->dev.parent; |
501 | |
502 | /* 10 seconds between monitor runs unless platform defines other |
503 | interval */ |
504 | charger->interval = msecs_to_jiffies( |
505 | m: (pdata->batmon_interval ? : 10) * 1000); |
506 | |
507 | charger->charge_milliamp = pdata->charge_milliamp; |
508 | charger->charge_millivolt = pdata->charge_millivolt; |
509 | charger->battery_info = pdata->battery_info; |
510 | charger->battery_low = pdata->battery_low; |
511 | charger->battery_critical = pdata->battery_critical; |
512 | |
513 | da9030_battery_convert_thresholds(charger, pdata); |
514 | |
515 | ret = da9030_battery_charger_init(charger); |
516 | if (ret) |
517 | goto err_charger_init; |
518 | |
519 | INIT_DELAYED_WORK(&charger->work, da9030_charging_monitor); |
520 | schedule_delayed_work(dwork: &charger->work, delay: charger->interval); |
521 | |
522 | charger->nb.notifier_call = da9030_battery_event; |
523 | ret = da903x_register_notifier(dev: charger->master, nb: &charger->nb, |
524 | DA9030_EVENT_CHDET | |
525 | DA9030_EVENT_VBATMON | |
526 | DA9030_EVENT_CHIOVER | |
527 | DA9030_EVENT_TBAT); |
528 | if (ret) |
529 | goto err_notifier; |
530 | |
531 | da9030_battery_setup_psy(charger); |
532 | psy_cfg.drv_data = charger; |
533 | charger->psy = power_supply_register(parent: &pdev->dev, desc: &charger->psy_desc, |
534 | cfg: &psy_cfg); |
535 | if (IS_ERR(ptr: charger->psy)) { |
536 | ret = PTR_ERR(ptr: charger->psy); |
537 | goto err_ps_register; |
538 | } |
539 | |
540 | charger->debug_file = da9030_bat_create_debugfs(charger); |
541 | platform_set_drvdata(pdev, data: charger); |
542 | return 0; |
543 | |
544 | err_ps_register: |
545 | da903x_unregister_notifier(dev: charger->master, nb: &charger->nb, |
546 | DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON | |
547 | DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT); |
548 | err_notifier: |
549 | cancel_delayed_work(dwork: &charger->work); |
550 | |
551 | err_charger_init: |
552 | return ret; |
553 | } |
554 | |
555 | static void da9030_battery_remove(struct platform_device *dev) |
556 | { |
557 | struct da9030_charger *charger = platform_get_drvdata(pdev: dev); |
558 | |
559 | da9030_bat_remove_debugfs(charger); |
560 | |
561 | da903x_unregister_notifier(dev: charger->master, nb: &charger->nb, |
562 | DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON | |
563 | DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT); |
564 | cancel_delayed_work_sync(dwork: &charger->work); |
565 | da9030_set_charge(charger, on: 0); |
566 | power_supply_unregister(psy: charger->psy); |
567 | } |
568 | |
569 | static struct platform_driver da903x_battery_driver = { |
570 | .driver = { |
571 | .name = "da903x-battery" , |
572 | }, |
573 | .probe = da9030_battery_probe, |
574 | .remove_new = da9030_battery_remove, |
575 | }; |
576 | |
577 | module_platform_driver(da903x_battery_driver); |
578 | |
579 | MODULE_DESCRIPTION("DA9030 battery charger driver" ); |
580 | MODULE_AUTHOR("Mike Rapoport, CompuLab" ); |
581 | MODULE_LICENSE("GPL" ); |
582 | |