diff options
Diffstat (limited to 'drivers/power/bq20z75.c')
-rw-r--r-- | drivers/power/bq20z75.c | 310 |
1 files changed, 263 insertions, 47 deletions
diff --git a/drivers/power/bq20z75.c b/drivers/power/bq20z75.c index 492da27e1a47..506585e31a5b 100644 --- a/drivers/power/bq20z75.c +++ b/drivers/power/bq20z75.c | |||
@@ -25,6 +25,10 @@ | |||
25 | #include <linux/power_supply.h> | 25 | #include <linux/power_supply.h> |
26 | #include <linux/i2c.h> | 26 | #include <linux/i2c.h> |
27 | #include <linux/slab.h> | 27 | #include <linux/slab.h> |
28 | #include <linux/interrupt.h> | ||
29 | #include <linux/gpio.h> | ||
30 | |||
31 | #include <linux/power/bq20z75.h> | ||
28 | 32 | ||
29 | enum { | 33 | enum { |
30 | REG_MANUFACTURER_DATA, | 34 | REG_MANUFACTURER_DATA, |
@@ -38,11 +42,22 @@ enum { | |||
38 | REG_CYCLE_COUNT, | 42 | REG_CYCLE_COUNT, |
39 | REG_SERIAL_NUMBER, | 43 | REG_SERIAL_NUMBER, |
40 | REG_REMAINING_CAPACITY, | 44 | REG_REMAINING_CAPACITY, |
45 | REG_REMAINING_CAPACITY_CHARGE, | ||
41 | REG_FULL_CHARGE_CAPACITY, | 46 | REG_FULL_CHARGE_CAPACITY, |
47 | REG_FULL_CHARGE_CAPACITY_CHARGE, | ||
42 | REG_DESIGN_CAPACITY, | 48 | REG_DESIGN_CAPACITY, |
49 | REG_DESIGN_CAPACITY_CHARGE, | ||
43 | REG_DESIGN_VOLTAGE, | 50 | REG_DESIGN_VOLTAGE, |
44 | }; | 51 | }; |
45 | 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 | |||
46 | /* manufacturer access defines */ | 61 | /* manufacturer access defines */ |
47 | #define MANUFACTURER_ACCESS_STATUS 0x0006 | 62 | #define MANUFACTURER_ACCESS_STATUS 0x0006 |
48 | #define MANUFACTURER_ACCESS_SLEEP 0x0011 | 63 | #define MANUFACTURER_ACCESS_SLEEP 0x0011 |
@@ -78,8 +93,12 @@ static const struct bq20z75_device_data { | |||
78 | BQ20Z75_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0E, 0, 100), | 93 | BQ20Z75_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0E, 0, 100), |
79 | [REG_REMAINING_CAPACITY] = | 94 | [REG_REMAINING_CAPACITY] = |
80 | BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_NOW, 0x0F, 0, 65535), | 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), | ||
81 | [REG_FULL_CHARGE_CAPACITY] = | 98 | [REG_FULL_CHARGE_CAPACITY] = |
82 | BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL, 0x10, 0, 65535), | 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), | ||
83 | [REG_TIME_TO_EMPTY] = | 102 | [REG_TIME_TO_EMPTY] = |
84 | BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0, | 103 | BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0, |
85 | 65535), | 104 | 65535), |
@@ -93,6 +112,9 @@ static const struct bq20z75_device_data { | |||
93 | [REG_DESIGN_CAPACITY] = | 112 | [REG_DESIGN_CAPACITY] = |
94 | BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 0x18, 0, | 113 | BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 0x18, 0, |
95 | 65535), | 114 | 65535), |
115 | [REG_DESIGN_CAPACITY_CHARGE] = | ||
116 | BQ20Z75_DATA(POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 0x18, 0, | ||
117 | 65535), | ||
96 | [REG_DESIGN_VOLTAGE] = | 118 | [REG_DESIGN_VOLTAGE] = |
97 | BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 0x19, 0, | 119 | BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 0x19, 0, |
98 | 65535), | 120 | 65535), |
@@ -117,39 +139,72 @@ static enum power_supply_property bq20z75_properties[] = { | |||
117 | POWER_SUPPLY_PROP_ENERGY_NOW, | 139 | POWER_SUPPLY_PROP_ENERGY_NOW, |
118 | POWER_SUPPLY_PROP_ENERGY_FULL, | 140 | POWER_SUPPLY_PROP_ENERGY_FULL, |
119 | POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, | 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, | ||
120 | }; | 145 | }; |
121 | 146 | ||
122 | struct bq20z75_info { | 147 | struct bq20z75_info { |
123 | struct i2c_client *client; | 148 | struct i2c_client *client; |
124 | struct power_supply power_supply; | 149 | struct power_supply power_supply; |
150 | struct bq20z75_platform_data *pdata; | ||
151 | bool is_present; | ||
152 | bool gpio_detect; | ||
153 | bool enable_detection; | ||
154 | int irq; | ||
125 | }; | 155 | }; |
126 | 156 | ||
127 | static int bq20z75_read_word_data(struct i2c_client *client, u8 address) | 157 | static int bq20z75_read_word_data(struct i2c_client *client, u8 address) |
128 | { | 158 | { |
129 | s32 ret; | 159 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); |
160 | s32 ret = 0; | ||
161 | int retries = 1; | ||
162 | |||
163 | if (bq20z75_device->pdata) | ||
164 | retries = max(bq20z75_device->pdata->i2c_retry_count + 1, 1); | ||
165 | |||
166 | while (retries > 0) { | ||
167 | ret = i2c_smbus_read_word_data(client, address); | ||
168 | if (ret >= 0) | ||
169 | break; | ||
170 | retries--; | ||
171 | } | ||
130 | 172 | ||
131 | ret = i2c_smbus_read_word_data(client, address); | ||
132 | if (ret < 0) { | 173 | if (ret < 0) { |
133 | dev_err(&client->dev, | 174 | dev_dbg(&client->dev, |
134 | "%s: i2c read at address 0x%x failed\n", | 175 | "%s: i2c read at address 0x%x failed\n", |
135 | __func__, address); | 176 | __func__, address); |
136 | return ret; | 177 | return ret; |
137 | } | 178 | } |
179 | |||
138 | return le16_to_cpu(ret); | 180 | return le16_to_cpu(ret); |
139 | } | 181 | } |
140 | 182 | ||
141 | static int bq20z75_write_word_data(struct i2c_client *client, u8 address, | 183 | static int bq20z75_write_word_data(struct i2c_client *client, u8 address, |
142 | u16 value) | 184 | u16 value) |
143 | { | 185 | { |
144 | s32 ret; | 186 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); |
187 | s32 ret = 0; | ||
188 | int retries = 1; | ||
189 | |||
190 | if (bq20z75_device->pdata) | ||
191 | retries = max(bq20z75_device->pdata->i2c_retry_count + 1, 1); | ||
192 | |||
193 | while (retries > 0) { | ||
194 | ret = i2c_smbus_write_word_data(client, address, | ||
195 | le16_to_cpu(value)); | ||
196 | if (ret >= 0) | ||
197 | break; | ||
198 | retries--; | ||
199 | } | ||
145 | 200 | ||
146 | ret = i2c_smbus_write_word_data(client, address, le16_to_cpu(value)); | ||
147 | if (ret < 0) { | 201 | if (ret < 0) { |
148 | dev_err(&client->dev, | 202 | dev_dbg(&client->dev, |
149 | "%s: i2c write to address 0x%x failed\n", | 203 | "%s: i2c write to address 0x%x failed\n", |
150 | __func__, address); | 204 | __func__, address); |
151 | return ret; | 205 | return ret; |
152 | } | 206 | } |
207 | |||
153 | return 0; | 208 | return 0; |
154 | } | 209 | } |
155 | 210 | ||
@@ -158,6 +213,19 @@ static int bq20z75_get_battery_presence_and_health( | |||
158 | union power_supply_propval *val) | 213 | union power_supply_propval *val) |
159 | { | 214 | { |
160 | s32 ret; | 215 | s32 ret; |
216 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | ||
217 | |||
218 | if (psp == POWER_SUPPLY_PROP_PRESENT && | ||
219 | bq20z75_device->gpio_detect) { | ||
220 | ret = gpio_get_value( | ||
221 | bq20z75_device->pdata->battery_detect); | ||
222 | if (ret == bq20z75_device->pdata->battery_detect_present) | ||
223 | val->intval = 1; | ||
224 | else | ||
225 | val->intval = 0; | ||
226 | bq20z75_device->is_present = val->intval; | ||
227 | return ret; | ||
228 | } | ||
161 | 229 | ||
162 | /* Write to ManufacturerAccess with | 230 | /* Write to ManufacturerAccess with |
163 | * ManufacturerAccess command and then | 231 | * ManufacturerAccess command and then |
@@ -165,9 +233,11 @@ static int bq20z75_get_battery_presence_and_health( | |||
165 | ret = bq20z75_write_word_data(client, | 233 | ret = bq20z75_write_word_data(client, |
166 | bq20z75_data[REG_MANUFACTURER_DATA].addr, | 234 | bq20z75_data[REG_MANUFACTURER_DATA].addr, |
167 | MANUFACTURER_ACCESS_STATUS); | 235 | MANUFACTURER_ACCESS_STATUS); |
168 | if (ret < 0) | 236 | if (ret < 0) { |
237 | if (psp == POWER_SUPPLY_PROP_PRESENT) | ||
238 | val->intval = 0; /* battery removed */ | ||
169 | return ret; | 239 | return ret; |
170 | 240 | } | |
171 | 241 | ||
172 | ret = bq20z75_read_word_data(client, | 242 | ret = bq20z75_read_word_data(client, |
173 | bq20z75_data[REG_MANUFACTURER_DATA].addr); | 243 | bq20z75_data[REG_MANUFACTURER_DATA].addr); |
@@ -248,30 +318,39 @@ static void bq20z75_unit_adjustment(struct i2c_client *client, | |||
248 | { | 318 | { |
249 | #define BASE_UNIT_CONVERSION 1000 | 319 | #define BASE_UNIT_CONVERSION 1000 |
250 | #define BATTERY_MODE_CAP_MULT_WATT (10 * BASE_UNIT_CONVERSION) | 320 | #define BATTERY_MODE_CAP_MULT_WATT (10 * BASE_UNIT_CONVERSION) |
251 | #define TIME_UNIT_CONVERSION 600 | 321 | #define TIME_UNIT_CONVERSION 60 |
252 | #define TEMP_KELVIN_TO_CELCIUS 2731 | 322 | #define TEMP_KELVIN_TO_CELSIUS 2731 |
253 | switch (psp) { | 323 | switch (psp) { |
254 | case POWER_SUPPLY_PROP_ENERGY_NOW: | 324 | case POWER_SUPPLY_PROP_ENERGY_NOW: |
255 | case POWER_SUPPLY_PROP_ENERGY_FULL: | 325 | case POWER_SUPPLY_PROP_ENERGY_FULL: |
256 | case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: | 326 | case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: |
327 | /* bq20z75 provides energy in units of 10mWh. | ||
328 | * Convert to µWh | ||
329 | */ | ||
257 | val->intval *= BATTERY_MODE_CAP_MULT_WATT; | 330 | val->intval *= BATTERY_MODE_CAP_MULT_WATT; |
258 | break; | 331 | break; |
259 | 332 | ||
260 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | 333 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
261 | case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: | 334 | case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: |
262 | case POWER_SUPPLY_PROP_CURRENT_NOW: | 335 | case POWER_SUPPLY_PROP_CURRENT_NOW: |
336 | case POWER_SUPPLY_PROP_CHARGE_NOW: | ||
337 | case POWER_SUPPLY_PROP_CHARGE_FULL: | ||
338 | case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: | ||
263 | val->intval *= BASE_UNIT_CONVERSION; | 339 | val->intval *= BASE_UNIT_CONVERSION; |
264 | break; | 340 | break; |
265 | 341 | ||
266 | case POWER_SUPPLY_PROP_TEMP: | 342 | case POWER_SUPPLY_PROP_TEMP: |
267 | /* bq20z75 provides battery tempreture in 0.1°K | 343 | /* bq20z75 provides battery temperature in 0.1K |
268 | * so convert it to 0.1°C */ | 344 | * so convert it to 0.1°C |
269 | val->intval -= TEMP_KELVIN_TO_CELCIUS; | 345 | */ |
270 | val->intval *= 10; | 346 | val->intval -= TEMP_KELVIN_TO_CELSIUS; |
271 | break; | 347 | break; |
272 | 348 | ||
273 | case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: | 349 | case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: |
274 | case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: | 350 | case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: |
351 | /* bq20z75 provides time to empty and time to full in minutes. | ||
352 | * Convert to seconds | ||
353 | */ | ||
275 | val->intval *= TIME_UNIT_CONVERSION; | 354 | val->intval *= TIME_UNIT_CONVERSION; |
276 | break; | 355 | break; |
277 | 356 | ||
@@ -281,11 +360,44 @@ static void bq20z75_unit_adjustment(struct i2c_client *client, | |||
281 | } | 360 | } |
282 | } | 361 | } |
283 | 362 | ||
363 | static enum bq20z75_battery_mode | ||
364 | bq20z75_set_battery_mode(struct i2c_client *client, | ||
365 | enum bq20z75_battery_mode mode) | ||
366 | { | ||
367 | int ret, original_val; | ||
368 | |||
369 | original_val = bq20z75_read_word_data(client, BATTERY_MODE_OFFSET); | ||
370 | if (original_val < 0) | ||
371 | return original_val; | ||
372 | |||
373 | if ((original_val & BATTERY_MODE_MASK) == mode) | ||
374 | return mode; | ||
375 | |||
376 | if (mode == BATTERY_MODE_AMPS) | ||
377 | ret = original_val & ~BATTERY_MODE_MASK; | ||
378 | else | ||
379 | ret = original_val | BATTERY_MODE_MASK; | ||
380 | |||
381 | ret = bq20z75_write_word_data(client, BATTERY_MODE_OFFSET, ret); | ||
382 | if (ret < 0) | ||
383 | return ret; | ||
384 | |||
385 | return original_val & BATTERY_MODE_MASK; | ||
386 | } | ||
387 | |||
284 | static int bq20z75_get_battery_capacity(struct i2c_client *client, | 388 | static int bq20z75_get_battery_capacity(struct i2c_client *client, |
285 | int reg_offset, enum power_supply_property psp, | 389 | int reg_offset, enum power_supply_property psp, |
286 | union power_supply_propval *val) | 390 | union power_supply_propval *val) |
287 | { | 391 | { |
288 | s32 ret; | 392 | s32 ret; |
393 | enum bq20z75_battery_mode mode = BATTERY_MODE_WATTS; | ||
394 | |||
395 | if (power_supply_is_amp_property(psp)) | ||
396 | mode = BATTERY_MODE_AMPS; | ||
397 | |||
398 | mode = bq20z75_set_battery_mode(client, mode); | ||
399 | if (mode < 0) | ||
400 | return mode; | ||
289 | 401 | ||
290 | ret = bq20z75_read_word_data(client, bq20z75_data[reg_offset].addr); | 402 | ret = bq20z75_read_word_data(client, bq20z75_data[reg_offset].addr); |
291 | if (ret < 0) | 403 | if (ret < 0) |
@@ -298,6 +410,10 @@ static int bq20z75_get_battery_capacity(struct i2c_client *client, | |||
298 | } else | 410 | } else |
299 | val->intval = ret; | 411 | val->intval = ret; |
300 | 412 | ||
413 | ret = bq20z75_set_battery_mode(client, mode); | ||
414 | if (ret < 0) | ||
415 | return ret; | ||
416 | |||
301 | return 0; | 417 | return 0; |
302 | } | 418 | } |
303 | 419 | ||
@@ -318,12 +434,25 @@ static int bq20z75_get_battery_serial_number(struct i2c_client *client, | |||
318 | return 0; | 434 | return 0; |
319 | } | 435 | } |
320 | 436 | ||
437 | static int bq20z75_get_property_index(struct i2c_client *client, | ||
438 | enum power_supply_property psp) | ||
439 | { | ||
440 | int count; | ||
441 | for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) | ||
442 | if (psp == bq20z75_data[count].psp) | ||
443 | return count; | ||
444 | |||
445 | dev_warn(&client->dev, | ||
446 | "%s: Invalid Property - %d\n", __func__, psp); | ||
447 | |||
448 | return -EINVAL; | ||
449 | } | ||
450 | |||
321 | static int bq20z75_get_property(struct power_supply *psy, | 451 | static int bq20z75_get_property(struct power_supply *psy, |
322 | enum power_supply_property psp, | 452 | enum power_supply_property psp, |
323 | union power_supply_propval *val) | 453 | union power_supply_propval *val) |
324 | { | 454 | { |
325 | int count; | 455 | int ret = 0; |
326 | int ret; | ||
327 | struct bq20z75_info *bq20z75_device = container_of(psy, | 456 | struct bq20z75_info *bq20z75_device = container_of(psy, |
328 | struct bq20z75_info, power_supply); | 457 | struct bq20z75_info, power_supply); |
329 | struct i2c_client *client = bq20z75_device->client; | 458 | struct i2c_client *client = bq20z75_device->client; |
@@ -332,8 +461,8 @@ static int bq20z75_get_property(struct power_supply *psy, | |||
332 | case POWER_SUPPLY_PROP_PRESENT: | 461 | case POWER_SUPPLY_PROP_PRESENT: |
333 | case POWER_SUPPLY_PROP_HEALTH: | 462 | case POWER_SUPPLY_PROP_HEALTH: |
334 | ret = bq20z75_get_battery_presence_and_health(client, psp, val); | 463 | ret = bq20z75_get_battery_presence_and_health(client, psp, val); |
335 | if (ret) | 464 | if (psp == POWER_SUPPLY_PROP_PRESENT) |
336 | return ret; | 465 | return 0; |
337 | break; | 466 | break; |
338 | 467 | ||
339 | case POWER_SUPPLY_PROP_TECHNOLOGY: | 468 | case POWER_SUPPLY_PROP_TECHNOLOGY: |
@@ -343,22 +472,19 @@ static int bq20z75_get_property(struct power_supply *psy, | |||
343 | case POWER_SUPPLY_PROP_ENERGY_NOW: | 472 | case POWER_SUPPLY_PROP_ENERGY_NOW: |
344 | case POWER_SUPPLY_PROP_ENERGY_FULL: | 473 | case POWER_SUPPLY_PROP_ENERGY_FULL: |
345 | case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: | 474 | case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: |
475 | case POWER_SUPPLY_PROP_CHARGE_NOW: | ||
476 | case POWER_SUPPLY_PROP_CHARGE_FULL: | ||
477 | case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: | ||
346 | case POWER_SUPPLY_PROP_CAPACITY: | 478 | case POWER_SUPPLY_PROP_CAPACITY: |
347 | for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) { | 479 | ret = bq20z75_get_property_index(client, psp); |
348 | if (psp == bq20z75_data[count].psp) | 480 | if (ret < 0) |
349 | break; | 481 | break; |
350 | } | ||
351 | |||
352 | ret = bq20z75_get_battery_capacity(client, count, psp, val); | ||
353 | if (ret) | ||
354 | return ret; | ||
355 | 482 | ||
483 | ret = bq20z75_get_battery_capacity(client, ret, psp, val); | ||
356 | break; | 484 | break; |
357 | 485 | ||
358 | case POWER_SUPPLY_PROP_SERIAL_NUMBER: | 486 | case POWER_SUPPLY_PROP_SERIAL_NUMBER: |
359 | ret = bq20z75_get_battery_serial_number(client, val); | 487 | ret = bq20z75_get_battery_serial_number(client, val); |
360 | if (ret) | ||
361 | return ret; | ||
362 | break; | 488 | break; |
363 | 489 | ||
364 | case POWER_SUPPLY_PROP_STATUS: | 490 | case POWER_SUPPLY_PROP_STATUS: |
@@ -369,15 +495,11 @@ static int bq20z75_get_property(struct power_supply *psy, | |||
369 | case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: | 495 | case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: |
370 | case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: | 496 | case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: |
371 | case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: | 497 | case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: |
372 | for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) { | 498 | ret = bq20z75_get_property_index(client, psp); |
373 | if (psp == bq20z75_data[count].psp) | 499 | if (ret < 0) |
374 | break; | 500 | break; |
375 | } | ||
376 | |||
377 | ret = bq20z75_get_battery_property(client, count, psp, val); | ||
378 | if (ret) | ||
379 | return ret; | ||
380 | 501 | ||
502 | ret = bq20z75_get_battery_property(client, ret, psp, val); | ||
381 | break; | 503 | break; |
382 | 504 | ||
383 | default: | 505 | default: |
@@ -386,26 +508,58 @@ static int bq20z75_get_property(struct power_supply *psy, | |||
386 | return -EINVAL; | 508 | return -EINVAL; |
387 | } | 509 | } |
388 | 510 | ||
389 | /* Convert units to match requirements for power supply class */ | 511 | if (!bq20z75_device->enable_detection) |
390 | bq20z75_unit_adjustment(client, psp, val); | 512 | goto done; |
513 | |||
514 | if (!bq20z75_device->gpio_detect && | ||
515 | bq20z75_device->is_present != (ret >= 0)) { | ||
516 | bq20z75_device->is_present = (ret >= 0); | ||
517 | power_supply_changed(&bq20z75_device->power_supply); | ||
518 | } | ||
519 | |||
520 | done: | ||
521 | if (!ret) { | ||
522 | /* Convert units to match requirements for power supply class */ | ||
523 | bq20z75_unit_adjustment(client, psp, val); | ||
524 | } | ||
391 | 525 | ||
392 | dev_dbg(&client->dev, | 526 | dev_dbg(&client->dev, |
393 | "%s: property = %d, value = %d\n", __func__, psp, val->intval); | 527 | "%s: property = %d, value = %x\n", __func__, psp, val->intval); |
528 | |||
529 | if (ret && bq20z75_device->is_present) | ||
530 | return ret; | ||
531 | |||
532 | /* battery not present, so return NODATA for properties */ | ||
533 | if (ret) | ||
534 | return -ENODATA; | ||
394 | 535 | ||
395 | return 0; | 536 | return 0; |
396 | } | 537 | } |
397 | 538 | ||
398 | static int bq20z75_probe(struct i2c_client *client, | 539 | static irqreturn_t bq20z75_irq(int irq, void *devid) |
540 | { | ||
541 | struct power_supply *battery = devid; | ||
542 | |||
543 | power_supply_changed(battery); | ||
544 | |||
545 | return IRQ_HANDLED; | ||
546 | } | ||
547 | |||
548 | static int __devinit bq20z75_probe(struct i2c_client *client, | ||
399 | const struct i2c_device_id *id) | 549 | const struct i2c_device_id *id) |
400 | { | 550 | { |
401 | struct bq20z75_info *bq20z75_device; | 551 | struct bq20z75_info *bq20z75_device; |
552 | struct bq20z75_platform_data *pdata = client->dev.platform_data; | ||
402 | int rc; | 553 | int rc; |
554 | int irq; | ||
403 | 555 | ||
404 | bq20z75_device = kzalloc(sizeof(struct bq20z75_info), GFP_KERNEL); | 556 | bq20z75_device = kzalloc(sizeof(struct bq20z75_info), GFP_KERNEL); |
405 | if (!bq20z75_device) | 557 | if (!bq20z75_device) |
406 | return -ENOMEM; | 558 | return -ENOMEM; |
407 | 559 | ||
408 | bq20z75_device->client = client; | 560 | bq20z75_device->client = client; |
561 | bq20z75_device->enable_detection = false; | ||
562 | bq20z75_device->gpio_detect = false; | ||
409 | bq20z75_device->power_supply.name = "battery"; | 563 | bq20z75_device->power_supply.name = "battery"; |
410 | bq20z75_device->power_supply.type = POWER_SUPPLY_TYPE_BATTERY; | 564 | bq20z75_device->power_supply.type = POWER_SUPPLY_TYPE_BATTERY; |
411 | bq20z75_device->power_supply.properties = bq20z75_properties; | 565 | bq20z75_device->power_supply.properties = bq20z75_properties; |
@@ -413,26 +567,86 @@ static int bq20z75_probe(struct i2c_client *client, | |||
413 | ARRAY_SIZE(bq20z75_properties); | 567 | ARRAY_SIZE(bq20z75_properties); |
414 | bq20z75_device->power_supply.get_property = bq20z75_get_property; | 568 | bq20z75_device->power_supply.get_property = bq20z75_get_property; |
415 | 569 | ||
570 | if (pdata) { | ||
571 | bq20z75_device->gpio_detect = | ||
572 | gpio_is_valid(pdata->battery_detect); | ||
573 | bq20z75_device->pdata = pdata; | ||
574 | } | ||
575 | |||
416 | i2c_set_clientdata(client, bq20z75_device); | 576 | i2c_set_clientdata(client, bq20z75_device); |
417 | 577 | ||
578 | if (!bq20z75_device->gpio_detect) | ||
579 | goto skip_gpio; | ||
580 | |||
581 | rc = gpio_request(pdata->battery_detect, dev_name(&client->dev)); | ||
582 | if (rc) { | ||
583 | dev_warn(&client->dev, "Failed to request gpio: %d\n", rc); | ||
584 | bq20z75_device->gpio_detect = false; | ||
585 | goto skip_gpio; | ||
586 | } | ||
587 | |||
588 | rc = gpio_direction_input(pdata->battery_detect); | ||
589 | if (rc) { | ||
590 | dev_warn(&client->dev, "Failed to get gpio as input: %d\n", rc); | ||
591 | gpio_free(pdata->battery_detect); | ||
592 | bq20z75_device->gpio_detect = false; | ||
593 | goto skip_gpio; | ||
594 | } | ||
595 | |||
596 | irq = gpio_to_irq(pdata->battery_detect); | ||
597 | if (irq <= 0) { | ||
598 | dev_warn(&client->dev, "Failed to get gpio as irq: %d\n", irq); | ||
599 | gpio_free(pdata->battery_detect); | ||
600 | bq20z75_device->gpio_detect = false; | ||
601 | goto skip_gpio; | ||
602 | } | ||
603 | |||
604 | rc = request_irq(irq, bq20z75_irq, | ||
605 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, | ||
606 | dev_name(&client->dev), &bq20z75_device->power_supply); | ||
607 | if (rc) { | ||
608 | dev_warn(&client->dev, "Failed to request irq: %d\n", rc); | ||
609 | gpio_free(pdata->battery_detect); | ||
610 | bq20z75_device->gpio_detect = false; | ||
611 | goto skip_gpio; | ||
612 | } | ||
613 | |||
614 | bq20z75_device->irq = irq; | ||
615 | |||
616 | skip_gpio: | ||
617 | |||
418 | rc = power_supply_register(&client->dev, &bq20z75_device->power_supply); | 618 | rc = power_supply_register(&client->dev, &bq20z75_device->power_supply); |
419 | if (rc) { | 619 | if (rc) { |
420 | dev_err(&client->dev, | 620 | dev_err(&client->dev, |
421 | "%s: Failed to register power supply\n", __func__); | 621 | "%s: Failed to register power supply\n", __func__); |
422 | kfree(bq20z75_device); | 622 | goto exit_psupply; |
423 | return rc; | ||
424 | } | 623 | } |
425 | 624 | ||
426 | dev_info(&client->dev, | 625 | dev_info(&client->dev, |
427 | "%s: battery gas gauge device registered\n", client->name); | 626 | "%s: battery gas gauge device registered\n", client->name); |
428 | 627 | ||
429 | return 0; | 628 | return 0; |
629 | |||
630 | exit_psupply: | ||
631 | if (bq20z75_device->irq) | ||
632 | free_irq(bq20z75_device->irq, &bq20z75_device->power_supply); | ||
633 | if (bq20z75_device->gpio_detect) | ||
634 | gpio_free(pdata->battery_detect); | ||
635 | |||
636 | kfree(bq20z75_device); | ||
637 | |||
638 | return rc; | ||
430 | } | 639 | } |
431 | 640 | ||
432 | static int bq20z75_remove(struct i2c_client *client) | 641 | static int __devexit bq20z75_remove(struct i2c_client *client) |
433 | { | 642 | { |
434 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | 643 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); |
435 | 644 | ||
645 | if (bq20z75_device->irq) | ||
646 | free_irq(bq20z75_device->irq, &bq20z75_device->power_supply); | ||
647 | if (bq20z75_device->gpio_detect) | ||
648 | gpio_free(bq20z75_device->pdata->battery_detect); | ||
649 | |||
436 | power_supply_unregister(&bq20z75_device->power_supply); | 650 | power_supply_unregister(&bq20z75_device->power_supply); |
437 | kfree(bq20z75_device); | 651 | kfree(bq20z75_device); |
438 | bq20z75_device = NULL; | 652 | bq20z75_device = NULL; |
@@ -444,13 +658,14 @@ static int bq20z75_remove(struct i2c_client *client) | |||
444 | static int bq20z75_suspend(struct i2c_client *client, | 658 | static int bq20z75_suspend(struct i2c_client *client, |
445 | pm_message_t state) | 659 | pm_message_t state) |
446 | { | 660 | { |
661 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | ||
447 | s32 ret; | 662 | s32 ret; |
448 | 663 | ||
449 | /* write to manufacturer access with sleep command */ | 664 | /* write to manufacturer access with sleep command */ |
450 | ret = bq20z75_write_word_data(client, | 665 | ret = bq20z75_write_word_data(client, |
451 | bq20z75_data[REG_MANUFACTURER_DATA].addr, | 666 | bq20z75_data[REG_MANUFACTURER_DATA].addr, |
452 | MANUFACTURER_ACCESS_SLEEP); | 667 | MANUFACTURER_ACCESS_SLEEP); |
453 | if (ret < 0) | 668 | if (bq20z75_device->is_present && ret < 0) |
454 | return ret; | 669 | return ret; |
455 | 670 | ||
456 | return 0; | 671 | return 0; |
@@ -465,10 +680,11 @@ static const struct i2c_device_id bq20z75_id[] = { | |||
465 | { "bq20z75", 0 }, | 680 | { "bq20z75", 0 }, |
466 | {} | 681 | {} |
467 | }; | 682 | }; |
683 | MODULE_DEVICE_TABLE(i2c, bq20z75_id); | ||
468 | 684 | ||
469 | static struct i2c_driver bq20z75_battery_driver = { | 685 | static struct i2c_driver bq20z75_battery_driver = { |
470 | .probe = bq20z75_probe, | 686 | .probe = bq20z75_probe, |
471 | .remove = bq20z75_remove, | 687 | .remove = __devexit_p(bq20z75_remove), |
472 | .suspend = bq20z75_suspend, | 688 | .suspend = bq20z75_suspend, |
473 | .resume = bq20z75_resume, | 689 | .resume = bq20z75_resume, |
474 | .id_table = bq20z75_id, | 690 | .id_table = bq20z75_id, |