aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRhyland Klein <rklein@nvidia.com>2010-09-05 18:31:23 -0400
committerAnton Vorontsov <cbouatmailru@gmail.com>2010-09-06 03:35:39 -0400
commita7640bfa10c558b7cbabb4b98d6bc356d3c2ec55 (patch)
tree6d07f0e17d45d91fcddf677674e15ce74e4f9bbb /drivers
parent2d98dae206daad05d794255b15bd0eb31c9294d0 (diff)
power_supply: Add driver for TI BQ20Z75 gas gauge IC
This driver depends on I2C and uses SMBUS for communication with the host. Signed-off-by: Rhyland Klein <rklein@nvidia.com> Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Reviewed-by: Jean Delvare <khali@linux-fr.org> Signed-off-by: Anton Vorontsov <cbouatmailru@gmail.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/power/Kconfig7
-rw-r--r--drivers/power/Makefile1
-rw-r--r--drivers/power/bq20z75.c385
3 files changed, 393 insertions, 0 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index c2a1b8b713f6..4d9a637539b6 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -109,6 +109,13 @@ config BATTERY_WM97XX
109 help 109 help
110 Say Y to enable support for battery measured by WM97xx aux port. 110 Say Y to enable support for battery measured by WM97xx aux port.
111 111
112config BATTERY_BQ20Z75
113 tristate "TI BQ20z75 gas gauge"
114 depends on I2C
115 help
116 Say Y to include support for TI BQ20z75 SBS-compliant
117 gas gauge and protection IC.
118
112config BATTERY_BQ27x00 119config BATTERY_BQ27x00
113 tristate "BQ27x00 battery driver" 120 tristate "BQ27x00 battery driver"
114 depends on I2C 121 depends on I2C
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index c73d381037a0..2799a8418186 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o
29obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o 29obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o
30obj-$(CONFIG_BATTERY_COLLIE) += collie_battery.o 30obj-$(CONFIG_BATTERY_COLLIE) += collie_battery.o
31obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o 31obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o
32obj-$(CONFIG_BATTERY_BQ20Z75) += bq20z75.o
32obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o 33obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o
33obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o 34obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
34obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o 35obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
diff --git a/drivers/power/bq20z75.c b/drivers/power/bq20z75.c
new file mode 100644
index 000000000000..a1c7ae2ec9f8
--- /dev/null
+++ b/drivers/power/bq20z75.c
@@ -0,0 +1,385 @@
1/*
2 * Gas Gauge driver for TI's BQ20Z75
3 *
4 * Copyright (c) 2010, NVIDIA Corporation.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20
21#include <linux/init.h>
22#include <linux/module.h>
23#include <linux/kernel.h>
24#include <linux/err.h>
25#include <linux/power_supply.h>
26#include <linux/i2c.h>
27#include <linux/slab.h>
28
29enum {
30 REG_MANUFACTURER_DATA,
31 REG_TEMPERATURE,
32 REG_VOLTAGE,
33 REG_CURRENT,
34 REG_CAPACITY,
35 REG_TIME_TO_EMPTY,
36 REG_TIME_TO_FULL,
37 REG_STATUS,
38 REG_CYCLE_COUNT,
39 REG_SERIAL_NUMBER
40};
41
42/* manufacturer access defines */
43#define MANUFACTURER_ACCESS_STATUS 0x0006
44#define MANUFACTURER_ACCESS_SLEEP 0x0011
45
46/* battery status value bits */
47#define BATTERY_CHARGING 0x40
48#define BATTERY_FULL_CHARGED 0x20
49#define BATTERY_FULL_DISCHARGED 0x10
50
51#define BQ20Z75_DATA(_psp, _addr, _min_value, _max_value) { \
52 .psp = _psp, \
53 .addr = _addr, \
54 .min_value = _min_value, \
55 .max_value = _max_value, \
56}
57
58static const struct bq20z75_device_data {
59 enum power_supply_property psp;
60 u8 addr;
61 int min_value;
62 int max_value;
63} bq20z75_data[] = {
64 [REG_MANUFACTURER_DATA] =
65 BQ20Z75_DATA(POWER_SUPPLY_PROP_PRESENT, 0x00, 0, 65535),
66 [REG_TEMPERATURE] =
67 BQ20Z75_DATA(POWER_SUPPLY_PROP_TEMP, 0x08, 0, 65535),
68 [REG_VOLTAGE] =
69 BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 20000),
70 [REG_CURRENT] =
71 BQ20Z75_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768,
72 32767),
73 [REG_CAPACITY] =
74 BQ20Z75_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0E, 0, 100),
75 [REG_TIME_TO_EMPTY] =
76 BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0,
77 65535),
78 [REG_TIME_TO_FULL] =
79 BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, 0x13, 0,
80 65535),
81 [REG_STATUS] =
82 BQ20Z75_DATA(POWER_SUPPLY_PROP_STATUS, 0x16, 0, 65535),
83 [REG_CYCLE_COUNT] =
84 BQ20Z75_DATA(POWER_SUPPLY_PROP_CYCLE_COUNT, 0x17, 0, 65535),
85 [REG_SERIAL_NUMBER] =
86 BQ20Z75_DATA(POWER_SUPPLY_PROP_SERIAL_NUMBER, 0x1C, 0, 65535),
87};
88
89static enum power_supply_property bq20z75_properties[] = {
90 POWER_SUPPLY_PROP_STATUS,
91 POWER_SUPPLY_PROP_HEALTH,
92 POWER_SUPPLY_PROP_PRESENT,
93 POWER_SUPPLY_PROP_TECHNOLOGY,
94 POWER_SUPPLY_PROP_CYCLE_COUNT,
95 POWER_SUPPLY_PROP_VOLTAGE_NOW,
96 POWER_SUPPLY_PROP_CURRENT_NOW,
97 POWER_SUPPLY_PROP_CAPACITY,
98 POWER_SUPPLY_PROP_TEMP,
99 POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
100 POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
101 POWER_SUPPLY_PROP_SERIAL_NUMBER,
102};
103
104struct bq20z75_info {
105 struct i2c_client *client;
106 struct power_supply power_supply;
107};
108
109static int bq20z75_get_battery_presence_and_health(
110 struct i2c_client *client, enum power_supply_property psp,
111 union power_supply_propval *val)
112{
113 s32 ret;
114
115 /* Write to ManufacturerAccess with
116 * ManufacturerAccess command and then
117 * read the status */
118 ret = i2c_smbus_write_word_data(client,
119 bq20z75_data[REG_MANUFACTURER_DATA].addr,
120 MANUFACTURER_ACCESS_STATUS);
121 if (ret < 0) {
122 dev_err(&client->dev,
123 "%s: i2c write for battery presence failed\n",
124 __func__);
125 return -ENODEV;
126 }
127
128 ret = i2c_smbus_read_word_data(client,
129 bq20z75_data[REG_MANUFACTURER_DATA].addr);
130 if (ret < 0) {
131 dev_err(&client->dev,
132 "%s: i2c read for battery presence failed\n",
133 __func__);
134 return -EIO;
135 }
136
137 if (ret < bq20z75_data[REG_MANUFACTURER_DATA].min_value ||
138 ret > bq20z75_data[REG_MANUFACTURER_DATA].max_value) {
139 val->intval = 0;
140 return 0;
141 }
142
143 /* Mask the upper nibble of 2nd byte and
144 * lower byte of response then
145 * shift the result by 8 to get status*/
146 ret &= 0x0F00;
147 ret >>= 8;
148 if (psp == POWER_SUPPLY_PROP_PRESENT) {
149 if (ret == 0x0F)
150 /* battery removed */
151 val->intval = 0;
152 else
153 val->intval = 1;
154 } else if (psp == POWER_SUPPLY_PROP_HEALTH) {
155 if (ret == 0x09)
156 val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
157 else if (ret == 0x0B)
158 val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
159 else if (ret == 0x0C)
160 val->intval = POWER_SUPPLY_HEALTH_DEAD;
161 else
162 val->intval = POWER_SUPPLY_HEALTH_GOOD;
163 }
164
165 return 0;
166}
167
168static int bq20z75_get_battery_property(struct i2c_client *client,
169 int reg_offset, enum power_supply_property psp,
170 union power_supply_propval *val)
171{
172 s32 ret;
173
174 ret = i2c_smbus_read_word_data(client,
175 bq20z75_data[reg_offset].addr);
176 if (ret < 0) {
177 dev_err(&client->dev,
178 "%s: i2c read for %d failed\n", __func__, reg_offset);
179 return -EIO;
180 }
181
182 if (ret >= bq20z75_data[reg_offset].min_value &&
183 ret <= bq20z75_data[reg_offset].max_value) {
184 val->intval = ret;
185 if (psp == POWER_SUPPLY_PROP_STATUS) {
186 if (ret & BATTERY_CHARGING)
187 val->intval = POWER_SUPPLY_STATUS_CHARGING;
188 else if (ret & BATTERY_FULL_CHARGED)
189 val->intval = POWER_SUPPLY_STATUS_FULL;
190 else if (ret & BATTERY_FULL_DISCHARGED)
191 val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
192 else
193 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
194 }
195 /* bq20z75 provides battery tempreture in 0.1°K
196 * so convert it to °C */
197 else if (psp == POWER_SUPPLY_PROP_TEMP)
198 val->intval = ret - 2731;
199 } else {
200 if (psp == POWER_SUPPLY_PROP_STATUS)
201 val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
202 else
203 val->intval = 0;
204 }
205
206 return 0;
207}
208
209static int bq20z75_get_battery_capacity(struct i2c_client *client,
210 union power_supply_propval *val)
211{
212 s32 ret;
213
214 ret = i2c_smbus_read_byte_data(client, bq20z75_data[REG_CAPACITY].addr);
215 if (ret < 0) {
216 dev_err(&client->dev,
217 "%s: i2c read for %d failed\n", __func__, REG_CAPACITY);
218 return -EIO;
219 }
220
221 /* bq20z75 spec says that this can be >100 %
222 * even if max value is 100 % */
223 val->intval = min(ret, 100);
224
225 return 0;
226}
227
228static int bq20z75_get_property(struct power_supply *psy,
229 enum power_supply_property psp,
230 union power_supply_propval *val)
231{
232 int count;
233 int ret;
234 struct bq20z75_info *bq20z75_device = container_of(psy,
235 struct bq20z75_info, power_supply);
236 struct i2c_client *client = bq20z75_device->client;
237
238 switch (psp) {
239 case POWER_SUPPLY_PROP_PRESENT:
240 case POWER_SUPPLY_PROP_HEALTH:
241 ret = bq20z75_get_battery_presence_and_health(client, psp, val);
242 if (ret)
243 return ret;
244 break;
245
246 case POWER_SUPPLY_PROP_TECHNOLOGY:
247 val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
248 break;
249
250 case POWER_SUPPLY_PROP_CAPACITY:
251 ret = bq20z75_get_battery_capacity(client, val);
252 if (ret)
253 return ret;
254 break;
255
256 case POWER_SUPPLY_PROP_STATUS:
257 case POWER_SUPPLY_PROP_CYCLE_COUNT:
258 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
259 case POWER_SUPPLY_PROP_CURRENT_NOW:
260 case POWER_SUPPLY_PROP_TEMP:
261 case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
262 case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
263 case POWER_SUPPLY_PROP_SERIAL_NUMBER:
264 for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) {
265 if (psp == bq20z75_data[count].psp)
266 break;
267 }
268
269 ret = bq20z75_get_battery_property(client, count, psp, val);
270 if (ret)
271 return ret;
272 break;
273
274 default:
275 dev_err(&client->dev,
276 "%s: INVALID property\n", __func__);
277 return -EINVAL;
278 }
279
280 dev_dbg(&client->dev,
281 "%s: property = %d, value = %d\n", __func__, psp, val->intval);
282
283 return 0;
284}
285
286static int bq20z75_probe(struct i2c_client *client,
287 const struct i2c_device_id *id)
288{
289 struct bq20z75_info *bq20z75_device;
290 int rc;
291
292 bq20z75_device = kzalloc(sizeof(struct bq20z75_info), GFP_KERNEL);
293 if (!bq20z75_device)
294 return -ENOMEM;
295
296 bq20z75_device->client = client;
297 bq20z75_device->power_supply.name = "battery";
298 bq20z75_device->power_supply.type = POWER_SUPPLY_TYPE_BATTERY;
299 bq20z75_device->power_supply.properties = bq20z75_properties;
300 bq20z75_device->power_supply.num_properties =
301 ARRAY_SIZE(bq20z75_properties);
302 bq20z75_device->power_supply.get_property = bq20z75_get_property;
303
304 i2c_set_clientdata(client, bq20z75_device);
305
306 rc = power_supply_register(&client->dev, &bq20z75_device->power_supply);
307 if (rc) {
308 dev_err(&client->dev,
309 "%s: Failed to register power supply\n", __func__);
310 kfree(bq20z75_device);
311 return rc;
312 }
313
314 dev_info(&client->dev,
315 "%s: battery gas gauge device registered\n", client->name);
316
317 return 0;
318}
319
320static int bq20z75_remove(struct i2c_client *client)
321{
322 struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client);
323
324 power_supply_unregister(&bq20z75_device->power_supply);
325 kfree(bq20z75_device);
326 bq20z75_device = NULL;
327
328 return 0;
329}
330
331#if defined CONFIG_PM
332static int bq20z75_suspend(struct i2c_client *client,
333 pm_message_t state)
334{
335 s32 ret;
336
337 /* write to manufacturer access with sleep command */
338 ret = i2c_smbus_write_word_data(client,
339 bq20z75_data[REG_MANUFACTURER_DATA].addr,
340 MANUFACTURER_ACCESS_SLEEP);
341 if (ret < 0) {
342 dev_err(&client->dev,
343 "%s: i2c write for %d failed\n",
344 __func__, MANUFACTURER_ACCESS_SLEEP);
345 return -EIO;
346 }
347
348 return 0;
349}
350#else
351#define bq20z75_suspend NULL
352#endif
353/* any smbus transaction will wake up bq20z75 */
354#define bq20z75_resume NULL
355
356static const struct i2c_device_id bq20z75_id[] = {
357 { "bq20z75", 0 },
358 {}
359};
360
361static struct i2c_driver bq20z75_battery_driver = {
362 .probe = bq20z75_probe,
363 .remove = bq20z75_remove,
364 .suspend = bq20z75_suspend,
365 .resume = bq20z75_resume,
366 .id_table = bq20z75_id,
367 .driver = {
368 .name = "bq20z75-battery",
369 },
370};
371
372static int __init bq20z75_battery_init(void)
373{
374 return i2c_add_driver(&bq20z75_battery_driver);
375}
376module_init(bq20z75_battery_init);
377
378static void __exit bq20z75_battery_exit(void)
379{
380 i2c_del_driver(&bq20z75_battery_driver);
381}
382module_exit(bq20z75_battery_exit);
383
384MODULE_DESCRIPTION("BQ20z75 battery monitor driver");
385MODULE_LICENSE("GPL");