]>
Commit | Line | Data |
---|---|---|
b996ad0e RG |
1 | /* |
2 | * BQ27x00 battery driver | |
3 | * | |
4 | * Copyright (C) 2008 Rodolfo Giometti <[email protected]> | |
5 | * Copyright (C) 2008 Eurotech S.p.A. <[email protected]> | |
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> | |
b996ad0e | 26 | #include <linux/i2c.h> |
8aef7e8f | 27 | #include <asm/unaligned.h> |
b996ad0e RG |
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 | |
b996ad0e RG |
36 | |
37 | /* If the system has several batteries we need a different name for each | |
38 | * of them... | |
39 | */ | |
40 | static DEFINE_IDR(battery_id); | |
41 | static DEFINE_MUTEX(battery_mutex); | |
42 | ||
43 | struct bq27x00_device_info; | |
44 | struct bq27x00_access_methods { | |
45 | int (*read)(u8 reg, int *rt_value, int b_single, | |
46 | struct bq27x00_device_info *di); | |
47 | }; | |
48 | ||
49 | struct bq27x00_device_info { | |
50 | struct device *dev; | |
51 | int id; | |
52 | int voltage_uV; | |
53 | int current_uA; | |
54 | int temp_C; | |
55 | int charge_rsoc; | |
56 | struct bq27x00_access_methods *bus; | |
57 | struct power_supply bat; | |
58 | ||
59 | struct i2c_client *client; | |
60 | }; | |
61 | ||
62 | static enum power_supply_property bq27x00_battery_props[] = { | |
63 | POWER_SUPPLY_PROP_PRESENT, | |
64 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | |
65 | POWER_SUPPLY_PROP_CURRENT_NOW, | |
66 | POWER_SUPPLY_PROP_CAPACITY, | |
67 | POWER_SUPPLY_PROP_TEMP, | |
68 | }; | |
69 | ||
70 | /* | |
71 | * Common code for BQ27x00 devices | |
72 | */ | |
73 | ||
74 | static int bq27x00_read(u8 reg, int *rt_value, int b_single, | |
75 | struct bq27x00_device_info *di) | |
76 | { | |
77 | int ret; | |
78 | ||
79 | ret = di->bus->read(reg, rt_value, b_single, di); | |
80 | *rt_value = be16_to_cpu(*rt_value); | |
81 | ||
82 | return ret; | |
83 | } | |
84 | ||
85 | /* | |
b731d7b6 | 86 | * Return the battery temperature in Celsius degrees |
b996ad0e RG |
87 | * Or < 0 if something fails. |
88 | */ | |
89 | static int bq27x00_battery_temperature(struct bq27x00_device_info *di) | |
90 | { | |
91 | int ret; | |
92 | int temp = 0; | |
93 | ||
94 | ret = bq27x00_read(BQ27x00_REG_TEMP, &temp, 0, di); | |
95 | if (ret) { | |
96 | dev_err(di->dev, "error reading temperature\n"); | |
97 | return ret; | |
98 | } | |
99 | ||
100 | return (temp >> 2) - 273; | |
101 | } | |
102 | ||
103 | /* | |
104 | * Return the battery Voltage in milivolts | |
105 | * Or < 0 if something fails. | |
106 | */ | |
107 | static int bq27x00_battery_voltage(struct bq27x00_device_info *di) | |
108 | { | |
109 | int ret; | |
110 | int volt = 0; | |
111 | ||
112 | ret = bq27x00_read(BQ27x00_REG_VOLT, &volt, 0, di); | |
113 | if (ret) { | |
114 | dev_err(di->dev, "error reading voltage\n"); | |
115 | return ret; | |
116 | } | |
117 | ||
118 | return volt; | |
119 | } | |
120 | ||
121 | /* | |
122 | * Return the battery average current | |
123 | * Note that current can be negative signed as well | |
124 | * Or 0 if something fails. | |
125 | */ | |
126 | static int bq27x00_battery_current(struct bq27x00_device_info *di) | |
127 | { | |
128 | int ret; | |
129 | int curr = 0; | |
130 | int flags = 0; | |
131 | ||
132 | ret = bq27x00_read(BQ27x00_REG_AI, &curr, 0, di); | |
133 | if (ret) { | |
134 | dev_err(di->dev, "error reading current\n"); | |
135 | return 0; | |
136 | } | |
137 | ret = bq27x00_read(BQ27x00_REG_FLAGS, &flags, 0, di); | |
138 | if (ret < 0) { | |
139 | dev_err(di->dev, "error reading flags\n"); | |
140 | return 0; | |
141 | } | |
142 | if ((flags & (1 << 7)) != 0) { | |
143 | dev_dbg(di->dev, "negative current!\n"); | |
144 | return -curr; | |
145 | } | |
146 | return curr; | |
147 | } | |
148 | ||
149 | /* | |
150 | * Return the battery Relative State-of-Charge | |
151 | * Or < 0 if something fails. | |
152 | */ | |
153 | static int bq27x00_battery_rsoc(struct bq27x00_device_info *di) | |
154 | { | |
155 | int ret; | |
156 | int rsoc = 0; | |
157 | ||
158 | ret = bq27x00_read(BQ27x00_REG_RSOC, &rsoc, 1, di); | |
159 | if (ret) { | |
160 | dev_err(di->dev, "error reading relative State-of-Charge\n"); | |
161 | return ret; | |
162 | } | |
163 | ||
164 | return rsoc >> 8; | |
165 | } | |
166 | ||
167 | #define to_bq27x00_device_info(x) container_of((x), \ | |
168 | struct bq27x00_device_info, bat); | |
169 | ||
170 | static int bq27x00_battery_get_property(struct power_supply *psy, | |
171 | enum power_supply_property psp, | |
172 | union power_supply_propval *val) | |
173 | { | |
174 | struct bq27x00_device_info *di = to_bq27x00_device_info(psy); | |
175 | ||
176 | switch (psp) { | |
177 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | |
178 | case POWER_SUPPLY_PROP_PRESENT: | |
179 | val->intval = bq27x00_battery_voltage(di); | |
180 | if (psp == POWER_SUPPLY_PROP_PRESENT) | |
181 | val->intval = val->intval <= 0 ? 0 : 1; | |
182 | break; | |
183 | case POWER_SUPPLY_PROP_CURRENT_NOW: | |
184 | val->intval = bq27x00_battery_current(di); | |
185 | break; | |
186 | case POWER_SUPPLY_PROP_CAPACITY: | |
187 | val->intval = bq27x00_battery_rsoc(di); | |
188 | break; | |
189 | case POWER_SUPPLY_PROP_TEMP: | |
190 | val->intval = bq27x00_battery_temperature(di); | |
191 | break; | |
192 | default: | |
193 | return -EINVAL; | |
194 | } | |
195 | ||
196 | return 0; | |
197 | } | |
198 | ||
199 | static void bq27x00_powersupply_init(struct bq27x00_device_info *di) | |
200 | { | |
201 | di->bat.type = POWER_SUPPLY_TYPE_BATTERY; | |
202 | di->bat.properties = bq27x00_battery_props; | |
203 | di->bat.num_properties = ARRAY_SIZE(bq27x00_battery_props); | |
204 | di->bat.get_property = bq27x00_battery_get_property; | |
205 | di->bat.external_power_changed = NULL; | |
206 | } | |
207 | ||
208 | /* | |
209 | * BQ27200 specific code | |
210 | */ | |
211 | ||
212 | static int bq27200_read(u8 reg, int *rt_value, int b_single, | |
213 | struct bq27x00_device_info *di) | |
214 | { | |
215 | struct i2c_client *client = di->client; | |
216 | struct i2c_msg msg[1]; | |
217 | unsigned char data[2]; | |
218 | int err; | |
219 | ||
220 | if (!client->adapter) | |
221 | return -ENODEV; | |
222 | ||
223 | msg->addr = client->addr; | |
224 | msg->flags = 0; | |
225 | msg->len = 1; | |
226 | msg->buf = data; | |
227 | ||
228 | data[0] = reg; | |
229 | err = i2c_transfer(client->adapter, msg, 1); | |
230 | ||
231 | if (err >= 0) { | |
232 | if (!b_single) | |
233 | msg->len = 2; | |
234 | else | |
235 | msg->len = 1; | |
236 | ||
237 | msg->flags = I2C_M_RD; | |
238 | err = i2c_transfer(client->adapter, msg, 1); | |
239 | if (err >= 0) { | |
240 | if (!b_single) | |
8aef7e8f | 241 | *rt_value = get_unaligned_be16(data); |
b996ad0e RG |
242 | else |
243 | *rt_value = data[0]; | |
244 | ||
245 | return 0; | |
246 | } | |
247 | } | |
248 | return err; | |
249 | } | |
250 | ||
251 | static int bq27200_battery_probe(struct i2c_client *client, | |
252 | const struct i2c_device_id *id) | |
253 | { | |
254 | char *name; | |
255 | struct bq27x00_device_info *di; | |
256 | struct bq27x00_access_methods *bus; | |
257 | int num; | |
258 | int retval = 0; | |
259 | ||
260 | /* Get new ID for the new battery device */ | |
261 | retval = idr_pre_get(&battery_id, GFP_KERNEL); | |
262 | if (retval == 0) | |
263 | return -ENOMEM; | |
264 | mutex_lock(&battery_mutex); | |
265 | retval = idr_get_new(&battery_id, client, &num); | |
266 | mutex_unlock(&battery_mutex); | |
267 | if (retval < 0) | |
268 | return retval; | |
269 | ||
270 | name = kasprintf(GFP_KERNEL, "bq27200-%d", num); | |
271 | if (!name) { | |
272 | dev_err(&client->dev, "failed to allocate device name\n"); | |
273 | retval = -ENOMEM; | |
274 | goto batt_failed_1; | |
275 | } | |
276 | ||
277 | di = kzalloc(sizeof(*di), GFP_KERNEL); | |
278 | if (!di) { | |
279 | dev_err(&client->dev, "failed to allocate device info data\n"); | |
280 | retval = -ENOMEM; | |
281 | goto batt_failed_2; | |
282 | } | |
283 | di->id = num; | |
284 | ||
285 | bus = kzalloc(sizeof(*bus), GFP_KERNEL); | |
286 | if (!bus) { | |
287 | dev_err(&client->dev, "failed to allocate access method " | |
288 | "data\n"); | |
289 | retval = -ENOMEM; | |
290 | goto batt_failed_3; | |
291 | } | |
292 | ||
293 | i2c_set_clientdata(client, di); | |
294 | di->dev = &client->dev; | |
295 | di->bat.name = name; | |
296 | bus->read = &bq27200_read; | |
297 | di->bus = bus; | |
298 | di->client = client; | |
299 | ||
300 | bq27x00_powersupply_init(di); | |
301 | ||
302 | retval = power_supply_register(&client->dev, &di->bat); | |
303 | if (retval) { | |
304 | dev_err(&client->dev, "failed to register battery\n"); | |
305 | goto batt_failed_4; | |
306 | } | |
307 | ||
308 | dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION); | |
309 | ||
310 | return 0; | |
311 | ||
312 | batt_failed_4: | |
313 | kfree(bus); | |
314 | batt_failed_3: | |
315 | kfree(di); | |
316 | batt_failed_2: | |
317 | kfree(name); | |
318 | batt_failed_1: | |
319 | mutex_lock(&battery_mutex); | |
320 | idr_remove(&battery_id, num); | |
321 | mutex_unlock(&battery_mutex); | |
322 | ||
323 | return retval; | |
324 | } | |
325 | ||
326 | static int bq27200_battery_remove(struct i2c_client *client) | |
327 | { | |
328 | struct bq27x00_device_info *di = i2c_get_clientdata(client); | |
329 | ||
330 | power_supply_unregister(&di->bat); | |
331 | ||
332 | kfree(di->bat.name); | |
333 | ||
334 | mutex_lock(&battery_mutex); | |
335 | idr_remove(&battery_id, di->id); | |
336 | mutex_unlock(&battery_mutex); | |
337 | ||
338 | kfree(di); | |
339 | ||
340 | return 0; | |
341 | } | |
342 | ||
343 | /* | |
344 | * Module stuff | |
345 | */ | |
346 | ||
347 | static const struct i2c_device_id bq27200_id[] = { | |
348 | { "bq27200", 0 }, | |
349 | {}, | |
350 | }; | |
351 | ||
352 | static struct i2c_driver bq27200_battery_driver = { | |
353 | .driver = { | |
354 | .name = "bq27200-battery", | |
355 | }, | |
356 | .probe = bq27200_battery_probe, | |
357 | .remove = bq27200_battery_remove, | |
358 | .id_table = bq27200_id, | |
359 | }; | |
360 | ||
361 | static int __init bq27x00_battery_init(void) | |
362 | { | |
363 | int ret; | |
364 | ||
365 | ret = i2c_add_driver(&bq27200_battery_driver); | |
366 | if (ret) | |
367 | printk(KERN_ERR "Unable to register BQ27200 driver\n"); | |
368 | ||
369 | return ret; | |
370 | } | |
371 | module_init(bq27x00_battery_init); | |
372 | ||
373 | static void __exit bq27x00_battery_exit(void) | |
374 | { | |
375 | i2c_del_driver(&bq27200_battery_driver); | |
376 | } | |
377 | module_exit(bq27x00_battery_exit); | |
378 | ||
379 | MODULE_AUTHOR("Rodolfo Giometti <[email protected]>"); | |
380 | MODULE_DESCRIPTION("BQ27x00 battery monitor driver"); | |
381 | MODULE_LICENSE("GPL"); |