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 | ||
185 | static DEVICE_ATTR(charger_state, 0444, charger_state_show, NULL); | |
186 | ||
187 | static void wm8350_charger_handler(struct wm8350 *wm8350, int irq, void *data) | |
188 | { | |
189 | struct wm8350_power *power = &wm8350->power; | |
190 | struct wm8350_charger_policy *policy = power->policy; | |
191 | ||
192 | switch (irq) { | |
14431aa0 MB |
193 | case WM8350_IRQ_CHG_BAT_FAIL: |
194 | dev_err(wm8350->dev, "battery failed\n"); | |
195 | break; | |
196 | case WM8350_IRQ_CHG_TO: | |
197 | dev_err(wm8350->dev, "charger timeout\n"); | |
14431aa0 MB |
198 | power_supply_changed(&power->battery); |
199 | break; | |
4008e879 MB |
200 | |
201 | case WM8350_IRQ_CHG_BAT_HOT: | |
202 | case WM8350_IRQ_CHG_BAT_COLD: | |
14431aa0 | 203 | case WM8350_IRQ_CHG_START: |
4008e879 | 204 | case WM8350_IRQ_CHG_END: |
14431aa0 MB |
205 | power_supply_changed(&power->battery); |
206 | break; | |
207 | ||
208 | case WM8350_IRQ_CHG_FAST_RDY: | |
209 | dev_dbg(wm8350->dev, "fast charger ready\n"); | |
210 | wm8350_charger_config(wm8350, policy); | |
211 | wm8350_reg_unlock(wm8350); | |
212 | wm8350_set_bits(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1, | |
213 | WM8350_CHG_FAST); | |
214 | wm8350_reg_lock(wm8350); | |
215 | break; | |
216 | ||
217 | case WM8350_IRQ_CHG_VBATT_LT_3P9: | |
218 | dev_warn(wm8350->dev, "battery < 3.9V\n"); | |
219 | break; | |
220 | case WM8350_IRQ_CHG_VBATT_LT_3P1: | |
221 | dev_warn(wm8350->dev, "battery < 3.1V\n"); | |
222 | break; | |
223 | case WM8350_IRQ_CHG_VBATT_LT_2P85: | |
224 | dev_warn(wm8350->dev, "battery < 2.85V\n"); | |
225 | break; | |
226 | ||
227 | /* Supply change. We will overnotify but it should do | |
228 | * no harm. */ | |
229 | case WM8350_IRQ_EXT_USB_FB: | |
230 | case WM8350_IRQ_EXT_WALL_FB: | |
231 | wm8350_charger_config(wm8350, policy); | |
232 | case WM8350_IRQ_EXT_BAT_FB: /* Fall through */ | |
233 | power_supply_changed(&power->battery); | |
234 | power_supply_changed(&power->usb); | |
235 | power_supply_changed(&power->ac); | |
236 | break; | |
237 | ||
238 | default: | |
239 | dev_err(wm8350->dev, "Unknown interrupt %d\n", irq); | |
240 | } | |
241 | } | |
242 | ||
243 | /********************************************************************* | |
244 | * AC Power | |
245 | *********************************************************************/ | |
246 | static int wm8350_ac_get_prop(struct power_supply *psy, | |
247 | enum power_supply_property psp, | |
248 | union power_supply_propval *val) | |
249 | { | |
250 | struct wm8350 *wm8350 = dev_get_drvdata(psy->dev->parent); | |
251 | int ret = 0; | |
252 | ||
253 | switch (psp) { | |
254 | case POWER_SUPPLY_PROP_ONLINE: | |
255 | val->intval = !!(wm8350_get_supplies(wm8350) & | |
256 | WM8350_LINE_SUPPLY); | |
257 | break; | |
258 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | |
259 | val->intval = wm8350_read_line_uvolts(wm8350); | |
260 | break; | |
261 | default: | |
262 | ret = -EINVAL; | |
263 | break; | |
264 | } | |
265 | return ret; | |
266 | } | |
267 | ||
268 | static enum power_supply_property wm8350_ac_props[] = { | |
269 | POWER_SUPPLY_PROP_ONLINE, | |
270 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | |
271 | }; | |
272 | ||
273 | /********************************************************************* | |
274 | * USB Power | |
275 | *********************************************************************/ | |
276 | static int wm8350_usb_get_prop(struct power_supply *psy, | |
277 | enum power_supply_property psp, | |
278 | union power_supply_propval *val) | |
279 | { | |
280 | struct wm8350 *wm8350 = dev_get_drvdata(psy->dev->parent); | |
281 | int ret = 0; | |
282 | ||
283 | switch (psp) { | |
284 | case POWER_SUPPLY_PROP_ONLINE: | |
285 | val->intval = !!(wm8350_get_supplies(wm8350) & | |
286 | WM8350_USB_SUPPLY); | |
287 | break; | |
288 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | |
289 | val->intval = wm8350_read_usb_uvolts(wm8350); | |
290 | break; | |
291 | default: | |
292 | ret = -EINVAL; | |
293 | break; | |
294 | } | |
295 | return ret; | |
296 | } | |
297 | ||
298 | static enum power_supply_property wm8350_usb_props[] = { | |
299 | POWER_SUPPLY_PROP_ONLINE, | |
300 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | |
301 | }; | |
302 | ||
303 | /********************************************************************* | |
304 | * Battery properties | |
305 | *********************************************************************/ | |
306 | ||
4008e879 MB |
307 | static int wm8350_bat_check_health(struct wm8350 *wm8350) |
308 | { | |
309 | u16 reg; | |
310 | ||
311 | if (wm8350_read_battery_uvolts(wm8350) < 2850000) | |
312 | return POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; | |
313 | ||
314 | reg = wm8350_reg_read(wm8350, WM8350_CHARGER_OVERRIDES); | |
315 | if (reg & WM8350_CHG_BATT_HOT_OVRDE) | |
316 | return POWER_SUPPLY_HEALTH_OVERHEAT; | |
317 | ||
318 | if (reg & WM8350_CHG_BATT_COLD_OVRDE) | |
319 | return POWER_SUPPLY_HEALTH_COLD; | |
320 | ||
321 | return POWER_SUPPLY_HEALTH_GOOD; | |
322 | } | |
323 | ||
14431aa0 MB |
324 | static int wm8350_bat_get_property(struct power_supply *psy, |
325 | enum power_supply_property psp, | |
326 | union power_supply_propval *val) | |
327 | { | |
328 | struct wm8350 *wm8350 = dev_get_drvdata(psy->dev->parent); | |
329 | int ret = 0; | |
330 | ||
331 | switch (psp) { | |
332 | case POWER_SUPPLY_PROP_STATUS: | |
333 | val->intval = wm8350_batt_status(wm8350); | |
334 | break; | |
335 | case POWER_SUPPLY_PROP_ONLINE: | |
336 | val->intval = !!(wm8350_get_supplies(wm8350) & | |
337 | WM8350_BATT_SUPPLY); | |
338 | break; | |
339 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | |
340 | val->intval = wm8350_read_battery_uvolts(wm8350); | |
341 | break; | |
4008e879 MB |
342 | case POWER_SUPPLY_PROP_HEALTH: |
343 | val->intval = wm8350_bat_check_health(wm8350); | |
344 | break; | |
14431aa0 MB |
345 | default: |
346 | ret = -EINVAL; | |
347 | break; | |
348 | } | |
349 | ||
350 | return ret; | |
351 | } | |
352 | ||
353 | static enum power_supply_property wm8350_bat_props[] = { | |
354 | POWER_SUPPLY_PROP_STATUS, | |
355 | POWER_SUPPLY_PROP_ONLINE, | |
356 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | |
4008e879 | 357 | POWER_SUPPLY_PROP_HEALTH, |
14431aa0 MB |
358 | }; |
359 | ||
360 | /********************************************************************* | |
361 | * Initialisation | |
362 | *********************************************************************/ | |
363 | ||
364 | static void wm8350_init_charger(struct wm8350 *wm8350) | |
365 | { | |
366 | /* register our interest in charger events */ | |
367 | wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT, | |
368 | wm8350_charger_handler, NULL); | |
369 | wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT); | |
370 | wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD, | |
371 | wm8350_charger_handler, NULL); | |
372 | wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD); | |
373 | wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL, | |
374 | wm8350_charger_handler, NULL); | |
375 | wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL); | |
376 | wm8350_register_irq(wm8350, WM8350_IRQ_CHG_TO, | |
377 | wm8350_charger_handler, NULL); | |
378 | wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_TO); | |
379 | wm8350_register_irq(wm8350, WM8350_IRQ_CHG_END, | |
380 | wm8350_charger_handler, NULL); | |
381 | wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_END); | |
382 | wm8350_register_irq(wm8350, WM8350_IRQ_CHG_START, | |
383 | wm8350_charger_handler, NULL); | |
384 | wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_START); | |
385 | wm8350_register_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY, | |
386 | wm8350_charger_handler, NULL); | |
387 | wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY); | |
388 | wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9, | |
389 | wm8350_charger_handler, NULL); | |
390 | wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9); | |
391 | wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1, | |
392 | wm8350_charger_handler, NULL); | |
393 | wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1); | |
394 | wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85, | |
395 | wm8350_charger_handler, NULL); | |
396 | wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85); | |
397 | ||
398 | /* and supply change events */ | |
399 | wm8350_register_irq(wm8350, WM8350_IRQ_EXT_USB_FB, | |
400 | wm8350_charger_handler, NULL); | |
401 | wm8350_unmask_irq(wm8350, WM8350_IRQ_EXT_USB_FB); | |
402 | wm8350_register_irq(wm8350, WM8350_IRQ_EXT_WALL_FB, | |
403 | wm8350_charger_handler, NULL); | |
404 | wm8350_unmask_irq(wm8350, WM8350_IRQ_EXT_WALL_FB); | |
405 | wm8350_register_irq(wm8350, WM8350_IRQ_EXT_BAT_FB, | |
406 | wm8350_charger_handler, NULL); | |
407 | wm8350_unmask_irq(wm8350, WM8350_IRQ_EXT_BAT_FB); | |
408 | } | |
409 | ||
410 | static void free_charger_irq(struct wm8350 *wm8350) | |
411 | { | |
412 | wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT); | |
413 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT); | |
414 | wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD); | |
415 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD); | |
416 | wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL); | |
417 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL); | |
418 | wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_TO); | |
419 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_TO); | |
420 | wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_END); | |
421 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_END); | |
422 | wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_START); | |
423 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_START); | |
424 | wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9); | |
425 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9); | |
426 | wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1); | |
427 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1); | |
428 | wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85); | |
429 | wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85); | |
430 | wm8350_mask_irq(wm8350, WM8350_IRQ_EXT_USB_FB); | |
431 | wm8350_free_irq(wm8350, WM8350_IRQ_EXT_USB_FB); | |
432 | wm8350_mask_irq(wm8350, WM8350_IRQ_EXT_WALL_FB); | |
433 | wm8350_free_irq(wm8350, WM8350_IRQ_EXT_WALL_FB); | |
434 | wm8350_mask_irq(wm8350, WM8350_IRQ_EXT_BAT_FB); | |
435 | wm8350_free_irq(wm8350, WM8350_IRQ_EXT_BAT_FB); | |
436 | } | |
437 | ||
438 | static __devinit int wm8350_power_probe(struct platform_device *pdev) | |
439 | { | |
440 | struct wm8350 *wm8350 = platform_get_drvdata(pdev); | |
441 | struct wm8350_power *power = &wm8350->power; | |
442 | struct wm8350_charger_policy *policy = power->policy; | |
443 | struct power_supply *usb = &power->usb; | |
444 | struct power_supply *battery = &power->battery; | |
445 | struct power_supply *ac = &power->ac; | |
446 | int ret; | |
447 | ||
448 | ac->name = "wm8350-ac"; | |
449 | ac->type = POWER_SUPPLY_TYPE_MAINS; | |
450 | ac->properties = wm8350_ac_props; | |
451 | ac->num_properties = ARRAY_SIZE(wm8350_ac_props); | |
452 | ac->get_property = wm8350_ac_get_prop; | |
453 | ret = power_supply_register(&pdev->dev, ac); | |
454 | if (ret) | |
455 | return ret; | |
456 | ||
457 | battery->name = "wm8350-battery"; | |
458 | battery->properties = wm8350_bat_props; | |
459 | battery->num_properties = ARRAY_SIZE(wm8350_bat_props); | |
460 | battery->get_property = wm8350_bat_get_property; | |
461 | battery->use_for_apm = 1; | |
462 | ret = power_supply_register(&pdev->dev, battery); | |
463 | if (ret) | |
464 | goto battery_failed; | |
465 | ||
466 | usb->name = "wm8350-usb", | |
467 | usb->type = POWER_SUPPLY_TYPE_USB; | |
468 | usb->properties = wm8350_usb_props; | |
469 | usb->num_properties = ARRAY_SIZE(wm8350_usb_props); | |
470 | usb->get_property = wm8350_usb_get_prop; | |
471 | ret = power_supply_register(&pdev->dev, usb); | |
472 | if (ret) | |
473 | goto usb_failed; | |
474 | ||
475 | ret = device_create_file(&pdev->dev, &dev_attr_charger_state); | |
476 | if (ret < 0) | |
477 | dev_warn(wm8350->dev, "failed to add charge sysfs: %d\n", ret); | |
478 | ret = 0; | |
479 | ||
480 | wm8350_init_charger(wm8350); | |
481 | if (wm8350_charger_config(wm8350, policy) == 0) { | |
482 | wm8350_reg_unlock(wm8350); | |
483 | wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CHG_ENA); | |
484 | wm8350_reg_lock(wm8350); | |
485 | } | |
486 | ||
487 | return ret; | |
488 | ||
489 | usb_failed: | |
490 | power_supply_unregister(battery); | |
491 | battery_failed: | |
492 | power_supply_unregister(ac); | |
493 | ||
494 | return ret; | |
495 | } | |
496 | ||
497 | static __devexit int wm8350_power_remove(struct platform_device *pdev) | |
498 | { | |
499 | struct wm8350 *wm8350 = platform_get_drvdata(pdev); | |
500 | struct wm8350_power *power = &wm8350->power; | |
501 | ||
502 | free_charger_irq(wm8350); | |
503 | device_remove_file(&pdev->dev, &dev_attr_charger_state); | |
504 | power_supply_unregister(&power->battery); | |
505 | power_supply_unregister(&power->ac); | |
506 | power_supply_unregister(&power->usb); | |
507 | return 0; | |
508 | } | |
509 | ||
510 | static struct platform_driver wm8350_power_driver = { | |
511 | .probe = wm8350_power_probe, | |
512 | .remove = __devexit_p(wm8350_power_remove), | |
513 | .driver = { | |
514 | .name = "wm8350-power", | |
515 | }, | |
516 | }; | |
517 | ||
518 | static int __init wm8350_power_init(void) | |
519 | { | |
520 | return platform_driver_register(&wm8350_power_driver); | |
521 | } | |
522 | module_init(wm8350_power_init); | |
523 | ||
524 | static void __exit wm8350_power_exit(void) | |
525 | { | |
526 | platform_driver_unregister(&wm8350_power_driver); | |
527 | } | |
528 | module_exit(wm8350_power_exit); | |
529 | ||
530 | MODULE_LICENSE("GPL"); | |
531 | MODULE_DESCRIPTION("Power supply driver for WM8350"); | |
532 | MODULE_ALIAS("platform:wm8350-power"); |