aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoranish kumar <anish198519851985@gmail.com>2012-09-21 12:10:00 -0400
committerJonathan Cameron <jic23@kernel.org>2012-09-22 05:40:23 -0400
commite60fea794e6ecb9ea4df2623c9498412afe31d4d (patch)
tree6557eafb208e12384dcf96c599c72a5d36ccc8e0
parentf2f13a68c37c13a7147b279b77b8fb2a36846059 (diff)
power: battery: Generic battery driver using IIO
Driver to allow use of the ADC drivers supported by the IIO subsystem for battery status monitoring. Connecting this driver to the relevant IIO device requires registration of the appropriate iio_map structure array by the IIO device driver (usually from platform data). If specified the driver will also make use of a gpio to provide interrupt driven notification that the battery is fully charged. In last version: Addressed concerns raised by lars: a. made the adc_bat per device. b. get the IIO channel using hardcoded channel names. c. Minor issues related to gpio_is_valid and some code refactoring. In V1: Addressed concerns raised by Anton: a. changed the struct name to gab(generic adc battery). b. Added some functions to neaten the code. c. Some minor coding guidelines changes. d. Used the latest function introduce by lars: iio_read_channel_processed to streamline the code. In V2: Addressed concerns by lars: a. No need of allocating memory for channels.Make it array. b. Code restructring, coding style and following kernel guidelines changes suggested by him. In V3: Addressed conerns by Anton: a. Added the copyright. b. Coding guidelines changes suggested by him. c. Added Makefile and Kconfig Signed-off-by: anish kumar <anish198519851985@gmail.com> Acked-by: Anton Vorontsov <cbouatmailru@gmail.com> Signed-off-by: Jonathan Cameron <jic23@kernel.org>
-rw-r--r--drivers/power/Kconfig7
-rw-r--r--drivers/power/Makefile1
-rw-r--r--drivers/power/generic-adc-battery.c422
-rw-r--r--include/linux/power/generic-adc-battery.h29
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
32config 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
32config MAX8925_POWER 39config 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
5power_supply-$(CONFIG_LEDS_TRIGGERS) += power_supply_leds.o 5power_supply-$(CONFIG_LEDS_TRIGGERS) += power_supply_leds.o
6 6
7obj-$(CONFIG_POWER_SUPPLY) += power_supply.o 7obj-$(CONFIG_POWER_SUPPLY) += power_supply.o
8obj-$(CONFIG_GENERIC_ADC_BATTERY) += generic-adc-battery.o
8 9
9obj-$(CONFIG_PDA_POWER) += pda_power.o 10obj-$(CONFIG_PDA_POWER) += pda_power.o
10obj-$(CONFIG_APM_POWER) += apm_power.o 11obj-$(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
29enum 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 */
40static const char *const gab_chan_name[] = {
41 [GAB_VOLTAGE] = "voltage",
42 [GAB_CURRENT] = "current",
43 [GAB_POWER] = "power",
44};
45
46struct 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
56static struct gab *to_generic_bat(struct power_supply *psy)
57{
58 return container_of(psy, struct gab, psy);
59}
60
61static 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
68static 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 */
85static 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
91static 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
102static 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
113static 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
129static 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
143static 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 }
196err:
197 return ret;
198}
199
200static 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
227static 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
239static 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
341err_gpio:
342 gpio_free(pdata->gpio_charge_finished);
343gpio_req_fail:
344 power_supply_unregister(psy);
345err_reg_fail:
346 for (chan = 0; ARRAY_SIZE(gab_chan_name); chan++)
347 iio_channel_release(adc_bat->channel[chan]);
348second_mem_fail:
349 kfree(psy->properties);
350first_mem_fail:
351 return ret;
352}
353
354static 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
376static 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
385static 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
399static 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
409static 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};
418module_platform_driver(gab_driver);
419
420MODULE_AUTHOR("anish kumar <anish198519851985@gmail.com>");
421MODULE_DESCRIPTION("generic battery driver using IIO");
422MODULE_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 */
21struct 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 */