diff options
author | Rhyland Klein <rklein@nvidia.com> | 2011-02-28 19:55:28 -0500 |
---|---|---|
committer | Anton Vorontsov <cbouatmailru@gmail.com> | 2011-03-01 14:24:01 -0500 |
commit | bb879101606dd7235d8f4ecd0f707b63281d0838 (patch) | |
tree | 74aae7149b888a1e54f23608314f795ee992d47f /drivers/power/bq20z75.c | |
parent | b5db7cde69f87178fb771970a2b512a452a9d680 (diff) |
bq20z75: Add optional battery detect gpio
Adding support for an optional gpio for battery detection. This is
passed in through the i2c platform data. It also accepts another
field, battery_detect_present to signify the gpio state which means
the battery is present, either 0 (low) or 1 (high).
Signed-off-by: Rhyland Klein <rklein@nvidia.com>
Signed-off-by: Anton Vorontsov <cbouatmailru@gmail.com>
Diffstat (limited to 'drivers/power/bq20z75.c')
-rw-r--r-- | drivers/power/bq20z75.c | 160 |
1 files changed, 129 insertions, 31 deletions
diff --git a/drivers/power/bq20z75.c b/drivers/power/bq20z75.c index 4141775e5ff6..a51e98d74d34 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, |
@@ -141,8 +145,13 @@ static enum power_supply_property bq20z75_properties[] = { | |||
141 | }; | 145 | }; |
142 | 146 | ||
143 | struct bq20z75_info { | 147 | struct bq20z75_info { |
144 | struct i2c_client *client; | 148 | struct i2c_client *client; |
145 | 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; | ||
146 | }; | 155 | }; |
147 | 156 | ||
148 | 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) |
@@ -179,6 +188,18 @@ static int bq20z75_get_battery_presence_and_health( | |||
179 | union power_supply_propval *val) | 188 | union power_supply_propval *val) |
180 | { | 189 | { |
181 | s32 ret; | 190 | s32 ret; |
191 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | ||
192 | |||
193 | if (psp == POWER_SUPPLY_PROP_PRESENT && | ||
194 | bq20z75_device->gpio_detect) { | ||
195 | ret = gpio_get_value( | ||
196 | bq20z75_device->pdata->battery_detect); | ||
197 | if (ret == bq20z75_device->pdata->battery_detect_present) | ||
198 | val->intval = 1; | ||
199 | else | ||
200 | val->intval = 0; | ||
201 | return ret; | ||
202 | } | ||
182 | 203 | ||
183 | /* Write to ManufacturerAccess with | 204 | /* Write to ManufacturerAccess with |
184 | * ManufacturerAccess command and then | 205 | * ManufacturerAccess command and then |
@@ -192,8 +213,11 @@ static int bq20z75_get_battery_presence_and_health( | |||
192 | 213 | ||
193 | ret = bq20z75_read_word_data(client, | 214 | ret = bq20z75_read_word_data(client, |
194 | bq20z75_data[REG_MANUFACTURER_DATA].addr); | 215 | bq20z75_data[REG_MANUFACTURER_DATA].addr); |
195 | if (ret < 0) | 216 | if (ret < 0) { |
217 | if (psp == POWER_SUPPLY_PROP_PRESENT) | ||
218 | val->intval = 0; /* battery removed */ | ||
196 | return ret; | 219 | return ret; |
220 | } | ||
197 | 221 | ||
198 | if (ret < bq20z75_data[REG_MANUFACTURER_DATA].min_value || | 222 | if (ret < bq20z75_data[REG_MANUFACTURER_DATA].min_value || |
199 | ret > bq20z75_data[REG_MANUFACTURER_DATA].max_value) { | 223 | ret > bq20z75_data[REG_MANUFACTURER_DATA].max_value) { |
@@ -397,8 +421,7 @@ static int bq20z75_get_property(struct power_supply *psy, | |||
397 | enum power_supply_property psp, | 421 | enum power_supply_property psp, |
398 | union power_supply_propval *val) | 422 | union power_supply_propval *val) |
399 | { | 423 | { |
400 | int ps_index; | 424 | int ret = 0; |
401 | int ret; | ||
402 | struct bq20z75_info *bq20z75_device = container_of(psy, | 425 | struct bq20z75_info *bq20z75_device = container_of(psy, |
403 | struct bq20z75_info, power_supply); | 426 | struct bq20z75_info, power_supply); |
404 | struct i2c_client *client = bq20z75_device->client; | 427 | struct i2c_client *client = bq20z75_device->client; |
@@ -407,8 +430,6 @@ static int bq20z75_get_property(struct power_supply *psy, | |||
407 | case POWER_SUPPLY_PROP_PRESENT: | 430 | case POWER_SUPPLY_PROP_PRESENT: |
408 | case POWER_SUPPLY_PROP_HEALTH: | 431 | case POWER_SUPPLY_PROP_HEALTH: |
409 | ret = bq20z75_get_battery_presence_and_health(client, psp, val); | 432 | ret = bq20z75_get_battery_presence_and_health(client, psp, val); |
410 | if (ret) | ||
411 | return ret; | ||
412 | break; | 433 | break; |
413 | 434 | ||
414 | case POWER_SUPPLY_PROP_TECHNOLOGY: | 435 | case POWER_SUPPLY_PROP_TECHNOLOGY: |
@@ -422,20 +443,15 @@ static int bq20z75_get_property(struct power_supply *psy, | |||
422 | case POWER_SUPPLY_PROP_CHARGE_FULL: | 443 | case POWER_SUPPLY_PROP_CHARGE_FULL: |
423 | case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: | 444 | case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: |
424 | case POWER_SUPPLY_PROP_CAPACITY: | 445 | case POWER_SUPPLY_PROP_CAPACITY: |
425 | ps_index = bq20z75_get_property_index(client, psp); | 446 | ret = bq20z75_get_property_index(client, psp); |
426 | if (ps_index < 0) | 447 | if (ret < 0) |
427 | return ps_index; | 448 | break; |
428 | |||
429 | ret = bq20z75_get_battery_capacity(client, ps_index, psp, val); | ||
430 | if (ret) | ||
431 | return ret; | ||
432 | 449 | ||
450 | ret = bq20z75_get_battery_capacity(client, ret, psp, val); | ||
433 | break; | 451 | break; |
434 | 452 | ||
435 | case POWER_SUPPLY_PROP_SERIAL_NUMBER: | 453 | case POWER_SUPPLY_PROP_SERIAL_NUMBER: |
436 | ret = bq20z75_get_battery_serial_number(client, val); | 454 | ret = bq20z75_get_battery_serial_number(client, val); |
437 | if (ret) | ||
438 | return ret; | ||
439 | break; | 455 | break; |
440 | 456 | ||
441 | case POWER_SUPPLY_PROP_STATUS: | 457 | case POWER_SUPPLY_PROP_STATUS: |
@@ -446,14 +462,11 @@ static int bq20z75_get_property(struct power_supply *psy, | |||
446 | case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: | 462 | case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: |
447 | case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: | 463 | case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: |
448 | case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: | 464 | case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: |
449 | ps_index = bq20z75_get_property_index(client, psp); | 465 | ret = bq20z75_get_property_index(client, psp); |
450 | if (ps_index < 0) | 466 | if (ret < 0) |
451 | return ps_index; | 467 | break; |
452 | |||
453 | ret = bq20z75_get_battery_property(client, ps_index, psp, val); | ||
454 | if (ret) | ||
455 | return ret; | ||
456 | 468 | ||
469 | ret = bq20z75_get_battery_property(client, ret, psp, val); | ||
457 | break; | 470 | break; |
458 | 471 | ||
459 | default: | 472 | default: |
@@ -462,26 +475,51 @@ static int bq20z75_get_property(struct power_supply *psy, | |||
462 | return -EINVAL; | 475 | return -EINVAL; |
463 | } | 476 | } |
464 | 477 | ||
465 | /* Convert units to match requirements for power supply class */ | 478 | if (!bq20z75_device->enable_detection) |
466 | bq20z75_unit_adjustment(client, psp, val); | 479 | goto done; |
480 | |||
481 | if (!bq20z75_device->gpio_detect && | ||
482 | bq20z75_device->is_present != (ret >= 0)) { | ||
483 | bq20z75_device->is_present = (ret >= 0); | ||
484 | power_supply_changed(&bq20z75_device->power_supply); | ||
485 | } | ||
486 | |||
487 | done: | ||
488 | if (!ret) { | ||
489 | /* Convert units to match requirements for power supply class */ | ||
490 | bq20z75_unit_adjustment(client, psp, val); | ||
491 | } | ||
467 | 492 | ||
468 | dev_dbg(&client->dev, | 493 | dev_dbg(&client->dev, |
469 | "%s: property = %d, value = %d\n", __func__, psp, val->intval); | 494 | "%s: property = %d, value = %d\n", __func__, psp, val->intval); |
470 | 495 | ||
471 | return 0; | 496 | return ret; |
472 | } | 497 | } |
473 | 498 | ||
474 | static int bq20z75_probe(struct i2c_client *client, | 499 | static irqreturn_t bq20z75_irq(int irq, void *devid) |
500 | { | ||
501 | struct power_supply *battery = devid; | ||
502 | |||
503 | power_supply_changed(battery); | ||
504 | |||
505 | return IRQ_HANDLED; | ||
506 | } | ||
507 | |||
508 | static int __devinit bq20z75_probe(struct i2c_client *client, | ||
475 | const struct i2c_device_id *id) | 509 | const struct i2c_device_id *id) |
476 | { | 510 | { |
477 | struct bq20z75_info *bq20z75_device; | 511 | struct bq20z75_info *bq20z75_device; |
512 | struct bq20z75_platform_data *pdata = client->dev.platform_data; | ||
478 | int rc; | 513 | int rc; |
514 | int irq; | ||
479 | 515 | ||
480 | bq20z75_device = kzalloc(sizeof(struct bq20z75_info), GFP_KERNEL); | 516 | bq20z75_device = kzalloc(sizeof(struct bq20z75_info), GFP_KERNEL); |
481 | if (!bq20z75_device) | 517 | if (!bq20z75_device) |
482 | return -ENOMEM; | 518 | return -ENOMEM; |
483 | 519 | ||
484 | bq20z75_device->client = client; | 520 | bq20z75_device->client = client; |
521 | bq20z75_device->enable_detection = false; | ||
522 | bq20z75_device->gpio_detect = false; | ||
485 | bq20z75_device->power_supply.name = "battery"; | 523 | bq20z75_device->power_supply.name = "battery"; |
486 | bq20z75_device->power_supply.type = POWER_SUPPLY_TYPE_BATTERY; | 524 | bq20z75_device->power_supply.type = POWER_SUPPLY_TYPE_BATTERY; |
487 | bq20z75_device->power_supply.properties = bq20z75_properties; | 525 | bq20z75_device->power_supply.properties = bq20z75_properties; |
@@ -489,26 +527,86 @@ static int bq20z75_probe(struct i2c_client *client, | |||
489 | ARRAY_SIZE(bq20z75_properties); | 527 | ARRAY_SIZE(bq20z75_properties); |
490 | bq20z75_device->power_supply.get_property = bq20z75_get_property; | 528 | bq20z75_device->power_supply.get_property = bq20z75_get_property; |
491 | 529 | ||
530 | if (pdata) { | ||
531 | bq20z75_device->gpio_detect = | ||
532 | gpio_is_valid(pdata->battery_detect); | ||
533 | bq20z75_device->pdata = pdata; | ||
534 | } | ||
535 | |||
492 | i2c_set_clientdata(client, bq20z75_device); | 536 | i2c_set_clientdata(client, bq20z75_device); |
493 | 537 | ||
538 | if (!bq20z75_device->gpio_detect) | ||
539 | goto skip_gpio; | ||
540 | |||
541 | rc = gpio_request(pdata->battery_detect, dev_name(&client->dev)); | ||
542 | if (rc) { | ||
543 | dev_warn(&client->dev, "Failed to request gpio: %d\n", rc); | ||
544 | bq20z75_device->gpio_detect = false; | ||
545 | goto skip_gpio; | ||
546 | } | ||
547 | |||
548 | rc = gpio_direction_input(pdata->battery_detect); | ||
549 | if (rc) { | ||
550 | dev_warn(&client->dev, "Failed to get gpio as input: %d\n", rc); | ||
551 | gpio_free(pdata->battery_detect); | ||
552 | bq20z75_device->gpio_detect = false; | ||
553 | goto skip_gpio; | ||
554 | } | ||
555 | |||
556 | irq = gpio_to_irq(pdata->battery_detect); | ||
557 | if (irq <= 0) { | ||
558 | dev_warn(&client->dev, "Failed to get gpio as irq: %d\n", irq); | ||
559 | gpio_free(pdata->battery_detect); | ||
560 | bq20z75_device->gpio_detect = false; | ||
561 | goto skip_gpio; | ||
562 | } | ||
563 | |||
564 | rc = request_irq(irq, bq20z75_irq, | ||
565 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, | ||
566 | dev_name(&client->dev), &bq20z75_device->power_supply); | ||
567 | if (rc) { | ||
568 | dev_warn(&client->dev, "Failed to request irq: %d\n", rc); | ||
569 | gpio_free(pdata->battery_detect); | ||
570 | bq20z75_device->gpio_detect = false; | ||
571 | goto skip_gpio; | ||
572 | } | ||
573 | |||
574 | bq20z75_device->irq = irq; | ||
575 | |||
576 | skip_gpio: | ||
577 | |||
494 | rc = power_supply_register(&client->dev, &bq20z75_device->power_supply); | 578 | rc = power_supply_register(&client->dev, &bq20z75_device->power_supply); |
495 | if (rc) { | 579 | if (rc) { |
496 | dev_err(&client->dev, | 580 | dev_err(&client->dev, |
497 | "%s: Failed to register power supply\n", __func__); | 581 | "%s: Failed to register power supply\n", __func__); |
498 | kfree(bq20z75_device); | 582 | goto exit_psupply; |
499 | return rc; | ||
500 | } | 583 | } |
501 | 584 | ||
502 | dev_info(&client->dev, | 585 | dev_info(&client->dev, |
503 | "%s: battery gas gauge device registered\n", client->name); | 586 | "%s: battery gas gauge device registered\n", client->name); |
504 | 587 | ||
505 | return 0; | 588 | return 0; |
589 | |||
590 | exit_psupply: | ||
591 | if (bq20z75_device->irq) | ||
592 | free_irq(bq20z75_device->irq, &bq20z75_device->power_supply); | ||
593 | if (bq20z75_device->gpio_detect) | ||
594 | gpio_free(pdata->battery_detect); | ||
595 | |||
596 | kfree(bq20z75_device); | ||
597 | |||
598 | return rc; | ||
506 | } | 599 | } |
507 | 600 | ||
508 | static int bq20z75_remove(struct i2c_client *client) | 601 | static int __devexit bq20z75_remove(struct i2c_client *client) |
509 | { | 602 | { |
510 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | 603 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); |
511 | 604 | ||
605 | if (bq20z75_device->irq) | ||
606 | free_irq(bq20z75_device->irq, &bq20z75_device->power_supply); | ||
607 | if (bq20z75_device->gpio_detect) | ||
608 | gpio_free(bq20z75_device->pdata->battery_detect); | ||
609 | |||
512 | power_supply_unregister(&bq20z75_device->power_supply); | 610 | power_supply_unregister(&bq20z75_device->power_supply); |
513 | kfree(bq20z75_device); | 611 | kfree(bq20z75_device); |
514 | bq20z75_device = NULL; | 612 | bq20z75_device = NULL; |
@@ -544,7 +642,7 @@ static const struct i2c_device_id bq20z75_id[] = { | |||
544 | 642 | ||
545 | static struct i2c_driver bq20z75_battery_driver = { | 643 | static struct i2c_driver bq20z75_battery_driver = { |
546 | .probe = bq20z75_probe, | 644 | .probe = bq20z75_probe, |
547 | .remove = bq20z75_remove, | 645 | .remove = __devexit_p(bq20z75_remove), |
548 | .suspend = bq20z75_suspend, | 646 | .suspend = bq20z75_suspend, |
549 | .resume = bq20z75_resume, | 647 | .resume = bq20z75_resume, |
550 | .id_table = bq20z75_id, | 648 | .id_table = bq20z75_id, |