diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/power/bq20z75.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/power/bq20z75.c')
-rw-r--r-- | drivers/power/bq20z75.c | 810 |
1 files changed, 810 insertions, 0 deletions
diff --git a/drivers/power/bq20z75.c b/drivers/power/bq20z75.c new file mode 100644 index 00000000000..32e5a8c5c3d --- /dev/null +++ b/drivers/power/bq20z75.c | |||
@@ -0,0 +1,810 @@ | |||
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 | #include <linux/interrupt.h> | ||
29 | #include <linux/gpio.h> | ||
30 | |||
31 | #include <linux/power/bq20z75.h> | ||
32 | |||
33 | enum { | ||
34 | REG_MANUFACTURER_DATA, | ||
35 | REG_TEMPERATURE, | ||
36 | REG_VOLTAGE, | ||
37 | REG_CURRENT, | ||
38 | REG_CAPACITY, | ||
39 | REG_TIME_TO_EMPTY, | ||
40 | REG_TIME_TO_FULL, | ||
41 | REG_STATUS, | ||
42 | REG_CYCLE_COUNT, | ||
43 | REG_SERIAL_NUMBER, | ||
44 | REG_REMAINING_CAPACITY, | ||
45 | REG_REMAINING_CAPACITY_CHARGE, | ||
46 | REG_FULL_CHARGE_CAPACITY, | ||
47 | REG_FULL_CHARGE_CAPACITY_CHARGE, | ||
48 | REG_DESIGN_CAPACITY, | ||
49 | REG_DESIGN_CAPACITY_CHARGE, | ||
50 | REG_DESIGN_VOLTAGE, | ||
51 | }; | ||
52 | |||
53 | /* Battery Mode defines */ | ||
54 | #define BATTERY_MODE_OFFSET 0x03 | ||
55 | #define BATTERY_MODE_MASK 0x8000 | ||
56 | enum bq20z75_battery_mode { | ||
57 | BATTERY_MODE_AMPS, | ||
58 | BATTERY_MODE_WATTS | ||
59 | }; | ||
60 | |||
61 | /* manufacturer access defines */ | ||
62 | #define MANUFACTURER_ACCESS_STATUS 0x0006 | ||
63 | #define MANUFACTURER_ACCESS_SLEEP 0x0011 | ||
64 | |||
65 | /* battery status value bits */ | ||
66 | #define BATTERY_DISCHARGING 0x40 | ||
67 | #define BATTERY_FULL_CHARGED 0x20 | ||
68 | #define BATTERY_FULL_DISCHARGED 0x10 | ||
69 | |||
70 | #define BQ20Z75_DATA(_psp, _addr, _min_value, _max_value) { \ | ||
71 | .psp = _psp, \ | ||
72 | .addr = _addr, \ | ||
73 | .min_value = _min_value, \ | ||
74 | .max_value = _max_value, \ | ||
75 | } | ||
76 | |||
77 | static const struct bq20z75_device_data { | ||
78 | enum power_supply_property psp; | ||
79 | u8 addr; | ||
80 | int min_value; | ||
81 | int max_value; | ||
82 | } bq20z75_data[] = { | ||
83 | [REG_MANUFACTURER_DATA] = | ||
84 | BQ20Z75_DATA(POWER_SUPPLY_PROP_PRESENT, 0x00, 0, 65535), | ||
85 | [REG_TEMPERATURE] = | ||
86 | BQ20Z75_DATA(POWER_SUPPLY_PROP_TEMP, 0x08, 0, 65535), | ||
87 | [REG_VOLTAGE] = | ||
88 | BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 20000), | ||
89 | [REG_CURRENT] = | ||
90 | BQ20Z75_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768, | ||
91 | 32767), | ||
92 | [REG_CAPACITY] = | ||
93 | BQ20Z75_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0E, 0, 100), | ||
94 | [REG_REMAINING_CAPACITY] = | ||
95 | BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_NOW, 0x0F, 0, 65535), | ||
96 | [REG_REMAINING_CAPACITY_CHARGE] = | ||
97 | BQ20Z75_DATA(POWER_SUPPLY_PROP_CHARGE_NOW, 0x0F, 0, 65535), | ||
98 | [REG_FULL_CHARGE_CAPACITY] = | ||
99 | BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL, 0x10, 0, 65535), | ||
100 | [REG_FULL_CHARGE_CAPACITY_CHARGE] = | ||
101 | BQ20Z75_DATA(POWER_SUPPLY_PROP_CHARGE_FULL, 0x10, 0, 65535), | ||
102 | [REG_TIME_TO_EMPTY] = | ||
103 | BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0, | ||
104 | 65535), | ||
105 | [REG_TIME_TO_FULL] = | ||
106 | BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, 0x13, 0, | ||
107 | 65535), | ||
108 | [REG_STATUS] = | ||
109 | BQ20Z75_DATA(POWER_SUPPLY_PROP_STATUS, 0x16, 0, 65535), | ||
110 | [REG_CYCLE_COUNT] = | ||
111 | BQ20Z75_DATA(POWER_SUPPLY_PROP_CYCLE_COUNT, 0x17, 0, 65535), | ||
112 | [REG_DESIGN_CAPACITY] = | ||
113 | BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 0x18, 0, | ||
114 | 65535), | ||
115 | [REG_DESIGN_CAPACITY_CHARGE] = | ||
116 | BQ20Z75_DATA(POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 0x18, 0, | ||
117 | 65535), | ||
118 | [REG_DESIGN_VOLTAGE] = | ||
119 | BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 0x19, 0, | ||
120 | 65535), | ||
121 | [REG_SERIAL_NUMBER] = | ||
122 | BQ20Z75_DATA(POWER_SUPPLY_PROP_SERIAL_NUMBER, 0x1C, 0, 65535), | ||
123 | }; | ||
124 | |||
125 | static enum power_supply_property bq20z75_properties[] = { | ||
126 | POWER_SUPPLY_PROP_STATUS, | ||
127 | POWER_SUPPLY_PROP_HEALTH, | ||
128 | POWER_SUPPLY_PROP_PRESENT, | ||
129 | POWER_SUPPLY_PROP_TECHNOLOGY, | ||
130 | POWER_SUPPLY_PROP_CYCLE_COUNT, | ||
131 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | ||
132 | POWER_SUPPLY_PROP_CURRENT_NOW, | ||
133 | POWER_SUPPLY_PROP_CAPACITY, | ||
134 | POWER_SUPPLY_PROP_TEMP, | ||
135 | POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, | ||
136 | POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, | ||
137 | POWER_SUPPLY_PROP_SERIAL_NUMBER, | ||
138 | POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, | ||
139 | POWER_SUPPLY_PROP_ENERGY_NOW, | ||
140 | POWER_SUPPLY_PROP_ENERGY_FULL, | ||
141 | POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, | ||
142 | POWER_SUPPLY_PROP_CHARGE_NOW, | ||
143 | POWER_SUPPLY_PROP_CHARGE_FULL, | ||
144 | POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, | ||
145 | }; | ||
146 | |||
147 | struct bq20z75_info { | ||
148 | struct i2c_client *client; | ||
149 | struct power_supply power_supply; | ||
150 | struct bq20z75_platform_data plat_data; | ||
151 | bool is_present; | ||
152 | bool gpio_detect; | ||
153 | bool enable_detection; | ||
154 | int irq; | ||
155 | int last_state; | ||
156 | int poll_time; | ||
157 | struct delayed_work work; | ||
158 | int ignore_changes; | ||
159 | }; | ||
160 | |||
161 | static int bq20z75_read_word_data(struct i2c_client *client, u8 address) | ||
162 | { | ||
163 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | ||
164 | s32 ret = 0; | ||
165 | int retries = 1; | ||
166 | |||
167 | retries = max(bq20z75_device->plat_data.i2c_retry_count + 1, 1); | ||
168 | |||
169 | while (retries > 0) { | ||
170 | ret = i2c_smbus_read_word_data(client, address); | ||
171 | if (ret >= 0) | ||
172 | break; | ||
173 | retries--; | ||
174 | } | ||
175 | |||
176 | if (ret < 0) { | ||
177 | dev_dbg(&client->dev, | ||
178 | "%s: i2c read at address 0x%x failed\n", | ||
179 | __func__, address); | ||
180 | return ret; | ||
181 | } | ||
182 | |||
183 | return le16_to_cpu(ret); | ||
184 | } | ||
185 | |||
186 | static int bq20z75_write_word_data(struct i2c_client *client, u8 address, | ||
187 | u16 value) | ||
188 | { | ||
189 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | ||
190 | s32 ret = 0; | ||
191 | int retries = 1; | ||
192 | |||
193 | retries = max(bq20z75_device->plat_data.i2c_retry_count + 1, 1); | ||
194 | |||
195 | while (retries > 0) { | ||
196 | ret = i2c_smbus_write_word_data(client, address, | ||
197 | le16_to_cpu(value)); | ||
198 | if (ret >= 0) | ||
199 | break; | ||
200 | retries--; | ||
201 | } | ||
202 | |||
203 | if (ret < 0) { | ||
204 | dev_dbg(&client->dev, | ||
205 | "%s: i2c write to address 0x%x failed\n", | ||
206 | __func__, address); | ||
207 | return ret; | ||
208 | } | ||
209 | |||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | static int bq20z75_get_battery_presence_and_health( | ||
214 | struct i2c_client *client, enum power_supply_property psp, | ||
215 | union power_supply_propval *val) | ||
216 | { | ||
217 | s32 ret; | ||
218 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | ||
219 | |||
220 | if (psp == POWER_SUPPLY_PROP_PRESENT && | ||
221 | bq20z75_device->gpio_detect) { | ||
222 | ret = gpio_get_value( | ||
223 | bq20z75_device->plat_data.battery_detect); | ||
224 | if (ret == bq20z75_device->plat_data.battery_detect_present) | ||
225 | val->intval = 1; | ||
226 | else | ||
227 | val->intval = 0; | ||
228 | bq20z75_device->is_present = val->intval; | ||
229 | return ret; | ||
230 | } | ||
231 | |||
232 | /* Write to ManufacturerAccess with | ||
233 | * ManufacturerAccess command and then | ||
234 | * read the status */ | ||
235 | ret = bq20z75_write_word_data(client, | ||
236 | bq20z75_data[REG_MANUFACTURER_DATA].addr, | ||
237 | MANUFACTURER_ACCESS_STATUS); | ||
238 | if (ret < 0) { | ||
239 | if (psp == POWER_SUPPLY_PROP_PRESENT) | ||
240 | val->intval = 0; /* battery removed */ | ||
241 | return ret; | ||
242 | } | ||
243 | |||
244 | ret = bq20z75_read_word_data(client, | ||
245 | bq20z75_data[REG_MANUFACTURER_DATA].addr); | ||
246 | if (ret < 0) | ||
247 | return ret; | ||
248 | |||
249 | if (ret < bq20z75_data[REG_MANUFACTURER_DATA].min_value || | ||
250 | ret > bq20z75_data[REG_MANUFACTURER_DATA].max_value) { | ||
251 | val->intval = 0; | ||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | /* Mask the upper nibble of 2nd byte and | ||
256 | * lower byte of response then | ||
257 | * shift the result by 8 to get status*/ | ||
258 | ret &= 0x0F00; | ||
259 | ret >>= 8; | ||
260 | if (psp == POWER_SUPPLY_PROP_PRESENT) { | ||
261 | if (ret == 0x0F) | ||
262 | /* battery removed */ | ||
263 | val->intval = 0; | ||
264 | else | ||
265 | val->intval = 1; | ||
266 | } else if (psp == POWER_SUPPLY_PROP_HEALTH) { | ||
267 | if (ret == 0x09) | ||
268 | val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; | ||
269 | else if (ret == 0x0B) | ||
270 | val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; | ||
271 | else if (ret == 0x0C) | ||
272 | val->intval = POWER_SUPPLY_HEALTH_DEAD; | ||
273 | else | ||
274 | val->intval = POWER_SUPPLY_HEALTH_GOOD; | ||
275 | } | ||
276 | |||
277 | return 0; | ||
278 | } | ||
279 | |||
280 | static int bq20z75_get_battery_property(struct i2c_client *client, | ||
281 | int reg_offset, enum power_supply_property psp, | ||
282 | union power_supply_propval *val) | ||
283 | { | ||
284 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | ||
285 | s32 ret; | ||
286 | |||
287 | ret = bq20z75_read_word_data(client, | ||
288 | bq20z75_data[reg_offset].addr); | ||
289 | if (ret < 0) | ||
290 | return ret; | ||
291 | |||
292 | /* returned values are 16 bit */ | ||
293 | if (bq20z75_data[reg_offset].min_value < 0) | ||
294 | ret = (s16)ret; | ||
295 | |||
296 | if (ret >= bq20z75_data[reg_offset].min_value && | ||
297 | ret <= bq20z75_data[reg_offset].max_value) { | ||
298 | val->intval = ret; | ||
299 | if (psp != POWER_SUPPLY_PROP_STATUS) | ||
300 | return 0; | ||
301 | |||
302 | if (ret & BATTERY_FULL_CHARGED) | ||
303 | val->intval = POWER_SUPPLY_STATUS_FULL; | ||
304 | else if (ret & BATTERY_FULL_DISCHARGED) | ||
305 | val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; | ||
306 | else if (ret & BATTERY_DISCHARGING) | ||
307 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; | ||
308 | else | ||
309 | val->intval = POWER_SUPPLY_STATUS_CHARGING; | ||
310 | |||
311 | if (bq20z75_device->poll_time == 0) | ||
312 | bq20z75_device->last_state = val->intval; | ||
313 | else if (bq20z75_device->last_state != val->intval) { | ||
314 | cancel_delayed_work_sync(&bq20z75_device->work); | ||
315 | power_supply_changed(&bq20z75_device->power_supply); | ||
316 | bq20z75_device->poll_time = 0; | ||
317 | } | ||
318 | } else { | ||
319 | if (psp == POWER_SUPPLY_PROP_STATUS) | ||
320 | val->intval = POWER_SUPPLY_STATUS_UNKNOWN; | ||
321 | else | ||
322 | val->intval = 0; | ||
323 | } | ||
324 | |||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | static void bq20z75_unit_adjustment(struct i2c_client *client, | ||
329 | enum power_supply_property psp, union power_supply_propval *val) | ||
330 | { | ||
331 | #define BASE_UNIT_CONVERSION 1000 | ||
332 | #define BATTERY_MODE_CAP_MULT_WATT (10 * BASE_UNIT_CONVERSION) | ||
333 | #define TIME_UNIT_CONVERSION 60 | ||
334 | #define TEMP_KELVIN_TO_CELSIUS 2731 | ||
335 | switch (psp) { | ||
336 | case POWER_SUPPLY_PROP_ENERGY_NOW: | ||
337 | case POWER_SUPPLY_PROP_ENERGY_FULL: | ||
338 | case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: | ||
339 | /* bq20z75 provides energy in units of 10mWh. | ||
340 | * Convert to µWh | ||
341 | */ | ||
342 | val->intval *= BATTERY_MODE_CAP_MULT_WATT; | ||
343 | break; | ||
344 | |||
345 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
346 | case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: | ||
347 | case POWER_SUPPLY_PROP_CURRENT_NOW: | ||
348 | case POWER_SUPPLY_PROP_CHARGE_NOW: | ||
349 | case POWER_SUPPLY_PROP_CHARGE_FULL: | ||
350 | case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: | ||
351 | val->intval *= BASE_UNIT_CONVERSION; | ||
352 | break; | ||
353 | |||
354 | case POWER_SUPPLY_PROP_TEMP: | ||
355 | /* bq20z75 provides battery temperature in 0.1K | ||
356 | * so convert it to 0.1°C | ||
357 | */ | ||
358 | val->intval -= TEMP_KELVIN_TO_CELSIUS; | ||
359 | break; | ||
360 | |||
361 | case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: | ||
362 | case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: | ||
363 | /* bq20z75 provides time to empty and time to full in minutes. | ||
364 | * Convert to seconds | ||
365 | */ | ||
366 | val->intval *= TIME_UNIT_CONVERSION; | ||
367 | break; | ||
368 | |||
369 | default: | ||
370 | dev_dbg(&client->dev, | ||
371 | "%s: no need for unit conversion %d\n", __func__, psp); | ||
372 | } | ||
373 | } | ||
374 | |||
375 | static enum bq20z75_battery_mode | ||
376 | bq20z75_set_battery_mode(struct i2c_client *client, | ||
377 | enum bq20z75_battery_mode mode) | ||
378 | { | ||
379 | int ret, original_val; | ||
380 | |||
381 | original_val = bq20z75_read_word_data(client, BATTERY_MODE_OFFSET); | ||
382 | if (original_val < 0) | ||
383 | return original_val; | ||
384 | |||
385 | if ((original_val & BATTERY_MODE_MASK) == mode) | ||
386 | return mode; | ||
387 | |||
388 | if (mode == BATTERY_MODE_AMPS) | ||
389 | ret = original_val & ~BATTERY_MODE_MASK; | ||
390 | else | ||
391 | ret = original_val | BATTERY_MODE_MASK; | ||
392 | |||
393 | ret = bq20z75_write_word_data(client, BATTERY_MODE_OFFSET, ret); | ||
394 | if (ret < 0) | ||
395 | return ret; | ||
396 | |||
397 | return original_val & BATTERY_MODE_MASK; | ||
398 | } | ||
399 | |||
400 | static int bq20z75_get_battery_capacity(struct i2c_client *client, | ||
401 | int reg_offset, enum power_supply_property psp, | ||
402 | union power_supply_propval *val) | ||
403 | { | ||
404 | s32 ret; | ||
405 | enum bq20z75_battery_mode mode = BATTERY_MODE_WATTS; | ||
406 | |||
407 | if (power_supply_is_amp_property(psp)) | ||
408 | mode = BATTERY_MODE_AMPS; | ||
409 | |||
410 | mode = bq20z75_set_battery_mode(client, mode); | ||
411 | if (mode < 0) | ||
412 | return mode; | ||
413 | |||
414 | ret = bq20z75_read_word_data(client, bq20z75_data[reg_offset].addr); | ||
415 | if (ret < 0) | ||
416 | return ret; | ||
417 | |||
418 | if (psp == POWER_SUPPLY_PROP_CAPACITY) { | ||
419 | /* bq20z75 spec says that this can be >100 % | ||
420 | * even if max value is 100 % */ | ||
421 | val->intval = min(ret, 100); | ||
422 | } else | ||
423 | val->intval = ret; | ||
424 | |||
425 | ret = bq20z75_set_battery_mode(client, mode); | ||
426 | if (ret < 0) | ||
427 | return ret; | ||
428 | |||
429 | return 0; | ||
430 | } | ||
431 | |||
432 | static char bq20z75_serial[5]; | ||
433 | static int bq20z75_get_battery_serial_number(struct i2c_client *client, | ||
434 | union power_supply_propval *val) | ||
435 | { | ||
436 | int ret; | ||
437 | |||
438 | ret = bq20z75_read_word_data(client, | ||
439 | bq20z75_data[REG_SERIAL_NUMBER].addr); | ||
440 | if (ret < 0) | ||
441 | return ret; | ||
442 | |||
443 | ret = sprintf(bq20z75_serial, "%04x", ret); | ||
444 | val->strval = bq20z75_serial; | ||
445 | |||
446 | return 0; | ||
447 | } | ||
448 | |||
449 | static int bq20z75_get_property_index(struct i2c_client *client, | ||
450 | enum power_supply_property psp) | ||
451 | { | ||
452 | int count; | ||
453 | for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) | ||
454 | if (psp == bq20z75_data[count].psp) | ||
455 | return count; | ||
456 | |||
457 | dev_warn(&client->dev, | ||
458 | "%s: Invalid Property - %d\n", __func__, psp); | ||
459 | |||
460 | return -EINVAL; | ||
461 | } | ||
462 | |||
463 | static int bq20z75_get_property(struct power_supply *psy, | ||
464 | enum power_supply_property psp, | ||
465 | union power_supply_propval *val) | ||
466 | { | ||
467 | int ret = 0; | ||
468 | struct bq20z75_info *bq20z75_device = container_of(psy, | ||
469 | struct bq20z75_info, power_supply); | ||
470 | struct i2c_client *client = bq20z75_device->client; | ||
471 | |||
472 | switch (psp) { | ||
473 | case POWER_SUPPLY_PROP_PRESENT: | ||
474 | case POWER_SUPPLY_PROP_HEALTH: | ||
475 | ret = bq20z75_get_battery_presence_and_health(client, psp, val); | ||
476 | if (psp == POWER_SUPPLY_PROP_PRESENT) | ||
477 | return 0; | ||
478 | break; | ||
479 | |||
480 | case POWER_SUPPLY_PROP_TECHNOLOGY: | ||
481 | val->intval = POWER_SUPPLY_TECHNOLOGY_LION; | ||
482 | break; | ||
483 | |||
484 | case POWER_SUPPLY_PROP_ENERGY_NOW: | ||
485 | case POWER_SUPPLY_PROP_ENERGY_FULL: | ||
486 | case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: | ||
487 | case POWER_SUPPLY_PROP_CHARGE_NOW: | ||
488 | case POWER_SUPPLY_PROP_CHARGE_FULL: | ||
489 | case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: | ||
490 | case POWER_SUPPLY_PROP_CAPACITY: | ||
491 | ret = bq20z75_get_property_index(client, psp); | ||
492 | if (ret < 0) | ||
493 | break; | ||
494 | |||
495 | ret = bq20z75_get_battery_capacity(client, ret, psp, val); | ||
496 | break; | ||
497 | |||
498 | case POWER_SUPPLY_PROP_SERIAL_NUMBER: | ||
499 | ret = bq20z75_get_battery_serial_number(client, val); | ||
500 | break; | ||
501 | |||
502 | case POWER_SUPPLY_PROP_STATUS: | ||
503 | case POWER_SUPPLY_PROP_CYCLE_COUNT: | ||
504 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
505 | case POWER_SUPPLY_PROP_CURRENT_NOW: | ||
506 | case POWER_SUPPLY_PROP_TEMP: | ||
507 | case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: | ||
508 | case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: | ||
509 | case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: | ||
510 | ret = bq20z75_get_property_index(client, psp); | ||
511 | if (ret < 0) | ||
512 | break; | ||
513 | |||
514 | ret = bq20z75_get_battery_property(client, ret, psp, val); | ||
515 | break; | ||
516 | |||
517 | default: | ||
518 | dev_err(&client->dev, | ||
519 | "%s: INVALID property\n", __func__); | ||
520 | return -EINVAL; | ||
521 | } | ||
522 | |||
523 | if (!bq20z75_device->enable_detection) | ||
524 | goto done; | ||
525 | |||
526 | if (!bq20z75_device->gpio_detect && | ||
527 | bq20z75_device->is_present != (ret >= 0)) { | ||
528 | bq20z75_device->is_present = (ret >= 0); | ||
529 | power_supply_changed(&bq20z75_device->power_supply); | ||
530 | } | ||
531 | |||
532 | done: | ||
533 | if (!ret) { | ||
534 | /* Convert units to match requirements for power supply class */ | ||
535 | bq20z75_unit_adjustment(client, psp, val); | ||
536 | } | ||
537 | |||
538 | dev_dbg(&client->dev, | ||
539 | "%s: property = %d, value = %x\n", __func__, psp, val->intval); | ||
540 | |||
541 | if (ret && bq20z75_device->is_present) | ||
542 | return ret; | ||
543 | |||
544 | /* battery not present, so return NODATA for properties */ | ||
545 | if (ret) | ||
546 | return -ENODATA; | ||
547 | |||
548 | return 0; | ||
549 | } | ||
550 | |||
551 | static irqreturn_t bq20z75_irq(int irq, void *devid) | ||
552 | { | ||
553 | struct power_supply *battery = devid; | ||
554 | |||
555 | power_supply_changed(battery); | ||
556 | |||
557 | return IRQ_HANDLED; | ||
558 | } | ||
559 | |||
560 | static void bq20z75_external_power_changed(struct power_supply *psy) | ||
561 | { | ||
562 | struct bq20z75_info *bq20z75_device; | ||
563 | |||
564 | bq20z75_device = container_of(psy, struct bq20z75_info, power_supply); | ||
565 | |||
566 | if (bq20z75_device->ignore_changes > 0) { | ||
567 | bq20z75_device->ignore_changes--; | ||
568 | return; | ||
569 | } | ||
570 | |||
571 | /* cancel outstanding work */ | ||
572 | cancel_delayed_work_sync(&bq20z75_device->work); | ||
573 | |||
574 | schedule_delayed_work(&bq20z75_device->work, HZ); | ||
575 | bq20z75_device->poll_time = bq20z75_device->plat_data.poll_retry_count; | ||
576 | } | ||
577 | |||
578 | static void bq20z75_delayed_work(struct work_struct *work) | ||
579 | { | ||
580 | struct bq20z75_info *bq20z75_device; | ||
581 | s32 ret; | ||
582 | |||
583 | bq20z75_device = container_of(work, struct bq20z75_info, work.work); | ||
584 | |||
585 | ret = bq20z75_read_word_data(bq20z75_device->client, | ||
586 | bq20z75_data[REG_STATUS].addr); | ||
587 | /* if the read failed, give up on this work */ | ||
588 | if (ret < 0) { | ||
589 | bq20z75_device->poll_time = 0; | ||
590 | return; | ||
591 | } | ||
592 | |||
593 | if (ret & BATTERY_FULL_CHARGED) | ||
594 | ret = POWER_SUPPLY_STATUS_FULL; | ||
595 | else if (ret & BATTERY_FULL_DISCHARGED) | ||
596 | ret = POWER_SUPPLY_STATUS_NOT_CHARGING; | ||
597 | else if (ret & BATTERY_DISCHARGING) | ||
598 | ret = POWER_SUPPLY_STATUS_DISCHARGING; | ||
599 | else | ||
600 | ret = POWER_SUPPLY_STATUS_CHARGING; | ||
601 | |||
602 | if (bq20z75_device->last_state != ret) { | ||
603 | bq20z75_device->poll_time = 0; | ||
604 | power_supply_changed(&bq20z75_device->power_supply); | ||
605 | return; | ||
606 | } | ||
607 | if (bq20z75_device->poll_time > 0) { | ||
608 | schedule_delayed_work(&bq20z75_device->work, HZ); | ||
609 | bq20z75_device->poll_time--; | ||
610 | return; | ||
611 | } | ||
612 | } | ||
613 | |||
614 | static int __devinit bq20z75_probe(struct i2c_client *client, | ||
615 | const struct i2c_device_id *id) | ||
616 | { | ||
617 | struct bq20z75_info *bq20z75_device; | ||
618 | struct bq20z75_platform_data *pdata = client->dev.platform_data; | ||
619 | int rc; | ||
620 | int irq; | ||
621 | |||
622 | bq20z75_device = kzalloc(sizeof(struct bq20z75_info), GFP_KERNEL); | ||
623 | if (!bq20z75_device) | ||
624 | return -ENOMEM; | ||
625 | |||
626 | bq20z75_device->client = client; | ||
627 | bq20z75_device->enable_detection = false; | ||
628 | bq20z75_device->gpio_detect = false; | ||
629 | bq20z75_device->power_supply.name = "battery"; | ||
630 | bq20z75_device->power_supply.type = POWER_SUPPLY_TYPE_BATTERY; | ||
631 | bq20z75_device->power_supply.properties = bq20z75_properties; | ||
632 | bq20z75_device->power_supply.num_properties = | ||
633 | ARRAY_SIZE(bq20z75_properties); | ||
634 | bq20z75_device->power_supply.get_property = bq20z75_get_property; | ||
635 | /* ignore first notification of external change, it is generated | ||
636 | * from the power_supply_register call back | ||
637 | */ | ||
638 | bq20z75_device->ignore_changes = 1; | ||
639 | bq20z75_device->last_state = POWER_SUPPLY_STATUS_UNKNOWN; | ||
640 | bq20z75_device->power_supply.external_power_changed = | ||
641 | bq20z75_external_power_changed; | ||
642 | |||
643 | if (pdata) { | ||
644 | bq20z75_device->gpio_detect = | ||
645 | gpio_is_valid(pdata->battery_detect); | ||
646 | memcpy(&bq20z75_device->plat_data, pdata, sizeof(struct bq20z75_platform_data)); | ||
647 | } | ||
648 | |||
649 | i2c_set_clientdata(client, bq20z75_device); | ||
650 | |||
651 | /* Probing for the presence of the bq20z75 */ | ||
652 | rc = bq20z75_read_word_data(client, | ||
653 | bq20z75_data[REG_SERIAL_NUMBER].addr); | ||
654 | |||
655 | if (rc < 0) { | ||
656 | dev_err(&client->dev, | ||
657 | "%s: bq20z75 is not responding\n", __func__); | ||
658 | rc = -ENODEV; | ||
659 | goto exit_mem_free; | ||
660 | } | ||
661 | |||
662 | if (!bq20z75_device->gpio_detect) | ||
663 | goto skip_gpio; | ||
664 | |||
665 | rc = gpio_request(pdata->battery_detect, dev_name(&client->dev)); | ||
666 | if (rc) { | ||
667 | dev_warn(&client->dev, "Failed to request gpio: %d\n", rc); | ||
668 | bq20z75_device->gpio_detect = false; | ||
669 | goto skip_gpio; | ||
670 | } | ||
671 | |||
672 | rc = gpio_direction_input(pdata->battery_detect); | ||
673 | if (rc) { | ||
674 | dev_warn(&client->dev, "Failed to get gpio as input: %d\n", rc); | ||
675 | gpio_free(pdata->battery_detect); | ||
676 | bq20z75_device->gpio_detect = false; | ||
677 | goto skip_gpio; | ||
678 | } | ||
679 | |||
680 | irq = gpio_to_irq(pdata->battery_detect); | ||
681 | if (irq <= 0) { | ||
682 | dev_warn(&client->dev, "Failed to get gpio as irq: %d\n", irq); | ||
683 | gpio_free(pdata->battery_detect); | ||
684 | bq20z75_device->gpio_detect = false; | ||
685 | goto skip_gpio; | ||
686 | } | ||
687 | |||
688 | rc = request_irq(irq, bq20z75_irq, | ||
689 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, | ||
690 | dev_name(&client->dev), &bq20z75_device->power_supply); | ||
691 | if (rc) { | ||
692 | dev_warn(&client->dev, "Failed to request irq: %d\n", rc); | ||
693 | gpio_free(pdata->battery_detect); | ||
694 | bq20z75_device->gpio_detect = false; | ||
695 | goto skip_gpio; | ||
696 | } | ||
697 | |||
698 | bq20z75_device->irq = irq; | ||
699 | |||
700 | skip_gpio: | ||
701 | rc = power_supply_register(&client->dev, &bq20z75_device->power_supply); | ||
702 | if (rc) { | ||
703 | dev_err(&client->dev, | ||
704 | "%s: Failed to register power supply\n", __func__); | ||
705 | goto exit_psupply; | ||
706 | } | ||
707 | |||
708 | dev_info(&client->dev, | ||
709 | "%s: battery gas gauge device registered\n", client->name); | ||
710 | |||
711 | INIT_DELAYED_WORK(&bq20z75_device->work, bq20z75_delayed_work); | ||
712 | |||
713 | bq20z75_device->enable_detection = true; | ||
714 | |||
715 | return 0; | ||
716 | |||
717 | exit_psupply: | ||
718 | if (bq20z75_device->irq) | ||
719 | free_irq(bq20z75_device->irq, &bq20z75_device->power_supply); | ||
720 | if (bq20z75_device->gpio_detect) | ||
721 | gpio_free(pdata->battery_detect); | ||
722 | |||
723 | exit_mem_free: | ||
724 | kfree(bq20z75_device); | ||
725 | |||
726 | return rc; | ||
727 | } | ||
728 | |||
729 | static int __devexit bq20z75_remove(struct i2c_client *client) | ||
730 | { | ||
731 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | ||
732 | |||
733 | if (bq20z75_device->irq) | ||
734 | free_irq(bq20z75_device->irq, &bq20z75_device->power_supply); | ||
735 | if (bq20z75_device->gpio_detect) | ||
736 | gpio_free(bq20z75_device->plat_data.battery_detect); | ||
737 | |||
738 | power_supply_unregister(&bq20z75_device->power_supply); | ||
739 | |||
740 | cancel_delayed_work_sync(&bq20z75_device->work); | ||
741 | |||
742 | kfree(bq20z75_device); | ||
743 | bq20z75_device = NULL; | ||
744 | |||
745 | return 0; | ||
746 | } | ||
747 | |||
748 | #if defined CONFIG_PM | ||
749 | static int bq20z75_suspend(struct i2c_client *client, | ||
750 | pm_message_t state) | ||
751 | { | ||
752 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | ||
753 | s32 ret; | ||
754 | |||
755 | if (bq20z75_device->poll_time > 0) | ||
756 | cancel_delayed_work_sync(&bq20z75_device->work); | ||
757 | |||
758 | /* write to manufacturer access with sleep command */ | ||
759 | ret = bq20z75_write_word_data(client, | ||
760 | bq20z75_data[REG_MANUFACTURER_DATA].addr, | ||
761 | MANUFACTURER_ACCESS_SLEEP); | ||
762 | if (bq20z75_device->is_present && ret < 0) | ||
763 | return ret; | ||
764 | |||
765 | return 0; | ||
766 | } | ||
767 | |||
768 | static int bq20z75_resume(struct i2c_client *client) | ||
769 | { | ||
770 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | ||
771 | |||
772 | schedule_delayed_work(&bq20z75_device->work, HZ); | ||
773 | return 0; | ||
774 | } | ||
775 | #else | ||
776 | #define bq20z75_suspend NULL | ||
777 | #define bq20z75_resume NULL | ||
778 | #endif | ||
779 | |||
780 | static const struct i2c_device_id bq20z75_id[] = { | ||
781 | { "bq20z75", 0 }, | ||
782 | {} | ||
783 | }; | ||
784 | MODULE_DEVICE_TABLE(i2c, bq20z75_id); | ||
785 | |||
786 | static struct i2c_driver bq20z75_battery_driver = { | ||
787 | .probe = bq20z75_probe, | ||
788 | .remove = __devexit_p(bq20z75_remove), | ||
789 | .suspend = bq20z75_suspend, | ||
790 | .resume = bq20z75_resume, | ||
791 | .id_table = bq20z75_id, | ||
792 | .driver = { | ||
793 | .name = "bq20z75-battery", | ||
794 | }, | ||
795 | }; | ||
796 | |||
797 | static int __init bq20z75_battery_init(void) | ||
798 | { | ||
799 | return i2c_add_driver(&bq20z75_battery_driver); | ||
800 | } | ||
801 | module_init(bq20z75_battery_init); | ||
802 | |||
803 | static void __exit bq20z75_battery_exit(void) | ||
804 | { | ||
805 | i2c_del_driver(&bq20z75_battery_driver); | ||
806 | } | ||
807 | module_exit(bq20z75_battery_exit); | ||
808 | |||
809 | MODULE_DESCRIPTION("BQ20z75 battery monitor driver"); | ||
810 | MODULE_LICENSE("GPL"); | ||