Blame view
drivers/power/wm97xx_battery.c
7.23 KB
4e9687d9c
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
/* * linux/drivers/power/wm97xx_battery.c * * Battery measurement code for WM97xx * * based on tosa_battery.c * * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/power_supply.h> #include <linux/wm97xx.h> #include <linux/spinlock.h> #include <linux/interrupt.h> #include <linux/gpio.h> |
7c87942ae
|
25 |
#include <linux/irq.h> |
5a0e3ad6a
|
26 |
#include <linux/slab.h> |
4e9687d9c
|
27 |
|
4e9687d9c
|
28 |
static struct work_struct bat_work; |
daf22c3c4
|
29 |
static DEFINE_MUTEX(work_lock); |
4e9687d9c
|
30 |
static int bat_status = POWER_SUPPLY_STATUS_UNKNOWN; |
4e9687d9c
|
31 32 33 34 |
static enum power_supply_property *prop; static unsigned long wm97xx_read_bat(struct power_supply *bat_ps) { |
297d716f6
|
35 |
struct wm97xx_pdata *wmdata = bat_ps->dev.parent->platform_data; |
b8bdc1d0c
|
36 |
struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata; |
297d716f6
|
37 |
return wm97xx_read_aux_adc(dev_get_drvdata(bat_ps->dev.parent), |
4e9687d9c
|
38 39 40 41 42 43 |
pdata->batt_aux) * pdata->batt_mult / pdata->batt_div; } static unsigned long wm97xx_read_temp(struct power_supply *bat_ps) { |
297d716f6
|
44 |
struct wm97xx_pdata *wmdata = bat_ps->dev.parent->platform_data; |
b8bdc1d0c
|
45 |
struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata; |
297d716f6
|
46 |
return wm97xx_read_aux_adc(dev_get_drvdata(bat_ps->dev.parent), |
4e9687d9c
|
47 48 49 50 51 52 53 54 |
pdata->temp_aux) * pdata->temp_mult / pdata->temp_div; } static int wm97xx_bat_get_property(struct power_supply *bat_ps, enum power_supply_property psp, union power_supply_propval *val) { |
297d716f6
|
55 |
struct wm97xx_pdata *wmdata = bat_ps->dev.parent->platform_data; |
b8bdc1d0c
|
56 |
struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata; |
4e9687d9c
|
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
switch (psp) { case POWER_SUPPLY_PROP_STATUS: val->intval = bat_status; break; case POWER_SUPPLY_PROP_TECHNOLOGY: val->intval = pdata->batt_tech; break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: if (pdata->batt_aux >= 0) val->intval = wm97xx_read_bat(bat_ps); else return -EINVAL; break; case POWER_SUPPLY_PROP_TEMP: if (pdata->temp_aux >= 0) val->intval = wm97xx_read_temp(bat_ps); else return -EINVAL; break; case POWER_SUPPLY_PROP_VOLTAGE_MAX: if (pdata->max_voltage >= 0) val->intval = pdata->max_voltage; else return -EINVAL; break; case POWER_SUPPLY_PROP_VOLTAGE_MIN: if (pdata->min_voltage >= 0) val->intval = pdata->min_voltage; else return -EINVAL; break; case POWER_SUPPLY_PROP_PRESENT: val->intval = 1; break; default: return -EINVAL; } return 0; } static void wm97xx_bat_external_power_changed(struct power_supply *bat_ps) { schedule_work(&bat_work); } static void wm97xx_bat_update(struct power_supply *bat_ps) { int old_status = bat_status; |
297d716f6
|
105 |
struct wm97xx_pdata *wmdata = bat_ps->dev.parent->platform_data; |
b8bdc1d0c
|
106 |
struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata; |
4e9687d9c
|
107 108 109 110 111 112 113 114 115 116 |
mutex_lock(&work_lock); bat_status = (pdata->charge_gpio >= 0) ? (gpio_get_value(pdata->charge_gpio) ? POWER_SUPPLY_STATUS_DISCHARGING : POWER_SUPPLY_STATUS_CHARGING) : POWER_SUPPLY_STATUS_UNKNOWN; if (old_status != bat_status) { |
297d716f6
|
117 118 |
pr_debug("%s: %i -> %i ", bat_ps->desc->name, old_status, |
4e9687d9c
|
119 120 121 122 123 124 |
bat_status); power_supply_changed(bat_ps); } mutex_unlock(&work_lock); } |
297d716f6
|
125 126 |
static struct power_supply *bat_psy; static struct power_supply_desc bat_psy_desc = { |
4e9687d9c
|
127 128 129 130 131 132 133 134 |
.type = POWER_SUPPLY_TYPE_BATTERY, .get_property = wm97xx_bat_get_property, .external_power_changed = wm97xx_bat_external_power_changed, .use_for_apm = 1, }; static void wm97xx_bat_work(struct work_struct *work) { |
297d716f6
|
135 |
wm97xx_bat_update(bat_psy); |
4e9687d9c
|
136 |
} |
7c87942ae
|
137 138 139 140 141 |
static irqreturn_t wm97xx_chrg_irq(int irq, void *data) { schedule_work(&bat_work); return IRQ_HANDLED; } |
4e9687d9c
|
142 |
#ifdef CONFIG_PM |
83a8af0d3
|
143 |
static int wm97xx_bat_suspend(struct device *dev) |
4e9687d9c
|
144 |
{ |
43829731d
|
145 |
flush_work(&bat_work); |
4e9687d9c
|
146 147 |
return 0; } |
83a8af0d3
|
148 |
static int wm97xx_bat_resume(struct device *dev) |
4e9687d9c
|
149 150 151 152 |
{ schedule_work(&bat_work); return 0; } |
83a8af0d3
|
153 |
|
471452104
|
154 |
static const struct dev_pm_ops wm97xx_bat_pm_ops = { |
83a8af0d3
|
155 156 157 |
.suspend = wm97xx_bat_suspend, .resume = wm97xx_bat_resume, }; |
4e9687d9c
|
158 |
#endif |
c8afa6406
|
159 |
static int wm97xx_bat_probe(struct platform_device *dev) |
4e9687d9c
|
160 161 162 163 |
{ int ret = 0; int props = 1; /* POWER_SUPPLY_PROP_PRESENT */ int i = 0; |
b8bdc1d0c
|
164 165 |
struct wm97xx_pdata *wmdata = dev->dev.platform_data; struct wm97xx_batt_pdata *pdata; |
12b336a8b
|
166 167 168 169 170 171 172 |
if (!wmdata) { dev_err(&dev->dev, "No platform data supplied "); return -EINVAL; } pdata = wmdata->batt_pdata; |
4e9687d9c
|
173 174 175 |
if (dev->id != -1) return -EINVAL; |
4e9687d9c
|
176 |
if (!pdata) { |
b8bdc1d0c
|
177 178 |
dev_err(&dev->dev, "No platform_data supplied "); |
4e9687d9c
|
179 180 |
return -EINVAL; } |
7c87942ae
|
181 |
if (gpio_is_valid(pdata->charge_gpio)) { |
4e9687d9c
|
182 183 184 185 186 187 |
ret = gpio_request(pdata->charge_gpio, "BATT CHRG"); if (ret) goto err; ret = gpio_direction_input(pdata->charge_gpio); if (ret) goto err2; |
7c87942ae
|
188 |
ret = request_irq(gpio_to_irq(pdata->charge_gpio), |
3b176b25a
|
189 |
wm97xx_chrg_irq, 0, |
f8d94eb77
|
190 |
"AC Detect", dev); |
7c87942ae
|
191 192 |
if (ret) goto err2; |
4e9687d9c
|
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
props++; /* POWER_SUPPLY_PROP_STATUS */ } if (pdata->batt_tech >= 0) props++; /* POWER_SUPPLY_PROP_TECHNOLOGY */ if (pdata->temp_aux >= 0) props++; /* POWER_SUPPLY_PROP_TEMP */ if (pdata->batt_aux >= 0) props++; /* POWER_SUPPLY_PROP_VOLTAGE_NOW */ if (pdata->max_voltage >= 0) props++; /* POWER_SUPPLY_PROP_VOLTAGE_MAX */ if (pdata->min_voltage >= 0) props++; /* POWER_SUPPLY_PROP_VOLTAGE_MIN */ prop = kzalloc(props * sizeof(*prop), GFP_KERNEL); |
588d4f0b0
|
208 209 |
if (!prop) { ret = -ENOMEM; |
7c87942ae
|
210 |
goto err3; |
588d4f0b0
|
211 |
} |
4e9687d9c
|
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
prop[i++] = POWER_SUPPLY_PROP_PRESENT; if (pdata->charge_gpio >= 0) prop[i++] = POWER_SUPPLY_PROP_STATUS; if (pdata->batt_tech >= 0) prop[i++] = POWER_SUPPLY_PROP_TECHNOLOGY; if (pdata->temp_aux >= 0) prop[i++] = POWER_SUPPLY_PROP_TEMP; if (pdata->batt_aux >= 0) prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_NOW; if (pdata->max_voltage >= 0) prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MAX; if (pdata->min_voltage >= 0) prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MIN; INIT_WORK(&bat_work, wm97xx_bat_work); if (!pdata->batt_name) { dev_info(&dev->dev, "Please consider setting proper battery " "name in platform definition file, falling " "back to name \"wm97xx-batt\" "); |
297d716f6
|
234 |
bat_psy_desc.name = "wm97xx-batt"; |
4e9687d9c
|
235 |
} else |
297d716f6
|
236 |
bat_psy_desc.name = pdata->batt_name; |
4e9687d9c
|
237 |
|
297d716f6
|
238 239 |
bat_psy_desc.properties = prop; bat_psy_desc.num_properties = props; |
4e9687d9c
|
240 |
|
297d716f6
|
241 242 |
bat_psy = power_supply_register(&dev->dev, &bat_psy_desc, NULL); if (!IS_ERR(bat_psy)) { |
4e9687d9c
|
243 |
schedule_work(&bat_work); |
297d716f6
|
244 245 |
} else { ret = PTR_ERR(bat_psy); |
7c87942ae
|
246 |
goto err4; |
297d716f6
|
247 |
} |
4e9687d9c
|
248 249 |
return 0; |
7c87942ae
|
250 |
err4: |
4e9687d9c
|
251 |
kfree(prop); |
7c87942ae
|
252 253 254 |
err3: if (gpio_is_valid(pdata->charge_gpio)) free_irq(gpio_to_irq(pdata->charge_gpio), dev); |
4e9687d9c
|
255 |
err2: |
7c87942ae
|
256 257 |
if (gpio_is_valid(pdata->charge_gpio)) gpio_free(pdata->charge_gpio); |
4e9687d9c
|
258 259 260 |
err: return ret; } |
415ec69fb
|
261 |
static int wm97xx_bat_remove(struct platform_device *dev) |
4e9687d9c
|
262 |
{ |
b8bdc1d0c
|
263 264 |
struct wm97xx_pdata *wmdata = dev->dev.platform_data; struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata; |
7c87942ae
|
265 266 |
if (pdata && gpio_is_valid(pdata->charge_gpio)) { free_irq(gpio_to_irq(pdata->charge_gpio), dev); |
4e9687d9c
|
267 |
gpio_free(pdata->charge_gpio); |
7c87942ae
|
268 |
} |
bc51e7ff5
|
269 |
cancel_work_sync(&bat_work); |
297d716f6
|
270 |
power_supply_unregister(bat_psy); |
4e9687d9c
|
271 272 273 274 275 276 277 |
kfree(prop); return 0; } static struct platform_driver wm97xx_bat_driver = { .driver = { .name = "wm97xx-battery", |
83a8af0d3
|
278 279 280 |
#ifdef CONFIG_PM .pm = &wm97xx_bat_pm_ops, #endif |
4e9687d9c
|
281 282 |
}, .probe = wm97xx_bat_probe, |
28ea73f4c
|
283 |
.remove = wm97xx_bat_remove, |
4e9687d9c
|
284 |
}; |
300bac7fb
|
285 |
module_platform_driver(wm97xx_bat_driver); |
4e9687d9c
|
286 287 288 289 |
MODULE_LICENSE("GPL"); MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); MODULE_DESCRIPTION("WM97xx battery driver"); |