aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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");