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