Root/
1 | /* |
2 | * linux/drivers/power/wm97xx_battery.c |
3 | * |
4 | * Battery measurement code for WM97xx |
5 | * |
6 | * based on tosa_battery.c |
7 | * |
8 | * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com> |
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 | |
16 | #include <linux/init.h> |
17 | #include <linux/kernel.h> |
18 | #include <linux/module.h> |
19 | #include <linux/platform_device.h> |
20 | #include <linux/power_supply.h> |
21 | #include <linux/wm97xx.h> |
22 | #include <linux/spinlock.h> |
23 | #include <linux/interrupt.h> |
24 | #include <linux/gpio.h> |
25 | #include <linux/irq.h> |
26 | #include <linux/slab.h> |
27 | |
28 | static struct work_struct bat_work; |
29 | static DEFINE_MUTEX(work_lock); |
30 | static int bat_status = POWER_SUPPLY_STATUS_UNKNOWN; |
31 | static enum power_supply_property *prop; |
32 | |
33 | static unsigned long wm97xx_read_bat(struct power_supply *bat_ps) |
34 | { |
35 | struct wm97xx_pdata *wmdata = bat_ps->dev->parent->platform_data; |
36 | struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata; |
37 | |
38 | return wm97xx_read_aux_adc(dev_get_drvdata(bat_ps->dev->parent), |
39 | pdata->batt_aux) * pdata->batt_mult / |
40 | pdata->batt_div; |
41 | } |
42 | |
43 | static unsigned long wm97xx_read_temp(struct power_supply *bat_ps) |
44 | { |
45 | struct wm97xx_pdata *wmdata = bat_ps->dev->parent->platform_data; |
46 | struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata; |
47 | |
48 | return wm97xx_read_aux_adc(dev_get_drvdata(bat_ps->dev->parent), |
49 | pdata->temp_aux) * pdata->temp_mult / |
50 | pdata->temp_div; |
51 | } |
52 | |
53 | static int wm97xx_bat_get_property(struct power_supply *bat_ps, |
54 | enum power_supply_property psp, |
55 | union power_supply_propval *val) |
56 | { |
57 | struct wm97xx_pdata *wmdata = bat_ps->dev->parent->platform_data; |
58 | struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata; |
59 | |
60 | switch (psp) { |
61 | case POWER_SUPPLY_PROP_STATUS: |
62 | val->intval = bat_status; |
63 | break; |
64 | case POWER_SUPPLY_PROP_TECHNOLOGY: |
65 | val->intval = pdata->batt_tech; |
66 | break; |
67 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
68 | if (pdata->batt_aux >= 0) |
69 | val->intval = wm97xx_read_bat(bat_ps); |
70 | else |
71 | return -EINVAL; |
72 | break; |
73 | case POWER_SUPPLY_PROP_TEMP: |
74 | if (pdata->temp_aux >= 0) |
75 | val->intval = wm97xx_read_temp(bat_ps); |
76 | else |
77 | return -EINVAL; |
78 | break; |
79 | case POWER_SUPPLY_PROP_VOLTAGE_MAX: |
80 | if (pdata->max_voltage >= 0) |
81 | val->intval = pdata->max_voltage; |
82 | else |
83 | return -EINVAL; |
84 | break; |
85 | case POWER_SUPPLY_PROP_VOLTAGE_MIN: |
86 | if (pdata->min_voltage >= 0) |
87 | val->intval = pdata->min_voltage; |
88 | else |
89 | return -EINVAL; |
90 | break; |
91 | case POWER_SUPPLY_PROP_PRESENT: |
92 | val->intval = 1; |
93 | break; |
94 | default: |
95 | return -EINVAL; |
96 | } |
97 | return 0; |
98 | } |
99 | |
100 | static void wm97xx_bat_external_power_changed(struct power_supply *bat_ps) |
101 | { |
102 | schedule_work(&bat_work); |
103 | } |
104 | |
105 | static void wm97xx_bat_update(struct power_supply *bat_ps) |
106 | { |
107 | int old_status = bat_status; |
108 | struct wm97xx_pdata *wmdata = bat_ps->dev->parent->platform_data; |
109 | struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata; |
110 | |
111 | mutex_lock(&work_lock); |
112 | |
113 | bat_status = (pdata->charge_gpio >= 0) ? |
114 | (gpio_get_value(pdata->charge_gpio) ? |
115 | POWER_SUPPLY_STATUS_DISCHARGING : |
116 | POWER_SUPPLY_STATUS_CHARGING) : |
117 | POWER_SUPPLY_STATUS_UNKNOWN; |
118 | |
119 | if (old_status != bat_status) { |
120 | pr_debug("%s: %i -> %i\n", bat_ps->name, old_status, |
121 | bat_status); |
122 | power_supply_changed(bat_ps); |
123 | } |
124 | |
125 | mutex_unlock(&work_lock); |
126 | } |
127 | |
128 | static struct power_supply bat_ps = { |
129 | .type = POWER_SUPPLY_TYPE_BATTERY, |
130 | .get_property = wm97xx_bat_get_property, |
131 | .external_power_changed = wm97xx_bat_external_power_changed, |
132 | .use_for_apm = 1, |
133 | }; |
134 | |
135 | static void wm97xx_bat_work(struct work_struct *work) |
136 | { |
137 | wm97xx_bat_update(&bat_ps); |
138 | } |
139 | |
140 | static irqreturn_t wm97xx_chrg_irq(int irq, void *data) |
141 | { |
142 | schedule_work(&bat_work); |
143 | return IRQ_HANDLED; |
144 | } |
145 | |
146 | #ifdef CONFIG_PM |
147 | static int wm97xx_bat_suspend(struct device *dev) |
148 | { |
149 | flush_work_sync(&bat_work); |
150 | return 0; |
151 | } |
152 | |
153 | static int wm97xx_bat_resume(struct device *dev) |
154 | { |
155 | schedule_work(&bat_work); |
156 | return 0; |
157 | } |
158 | |
159 | static const struct dev_pm_ops wm97xx_bat_pm_ops = { |
160 | .suspend = wm97xx_bat_suspend, |
161 | .resume = wm97xx_bat_resume, |
162 | }; |
163 | #endif |
164 | |
165 | static int __devinit wm97xx_bat_probe(struct platform_device *dev) |
166 | { |
167 | int ret = 0; |
168 | int props = 1; /* POWER_SUPPLY_PROP_PRESENT */ |
169 | int i = 0; |
170 | struct wm97xx_pdata *wmdata = dev->dev.platform_data; |
171 | struct wm97xx_batt_pdata *pdata; |
172 | |
173 | if (!wmdata) { |
174 | dev_err(&dev->dev, "No platform data supplied\n"); |
175 | return -EINVAL; |
176 | } |
177 | |
178 | pdata = wmdata->batt_pdata; |
179 | |
180 | if (dev->id != -1) |
181 | return -EINVAL; |
182 | |
183 | if (!pdata) { |
184 | dev_err(&dev->dev, "No platform_data supplied\n"); |
185 | return -EINVAL; |
186 | } |
187 | |
188 | if (gpio_is_valid(pdata->charge_gpio)) { |
189 | ret = gpio_request(pdata->charge_gpio, "BATT CHRG"); |
190 | if (ret) |
191 | goto err; |
192 | ret = gpio_direction_input(pdata->charge_gpio); |
193 | if (ret) |
194 | goto err2; |
195 | ret = request_irq(gpio_to_irq(pdata->charge_gpio), |
196 | wm97xx_chrg_irq, 0, |
197 | "AC Detect", dev); |
198 | if (ret) |
199 | goto err2; |
200 | props++; /* POWER_SUPPLY_PROP_STATUS */ |
201 | } |
202 | |
203 | if (pdata->batt_tech >= 0) |
204 | props++; /* POWER_SUPPLY_PROP_TECHNOLOGY */ |
205 | if (pdata->temp_aux >= 0) |
206 | props++; /* POWER_SUPPLY_PROP_TEMP */ |
207 | if (pdata->batt_aux >= 0) |
208 | props++; /* POWER_SUPPLY_PROP_VOLTAGE_NOW */ |
209 | if (pdata->max_voltage >= 0) |
210 | props++; /* POWER_SUPPLY_PROP_VOLTAGE_MAX */ |
211 | if (pdata->min_voltage >= 0) |
212 | props++; /* POWER_SUPPLY_PROP_VOLTAGE_MIN */ |
213 | |
214 | prop = kzalloc(props * sizeof(*prop), GFP_KERNEL); |
215 | if (!prop) |
216 | goto err3; |
217 | |
218 | prop[i++] = POWER_SUPPLY_PROP_PRESENT; |
219 | if (pdata->charge_gpio >= 0) |
220 | prop[i++] = POWER_SUPPLY_PROP_STATUS; |
221 | if (pdata->batt_tech >= 0) |
222 | prop[i++] = POWER_SUPPLY_PROP_TECHNOLOGY; |
223 | if (pdata->temp_aux >= 0) |
224 | prop[i++] = POWER_SUPPLY_PROP_TEMP; |
225 | if (pdata->batt_aux >= 0) |
226 | prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_NOW; |
227 | if (pdata->max_voltage >= 0) |
228 | prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MAX; |
229 | if (pdata->min_voltage >= 0) |
230 | prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MIN; |
231 | |
232 | INIT_WORK(&bat_work, wm97xx_bat_work); |
233 | |
234 | if (!pdata->batt_name) { |
235 | dev_info(&dev->dev, "Please consider setting proper battery " |
236 | "name in platform definition file, falling " |
237 | "back to name \"wm97xx-batt\"\n"); |
238 | bat_ps.name = "wm97xx-batt"; |
239 | } else |
240 | bat_ps.name = pdata->batt_name; |
241 | |
242 | bat_ps.properties = prop; |
243 | bat_ps.num_properties = props; |
244 | |
245 | ret = power_supply_register(&dev->dev, &bat_ps); |
246 | if (!ret) |
247 | schedule_work(&bat_work); |
248 | else |
249 | goto err4; |
250 | |
251 | return 0; |
252 | err4: |
253 | kfree(prop); |
254 | err3: |
255 | if (gpio_is_valid(pdata->charge_gpio)) |
256 | free_irq(gpio_to_irq(pdata->charge_gpio), dev); |
257 | err2: |
258 | if (gpio_is_valid(pdata->charge_gpio)) |
259 | gpio_free(pdata->charge_gpio); |
260 | err: |
261 | return ret; |
262 | } |
263 | |
264 | static int __devexit wm97xx_bat_remove(struct platform_device *dev) |
265 | { |
266 | struct wm97xx_pdata *wmdata = dev->dev.platform_data; |
267 | struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata; |
268 | |
269 | if (pdata && gpio_is_valid(pdata->charge_gpio)) { |
270 | free_irq(gpio_to_irq(pdata->charge_gpio), dev); |
271 | gpio_free(pdata->charge_gpio); |
272 | } |
273 | cancel_work_sync(&bat_work); |
274 | power_supply_unregister(&bat_ps); |
275 | kfree(prop); |
276 | return 0; |
277 | } |
278 | |
279 | static struct platform_driver wm97xx_bat_driver = { |
280 | .driver = { |
281 | .name = "wm97xx-battery", |
282 | .owner = THIS_MODULE, |
283 | #ifdef CONFIG_PM |
284 | .pm = &wm97xx_bat_pm_ops, |
285 | #endif |
286 | }, |
287 | .probe = wm97xx_bat_probe, |
288 | .remove = __devexit_p(wm97xx_bat_remove), |
289 | }; |
290 | |
291 | module_platform_driver(wm97xx_bat_driver); |
292 | |
293 | MODULE_LICENSE("GPL"); |
294 | MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); |
295 | MODULE_DESCRIPTION("WM97xx battery driver"); |
296 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9