1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Battery driver for wm8350 PMIC |
4 | * |
5 | * Copyright 2007, 2008 Wolfson Microelectronics PLC. |
6 | * |
7 | * Based on OLPC Battery Driver |
8 | * |
9 | * Copyright 2006 David Woodhouse <dwmw2@infradead.org> |
10 | */ |
11 | |
12 | #include <linux/module.h> |
13 | #include <linux/err.h> |
14 | #include <linux/platform_device.h> |
15 | #include <linux/power_supply.h> |
16 | #include <linux/mfd/wm8350/supply.h> |
17 | #include <linux/mfd/wm8350/core.h> |
18 | #include <linux/mfd/wm8350/comparator.h> |
19 | |
20 | static int wm8350_read_battery_uvolts(struct wm8350 *wm8350) |
21 | { |
22 | return wm8350_read_auxadc(wm8350, WM8350_AUXADC_BATT, scale: 0, vref: 0) |
23 | * WM8350_AUX_COEFF; |
24 | } |
25 | |
26 | static int wm8350_read_line_uvolts(struct wm8350 *wm8350) |
27 | { |
28 | return wm8350_read_auxadc(wm8350, WM8350_AUXADC_LINE, scale: 0, vref: 0) |
29 | * WM8350_AUX_COEFF; |
30 | } |
31 | |
32 | static int wm8350_read_usb_uvolts(struct wm8350 *wm8350) |
33 | { |
34 | return wm8350_read_auxadc(wm8350, WM8350_AUXADC_USB, scale: 0, vref: 0) |
35 | * WM8350_AUX_COEFF; |
36 | } |
37 | |
38 | #define WM8350_BATT_SUPPLY 1 |
39 | #define WM8350_USB_SUPPLY 2 |
40 | #define WM8350_LINE_SUPPLY 4 |
41 | |
42 | static inline int wm8350_charge_time_min(struct wm8350 *wm8350, int min) |
43 | { |
44 | if (!wm8350->power.rev_g_coeff) |
45 | return (((min - 30) / 15) & 0xf) << 8; |
46 | else |
47 | return (((min - 30) / 30) & 0xf) << 8; |
48 | } |
49 | |
50 | static int wm8350_get_supplies(struct wm8350 *wm8350) |
51 | { |
52 | u16 sm, ov, co, chrg; |
53 | int supplies = 0; |
54 | |
55 | sm = wm8350_reg_read(wm8350, WM8350_STATE_MACHINE_STATUS); |
56 | ov = wm8350_reg_read(wm8350, WM8350_MISC_OVERRIDES); |
57 | co = wm8350_reg_read(wm8350, WM8350_COMPARATOR_OVERRIDES); |
58 | chrg = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2); |
59 | |
60 | /* USB_SM */ |
61 | sm = (sm & WM8350_USB_SM_MASK) >> WM8350_USB_SM_SHIFT; |
62 | |
63 | /* CHG_ISEL */ |
64 | chrg &= WM8350_CHG_ISEL_MASK; |
65 | |
66 | /* If the USB state machine is active then we're using that with or |
67 | * without battery, otherwise check for wall supply */ |
68 | if (((sm == WM8350_USB_SM_100_SLV) || |
69 | (sm == WM8350_USB_SM_500_SLV) || |
70 | (sm == WM8350_USB_SM_STDBY_SLV)) |
71 | && !(ov & WM8350_USB_LIMIT_OVRDE)) |
72 | supplies = WM8350_USB_SUPPLY; |
73 | else if (((sm == WM8350_USB_SM_100_SLV) || |
74 | (sm == WM8350_USB_SM_500_SLV) || |
75 | (sm == WM8350_USB_SM_STDBY_SLV)) |
76 | && (ov & WM8350_USB_LIMIT_OVRDE) && (chrg == 0)) |
77 | supplies = WM8350_USB_SUPPLY | WM8350_BATT_SUPPLY; |
78 | else if (co & WM8350_WALL_FB_OVRDE) |
79 | supplies = WM8350_LINE_SUPPLY; |
80 | else |
81 | supplies = WM8350_BATT_SUPPLY; |
82 | |
83 | return supplies; |
84 | } |
85 | |
86 | static int wm8350_charger_config(struct wm8350 *wm8350, |
87 | struct wm8350_charger_policy *policy) |
88 | { |
89 | u16 reg, eoc_mA, fast_limit_mA; |
90 | |
91 | if (!policy) { |
92 | dev_warn(wm8350->dev, |
93 | "No charger policy, charger not configured.\n" ); |
94 | return -EINVAL; |
95 | } |
96 | |
97 | /* make sure USB fast charge current is not > 500mA */ |
98 | if (policy->fast_limit_USB_mA > 500) { |
99 | dev_err(wm8350->dev, "USB fast charge > 500mA\n" ); |
100 | return -EINVAL; |
101 | } |
102 | |
103 | eoc_mA = WM8350_CHG_EOC_mA(policy->eoc_mA); |
104 | |
105 | wm8350_reg_unlock(wm8350); |
106 | |
107 | reg = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1) |
108 | & WM8350_CHG_ENA_R168; |
109 | wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1, |
110 | val: reg | eoc_mA | policy->trickle_start_mV | |
111 | WM8350_CHG_TRICKLE_TEMP_CHOKE | |
112 | WM8350_CHG_TRICKLE_USB_CHOKE | |
113 | WM8350_CHG_FAST_USB_THROTTLE); |
114 | |
115 | if (wm8350_get_supplies(wm8350) & WM8350_USB_SUPPLY) { |
116 | fast_limit_mA = |
117 | WM8350_CHG_FAST_LIMIT_mA(policy->fast_limit_USB_mA); |
118 | wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2, |
119 | val: policy->charge_mV | policy->trickle_charge_USB_mA | |
120 | fast_limit_mA | wm8350_charge_time_min(wm8350, |
121 | min: policy->charge_timeout)); |
122 | |
123 | } else { |
124 | fast_limit_mA = |
125 | WM8350_CHG_FAST_LIMIT_mA(policy->fast_limit_mA); |
126 | wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2, |
127 | val: policy->charge_mV | policy->trickle_charge_mA | |
128 | fast_limit_mA | wm8350_charge_time_min(wm8350, |
129 | min: policy->charge_timeout)); |
130 | } |
131 | |
132 | wm8350_reg_lock(wm8350); |
133 | return 0; |
134 | } |
135 | |
136 | static int wm8350_batt_status(struct wm8350 *wm8350) |
137 | { |
138 | u16 state; |
139 | |
140 | state = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2); |
141 | state &= WM8350_CHG_STS_MASK; |
142 | |
143 | switch (state) { |
144 | case WM8350_CHG_STS_OFF: |
145 | return POWER_SUPPLY_STATUS_DISCHARGING; |
146 | |
147 | case WM8350_CHG_STS_TRICKLE: |
148 | case WM8350_CHG_STS_FAST: |
149 | return POWER_SUPPLY_STATUS_CHARGING; |
150 | |
151 | default: |
152 | return POWER_SUPPLY_STATUS_UNKNOWN; |
153 | } |
154 | } |
155 | |
156 | static ssize_t charger_state_show(struct device *dev, |
157 | struct device_attribute *attr, char *buf) |
158 | { |
159 | struct wm8350 *wm8350 = dev_get_drvdata(dev); |
160 | char *charge; |
161 | int state; |
162 | |
163 | state = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2) & |
164 | WM8350_CHG_STS_MASK; |
165 | switch (state) { |
166 | case WM8350_CHG_STS_OFF: |
167 | charge = "Charger Off" ; |
168 | break; |
169 | case WM8350_CHG_STS_TRICKLE: |
170 | charge = "Trickle Charging" ; |
171 | break; |
172 | case WM8350_CHG_STS_FAST: |
173 | charge = "Fast Charging" ; |
174 | break; |
175 | default: |
176 | return 0; |
177 | } |
178 | |
179 | return sysfs_emit(buf, fmt: "%s\n" , charge); |
180 | } |
181 | |
182 | static DEVICE_ATTR_RO(charger_state); |
183 | |
184 | static irqreturn_t wm8350_charger_handler(int irq, void *data) |
185 | { |
186 | struct wm8350 *wm8350 = data; |
187 | struct wm8350_power *power = &wm8350->power; |
188 | struct wm8350_charger_policy *policy = power->policy; |
189 | |
190 | switch (irq - wm8350->irq_base) { |
191 | case WM8350_IRQ_CHG_BAT_FAIL: |
192 | dev_err(wm8350->dev, "battery failed\n" ); |
193 | break; |
194 | case WM8350_IRQ_CHG_TO: |
195 | dev_err(wm8350->dev, "charger timeout\n" ); |
196 | power_supply_changed(psy: power->battery); |
197 | break; |
198 | |
199 | case WM8350_IRQ_CHG_BAT_HOT: |
200 | case WM8350_IRQ_CHG_BAT_COLD: |
201 | case WM8350_IRQ_CHG_START: |
202 | case WM8350_IRQ_CHG_END: |
203 | power_supply_changed(psy: power->battery); |
204 | break; |
205 | |
206 | case WM8350_IRQ_CHG_FAST_RDY: |
207 | dev_dbg(wm8350->dev, "fast charger ready\n" ); |
208 | wm8350_charger_config(wm8350, policy); |
209 | wm8350_reg_unlock(wm8350); |
210 | wm8350_set_bits(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1, |
211 | WM8350_CHG_FAST); |
212 | wm8350_reg_lock(wm8350); |
213 | break; |
214 | |
215 | case WM8350_IRQ_CHG_VBATT_LT_3P9: |
216 | dev_warn(wm8350->dev, "battery < 3.9V\n" ); |
217 | break; |
218 | case WM8350_IRQ_CHG_VBATT_LT_3P1: |
219 | dev_warn(wm8350->dev, "battery < 3.1V\n" ); |
220 | break; |
221 | case WM8350_IRQ_CHG_VBATT_LT_2P85: |
222 | dev_warn(wm8350->dev, "battery < 2.85V\n" ); |
223 | break; |
224 | |
225 | /* Supply change. We will overnotify but it should do |
226 | * no harm. */ |
227 | case WM8350_IRQ_EXT_USB_FB: |
228 | case WM8350_IRQ_EXT_WALL_FB: |
229 | wm8350_charger_config(wm8350, policy); |
230 | fallthrough; |
231 | case WM8350_IRQ_EXT_BAT_FB: |
232 | power_supply_changed(psy: power->battery); |
233 | power_supply_changed(psy: power->usb); |
234 | power_supply_changed(psy: power->ac); |
235 | break; |
236 | |
237 | default: |
238 | dev_err(wm8350->dev, "Unknown interrupt %d\n" , irq); |
239 | } |
240 | |
241 | return IRQ_HANDLED; |
242 | } |
243 | |
244 | /********************************************************************* |
245 | * AC Power |
246 | *********************************************************************/ |
247 | static int wm8350_ac_get_prop(struct power_supply *psy, |
248 | enum power_supply_property psp, |
249 | union power_supply_propval *val) |
250 | { |
251 | struct wm8350 *wm8350 = dev_get_drvdata(dev: psy->dev.parent); |
252 | int ret = 0; |
253 | |
254 | switch (psp) { |
255 | case POWER_SUPPLY_PROP_ONLINE: |
256 | val->intval = !!(wm8350_get_supplies(wm8350) & |
257 | WM8350_LINE_SUPPLY); |
258 | break; |
259 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
260 | val->intval = wm8350_read_line_uvolts(wm8350); |
261 | break; |
262 | default: |
263 | ret = -EINVAL; |
264 | break; |
265 | } |
266 | return ret; |
267 | } |
268 | |
269 | static enum power_supply_property wm8350_ac_props[] = { |
270 | POWER_SUPPLY_PROP_ONLINE, |
271 | POWER_SUPPLY_PROP_VOLTAGE_NOW, |
272 | }; |
273 | |
274 | /********************************************************************* |
275 | * USB Power |
276 | *********************************************************************/ |
277 | static int wm8350_usb_get_prop(struct power_supply *psy, |
278 | enum power_supply_property psp, |
279 | union power_supply_propval *val) |
280 | { |
281 | struct wm8350 *wm8350 = dev_get_drvdata(dev: psy->dev.parent); |
282 | int ret = 0; |
283 | |
284 | switch (psp) { |
285 | case POWER_SUPPLY_PROP_ONLINE: |
286 | val->intval = !!(wm8350_get_supplies(wm8350) & |
287 | WM8350_USB_SUPPLY); |
288 | break; |
289 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
290 | val->intval = wm8350_read_usb_uvolts(wm8350); |
291 | break; |
292 | default: |
293 | ret = -EINVAL; |
294 | break; |
295 | } |
296 | return ret; |
297 | } |
298 | |
299 | static enum power_supply_property wm8350_usb_props[] = { |
300 | POWER_SUPPLY_PROP_ONLINE, |
301 | POWER_SUPPLY_PROP_VOLTAGE_NOW, |
302 | }; |
303 | |
304 | /********************************************************************* |
305 | * Battery properties |
306 | *********************************************************************/ |
307 | |
308 | static int wm8350_bat_check_health(struct wm8350 *wm8350) |
309 | { |
310 | u16 reg; |
311 | |
312 | if (wm8350_read_battery_uvolts(wm8350) < 2850000) |
313 | return POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; |
314 | |
315 | reg = wm8350_reg_read(wm8350, WM8350_CHARGER_OVERRIDES); |
316 | if (reg & WM8350_CHG_BATT_HOT_OVRDE) |
317 | return POWER_SUPPLY_HEALTH_OVERHEAT; |
318 | |
319 | if (reg & WM8350_CHG_BATT_COLD_OVRDE) |
320 | return POWER_SUPPLY_HEALTH_COLD; |
321 | |
322 | return POWER_SUPPLY_HEALTH_GOOD; |
323 | } |
324 | |
325 | static int wm8350_bat_get_charge_type(struct wm8350 *wm8350) |
326 | { |
327 | int state; |
328 | |
329 | state = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2) & |
330 | WM8350_CHG_STS_MASK; |
331 | switch (state) { |
332 | case WM8350_CHG_STS_OFF: |
333 | return POWER_SUPPLY_CHARGE_TYPE_NONE; |
334 | case WM8350_CHG_STS_TRICKLE: |
335 | return POWER_SUPPLY_CHARGE_TYPE_TRICKLE; |
336 | case WM8350_CHG_STS_FAST: |
337 | return POWER_SUPPLY_CHARGE_TYPE_FAST; |
338 | default: |
339 | return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; |
340 | } |
341 | } |
342 | |
343 | static int wm8350_bat_get_property(struct power_supply *psy, |
344 | enum power_supply_property psp, |
345 | union power_supply_propval *val) |
346 | { |
347 | struct wm8350 *wm8350 = dev_get_drvdata(dev: psy->dev.parent); |
348 | int ret = 0; |
349 | |
350 | switch (psp) { |
351 | case POWER_SUPPLY_PROP_STATUS: |
352 | val->intval = wm8350_batt_status(wm8350); |
353 | break; |
354 | case POWER_SUPPLY_PROP_ONLINE: |
355 | val->intval = !!(wm8350_get_supplies(wm8350) & |
356 | WM8350_BATT_SUPPLY); |
357 | break; |
358 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
359 | val->intval = wm8350_read_battery_uvolts(wm8350); |
360 | break; |
361 | case POWER_SUPPLY_PROP_HEALTH: |
362 | val->intval = wm8350_bat_check_health(wm8350); |
363 | break; |
364 | case POWER_SUPPLY_PROP_CHARGE_TYPE: |
365 | val->intval = wm8350_bat_get_charge_type(wm8350); |
366 | break; |
367 | default: |
368 | ret = -EINVAL; |
369 | break; |
370 | } |
371 | |
372 | return ret; |
373 | } |
374 | |
375 | static enum power_supply_property wm8350_bat_props[] = { |
376 | POWER_SUPPLY_PROP_STATUS, |
377 | POWER_SUPPLY_PROP_ONLINE, |
378 | POWER_SUPPLY_PROP_VOLTAGE_NOW, |
379 | POWER_SUPPLY_PROP_HEALTH, |
380 | POWER_SUPPLY_PROP_CHARGE_TYPE, |
381 | }; |
382 | |
383 | static const struct power_supply_desc wm8350_ac_desc = { |
384 | .name = "wm8350-ac" , |
385 | .type = POWER_SUPPLY_TYPE_MAINS, |
386 | .properties = wm8350_ac_props, |
387 | .num_properties = ARRAY_SIZE(wm8350_ac_props), |
388 | .get_property = wm8350_ac_get_prop, |
389 | }; |
390 | |
391 | static const struct power_supply_desc wm8350_battery_desc = { |
392 | .name = "wm8350-battery" , |
393 | .properties = wm8350_bat_props, |
394 | .num_properties = ARRAY_SIZE(wm8350_bat_props), |
395 | .get_property = wm8350_bat_get_property, |
396 | .use_for_apm = 1, |
397 | }; |
398 | |
399 | static const struct power_supply_desc wm8350_usb_desc = { |
400 | .name = "wm8350-usb" , |
401 | .type = POWER_SUPPLY_TYPE_USB, |
402 | .properties = wm8350_usb_props, |
403 | .num_properties = ARRAY_SIZE(wm8350_usb_props), |
404 | .get_property = wm8350_usb_get_prop, |
405 | }; |
406 | |
407 | /********************************************************************* |
408 | * Initialisation |
409 | *********************************************************************/ |
410 | |
411 | static int wm8350_init_charger(struct wm8350 *wm8350) |
412 | { |
413 | int ret; |
414 | |
415 | /* register our interest in charger events */ |
416 | ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT, |
417 | handler: wm8350_charger_handler, flags: 0, name: "Battery hot" , data: wm8350); |
418 | if (ret) |
419 | goto err; |
420 | |
421 | ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD, |
422 | handler: wm8350_charger_handler, flags: 0, name: "Battery cold" , data: wm8350); |
423 | if (ret) |
424 | goto free_chg_bat_hot; |
425 | |
426 | ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL, |
427 | handler: wm8350_charger_handler, flags: 0, name: "Battery fail" , data: wm8350); |
428 | if (ret) |
429 | goto free_chg_bat_cold; |
430 | |
431 | ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_TO, |
432 | handler: wm8350_charger_handler, flags: 0, |
433 | name: "Charger timeout" , data: wm8350); |
434 | if (ret) |
435 | goto free_chg_bat_fail; |
436 | |
437 | ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_END, |
438 | handler: wm8350_charger_handler, flags: 0, |
439 | name: "Charge end" , data: wm8350); |
440 | if (ret) |
441 | goto free_chg_to; |
442 | |
443 | ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_START, |
444 | handler: wm8350_charger_handler, flags: 0, |
445 | name: "Charge start" , data: wm8350); |
446 | if (ret) |
447 | goto free_chg_end; |
448 | |
449 | ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY, |
450 | handler: wm8350_charger_handler, flags: 0, |
451 | name: "Fast charge ready" , data: wm8350); |
452 | if (ret) |
453 | goto free_chg_start; |
454 | |
455 | ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9, |
456 | handler: wm8350_charger_handler, flags: 0, |
457 | name: "Battery <3.9V" , data: wm8350); |
458 | if (ret) |
459 | goto free_chg_fast_rdy; |
460 | |
461 | ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1, |
462 | handler: wm8350_charger_handler, flags: 0, |
463 | name: "Battery <3.1V" , data: wm8350); |
464 | if (ret) |
465 | goto free_chg_vbatt_lt_3p9; |
466 | |
467 | ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85, |
468 | handler: wm8350_charger_handler, flags: 0, |
469 | name: "Battery <2.85V" , data: wm8350); |
470 | if (ret) |
471 | goto free_chg_vbatt_lt_3p1; |
472 | |
473 | /* and supply change events */ |
474 | ret = wm8350_register_irq(wm8350, WM8350_IRQ_EXT_USB_FB, |
475 | handler: wm8350_charger_handler, flags: 0, name: "USB" , data: wm8350); |
476 | if (ret) |
477 | goto free_chg_vbatt_lt_2p85; |
478 | |
479 | ret = wm8350_register_irq(wm8350, WM8350_IRQ_EXT_WALL_FB, |
480 | handler: wm8350_charger_handler, flags: 0, name: "Wall" , data: wm8350); |
481 | if (ret) |
482 | goto free_ext_usb_fb; |
483 | |
484 | ret = wm8350_register_irq(wm8350, WM8350_IRQ_EXT_BAT_FB, |
485 | handler: wm8350_charger_handler, flags: 0, name: "Battery" , data: wm8350); |
486 | if (ret) |
487 | goto free_ext_wall_fb; |
488 | |
489 | return 0; |
490 | |
491 | free_ext_wall_fb: |
492 | wm8350_free_irq(wm8350, WM8350_IRQ_EXT_WALL_FB, data: wm8350); |
493 | free_ext_usb_fb: |
494 | wm8350_free_irq(wm8350, WM8350_IRQ_EXT_USB_FB, data: wm8350); |
495 | free_chg_vbatt_lt_2p85: |
496 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85, data: wm8350); |
497 | free_chg_vbatt_lt_3p1: |
498 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1, data: wm8350); |
499 | free_chg_vbatt_lt_3p9: |
500 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9, data: wm8350); |
501 | free_chg_fast_rdy: |
502 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY, data: wm8350); |
503 | free_chg_start: |
504 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_START, data: wm8350); |
505 | free_chg_end: |
506 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_END, data: wm8350); |
507 | free_chg_to: |
508 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_TO, data: wm8350); |
509 | free_chg_bat_fail: |
510 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL, data: wm8350); |
511 | free_chg_bat_cold: |
512 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD, data: wm8350); |
513 | free_chg_bat_hot: |
514 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT, data: wm8350); |
515 | err: |
516 | return ret; |
517 | } |
518 | |
519 | static void free_charger_irq(struct wm8350 *wm8350) |
520 | { |
521 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT, data: wm8350); |
522 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD, data: wm8350); |
523 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL, data: wm8350); |
524 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_TO, data: wm8350); |
525 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_END, data: wm8350); |
526 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_START, data: wm8350); |
527 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY, data: wm8350); |
528 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9, data: wm8350); |
529 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1, data: wm8350); |
530 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85, data: wm8350); |
531 | wm8350_free_irq(wm8350, WM8350_IRQ_EXT_USB_FB, data: wm8350); |
532 | wm8350_free_irq(wm8350, WM8350_IRQ_EXT_WALL_FB, data: wm8350); |
533 | wm8350_free_irq(wm8350, WM8350_IRQ_EXT_BAT_FB, data: wm8350); |
534 | } |
535 | |
536 | static int wm8350_power_probe(struct platform_device *pdev) |
537 | { |
538 | struct wm8350 *wm8350 = platform_get_drvdata(pdev); |
539 | struct wm8350_power *power = &wm8350->power; |
540 | struct wm8350_charger_policy *policy = power->policy; |
541 | int ret; |
542 | |
543 | power->ac = power_supply_register(parent: &pdev->dev, desc: &wm8350_ac_desc, NULL); |
544 | if (IS_ERR(ptr: power->ac)) |
545 | return PTR_ERR(ptr: power->ac); |
546 | |
547 | power->battery = power_supply_register(parent: &pdev->dev, desc: &wm8350_battery_desc, |
548 | NULL); |
549 | if (IS_ERR(ptr: power->battery)) { |
550 | ret = PTR_ERR(ptr: power->battery); |
551 | goto battery_failed; |
552 | } |
553 | |
554 | power->usb = power_supply_register(parent: &pdev->dev, desc: &wm8350_usb_desc, NULL); |
555 | if (IS_ERR(ptr: power->usb)) { |
556 | ret = PTR_ERR(ptr: power->usb); |
557 | goto usb_failed; |
558 | } |
559 | |
560 | ret = device_create_file(device: &pdev->dev, entry: &dev_attr_charger_state); |
561 | if (ret < 0) |
562 | dev_warn(wm8350->dev, "failed to add charge sysfs: %d\n" , ret); |
563 | ret = 0; |
564 | |
565 | wm8350_init_charger(wm8350); |
566 | if (wm8350_charger_config(wm8350, policy) == 0) { |
567 | wm8350_reg_unlock(wm8350); |
568 | wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CHG_ENA); |
569 | wm8350_reg_lock(wm8350); |
570 | } |
571 | |
572 | return ret; |
573 | |
574 | usb_failed: |
575 | power_supply_unregister(psy: power->battery); |
576 | battery_failed: |
577 | power_supply_unregister(psy: power->ac); |
578 | |
579 | return ret; |
580 | } |
581 | |
582 | static void wm8350_power_remove(struct platform_device *pdev) |
583 | { |
584 | struct wm8350 *wm8350 = platform_get_drvdata(pdev); |
585 | struct wm8350_power *power = &wm8350->power; |
586 | |
587 | free_charger_irq(wm8350); |
588 | device_remove_file(dev: &pdev->dev, attr: &dev_attr_charger_state); |
589 | power_supply_unregister(psy: power->battery); |
590 | power_supply_unregister(psy: power->ac); |
591 | power_supply_unregister(psy: power->usb); |
592 | } |
593 | |
594 | static struct platform_driver wm8350_power_driver = { |
595 | .probe = wm8350_power_probe, |
596 | .remove_new = wm8350_power_remove, |
597 | .driver = { |
598 | .name = "wm8350-power" , |
599 | }, |
600 | }; |
601 | |
602 | module_platform_driver(wm8350_power_driver); |
603 | |
604 | MODULE_LICENSE("GPL" ); |
605 | MODULE_DESCRIPTION("Power supply driver for WM8350" ); |
606 | MODULE_ALIAS("platform:wm8350-power" ); |
607 | |