~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to drivers/power/wm97xx_battery.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 DEFINE_MUTEX(bat_lock);
 
29
static struct work_struct bat_work;
 
30
static struct mutex work_lock;
 
31
static int bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
 
32
static enum power_supply_property *prop;
 
33
 
 
34
static unsigned long wm97xx_read_bat(struct power_supply *bat_ps)
 
35
{
 
36
        struct wm97xx_pdata *wmdata = bat_ps->dev->parent->platform_data;
 
37
        struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata;
 
38
 
 
39
        return wm97xx_read_aux_adc(dev_get_drvdata(bat_ps->dev->parent),
 
40
                                        pdata->batt_aux) * pdata->batt_mult /
 
41
                                        pdata->batt_div;
 
42
}
 
43
 
 
44
static unsigned long wm97xx_read_temp(struct power_supply *bat_ps)
 
45
{
 
46
        struct wm97xx_pdata *wmdata = bat_ps->dev->parent->platform_data;
 
47
        struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata;
 
48
 
 
49
        return wm97xx_read_aux_adc(dev_get_drvdata(bat_ps->dev->parent),
 
50
                                        pdata->temp_aux) * pdata->temp_mult /
 
51
                                        pdata->temp_div;
 
52
}
 
53
 
 
54
static int wm97xx_bat_get_property(struct power_supply *bat_ps,
 
55
                            enum power_supply_property psp,
 
56
                            union power_supply_propval *val)
 
57
{
 
58
        struct wm97xx_pdata *wmdata = bat_ps->dev->parent->platform_data;
 
59
        struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata;
 
60
 
 
61
        switch (psp) {
 
62
        case POWER_SUPPLY_PROP_STATUS:
 
63
                val->intval = bat_status;
 
64
                break;
 
65
        case POWER_SUPPLY_PROP_TECHNOLOGY:
 
66
                val->intval = pdata->batt_tech;
 
67
                break;
 
68
        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
 
69
                if (pdata->batt_aux >= 0)
 
70
                        val->intval = wm97xx_read_bat(bat_ps);
 
71
                else
 
72
                        return -EINVAL;
 
73
                break;
 
74
        case POWER_SUPPLY_PROP_TEMP:
 
75
                if (pdata->temp_aux >= 0)
 
76
                        val->intval = wm97xx_read_temp(bat_ps);
 
77
                else
 
78
                        return -EINVAL;
 
79
                break;
 
80
        case POWER_SUPPLY_PROP_VOLTAGE_MAX:
 
81
                if (pdata->max_voltage >= 0)
 
82
                        val->intval = pdata->max_voltage;
 
83
                else
 
84
                        return -EINVAL;
 
85
                break;
 
86
        case POWER_SUPPLY_PROP_VOLTAGE_MIN:
 
87
                if (pdata->min_voltage >= 0)
 
88
                        val->intval = pdata->min_voltage;
 
89
                else
 
90
                        return -EINVAL;
 
91
                break;
 
92
        case POWER_SUPPLY_PROP_PRESENT:
 
93
                val->intval = 1;
 
94
                break;
 
95
        default:
 
96
                return -EINVAL;
 
97
        }
 
98
        return 0;
 
99
}
 
100
 
 
101
static void wm97xx_bat_external_power_changed(struct power_supply *bat_ps)
 
102
{
 
103
        schedule_work(&bat_work);
 
104
}
 
105
 
 
106
static void wm97xx_bat_update(struct power_supply *bat_ps)
 
107
{
 
108
        int old_status = bat_status;
 
109
        struct wm97xx_pdata *wmdata = bat_ps->dev->parent->platform_data;
 
110
        struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata;
 
111
 
 
112
        mutex_lock(&work_lock);
 
113
 
 
114
        bat_status = (pdata->charge_gpio >= 0) ?
 
115
                        (gpio_get_value(pdata->charge_gpio) ?
 
116
                        POWER_SUPPLY_STATUS_DISCHARGING :
 
117
                        POWER_SUPPLY_STATUS_CHARGING) :
 
118
                        POWER_SUPPLY_STATUS_UNKNOWN;
 
119
 
 
120
        if (old_status != bat_status) {
 
121
                pr_debug("%s: %i -> %i\n", bat_ps->name, old_status,
 
122
                                        bat_status);
 
123
                power_supply_changed(bat_ps);
 
124
        }
 
125
 
 
126
        mutex_unlock(&work_lock);
 
127
}
 
128
 
 
129
static struct power_supply bat_ps = {
 
130
        .type                   = POWER_SUPPLY_TYPE_BATTERY,
 
131
        .get_property           = wm97xx_bat_get_property,
 
132
        .external_power_changed = wm97xx_bat_external_power_changed,
 
133
        .use_for_apm            = 1,
 
134
};
 
135
 
 
136
static void wm97xx_bat_work(struct work_struct *work)
 
137
{
 
138
        wm97xx_bat_update(&bat_ps);
 
139
}
 
140
 
 
141
static irqreturn_t wm97xx_chrg_irq(int irq, void *data)
 
142
{
 
143
        schedule_work(&bat_work);
 
144
        return IRQ_HANDLED;
 
145
}
 
146
 
 
147
#ifdef CONFIG_PM
 
148
static int wm97xx_bat_suspend(struct device *dev)
 
149
{
 
150
        flush_work_sync(&bat_work);
 
151
        return 0;
 
152
}
 
153
 
 
154
static int wm97xx_bat_resume(struct device *dev)
 
155
{
 
156
        schedule_work(&bat_work);
 
157
        return 0;
 
158
}
 
159
 
 
160
static const struct dev_pm_ops wm97xx_bat_pm_ops = {
 
161
        .suspend        = wm97xx_bat_suspend,
 
162
        .resume         = wm97xx_bat_resume,
 
163
};
 
164
#endif
 
165
 
 
166
static int __devinit wm97xx_bat_probe(struct platform_device *dev)
 
167
{
 
168
        int ret = 0;
 
169
        int props = 1;  /* POWER_SUPPLY_PROP_PRESENT */
 
170
        int i = 0;
 
171
        struct wm97xx_pdata *wmdata = dev->dev.platform_data;
 
172
        struct wm97xx_batt_pdata *pdata;
 
173
 
 
174
        if (!wmdata) {
 
175
                dev_err(&dev->dev, "No platform data supplied\n");
 
176
                return -EINVAL;
 
177
        }
 
178
 
 
179
        pdata = wmdata->batt_pdata;
 
180
 
 
181
        if (dev->id != -1)
 
182
                return -EINVAL;
 
183
 
 
184
        mutex_init(&work_lock);
 
185
 
 
186
        if (!pdata) {
 
187
                dev_err(&dev->dev, "No platform_data supplied\n");
 
188
                return -EINVAL;
 
189
        }
 
190
 
 
191
        if (gpio_is_valid(pdata->charge_gpio)) {
 
192
                ret = gpio_request(pdata->charge_gpio, "BATT CHRG");
 
193
                if (ret)
 
194
                        goto err;
 
195
                ret = gpio_direction_input(pdata->charge_gpio);
 
196
                if (ret)
 
197
                        goto err2;
 
198
                ret = request_irq(gpio_to_irq(pdata->charge_gpio),
 
199
                                wm97xx_chrg_irq, IRQF_DISABLED,
 
200
                                "AC Detect", dev);
 
201
                if (ret)
 
202
                        goto err2;
 
203
                props++;        /* POWER_SUPPLY_PROP_STATUS */
 
204
        }
 
205
 
 
206
        if (pdata->batt_tech >= 0)
 
207
                props++;        /* POWER_SUPPLY_PROP_TECHNOLOGY */
 
208
        if (pdata->temp_aux >= 0)
 
209
                props++;        /* POWER_SUPPLY_PROP_TEMP */
 
210
        if (pdata->batt_aux >= 0)
 
211
                props++;        /* POWER_SUPPLY_PROP_VOLTAGE_NOW */
 
212
        if (pdata->max_voltage >= 0)
 
213
                props++;        /* POWER_SUPPLY_PROP_VOLTAGE_MAX */
 
214
        if (pdata->min_voltage >= 0)
 
215
                props++;        /* POWER_SUPPLY_PROP_VOLTAGE_MIN */
 
216
 
 
217
        prop = kzalloc(props * sizeof(*prop), GFP_KERNEL);
 
218
        if (!prop)
 
219
                goto err3;
 
220
 
 
221
        prop[i++] = POWER_SUPPLY_PROP_PRESENT;
 
222
        if (pdata->charge_gpio >= 0)
 
223
                prop[i++] = POWER_SUPPLY_PROP_STATUS;
 
224
        if (pdata->batt_tech >= 0)
 
225
                prop[i++] = POWER_SUPPLY_PROP_TECHNOLOGY;
 
226
        if (pdata->temp_aux >= 0)
 
227
                prop[i++] = POWER_SUPPLY_PROP_TEMP;
 
228
        if (pdata->batt_aux >= 0)
 
229
                prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_NOW;
 
230
        if (pdata->max_voltage >= 0)
 
231
                prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MAX;
 
232
        if (pdata->min_voltage >= 0)
 
233
                prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MIN;
 
234
 
 
235
        INIT_WORK(&bat_work, wm97xx_bat_work);
 
236
 
 
237
        if (!pdata->batt_name) {
 
238
                dev_info(&dev->dev, "Please consider setting proper battery "
 
239
                                "name in platform definition file, falling "
 
240
                                "back to name \"wm97xx-batt\"\n");
 
241
                bat_ps.name = "wm97xx-batt";
 
242
        } else
 
243
                bat_ps.name = pdata->batt_name;
 
244
 
 
245
        bat_ps.properties = prop;
 
246
        bat_ps.num_properties = props;
 
247
 
 
248
        ret = power_supply_register(&dev->dev, &bat_ps);
 
249
        if (!ret)
 
250
                schedule_work(&bat_work);
 
251
        else
 
252
                goto err4;
 
253
 
 
254
        return 0;
 
255
err4:
 
256
        kfree(prop);
 
257
err3:
 
258
        if (gpio_is_valid(pdata->charge_gpio))
 
259
                free_irq(gpio_to_irq(pdata->charge_gpio), dev);
 
260
err2:
 
261
        if (gpio_is_valid(pdata->charge_gpio))
 
262
                gpio_free(pdata->charge_gpio);
 
263
err:
 
264
        return ret;
 
265
}
 
266
 
 
267
static int __devexit wm97xx_bat_remove(struct platform_device *dev)
 
268
{
 
269
        struct wm97xx_pdata *wmdata = dev->dev.platform_data;
 
270
        struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata;
 
271
 
 
272
        if (pdata && gpio_is_valid(pdata->charge_gpio)) {
 
273
                free_irq(gpio_to_irq(pdata->charge_gpio), dev);
 
274
                gpio_free(pdata->charge_gpio);
 
275
        }
 
276
        cancel_work_sync(&bat_work);
 
277
        power_supply_unregister(&bat_ps);
 
278
        kfree(prop);
 
279
        return 0;
 
280
}
 
281
 
 
282
static struct platform_driver wm97xx_bat_driver = {
 
283
        .driver = {
 
284
                .name   = "wm97xx-battery",
 
285
                .owner  = THIS_MODULE,
 
286
#ifdef CONFIG_PM
 
287
                .pm     = &wm97xx_bat_pm_ops,
 
288
#endif
 
289
        },
 
290
        .probe          = wm97xx_bat_probe,
 
291
        .remove         = __devexit_p(wm97xx_bat_remove),
 
292
};
 
293
 
 
294
static int __init wm97xx_bat_init(void)
 
295
{
 
296
        return platform_driver_register(&wm97xx_bat_driver);
 
297
}
 
298
 
 
299
static void __exit wm97xx_bat_exit(void)
 
300
{
 
301
        platform_driver_unregister(&wm97xx_bat_driver);
 
302
}
 
303
 
 
304
module_init(wm97xx_bat_init);
 
305
module_exit(wm97xx_bat_exit);
 
306
 
 
307
MODULE_LICENSE("GPL");
 
308
MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
 
309
MODULE_DESCRIPTION("WM97xx battery driver");