Date:2010-04-24 12:28:54 (10 years 2 months ago)
Author:Lars C.
Commit:acda7d5ed39aa569e222530cad8bbf5d0863eeec
Message:Add jz4740 battery driver

Files: drivers/power/Kconfig (1 diff)
drivers/power/Makefile (1 diff)
drivers/power/jz4740-battery.c (1 diff)
include/linux/power/jz4740-battery.h (1 diff)

Change Details

drivers/power/Kconfig
131131    help
132132     Say Y to include support for NXP PCF50633 Main Battery Charger.
133133
134config BATTERY_JZ4740
135    tristate "Ingenic JZ4720/JZ4740 battery"
136    depends on SOC_JZ4740
137    depends on JZ4740_ADC
138    help
139      Say Y to enable support for the battery on Ingenic JZ4720/JZ4740 based
140      boards.
141
142      This driver can be build as a module. If so, the module will be
143      called jz4740-battery.
144
134145endif # POWER_SUPPLY
drivers/power/Makefile
3232obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
3333obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
3434obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
35obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o
drivers/power/jz4740-battery.c
1/*
2 * Battery measurement code for Ingenic JZ SOC.
3 *
4 * Copyright (C) 2009 Jiejing Zhang <kzjeef@gmail.com>
5 * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
6 *
7 * based on tosa_battery.c
8 *
9 * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com>
10*
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
14 *
15 */
16
17#include <linux/interrupt.h>
18#include <linux/kernel.h>
19#include <linux/module.h>
20#include <linux/platform_device.h>
21#include <linux/slab.h>
22#include <linux/spinlock.h>
23
24#include <linux/delay.h>
25#include <linux/gpio.h>
26#include <linux/power_supply.h>
27
28#include <linux/power/jz4740-battery.h>
29#include <linux/jz4740-adc.h>
30
31struct jz_battery {
32    struct jz_battery_platform_data *pdata;
33
34    int charge_irq;
35
36    int status;
37    long voltage;
38
39    struct power_supply battery;
40    struct delayed_work work;
41};
42
43static inline struct jz_battery *psy_to_jz_battery(struct power_supply *psy)
44{
45    return container_of(psy, struct jz_battery, battery);
46}
47
48static long jz_battery_read_voltage(struct jz_battery *jz_battery)
49{
50    struct device *adc = jz_battery->battery.dev->parent->parent;
51    enum jz_adc_battery_scale scale;
52
53    if (jz_battery->pdata->info.voltage_max_design > 2500000)
54        scale = JZ_ADC_BATTERY_SCALE_7V5;
55    else
56        scale = JZ_ADC_BATTERY_SCALE_2V5;
57
58    return jz4740_adc_read_battery_voltage(adc, scale);
59}
60
61static int jz_battery_get_capacity(struct power_supply *psy)
62{
63    struct jz_battery *jz_battery = psy_to_jz_battery(psy);
64    struct power_supply_info *info = &jz_battery->pdata->info;
65    long voltage;
66    int ret;
67    int voltage_span;
68
69    voltage = jz_battery_read_voltage(jz_battery);
70
71    if (voltage < 0)
72        return voltage;
73
74    voltage_span = info->voltage_max_design - info->voltage_min_design;
75    ret = ((voltage - info->voltage_min_design) * 100) / voltage_span;
76
77    if (ret > 100)
78        ret = 100;
79    else if (ret < 0)
80        ret = 0;
81
82    return ret;
83}
84
85static int jz_battery_get_property(struct power_supply *psy,
86                enum power_supply_property psp,
87                union power_supply_propval *val)
88{
89    struct jz_battery *jz_battery = psy_to_jz_battery(psy);
90    struct power_supply_info *info = &jz_battery->pdata->info;
91    long voltage;
92
93    switch (psp) {
94    case POWER_SUPPLY_PROP_STATUS:
95        val->intval = jz_battery->status;
96        break;
97    case POWER_SUPPLY_PROP_TECHNOLOGY:
98        val->intval = jz_battery->pdata->info.technology;
99        break;
100    case POWER_SUPPLY_PROP_HEALTH:
101        voltage = jz_battery_read_voltage(jz_battery);
102        if (voltage < info->voltage_min_design)
103            val->intval = POWER_SUPPLY_HEALTH_DEAD;
104        else
105            val->intval = POWER_SUPPLY_HEALTH_GOOD;
106        break;
107    case POWER_SUPPLY_PROP_CAPACITY:
108        val->intval = jz_battery_get_capacity(psy);
109        break;
110    case POWER_SUPPLY_PROP_VOLTAGE_NOW:
111        val->intval = jz_battery_read_voltage(jz_battery);
112        if (val->intval < 0)
113            return val->intval;
114        break;
115    case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
116        val->intval = info->voltage_max_design;
117        break;
118    case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
119        val->intval = info->voltage_min_design;
120        break;
121    case POWER_SUPPLY_PROP_PRESENT:
122        val->intval = 1;
123        break;
124    default:
125        return -EINVAL;
126    }
127    return 0;
128}
129
130static void jz_battery_external_power_changed(struct power_supply *psy)
131{
132    struct jz_battery *jz_battery = psy_to_jz_battery(psy);
133
134    cancel_delayed_work(&jz_battery->work);
135    schedule_delayed_work(&jz_battery->work, 0);
136}
137
138static irqreturn_t jz_battery_charge_irq(int irq, void *data)
139{
140    struct jz_battery *jz_battery = data;
141
142    cancel_delayed_work(&jz_battery->work);
143    schedule_delayed_work(&jz_battery->work, 0);
144
145    return IRQ_HANDLED;
146}
147
148static void jz_battery_update(struct jz_battery *jz_battery)
149{
150    int status;
151    long voltage;
152    long voltage_difference;
153    bool has_changed = 0;
154
155    if (gpio_is_valid(jz_battery->pdata->gpio_charge)) {
156        int is_charging;
157
158        is_charging = gpio_get_value(jz_battery->pdata->gpio_charge);
159        is_charging ^= jz_battery->pdata->gpio_charge_active_low;
160        if (is_charging)
161            status = POWER_SUPPLY_STATUS_CHARGING;
162        else
163            status = POWER_SUPPLY_STATUS_NOT_CHARGING;
164
165        if (status != jz_battery->status) {
166            jz_battery->status = status;
167            has_changed = 1;
168        }
169    }
170
171    voltage = jz_battery_read_voltage(jz_battery);
172    voltage_difference = voltage - jz_battery->voltage;
173    if (voltage_difference > 50000 || voltage_difference < 50000) {
174        jz_battery->voltage = voltage;
175        has_changed = 1;
176    }
177    if (has_changed)
178        power_supply_changed(&jz_battery->battery);
179}
180
181static enum power_supply_property jz_battery_properties[] = {
182    POWER_SUPPLY_PROP_STATUS,
183    POWER_SUPPLY_PROP_TECHNOLOGY,
184    POWER_SUPPLY_PROP_HEALTH,
185    POWER_SUPPLY_PROP_CAPACITY,
186    POWER_SUPPLY_PROP_VOLTAGE_NOW,
187    POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
188    POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
189    POWER_SUPPLY_PROP_PRESENT,
190};
191
192static void jz_battery_work(struct work_struct *work)
193{
194    /* Too small interval will increase system workload */
195    const int interval = HZ * 30;
196    struct jz_battery *jz_battery = container_of(work, struct jz_battery,
197                        work.work);
198
199    jz_battery_update(jz_battery);
200    schedule_delayed_work(&jz_battery->work, interval);
201}
202
203static int jz_battery_probe(struct platform_device *pdev)
204{
205    int ret = 0;
206    struct jz_battery_platform_data *pdata = pdev->dev.platform_data;
207    struct jz_battery *jz_battery;
208    struct power_supply *battery;
209
210    if (!pdev->dev.platform_data) {
211        dev_err(&pdev->dev, "No platform data\n");
212        return -EINVAL;
213    }
214
215    jz_battery = kzalloc(sizeof(*jz_battery), GFP_KERNEL);
216
217    if (!jz_battery) {
218        dev_err(&pdev->dev, "Failed to allocate driver structure\n");
219        return -ENOMEM;
220    }
221
222    battery = &jz_battery->battery;
223    battery->name = pdata->info.name;
224    battery->type = POWER_SUPPLY_TYPE_BATTERY;
225    battery->properties = jz_battery_properties;
226    battery->num_properties = ARRAY_SIZE(jz_battery_properties);
227    battery->get_property = jz_battery_get_property;
228    battery->external_power_changed = jz_battery_external_power_changed;
229    battery->use_for_apm = 1;
230
231    jz_battery->pdata = pdata;
232
233    INIT_DELAYED_WORK(&jz_battery->work, jz_battery_work);
234
235    if (gpio_is_valid(pdata->gpio_charge)) {
236        ret = gpio_request(pdata->gpio_charge, dev_name(&pdev->dev));
237        if (ret) {
238            dev_err(&pdev->dev, "charger state gpio request failed.\n");
239            goto err_free;
240        }
241        ret = gpio_direction_input(pdata->gpio_charge);
242        if (ret) {
243            dev_err(&pdev->dev, "charger state gpio set direction failed.\n");
244            goto err_free_gpio;
245        }
246
247        jz_battery->charge_irq = gpio_to_irq(pdata->gpio_charge);
248
249        if (jz_battery->charge_irq >= 0) {
250            ret = request_irq(jz_battery->charge_irq,
251                    jz_battery_charge_irq,
252                    IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
253                    dev_name(&pdev->dev), jz_battery);
254            if (ret) {
255                dev_err(&pdev->dev, "Failed to request charge irq: %d\n", ret);
256                goto err_free_gpio;
257            }
258        }
259    } else {
260        jz_battery->charge_irq = -1;
261    }
262
263
264    ret = power_supply_register(&pdev->dev, &jz_battery->battery);
265    if (ret) {
266        dev_err(&pdev->dev, "power supply battery register failed.\n");
267        goto err_free_irq;
268    }
269
270    platform_set_drvdata(pdev, jz_battery);
271    schedule_delayed_work(&jz_battery->work, 0);
272
273    return 0;
274
275err_free_irq:
276    if (jz_battery->charge_irq >= 0)
277        free_irq(jz_battery->charge_irq, jz_battery);
278err_free_gpio:
279    if (gpio_is_valid(pdata->gpio_charge))
280        gpio_free(jz_battery->pdata->gpio_charge);
281err_free:
282    kfree(jz_battery);
283    return ret;
284}
285
286static int jz_battery_remove(struct platform_device *pdev)
287{
288    struct jz_battery *jz_battery = platform_get_drvdata(pdev);
289
290    cancel_delayed_work_sync(&jz_battery->work);
291
292    if (gpio_is_valid(jz_battery->pdata->gpio_charge)) {
293        if (jz_battery->charge_irq >= 0)
294            free_irq(jz_battery->charge_irq, jz_battery);
295        gpio_free(jz_battery->pdata->gpio_charge);
296    }
297
298    power_supply_unregister(&jz_battery->battery);
299
300    return 0;
301}
302
303#ifdef CONFIG_PM
304static int jz_battery_suspend(struct device *dev)
305{
306    struct jz_battery *jz_battery = dev_get_drvdata(dev);
307
308    cancel_delayed_work_sync(&jz_battery->work);
309    jz_battery->status = POWER_SUPPLY_STATUS_UNKNOWN;
310
311    return 0;
312}
313
314static int jz_battery_resume(struct device *dev)
315{
316    struct jz_battery *jz_battery = dev_get_drvdata(dev);
317
318    schedule_delayed_work(&jz_battery->work, 0);
319
320    return 0;
321}
322
323static const struct dev_pm_ops jz_battery_pm_ops = {
324    .suspend = jz_battery_suspend,
325    .resume = jz_battery_resume,
326};
327
328#define JZ_BATTERY_PM_OPS (&jz_battery_pm_ops)
329
330#else
331#define JZ_BATTERY_PM_OPS NULL
332#endif
333
334static struct platform_driver jz_battery_driver = {
335    .probe = jz_battery_probe,
336    .remove = __devexit_p(jz_battery_remove),
337    .driver = {
338        .name = "jz4740-battery",
339        .owner = THIS_MODULE,
340        .pm = JZ_BATTERY_PM_OPS,
341    },
342};
343
344static int __init jz_battery_init(void)
345{
346    return platform_driver_register(&jz_battery_driver);
347}
348module_init(jz_battery_init);
349
350static void __exit jz_battery_exit(void)
351{
352    platform_driver_unregister(&jz_battery_driver);
353}
354module_exit(jz_battery_exit);
355
356MODULE_LICENSE("GPL");
357MODULE_AUTHOR("Jiejing Zhang <kzjeef@gmail.com>");
358MODULE_DESCRIPTION("JZ4720/JZ4740 SoC battery driver");
include/linux/power/jz4740-battery.h
1/*
2 * Copyright (C) 2009, Jiejing Zhang <kzjeef@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * You should have received a copy of the GNU General Public License along
10 * with this program; if not, write to the Free Software Foundation, Inc.,
11 * 675 Mass Ave, Cambridge, MA 02139, USA.
12 *
13 */
14
15#ifndef __JZ4740_BATTERY_H
16#define __JZ4740_BATTERY_H
17
18struct jz_battery_platform_data {
19    struct power_supply_info info;
20    int gpio_charge; /* GPIO port of Charger state */
21    int gpio_charge_active_low;
22};
23
24#endif

Archive Download the corresponding diff file



interactive