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