diff options
| author | Rhyland Klein <rklein@nvidia.com> | 2011-05-24 15:05:59 -0400 |
|---|---|---|
| committer | Anton Vorontsov <cbouatmailru@gmail.com> | 2011-07-08 08:53:07 -0400 |
| commit | 58ddafae2d7102d8f493840cb7aca0b4b2326b2a (patch) | |
| tree | 33490b6266c2e9367bcf3549860aae7ee18ed857 | |
| parent | 906649de0eacde7a643b5a0750de6207441ad54f (diff) | |
bq20z75: Add support for external notification
Adding support for external power change notification. One problem found
is that there is a lag time before the sensor will return a new status.
To ensure that we only fire off the power_supply_changed event when the
status returned from the sensor is actually different, we delay sending
the the notification, and instead poll on it looking for a change. The
amount of time to poll is configurable via platform data.
Signed-off-by: Rhyland Klein <rklein@nvidia.com>
Signed-off-by: Anton Vorontsov <cbouatmailru@gmail.com>
| -rw-r--r-- | drivers/power/bq20z75.c | 101 | ||||
| -rw-r--r-- | include/linux/power/bq20z75.h | 3 |
2 files changed, 95 insertions, 9 deletions
diff --git a/drivers/power/bq20z75.c b/drivers/power/bq20z75.c index 506585e31a5b..e1adc0e027ac 100644 --- a/drivers/power/bq20z75.c +++ b/drivers/power/bq20z75.c | |||
| @@ -152,6 +152,10 @@ struct bq20z75_info { | |||
| 152 | bool gpio_detect; | 152 | bool gpio_detect; |
| 153 | bool enable_detection; | 153 | bool enable_detection; |
| 154 | int irq; | 154 | int irq; |
| 155 | int last_state; | ||
| 156 | int poll_time; | ||
| 157 | struct delayed_work work; | ||
| 158 | int ignore_changes; | ||
| 155 | }; | 159 | }; |
| 156 | 160 | ||
| 157 | static int bq20z75_read_word_data(struct i2c_client *client, u8 address) | 161 | static int bq20z75_read_word_data(struct i2c_client *client, u8 address) |
| @@ -279,6 +283,7 @@ static int bq20z75_get_battery_property(struct i2c_client *client, | |||
| 279 | int reg_offset, enum power_supply_property psp, | 283 | int reg_offset, enum power_supply_property psp, |
| 280 | union power_supply_propval *val) | 284 | union power_supply_propval *val) |
| 281 | { | 285 | { |
| 286 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | ||
| 282 | s32 ret; | 287 | s32 ret; |
| 283 | 288 | ||
| 284 | ret = bq20z75_read_word_data(client, | 289 | ret = bq20z75_read_word_data(client, |
| @@ -293,15 +298,24 @@ static int bq20z75_get_battery_property(struct i2c_client *client, | |||
| 293 | if (ret >= bq20z75_data[reg_offset].min_value && | 298 | if (ret >= bq20z75_data[reg_offset].min_value && |
| 294 | ret <= bq20z75_data[reg_offset].max_value) { | 299 | ret <= bq20z75_data[reg_offset].max_value) { |
| 295 | val->intval = ret; | 300 | val->intval = ret; |
| 296 | if (psp == POWER_SUPPLY_PROP_STATUS) { | 301 | if (psp != POWER_SUPPLY_PROP_STATUS) |
| 297 | if (ret & BATTERY_FULL_CHARGED) | 302 | return 0; |
| 298 | val->intval = POWER_SUPPLY_STATUS_FULL; | 303 | |
| 299 | else if (ret & BATTERY_FULL_DISCHARGED) | 304 | if (ret & BATTERY_FULL_CHARGED) |
| 300 | val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; | 305 | val->intval = POWER_SUPPLY_STATUS_FULL; |
| 301 | else if (ret & BATTERY_DISCHARGING) | 306 | else if (ret & BATTERY_FULL_DISCHARGED) |
| 302 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; | 307 | val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; |
| 303 | else | 308 | else if (ret & BATTERY_DISCHARGING) |
| 304 | val->intval = POWER_SUPPLY_STATUS_CHARGING; | 309 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; |
| 310 | else | ||
| 311 | val->intval = POWER_SUPPLY_STATUS_CHARGING; | ||
| 312 | |||
| 313 | if (bq20z75_device->poll_time == 0) | ||
| 314 | bq20z75_device->last_state = val->intval; | ||
| 315 | else if (bq20z75_device->last_state != val->intval) { | ||
| 316 | cancel_delayed_work_sync(&bq20z75_device->work); | ||
| 317 | power_supply_changed(&bq20z75_device->power_supply); | ||
| 318 | bq20z75_device->poll_time = 0; | ||
| 305 | } | 319 | } |
| 306 | } else { | 320 | } else { |
| 307 | if (psp == POWER_SUPPLY_PROP_STATUS) | 321 | if (psp == POWER_SUPPLY_PROP_STATUS) |
| @@ -545,6 +559,60 @@ static irqreturn_t bq20z75_irq(int irq, void *devid) | |||
| 545 | return IRQ_HANDLED; | 559 | return IRQ_HANDLED; |
| 546 | } | 560 | } |
| 547 | 561 | ||
| 562 | static void bq20z75_external_power_changed(struct power_supply *psy) | ||
| 563 | { | ||
| 564 | struct bq20z75_info *bq20z75_device; | ||
| 565 | |||
| 566 | bq20z75_device = container_of(psy, struct bq20z75_info, power_supply); | ||
| 567 | |||
| 568 | if (bq20z75_device->ignore_changes > 0) { | ||
| 569 | bq20z75_device->ignore_changes--; | ||
| 570 | return; | ||
| 571 | } | ||
| 572 | |||
| 573 | /* cancel outstanding work */ | ||
| 574 | cancel_delayed_work_sync(&bq20z75_device->work); | ||
| 575 | |||
| 576 | schedule_delayed_work(&bq20z75_device->work, HZ); | ||
| 577 | bq20z75_device->poll_time = bq20z75_device->pdata->poll_retry_count; | ||
| 578 | } | ||
| 579 | |||
| 580 | static void bq20z75_delayed_work(struct work_struct *work) | ||
| 581 | { | ||
| 582 | struct bq20z75_info *bq20z75_device; | ||
| 583 | s32 ret; | ||
| 584 | |||
| 585 | bq20z75_device = container_of(work, struct bq20z75_info, work.work); | ||
| 586 | |||
| 587 | ret = bq20z75_read_word_data(bq20z75_device->client, | ||
| 588 | bq20z75_data[REG_STATUS].addr); | ||
| 589 | /* if the read failed, give up on this work */ | ||
| 590 | if (ret < 0) { | ||
| 591 | bq20z75_device->poll_time = 0; | ||
| 592 | return; | ||
| 593 | } | ||
| 594 | |||
| 595 | if (ret & BATTERY_FULL_CHARGED) | ||
| 596 | ret = POWER_SUPPLY_STATUS_FULL; | ||
| 597 | else if (ret & BATTERY_FULL_DISCHARGED) | ||
| 598 | ret = POWER_SUPPLY_STATUS_NOT_CHARGING; | ||
| 599 | else if (ret & BATTERY_DISCHARGING) | ||
| 600 | ret = POWER_SUPPLY_STATUS_DISCHARGING; | ||
| 601 | else | ||
| 602 | ret = POWER_SUPPLY_STATUS_CHARGING; | ||
| 603 | |||
| 604 | if (bq20z75_device->last_state != ret) { | ||
| 605 | bq20z75_device->poll_time = 0; | ||
| 606 | power_supply_changed(&bq20z75_device->power_supply); | ||
| 607 | return; | ||
| 608 | } | ||
| 609 | if (bq20z75_device->poll_time > 0) { | ||
| 610 | schedule_delayed_work(&bq20z75_device->work, HZ); | ||
| 611 | bq20z75_device->poll_time--; | ||
| 612 | return; | ||
| 613 | } | ||
| 614 | } | ||
| 615 | |||
| 548 | static int __devinit bq20z75_probe(struct i2c_client *client, | 616 | static int __devinit bq20z75_probe(struct i2c_client *client, |
| 549 | const struct i2c_device_id *id) | 617 | const struct i2c_device_id *id) |
| 550 | { | 618 | { |
| @@ -566,6 +634,13 @@ static int __devinit bq20z75_probe(struct i2c_client *client, | |||
| 566 | bq20z75_device->power_supply.num_properties = | 634 | bq20z75_device->power_supply.num_properties = |
| 567 | ARRAY_SIZE(bq20z75_properties); | 635 | ARRAY_SIZE(bq20z75_properties); |
| 568 | bq20z75_device->power_supply.get_property = bq20z75_get_property; | 636 | bq20z75_device->power_supply.get_property = bq20z75_get_property; |
| 637 | /* ignore first notification of external change, it is generated | ||
| 638 | * from the power_supply_register call back | ||
| 639 | */ | ||
| 640 | bq20z75_device->ignore_changes = 1; | ||
| 641 | bq20z75_device->last_state = POWER_SUPPLY_STATUS_UNKNOWN; | ||
| 642 | bq20z75_device->power_supply.external_power_changed = | ||
| 643 | bq20z75_external_power_changed; | ||
| 569 | 644 | ||
| 570 | if (pdata) { | 645 | if (pdata) { |
| 571 | bq20z75_device->gpio_detect = | 646 | bq20z75_device->gpio_detect = |
| @@ -625,6 +700,8 @@ skip_gpio: | |||
| 625 | dev_info(&client->dev, | 700 | dev_info(&client->dev, |
| 626 | "%s: battery gas gauge device registered\n", client->name); | 701 | "%s: battery gas gauge device registered\n", client->name); |
| 627 | 702 | ||
| 703 | INIT_DELAYED_WORK(&bq20z75_device->work, bq20z75_delayed_work); | ||
| 704 | |||
| 628 | return 0; | 705 | return 0; |
| 629 | 706 | ||
| 630 | exit_psupply: | 707 | exit_psupply: |
| @@ -648,6 +725,9 @@ static int __devexit bq20z75_remove(struct i2c_client *client) | |||
| 648 | gpio_free(bq20z75_device->pdata->battery_detect); | 725 | gpio_free(bq20z75_device->pdata->battery_detect); |
| 649 | 726 | ||
| 650 | power_supply_unregister(&bq20z75_device->power_supply); | 727 | power_supply_unregister(&bq20z75_device->power_supply); |
| 728 | |||
| 729 | cancel_delayed_work_sync(&bq20z75_device->work); | ||
| 730 | |||
| 651 | kfree(bq20z75_device); | 731 | kfree(bq20z75_device); |
| 652 | bq20z75_device = NULL; | 732 | bq20z75_device = NULL; |
| 653 | 733 | ||
| @@ -661,6 +741,9 @@ static int bq20z75_suspend(struct i2c_client *client, | |||
| 661 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | 741 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); |
| 662 | s32 ret; | 742 | s32 ret; |
| 663 | 743 | ||
| 744 | if (bq20z75_device->poll_time > 0) | ||
| 745 | cancel_delayed_work_sync(&bq20z75_device->work); | ||
| 746 | |||
| 664 | /* write to manufacturer access with sleep command */ | 747 | /* write to manufacturer access with sleep command */ |
| 665 | ret = bq20z75_write_word_data(client, | 748 | ret = bq20z75_write_word_data(client, |
| 666 | bq20z75_data[REG_MANUFACTURER_DATA].addr, | 749 | bq20z75_data[REG_MANUFACTURER_DATA].addr, |
diff --git a/include/linux/power/bq20z75.h b/include/linux/power/bq20z75.h index b0843b68af92..1398eb004e83 100644 --- a/include/linux/power/bq20z75.h +++ b/include/linux/power/bq20z75.h | |||
| @@ -29,11 +29,14 @@ | |||
| 29 | * @battery_detect: GPIO which is used to detect battery presence | 29 | * @battery_detect: GPIO which is used to detect battery presence |
| 30 | * @battery_detect_present: gpio state when battery is present (0 / 1) | 30 | * @battery_detect_present: gpio state when battery is present (0 / 1) |
| 31 | * @i2c_retry_count: # of times to retry on i2c IO failure | 31 | * @i2c_retry_count: # of times to retry on i2c IO failure |
| 32 | * @poll_retry_count: # of times to retry looking for new status after | ||
| 33 | * external change notification | ||
| 32 | */ | 34 | */ |
| 33 | struct bq20z75_platform_data { | 35 | struct bq20z75_platform_data { |
| 34 | int battery_detect; | 36 | int battery_detect; |
| 35 | int battery_detect_present; | 37 | int battery_detect_present; |
| 36 | int i2c_retry_count; | 38 | int i2c_retry_count; |
| 39 | int poll_retry_count; | ||
| 37 | }; | 40 | }; |
| 38 | 41 | ||
| 39 | #endif | 42 | #endif |
