diff options
-rw-r--r-- | drivers/power/Kconfig | 7 | ||||
-rw-r--r-- | drivers/power/Makefile | 1 | ||||
-rw-r--r-- | drivers/power/generic-adc-battery.c | 422 | ||||
-rw-r--r-- | include/linux/power/generic-adc-battery.h | 29 |
4 files changed, 459 insertions, 0 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index c1892f321c46..80978196aae8 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig | |||
@@ -29,6 +29,13 @@ config APM_POWER | |||
29 | Say Y here to enable support APM status emulation using | 29 | Say Y here to enable support APM status emulation using |
30 | battery class devices. | 30 | battery class devices. |
31 | 31 | ||
32 | config GENERIC_ADC_BATTERY | ||
33 | tristate "Generic battery support using IIO" | ||
34 | depends on IIO | ||
35 | help | ||
36 | Say Y here to enable support for the generic battery driver | ||
37 | which uses IIO framework to read adc. | ||
38 | |||
32 | config MAX8925_POWER | 39 | config MAX8925_POWER |
33 | tristate "MAX8925 battery charger support" | 40 | tristate "MAX8925 battery charger support" |
34 | depends on MFD_MAX8925 | 41 | depends on MFD_MAX8925 |
diff --git a/drivers/power/Makefile b/drivers/power/Makefile index ee58afb1e71f..e0b4d4284e1d 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile | |||
@@ -5,6 +5,7 @@ power_supply-$(CONFIG_SYSFS) += power_supply_sysfs.o | |||
5 | power_supply-$(CONFIG_LEDS_TRIGGERS) += power_supply_leds.o | 5 | power_supply-$(CONFIG_LEDS_TRIGGERS) += power_supply_leds.o |
6 | 6 | ||
7 | obj-$(CONFIG_POWER_SUPPLY) += power_supply.o | 7 | obj-$(CONFIG_POWER_SUPPLY) += power_supply.o |
8 | obj-$(CONFIG_GENERIC_ADC_BATTERY) += generic-adc-battery.o | ||
8 | 9 | ||
9 | obj-$(CONFIG_PDA_POWER) += pda_power.o | 10 | obj-$(CONFIG_PDA_POWER) += pda_power.o |
10 | obj-$(CONFIG_APM_POWER) += apm_power.o | 11 | obj-$(CONFIG_APM_POWER) += apm_power.o |
diff --git a/drivers/power/generic-adc-battery.c b/drivers/power/generic-adc-battery.c new file mode 100644 index 000000000000..9bdf44470396 --- /dev/null +++ b/drivers/power/generic-adc-battery.c | |||
@@ -0,0 +1,422 @@ | |||
1 | /* | ||
2 | * Generic battery driver code using IIO | ||
3 | * Copyright (C) 2012, Anish Kumar <anish198519851985@gmail.com> | ||
4 | * based on jz4740-battery.c | ||
5 | * based on s3c_adc_battery.c | ||
6 | * | ||
7 | * This file is subject to the terms and conditions of the GNU General Public | ||
8 | * License. See the file COPYING in the main directory of this archive for | ||
9 | * more details. | ||
10 | * | ||
11 | */ | ||
12 | #include <linux/interrupt.h> | ||
13 | #include <linux/platform_device.h> | ||
14 | #include <linux/power_supply.h> | ||
15 | #include <linux/gpio.h> | ||
16 | #include <linux/err.h> | ||
17 | #include <linux/timer.h> | ||
18 | #include <linux/jiffies.h> | ||
19 | #include <linux/errno.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/iio/consumer.h> | ||
24 | #include <linux/iio/types.h> | ||
25 | #include <linux/power/generic-adc-battery.h> | ||
26 | |||
27 | #define JITTER_DEFAULT 10 /* hope 10ms is enough */ | ||
28 | |||
29 | enum gab_chan_type { | ||
30 | GAB_VOLTAGE = 0, | ||
31 | GAB_CURRENT, | ||
32 | GAB_POWER, | ||
33 | GAB_MAX_CHAN_TYPE | ||
34 | }; | ||
35 | |||
36 | /* | ||
37 | * gab_chan_name suggests the standard channel names for commonly used | ||
38 | * channel types. | ||
39 | */ | ||
40 | static const char *const gab_chan_name[] = { | ||
41 | [GAB_VOLTAGE] = "voltage", | ||
42 | [GAB_CURRENT] = "current", | ||
43 | [GAB_POWER] = "power", | ||
44 | }; | ||
45 | |||
46 | struct gab { | ||
47 | struct power_supply psy; | ||
48 | struct iio_channel *channel[GAB_MAX_CHAN_TYPE]; | ||
49 | struct gab_platform_data *pdata; | ||
50 | struct delayed_work bat_work; | ||
51 | int level; | ||
52 | int status; | ||
53 | bool cable_plugged; | ||
54 | }; | ||
55 | |||
56 | static struct gab *to_generic_bat(struct power_supply *psy) | ||
57 | { | ||
58 | return container_of(psy, struct gab, psy); | ||
59 | } | ||
60 | |||
61 | static void gab_ext_power_changed(struct power_supply *psy) | ||
62 | { | ||
63 | struct gab *adc_bat = to_generic_bat(psy); | ||
64 | |||
65 | schedule_delayed_work(&adc_bat->bat_work, msecs_to_jiffies(0)); | ||
66 | } | ||
67 | |||
68 | static const enum power_supply_property gab_props[] = { | ||
69 | POWER_SUPPLY_PROP_STATUS, | ||
70 | POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, | ||
71 | POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN, | ||
72 | POWER_SUPPLY_PROP_CHARGE_NOW, | ||
73 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | ||
74 | POWER_SUPPLY_PROP_CURRENT_NOW, | ||
75 | POWER_SUPPLY_PROP_TECHNOLOGY, | ||
76 | POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, | ||
77 | POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, | ||
78 | POWER_SUPPLY_PROP_MODEL_NAME, | ||
79 | }; | ||
80 | |||
81 | /* | ||
82 | * This properties are set based on the received platform data and this | ||
83 | * should correspond one-to-one with enum chan_type. | ||
84 | */ | ||
85 | static const enum power_supply_property gab_dyn_props[] = { | ||
86 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | ||
87 | POWER_SUPPLY_PROP_CURRENT_NOW, | ||
88 | POWER_SUPPLY_PROP_POWER_NOW, | ||
89 | }; | ||
90 | |||
91 | static bool gab_charge_finished(struct gab *adc_bat) | ||
92 | { | ||
93 | struct gab_platform_data *pdata = adc_bat->pdata; | ||
94 | bool ret = gpio_get_value(pdata->gpio_charge_finished); | ||
95 | bool inv = pdata->gpio_inverted; | ||
96 | |||
97 | if (!gpio_is_valid(pdata->gpio_charge_finished)) | ||
98 | return false; | ||
99 | return ret ^ inv; | ||
100 | } | ||
101 | |||
102 | static int gab_get_status(struct gab *adc_bat) | ||
103 | { | ||
104 | struct gab_platform_data *pdata = adc_bat->pdata; | ||
105 | struct power_supply_info *bat_info; | ||
106 | |||
107 | bat_info = &pdata->battery_info; | ||
108 | if (adc_bat->level == bat_info->charge_full_design) | ||
109 | return POWER_SUPPLY_STATUS_FULL; | ||
110 | return adc_bat->status; | ||
111 | } | ||
112 | |||
113 | static enum gab_chan_type gab_prop_to_chan(enum power_supply_property psp) | ||
114 | { | ||
115 | switch (psp) { | ||
116 | case POWER_SUPPLY_PROP_POWER_NOW: | ||
117 | return GAB_POWER; | ||
118 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
119 | return GAB_VOLTAGE; | ||
120 | case POWER_SUPPLY_PROP_CURRENT_NOW: | ||
121 | return GAB_CURRENT; | ||
122 | default: | ||
123 | WARN_ON(1); | ||
124 | break; | ||
125 | } | ||
126 | return GAB_POWER; | ||
127 | } | ||
128 | |||
129 | static int read_channel(struct gab *adc_bat, enum power_supply_property psp, | ||
130 | int *result) | ||
131 | { | ||
132 | int ret; | ||
133 | int chan_index; | ||
134 | |||
135 | chan_index = gab_prop_to_chan(psp); | ||
136 | ret = iio_read_channel_processed(adc_bat->channel[chan_index], | ||
137 | result); | ||
138 | if (ret < 0) | ||
139 | pr_err("read channel error\n"); | ||
140 | return ret; | ||
141 | } | ||
142 | |||
143 | static int gab_get_property(struct power_supply *psy, | ||
144 | enum power_supply_property psp, union power_supply_propval *val) | ||
145 | { | ||
146 | struct gab *adc_bat; | ||
147 | struct gab_platform_data *pdata; | ||
148 | struct power_supply_info *bat_info; | ||
149 | int result = 0; | ||
150 | int ret = 0; | ||
151 | |||
152 | adc_bat = to_generic_bat(psy); | ||
153 | if (!adc_bat) { | ||
154 | dev_err(psy->dev, "no battery infos ?!\n"); | ||
155 | return -EINVAL; | ||
156 | } | ||
157 | pdata = adc_bat->pdata; | ||
158 | bat_info = &pdata->battery_info; | ||
159 | |||
160 | switch (psp) { | ||
161 | case POWER_SUPPLY_PROP_STATUS: | ||
162 | gab_get_status(adc_bat); | ||
163 | break; | ||
164 | case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN: | ||
165 | val->intval = 0; | ||
166 | break; | ||
167 | case POWER_SUPPLY_PROP_CHARGE_NOW: | ||
168 | val->intval = pdata->cal_charge(result); | ||
169 | break; | ||
170 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
171 | case POWER_SUPPLY_PROP_CURRENT_NOW: | ||
172 | case POWER_SUPPLY_PROP_POWER_NOW: | ||
173 | ret = read_channel(adc_bat, psp, &result); | ||
174 | if (ret < 0) | ||
175 | goto err; | ||
176 | val->intval = result; | ||
177 | break; | ||
178 | case POWER_SUPPLY_PROP_TECHNOLOGY: | ||
179 | val->intval = bat_info->technology; | ||
180 | break; | ||
181 | case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: | ||
182 | val->intval = bat_info->voltage_min_design; | ||
183 | break; | ||
184 | case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: | ||
185 | val->intval = bat_info->voltage_max_design; | ||
186 | break; | ||
187 | case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: | ||
188 | val->intval = bat_info->charge_full_design; | ||
189 | break; | ||
190 | case POWER_SUPPLY_PROP_MODEL_NAME: | ||
191 | val->strval = bat_info->name; | ||
192 | break; | ||
193 | default: | ||
194 | return -EINVAL; | ||
195 | } | ||
196 | err: | ||
197 | return ret; | ||
198 | } | ||
199 | |||
200 | static void gab_work(struct work_struct *work) | ||
201 | { | ||
202 | struct gab *adc_bat; | ||
203 | struct gab_platform_data *pdata; | ||
204 | struct delayed_work *delayed_work; | ||
205 | bool is_plugged; | ||
206 | int status; | ||
207 | |||
208 | delayed_work = container_of(work, struct delayed_work, work); | ||
209 | adc_bat = container_of(delayed_work, struct gab, bat_work); | ||
210 | pdata = adc_bat->pdata; | ||
211 | status = adc_bat->status; | ||
212 | |||
213 | is_plugged = power_supply_am_i_supplied(&adc_bat->psy); | ||
214 | adc_bat->cable_plugged = is_plugged; | ||
215 | |||
216 | if (!is_plugged) | ||
217 | adc_bat->status = POWER_SUPPLY_STATUS_DISCHARGING; | ||
218 | else if (gab_charge_finished(adc_bat)) | ||
219 | adc_bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING; | ||
220 | else | ||
221 | adc_bat->status = POWER_SUPPLY_STATUS_CHARGING; | ||
222 | |||
223 | if (status != adc_bat->status) | ||
224 | power_supply_changed(&adc_bat->psy); | ||
225 | } | ||
226 | |||
227 | static irqreturn_t gab_charged(int irq, void *dev_id) | ||
228 | { | ||
229 | struct gab *adc_bat = dev_id; | ||
230 | struct gab_platform_data *pdata = adc_bat->pdata; | ||
231 | int delay; | ||
232 | |||
233 | delay = pdata->jitter_delay ? pdata->jitter_delay : JITTER_DEFAULT; | ||
234 | schedule_delayed_work(&adc_bat->bat_work, | ||
235 | msecs_to_jiffies(delay)); | ||
236 | return IRQ_HANDLED; | ||
237 | } | ||
238 | |||
239 | static int __devinit gab_probe(struct platform_device *pdev) | ||
240 | { | ||
241 | struct gab *adc_bat; | ||
242 | struct power_supply *psy; | ||
243 | struct gab_platform_data *pdata = pdev->dev.platform_data; | ||
244 | enum power_supply_property *properties; | ||
245 | int ret = 0; | ||
246 | int chan; | ||
247 | int index = 0; | ||
248 | |||
249 | adc_bat = devm_kzalloc(&pdev->dev, sizeof(*adc_bat), GFP_KERNEL); | ||
250 | if (!adc_bat) { | ||
251 | dev_err(&pdev->dev, "failed to allocate memory\n"); | ||
252 | return -ENOMEM; | ||
253 | } | ||
254 | |||
255 | psy = &adc_bat->psy; | ||
256 | psy->name = pdata->battery_info.name; | ||
257 | |||
258 | /* bootup default values for the battery */ | ||
259 | adc_bat->cable_plugged = false; | ||
260 | adc_bat->status = POWER_SUPPLY_STATUS_DISCHARGING; | ||
261 | psy->type = POWER_SUPPLY_TYPE_BATTERY; | ||
262 | psy->get_property = gab_get_property; | ||
263 | psy->external_power_changed = gab_ext_power_changed; | ||
264 | adc_bat->pdata = pdata; | ||
265 | |||
266 | /* calculate the total number of channels */ | ||
267 | chan = ARRAY_SIZE(gab_chan_name); | ||
268 | |||
269 | /* | ||
270 | * copying the static properties and allocating extra memory for holding | ||
271 | * the extra configurable properties received from platform data. | ||
272 | */ | ||
273 | psy->properties = kcalloc(ARRAY_SIZE(gab_props) + | ||
274 | ARRAY_SIZE(gab_chan_name), | ||
275 | sizeof(*psy->properties), GFP_KERNEL); | ||
276 | if (!psy->properties) { | ||
277 | ret = -ENOMEM; | ||
278 | goto first_mem_fail; | ||
279 | } | ||
280 | |||
281 | memcpy(psy->properties, gab_props, sizeof(gab_props)); | ||
282 | properties = psy->properties + sizeof(gab_props); | ||
283 | |||
284 | /* | ||
285 | * getting channel from iio and copying the battery properties | ||
286 | * based on the channel supported by consumer device. | ||
287 | */ | ||
288 | for (chan = 0; chan < ARRAY_SIZE(gab_chan_name); chan++) { | ||
289 | adc_bat->channel[chan] = iio_channel_get(dev_name(&pdev->dev), | ||
290 | gab_chan_name[chan]); | ||
291 | if (IS_ERR(adc_bat->channel[chan])) { | ||
292 | ret = PTR_ERR(adc_bat->channel[chan]); | ||
293 | } else { | ||
294 | /* copying properties for supported channels only */ | ||
295 | memcpy(properties + sizeof(*(psy->properties)) * index, | ||
296 | &gab_dyn_props[chan], | ||
297 | sizeof(gab_dyn_props[chan])); | ||
298 | index++; | ||
299 | } | ||
300 | } | ||
301 | |||
302 | /* none of the channels are supported so let's bail out */ | ||
303 | if (index == ARRAY_SIZE(gab_chan_name)) | ||
304 | goto second_mem_fail; | ||
305 | |||
306 | /* | ||
307 | * Total number of properties is equal to static properties | ||
308 | * plus the dynamic properties.Some properties may not be set | ||
309 | * as come channels may be not be supported by the device.So | ||
310 | * we need to take care of that. | ||
311 | */ | ||
312 | psy->num_properties = ARRAY_SIZE(gab_props) + index; | ||
313 | |||
314 | ret = power_supply_register(&pdev->dev, psy); | ||
315 | if (ret) | ||
316 | goto err_reg_fail; | ||
317 | |||
318 | INIT_DELAYED_WORK(&adc_bat->bat_work, gab_work); | ||
319 | |||
320 | if (gpio_is_valid(pdata->gpio_charge_finished)) { | ||
321 | int irq; | ||
322 | ret = gpio_request(pdata->gpio_charge_finished, "charged"); | ||
323 | if (ret) | ||
324 | goto gpio_req_fail; | ||
325 | |||
326 | irq = gpio_to_irq(pdata->gpio_charge_finished); | ||
327 | ret = request_any_context_irq(irq, gab_charged, | ||
328 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, | ||
329 | "battery charged", adc_bat); | ||
330 | if (ret) | ||
331 | goto err_gpio; | ||
332 | } | ||
333 | |||
334 | platform_set_drvdata(pdev, adc_bat); | ||
335 | |||
336 | /* Schedule timer to check current status */ | ||
337 | schedule_delayed_work(&adc_bat->bat_work, | ||
338 | msecs_to_jiffies(0)); | ||
339 | return 0; | ||
340 | |||
341 | err_gpio: | ||
342 | gpio_free(pdata->gpio_charge_finished); | ||
343 | gpio_req_fail: | ||
344 | power_supply_unregister(psy); | ||
345 | err_reg_fail: | ||
346 | for (chan = 0; ARRAY_SIZE(gab_chan_name); chan++) | ||
347 | iio_channel_release(adc_bat->channel[chan]); | ||
348 | second_mem_fail: | ||
349 | kfree(psy->properties); | ||
350 | first_mem_fail: | ||
351 | return ret; | ||
352 | } | ||
353 | |||
354 | static int __devexit gab_remove(struct platform_device *pdev) | ||
355 | { | ||
356 | int chan; | ||
357 | struct gab *adc_bat = platform_get_drvdata(pdev); | ||
358 | struct gab_platform_data *pdata = adc_bat->pdata; | ||
359 | |||
360 | power_supply_unregister(&adc_bat->psy); | ||
361 | |||
362 | if (gpio_is_valid(pdata->gpio_charge_finished)) { | ||
363 | free_irq(gpio_to_irq(pdata->gpio_charge_finished), adc_bat); | ||
364 | gpio_free(pdata->gpio_charge_finished); | ||
365 | } | ||
366 | |||
367 | for (chan = 0; ARRAY_SIZE(gab_chan_name); chan++) | ||
368 | iio_channel_release(adc_bat->channel[chan]); | ||
369 | |||
370 | kfree(adc_bat->psy.properties); | ||
371 | cancel_delayed_work(&adc_bat->bat_work); | ||
372 | return 0; | ||
373 | } | ||
374 | |||
375 | #ifdef CONFIG_PM | ||
376 | static int gab_suspend(struct device *dev) | ||
377 | { | ||
378 | struct gab *adc_bat = dev_get_drvdata(dev); | ||
379 | |||
380 | cancel_delayed_work_sync(&adc_bat->bat_work); | ||
381 | adc_bat->status = POWER_SUPPLY_STATUS_UNKNOWN; | ||
382 | return 0; | ||
383 | } | ||
384 | |||
385 | static int gab_resume(struct device *dev) | ||
386 | { | ||
387 | struct gab *adc_bat = dev_get_drvdata(dev); | ||
388 | struct gab_platform_data *pdata = adc_bat->pdata; | ||
389 | int delay; | ||
390 | |||
391 | delay = pdata->jitter_delay ? pdata->jitter_delay : JITTER_DEFAULT; | ||
392 | |||
393 | /* Schedule timer to check current status */ | ||
394 | schedule_delayed_work(&adc_bat->bat_work, | ||
395 | msecs_to_jiffies(delay)); | ||
396 | return 0; | ||
397 | } | ||
398 | |||
399 | static const struct dev_pm_ops gab_pm_ops = { | ||
400 | .suspend = gab_suspend, | ||
401 | .resume = gab_resume, | ||
402 | }; | ||
403 | |||
404 | #define GAB_PM_OPS (&gab_pm_ops) | ||
405 | #else | ||
406 | #define GAB_PM_OPS (NULL) | ||
407 | #endif | ||
408 | |||
409 | static struct platform_driver gab_driver = { | ||
410 | .driver = { | ||
411 | .name = "generic-adc-battery", | ||
412 | .owner = THIS_MODULE, | ||
413 | .pm = GAB_PM_OPS | ||
414 | }, | ||
415 | .probe = gab_probe, | ||
416 | .remove = __devexit_p(gab_remove), | ||
417 | }; | ||
418 | module_platform_driver(gab_driver); | ||
419 | |||
420 | MODULE_AUTHOR("anish kumar <anish198519851985@gmail.com>"); | ||
421 | MODULE_DESCRIPTION("generic battery driver using IIO"); | ||
422 | MODULE_LICENSE("GPL"); | ||
diff --git a/include/linux/power/generic-adc-battery.h b/include/linux/power/generic-adc-battery.h new file mode 100644 index 000000000000..b1ebe08533b6 --- /dev/null +++ b/include/linux/power/generic-adc-battery.h | |||
@@ -0,0 +1,29 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012, Anish Kumar <anish198519851985@gmail.com> | ||
3 | * This program is free software; you can redistribute it and/or modify | ||
4 | * it under the terms of the GNU General Public License version 2 as | ||
5 | * published by the Free Software Foundation. | ||
6 | */ | ||
7 | |||
8 | #ifndef GENERIC_ADC_BATTERY_H | ||
9 | #define GENERIC_ADC_BATTERY_H | ||
10 | |||
11 | /** | ||
12 | * struct gab_platform_data - platform_data for generic adc iio battery driver. | ||
13 | * @battery_info: recommended structure to specify static power supply | ||
14 | * parameters | ||
15 | * @cal_charge: calculate charge level. | ||
16 | * @gpio_charge_finished: gpio for the charger. | ||
17 | * @gpio_inverted: Should be 1 if the GPIO is active low otherwise 0 | ||
18 | * @jitter_delay: delay required after the interrupt to check battery | ||
19 | * status.Default set is 10ms. | ||
20 | */ | ||
21 | struct gab_platform_data { | ||
22 | struct power_supply_info battery_info; | ||
23 | int (*cal_charge)(long value); | ||
24 | int gpio_charge_finished; | ||
25 | bool gpio_inverted; | ||
26 | int jitter_delay; | ||
27 | }; | ||
28 | |||
29 | #endif /* GENERIC_ADC_BATTERY_H */ | ||