Root/target/linux/xburst/files-2.6.32/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#include <linux/kernel.h>
17#include <linux/module.h>
18#include <linux/power_supply.h>
19#include <linux/delay.h>
20#include <linux/spinlock.h>
21#include <linux/interrupt.h>
22#include <linux/platform_device.h>
23#include <linux/gpio.h>
24#include <linux/interrupt.h>
25
26#include <linux/power/jz4740-battery.h>
27#include <linux/jz4740-adc.h>
28
29struct jz_battery {
30    struct jz_battery_platform_data *pdata;
31
32    int charge_irq;
33
34    int status;
35    long voltage;
36
37    struct power_supply battery;
38    struct delayed_work work;
39};
40
41static inline struct jz_battery *psy_to_jz_battery(struct power_supply *psy)
42{
43    return container_of(psy, struct jz_battery, battery);
44}
45
46static long jz_battery_read_voltage(struct jz_battery *jz_battery)
47{
48    struct device *adc = jz_battery->battery.dev->parent->parent;
49    enum jz_adc_battery_scale scale;
50
51    if (jz_battery->pdata->info.voltage_max_design > 2500000)
52        scale = JZ_ADC_BATTERY_SCALE_7V5;
53    else
54        scale = JZ_ADC_BATTERY_SCALE_2V5;
55
56    return jz4740_adc_read_battery_voltage(adc, scale);
57}
58
59static int jz_battery_get_capacity(struct power_supply *psy)
60{
61    struct jz_battery *jz_battery = psy_to_jz_battery(psy);
62    struct power_supply_info *info = &jz_battery->pdata->info;
63    long voltage;
64    int ret;
65    int voltage_span;
66
67    voltage = jz_battery_read_voltage(jz_battery);
68
69    if (voltage < 0)
70        return voltage;
71
72    voltage_span = info->voltage_max_design - info->voltage_min_design;
73    ret = ((voltage - info->voltage_min_design) * 100) / voltage_span;
74
75    if (ret > 100)
76        ret = 100;
77    else if (ret < 0)
78        ret = 0;
79
80    return ret;
81}
82
83static int jz_battery_get_property(struct power_supply *psy,
84                enum power_supply_property psp,
85                union power_supply_propval *val)
86{
87    struct jz_battery *jz_battery = psy_to_jz_battery(psy);
88    struct power_supply_info *info = &jz_battery->pdata->info;
89    long voltage;
90
91    switch (psp) {
92    case POWER_SUPPLY_PROP_STATUS:
93        val->intval = jz_battery->status;
94        break;
95    case POWER_SUPPLY_PROP_TECHNOLOGY:
96        val->intval = jz_battery->pdata->info.technology;
97        break;
98    case POWER_SUPPLY_PROP_HEALTH:
99        voltage = jz_battery_read_voltage(jz_battery);
100        if (voltage < info->voltage_min_design)
101            val->intval = POWER_SUPPLY_HEALTH_DEAD;
102        else
103            val->intval = POWER_SUPPLY_HEALTH_GOOD;
104        break;
105    case POWER_SUPPLY_PROP_CAPACITY:
106        val->intval = jz_battery_get_capacity(psy);
107        break;
108    case POWER_SUPPLY_PROP_VOLTAGE_NOW:
109        val->intval = jz_battery_read_voltage(jz_battery);
110        if (val->intval < 0)
111            return val->intval;
112        break;
113    case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
114        val->intval = info->voltage_max_design;
115        break;
116    case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
117        val->intval = info->voltage_min_design;
118        break;
119    case POWER_SUPPLY_PROP_PRESENT:
120        val->intval = 1;
121        break;
122    default:
123        return -EINVAL;
124    }
125    return 0;
126}
127
128static void jz_battery_external_power_changed(struct power_supply *psy)
129{
130    struct jz_battery *jz_battery = psy_to_jz_battery(psy);
131
132    cancel_delayed_work(&jz_battery->work);
133    schedule_delayed_work(&jz_battery->work, 0);
134}
135
136static irqreturn_t jz_battery_charge_irq(int irq, void *data)
137{
138    struct jz_battery *jz_battery = data;
139
140    cancel_delayed_work(&jz_battery->work);
141    schedule_delayed_work(&jz_battery->work, 0);
142
143    return IRQ_HANDLED;
144}
145
146static void jz_battery_update(struct jz_battery *jz_battery)
147{
148    int status;
149    long voltage;
150    long voltage_difference;
151    bool has_changed = 0;
152
153    if (gpio_is_valid(jz_battery->pdata->gpio_charge)) {
154        int is_charging;
155
156        is_charging = gpio_get_value(jz_battery->pdata->gpio_charge);
157        is_charging ^= jz_battery->pdata->gpio_charge_active_low;
158        if (is_charging)
159            status = POWER_SUPPLY_STATUS_CHARGING;
160        else
161            status = POWER_SUPPLY_STATUS_NOT_CHARGING;
162
163        if (status != jz_battery->status) {
164            jz_battery->status = status;
165            has_changed = 1;
166        }
167    }
168
169    voltage = jz_battery_read_voltage(jz_battery);
170    voltage_difference = voltage - jz_battery->voltage;
171    if (voltage_difference > 50000 || voltage_difference < 50000) {
172        jz_battery->voltage = voltage;
173        has_changed = 1;
174    }
175    if (has_changed)
176        power_supply_changed(&jz_battery->battery);
177}
178
179static enum power_supply_property jz_battery_properties[] = {
180    POWER_SUPPLY_PROP_STATUS,
181    POWER_SUPPLY_PROP_TECHNOLOGY,
182    POWER_SUPPLY_PROP_HEALTH,
183    POWER_SUPPLY_PROP_CAPACITY,
184    POWER_SUPPLY_PROP_VOLTAGE_NOW,
185    POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
186    POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
187    POWER_SUPPLY_PROP_PRESENT,
188};
189
190static void jz_battery_work(struct work_struct *work)
191{
192    /* Too small interval will increase system workload */
193    const int interval = HZ * 30;
194    struct jz_battery *jz_battery = container_of(work, struct jz_battery,
195                        work.work);
196
197    jz_battery_update(jz_battery);
198    schedule_delayed_work(&jz_battery->work, interval);
199}
200
201static int jz_battery_probe(struct platform_device *pdev)
202{
203    int ret = 0;
204    struct jz_battery_platform_data *pdata = pdev->dev.platform_data;
205    struct jz_battery *jz_battery;
206    struct power_supply *battery;
207
208    if (!pdev->dev.platform_data) {
209        dev_err(&pdev->dev, "No platform data\n");
210        return -EINVAL;
211    }
212
213    jz_battery = kzalloc(sizeof(*jz_battery), GFP_KERNEL);
214
215    if (!jz_battery) {
216        dev_err(&pdev->dev, "Failed to allocate driver structure\n");
217        return -ENOMEM;
218    }
219
220    battery = &jz_battery->battery;
221    battery->name = pdata->info.name;
222    battery->type = POWER_SUPPLY_TYPE_BATTERY;
223    battery->properties = jz_battery_properties;
224    battery->num_properties = ARRAY_SIZE(jz_battery_properties);
225    battery->get_property = jz_battery_get_property;
226    battery->external_power_changed = jz_battery_external_power_changed;
227    battery->use_for_apm = 1;
228
229    jz_battery->pdata = pdata;
230
231    INIT_DELAYED_WORK(&jz_battery->work, jz_battery_work);
232
233    if (gpio_is_valid(pdata->gpio_charge)) {
234        ret = gpio_request(pdata->gpio_charge, dev_name(&pdev->dev));
235        if (ret) {
236            dev_err(&pdev->dev, "charger state gpio request failed.\n");
237            goto err_free;
238        }
239        ret = gpio_direction_input(pdata->gpio_charge);
240        if (ret) {
241            dev_err(&pdev->dev, "charger state gpio set direction failed.\n");
242            goto err_free_gpio;
243        }
244
245        jz_battery->charge_irq = gpio_to_irq(pdata->gpio_charge);
246
247        if (jz_battery->charge_irq >= 0) {
248            ret = request_irq(jz_battery->charge_irq,
249                    jz_battery_charge_irq,
250                    IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
251                    dev_name(&pdev->dev), jz_battery);
252            if (ret) {
253                dev_err(&pdev->dev, "Failed to request charge irq: %d\n", ret);
254                goto err_free_gpio;
255            }
256        }
257    } else {
258        jz_battery->charge_irq = -1;
259    }
260
261
262    ret = power_supply_register(&pdev->dev, &jz_battery->battery);
263    if (ret) {
264        dev_err(&pdev->dev, "power supply battery register failed.\n");
265        goto err_free_irq;
266    }
267
268    platform_set_drvdata(pdev, jz_battery);
269    schedule_delayed_work(&jz_battery->work, 0);
270
271    return 0;
272
273err_free_irq:
274    if (jz_battery->charge_irq >= 0)
275        free_irq(jz_battery->charge_irq, jz_battery);
276err_free_gpio:
277    if (gpio_is_valid(pdata->gpio_charge))
278        gpio_free(jz_battery->pdata->gpio_charge);
279err_free:
280    kfree(jz_battery);
281    return ret;
282}
283
284static int jz_battery_remove(struct platform_device *pdev)
285{
286    struct jz_battery *jz_battery = platform_get_drvdata(pdev);
287
288    cancel_delayed_work_sync(&jz_battery->work);
289
290    if (gpio_is_valid(jz_battery->pdata->gpio_charge)) {
291        if (jz_battery->charge_irq >= 0)
292            free_irq(jz_battery->charge_irq, jz_battery);
293        gpio_free(jz_battery->pdata->gpio_charge);
294    }
295
296    power_supply_unregister(&jz_battery->battery);
297
298    return 0;
299}
300
301#ifdef CONFIG_PM
302static int jz_battery_suspend(struct platform_device *pdev, pm_message_t state)
303{
304    struct jz_battery *jz_battery = platform_get_drvdata(pdev);
305
306    cancel_delayed_work_sync(&jz_battery->work);
307    jz_battery->status = POWER_SUPPLY_STATUS_UNKNOWN;
308
309    return 0;
310}
311
312static int jz_battery_resume(struct platform_device *pdev)
313{
314    struct jz_battery *jz_battery = platform_get_drvdata(pdev);
315
316    schedule_delayed_work(&jz_battery->work, 0);
317
318    return 0;
319}
320#else
321#define jz_battery_suspend NULL
322#define jz_battery_resume NULL
323#endif
324
325static struct platform_driver jz_battery_driver = {
326    .probe = jz_battery_probe,
327    .remove = __devexit_p(jz_battery_remove),
328    .suspend = jz_battery_suspend,
329    .resume = jz_battery_resume,
330    .driver = {
331        .name = "jz4740-battery",
332        .owner = THIS_MODULE,
333    },
334};
335
336static int __init jz_battery_init(void)
337{
338    return platform_driver_register(&jz_battery_driver);
339}
340module_init(jz_battery_init);
341
342static void __exit jz_battery_exit(void)
343{
344    platform_driver_unregister(&jz_battery_driver);
345}
346module_exit(jz_battery_exit);
347
348MODULE_LICENSE("GPL");
349MODULE_AUTHOR("Jiejing Zhang <kzjeef@gmail.com>");
350MODULE_DESCRIPTION("JZ4720/JZ4740 SoC battery driver");
351

Archive Download this file



interactive