aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRodolfo Giometti <giometti@linux.it>2008-08-20 19:52:58 -0400
committerAnton Vorontsov <cbouatmailru@gmail.com>2008-08-31 18:46:12 -0400
commitb996ad0e9fb15ca4acc60bcd0380912117a45d13 (patch)
tree136fc068f1e5e92a94d0e93a84e07c825821c941 /drivers
parent942ed161944b3476639916cf544e6975b29c985a (diff)
power_supply: Support for Texas Instruments BQ27200 battery managers
These battery managers came in two different packages: one for I2C busses (BQ27200) and one for HDQ busses (BQ27000). This driver currently supports only the I2C chip version but the code is designed in order to easily allow the HDQ chip version integration. [akpm@linux-foundation.org: coding-style fixes] [akpm@linux-foundation.org: make things static, use kasprintf()] Signed-off-by: Rodolfo Giometti <giometti@linux.it> Cc: David Woodhouse <dwmw2@infradead.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Anton Vorontsov <cbouatmailru@gmail.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/power/Kconfig6
-rw-r--r--drivers/power/Makefile1
-rw-r--r--drivers/power/bq27x00_battery.c382
3 files changed, 389 insertions, 0 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 9ce55850271a..b2bd104b9869 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -62,4 +62,10 @@ config BATTERY_PALMTX
62 help 62 help
63 Say Y to enable support for the battery in Palm T|X. 63 Say Y to enable support for the battery in Palm T|X.
64 64
65config BATTERY_BQ27x00
66 tristate "BQ27200 battery driver"
67 depends on I2C
68 help
69 Say Y here to enable support for batteries with BQ27200(I2C) chip.
70
65endif # POWER_SUPPLY 71endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 4706bf8ff459..6cb301b779a7 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -22,3 +22,4 @@ obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o
22obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o 22obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o
23obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o 23obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o
24obj-$(CONFIG_BATTERY_PALMTX) += palmtx_battery.o 24obj-$(CONFIG_BATTERY_PALMTX) += palmtx_battery.o
25obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o
diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c
new file mode 100644
index 000000000000..62d4948e8206
--- /dev/null
+++ b/drivers/power/bq27x00_battery.c
@@ -0,0 +1,382 @@
1/*
2 * BQ27x00 battery driver
3 *
4 * Copyright (C) 2008 Rodolfo Giometti <giometti@linux.it>
5 * Copyright (C) 2008 Eurotech S.p.A. <info@eurotech.it>
6 *
7 * Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc.
8 *
9 * This package is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 *
13 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16 *
17 */
18#include <linux/module.h>
19#include <linux/param.h>
20#include <linux/jiffies.h>
21#include <linux/workqueue.h>
22#include <linux/delay.h>
23#include <linux/platform_device.h>
24#include <linux/power_supply.h>
25#include <linux/idr.h>
26
27#include <linux/i2c.h>
28
29#define DRIVER_VERSION "1.0.0"
30
31#define BQ27x00_REG_TEMP 0x06
32#define BQ27x00_REG_VOLT 0x08
33#define BQ27x00_REG_RSOC 0x0B /* Relative State-of-Charge */
34#define BQ27x00_REG_AI 0x14
35#define BQ27x00_REG_FLAGS 0x0A
36#define HIGH_BYTE(A) ((A) << 8)
37
38/* If the system has several batteries we need a different name for each
39 * of them...
40 */
41static DEFINE_IDR(battery_id);
42static DEFINE_MUTEX(battery_mutex);
43
44struct bq27x00_device_info;
45struct bq27x00_access_methods {
46 int (*read)(u8 reg, int *rt_value, int b_single,
47 struct bq27x00_device_info *di);
48};
49
50struct bq27x00_device_info {
51 struct device *dev;
52 int id;
53 int voltage_uV;
54 int current_uA;
55 int temp_C;
56 int charge_rsoc;
57 struct bq27x00_access_methods *bus;
58 struct power_supply bat;
59
60 struct i2c_client *client;
61};
62
63static enum power_supply_property bq27x00_battery_props[] = {
64 POWER_SUPPLY_PROP_PRESENT,
65 POWER_SUPPLY_PROP_VOLTAGE_NOW,
66 POWER_SUPPLY_PROP_CURRENT_NOW,
67 POWER_SUPPLY_PROP_CAPACITY,
68 POWER_SUPPLY_PROP_TEMP,
69};
70
71/*
72 * Common code for BQ27x00 devices
73 */
74
75static int bq27x00_read(u8 reg, int *rt_value, int b_single,
76 struct bq27x00_device_info *di)
77{
78 int ret;
79
80 ret = di->bus->read(reg, rt_value, b_single, di);
81 *rt_value = be16_to_cpu(*rt_value);
82
83 return ret;
84}
85
86/*
87 * Return the battery temperature in Celcius degrees
88 * Or < 0 if something fails.
89 */
90static int bq27x00_battery_temperature(struct bq27x00_device_info *di)
91{
92 int ret;
93 int temp = 0;
94
95 ret = bq27x00_read(BQ27x00_REG_TEMP, &temp, 0, di);
96 if (ret) {
97 dev_err(di->dev, "error reading temperature\n");
98 return ret;
99 }
100
101 return (temp >> 2) - 273;
102}
103
104/*
105 * Return the battery Voltage in milivolts
106 * Or < 0 if something fails.
107 */
108static int bq27x00_battery_voltage(struct bq27x00_device_info *di)
109{
110 int ret;
111 int volt = 0;
112
113 ret = bq27x00_read(BQ27x00_REG_VOLT, &volt, 0, di);
114 if (ret) {
115 dev_err(di->dev, "error reading voltage\n");
116 return ret;
117 }
118
119 return volt;
120}
121
122/*
123 * Return the battery average current
124 * Note that current can be negative signed as well
125 * Or 0 if something fails.
126 */
127static int bq27x00_battery_current(struct bq27x00_device_info *di)
128{
129 int ret;
130 int curr = 0;
131 int flags = 0;
132
133 ret = bq27x00_read(BQ27x00_REG_AI, &curr, 0, di);
134 if (ret) {
135 dev_err(di->dev, "error reading current\n");
136 return 0;
137 }
138 ret = bq27x00_read(BQ27x00_REG_FLAGS, &flags, 0, di);
139 if (ret < 0) {
140 dev_err(di->dev, "error reading flags\n");
141 return 0;
142 }
143 if ((flags & (1 << 7)) != 0) {
144 dev_dbg(di->dev, "negative current!\n");
145 return -curr;
146 }
147 return curr;
148}
149
150/*
151 * Return the battery Relative State-of-Charge
152 * Or < 0 if something fails.
153 */
154static int bq27x00_battery_rsoc(struct bq27x00_device_info *di)
155{
156 int ret;
157 int rsoc = 0;
158
159 ret = bq27x00_read(BQ27x00_REG_RSOC, &rsoc, 1, di);
160 if (ret) {
161 dev_err(di->dev, "error reading relative State-of-Charge\n");
162 return ret;
163 }
164
165 return rsoc >> 8;
166}
167
168#define to_bq27x00_device_info(x) container_of((x), \
169 struct bq27x00_device_info, bat);
170
171static int bq27x00_battery_get_property(struct power_supply *psy,
172 enum power_supply_property psp,
173 union power_supply_propval *val)
174{
175 struct bq27x00_device_info *di = to_bq27x00_device_info(psy);
176
177 switch (psp) {
178 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
179 case POWER_SUPPLY_PROP_PRESENT:
180 val->intval = bq27x00_battery_voltage(di);
181 if (psp == POWER_SUPPLY_PROP_PRESENT)
182 val->intval = val->intval <= 0 ? 0 : 1;
183 break;
184 case POWER_SUPPLY_PROP_CURRENT_NOW:
185 val->intval = bq27x00_battery_current(di);
186 break;
187 case POWER_SUPPLY_PROP_CAPACITY:
188 val->intval = bq27x00_battery_rsoc(di);
189 break;
190 case POWER_SUPPLY_PROP_TEMP:
191 val->intval = bq27x00_battery_temperature(di);
192 break;
193 default:
194 return -EINVAL;
195 }
196
197 return 0;
198}
199
200static void bq27x00_powersupply_init(struct bq27x00_device_info *di)
201{
202 di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
203 di->bat.properties = bq27x00_battery_props;
204 di->bat.num_properties = ARRAY_SIZE(bq27x00_battery_props);
205 di->bat.get_property = bq27x00_battery_get_property;
206 di->bat.external_power_changed = NULL;
207}
208
209/*
210 * BQ27200 specific code
211 */
212
213static int bq27200_read(u8 reg, int *rt_value, int b_single,
214 struct bq27x00_device_info *di)
215{
216 struct i2c_client *client = di->client;
217 struct i2c_msg msg[1];
218 unsigned char data[2];
219 int err;
220
221 if (!client->adapter)
222 return -ENODEV;
223
224 msg->addr = client->addr;
225 msg->flags = 0;
226 msg->len = 1;
227 msg->buf = data;
228
229 data[0] = reg;
230 err = i2c_transfer(client->adapter, msg, 1);
231
232 if (err >= 0) {
233 if (!b_single)
234 msg->len = 2;
235 else
236 msg->len = 1;
237
238 msg->flags = I2C_M_RD;
239 err = i2c_transfer(client->adapter, msg, 1);
240 if (err >= 0) {
241 if (!b_single)
242 *rt_value = data[1] | HIGH_BYTE(data[0]);
243 else
244 *rt_value = data[0];
245
246 return 0;
247 }
248 }
249 return err;
250}
251
252static int bq27200_battery_probe(struct i2c_client *client,
253 const struct i2c_device_id *id)
254{
255 char *name;
256 struct bq27x00_device_info *di;
257 struct bq27x00_access_methods *bus;
258 int num;
259 int retval = 0;
260
261 /* Get new ID for the new battery device */
262 retval = idr_pre_get(&battery_id, GFP_KERNEL);
263 if (retval == 0)
264 return -ENOMEM;
265 mutex_lock(&battery_mutex);
266 retval = idr_get_new(&battery_id, client, &num);
267 mutex_unlock(&battery_mutex);
268 if (retval < 0)
269 return retval;
270
271 name = kasprintf(GFP_KERNEL, "bq27200-%d", num);
272 if (!name) {
273 dev_err(&client->dev, "failed to allocate device name\n");
274 retval = -ENOMEM;
275 goto batt_failed_1;
276 }
277
278 di = kzalloc(sizeof(*di), GFP_KERNEL);
279 if (!di) {
280 dev_err(&client->dev, "failed to allocate device info data\n");
281 retval = -ENOMEM;
282 goto batt_failed_2;
283 }
284 di->id = num;
285
286 bus = kzalloc(sizeof(*bus), GFP_KERNEL);
287 if (!bus) {
288 dev_err(&client->dev, "failed to allocate access method "
289 "data\n");
290 retval = -ENOMEM;
291 goto batt_failed_3;
292 }
293
294 i2c_set_clientdata(client, di);
295 di->dev = &client->dev;
296 di->bat.name = name;
297 bus->read = &bq27200_read;
298 di->bus = bus;
299 di->client = client;
300
301 bq27x00_powersupply_init(di);
302
303 retval = power_supply_register(&client->dev, &di->bat);
304 if (retval) {
305 dev_err(&client->dev, "failed to register battery\n");
306 goto batt_failed_4;
307 }
308
309 dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION);
310
311 return 0;
312
313batt_failed_4:
314 kfree(bus);
315batt_failed_3:
316 kfree(di);
317batt_failed_2:
318 kfree(name);
319batt_failed_1:
320 mutex_lock(&battery_mutex);
321 idr_remove(&battery_id, num);
322 mutex_unlock(&battery_mutex);
323
324 return retval;
325}
326
327static int bq27200_battery_remove(struct i2c_client *client)
328{
329 struct bq27x00_device_info *di = i2c_get_clientdata(client);
330
331 power_supply_unregister(&di->bat);
332
333 kfree(di->bat.name);
334
335 mutex_lock(&battery_mutex);
336 idr_remove(&battery_id, di->id);
337 mutex_unlock(&battery_mutex);
338
339 kfree(di);
340
341 return 0;
342}
343
344/*
345 * Module stuff
346 */
347
348static const struct i2c_device_id bq27200_id[] = {
349 { "bq27200", 0 },
350 {},
351};
352
353static struct i2c_driver bq27200_battery_driver = {
354 .driver = {
355 .name = "bq27200-battery",
356 },
357 .probe = bq27200_battery_probe,
358 .remove = bq27200_battery_remove,
359 .id_table = bq27200_id,
360};
361
362static int __init bq27x00_battery_init(void)
363{
364 int ret;
365
366 ret = i2c_add_driver(&bq27200_battery_driver);
367 if (ret)
368 printk(KERN_ERR "Unable to register BQ27200 driver\n");
369
370 return ret;
371}
372module_init(bq27x00_battery_init);
373
374static void __exit bq27x00_battery_exit(void)
375{
376 i2c_del_driver(&bq27200_battery_driver);
377}
378module_exit(bq27x00_battery_exit);
379
380MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
381MODULE_DESCRIPTION("BQ27x00 battery monitor driver");
382MODULE_LICENSE("GPL");