Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Rhyland Klein and committed by
Anton Vorontsov
58ddafae 906649de

+95 -9
+92 -9
drivers/power/bq20z75.c
··· 152 152 bool gpio_detect; 153 153 bool enable_detection; 154 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 161 static int bq20z75_read_word_data(struct i2c_client *client, u8 address) ··· 283 279 int reg_offset, enum power_supply_property psp, 284 280 union power_supply_propval *val) 285 281 { 282 + struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); 286 283 s32 ret; 287 284 288 285 ret = bq20z75_read_word_data(client, ··· 298 293 if (ret >= bq20z75_data[reg_offset].min_value && 299 294 ret <= bq20z75_data[reg_offset].max_value) { 300 295 val->intval = ret; 301 - if (psp == POWER_SUPPLY_PROP_STATUS) { 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; 296 + if (psp != POWER_SUPPLY_PROP_STATUS) 297 + return 0; 298 + 299 + if (ret & BATTERY_FULL_CHARGED) 300 + val->intval = POWER_SUPPLY_STATUS_FULL; 301 + else if (ret & BATTERY_FULL_DISCHARGED) 302 + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 303 + else if (ret & BATTERY_DISCHARGING) 304 + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 305 + else 306 + val->intval = POWER_SUPPLY_STATUS_CHARGING; 307 + 308 + if (bq20z75_device->poll_time == 0) 309 + bq20z75_device->last_state = val->intval; 310 + else if (bq20z75_device->last_state != val->intval) { 311 + cancel_delayed_work_sync(&bq20z75_device->work); 312 + power_supply_changed(&bq20z75_device->power_supply); 313 + bq20z75_device->poll_time = 0; 310 314 } 311 315 } else { 312 316 if (psp == POWER_SUPPLY_PROP_STATUS) ··· 559 545 return IRQ_HANDLED; 560 546 } 561 547 548 + static void bq20z75_external_power_changed(struct power_supply *psy) 549 + { 550 + struct bq20z75_info *bq20z75_device; 551 + 552 + bq20z75_device = container_of(psy, struct bq20z75_info, power_supply); 553 + 554 + if (bq20z75_device->ignore_changes > 0) { 555 + bq20z75_device->ignore_changes--; 556 + return; 557 + } 558 + 559 + /* cancel outstanding work */ 560 + cancel_delayed_work_sync(&bq20z75_device->work); 561 + 562 + schedule_delayed_work(&bq20z75_device->work, HZ); 563 + bq20z75_device->poll_time = bq20z75_device->pdata->poll_retry_count; 564 + } 565 + 566 + static void bq20z75_delayed_work(struct work_struct *work) 567 + { 568 + struct bq20z75_info *bq20z75_device; 569 + s32 ret; 570 + 571 + bq20z75_device = container_of(work, struct bq20z75_info, work.work); 572 + 573 + ret = bq20z75_read_word_data(bq20z75_device->client, 574 + bq20z75_data[REG_STATUS].addr); 575 + /* if the read failed, give up on this work */ 576 + if (ret < 0) { 577 + bq20z75_device->poll_time = 0; 578 + return; 579 + } 580 + 581 + if (ret & BATTERY_FULL_CHARGED) 582 + ret = POWER_SUPPLY_STATUS_FULL; 583 + else if (ret & BATTERY_FULL_DISCHARGED) 584 + ret = POWER_SUPPLY_STATUS_NOT_CHARGING; 585 + else if (ret & BATTERY_DISCHARGING) 586 + ret = POWER_SUPPLY_STATUS_DISCHARGING; 587 + else 588 + ret = POWER_SUPPLY_STATUS_CHARGING; 589 + 590 + if (bq20z75_device->last_state != ret) { 591 + bq20z75_device->poll_time = 0; 592 + power_supply_changed(&bq20z75_device->power_supply); 593 + return; 594 + } 595 + if (bq20z75_device->poll_time > 0) { 596 + schedule_delayed_work(&bq20z75_device->work, HZ); 597 + bq20z75_device->poll_time--; 598 + return; 599 + } 600 + } 601 + 562 602 static int __devinit bq20z75_probe(struct i2c_client *client, 563 603 const struct i2c_device_id *id) 564 604 { ··· 634 566 bq20z75_device->power_supply.num_properties = 635 567 ARRAY_SIZE(bq20z75_properties); 636 568 bq20z75_device->power_supply.get_property = bq20z75_get_property; 569 + /* ignore first notification of external change, it is generated 570 + * from the power_supply_register call back 571 + */ 572 + bq20z75_device->ignore_changes = 1; 573 + bq20z75_device->last_state = POWER_SUPPLY_STATUS_UNKNOWN; 574 + bq20z75_device->power_supply.external_power_changed = 575 + bq20z75_external_power_changed; 637 576 638 577 if (pdata) { 639 578 bq20z75_device->gpio_detect = ··· 700 625 dev_info(&client->dev, 701 626 "%s: battery gas gauge device registered\n", client->name); 702 627 628 + INIT_DELAYED_WORK(&bq20z75_device->work, bq20z75_delayed_work); 629 + 703 630 return 0; 704 631 705 632 exit_psupply: ··· 725 648 gpio_free(bq20z75_device->pdata->battery_detect); 726 649 727 650 power_supply_unregister(&bq20z75_device->power_supply); 651 + 652 + cancel_delayed_work_sync(&bq20z75_device->work); 653 + 728 654 kfree(bq20z75_device); 729 655 bq20z75_device = NULL; 730 656 ··· 740 660 { 741 661 struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); 742 662 s32 ret; 663 + 664 + if (bq20z75_device->poll_time > 0) 665 + cancel_delayed_work_sync(&bq20z75_device->work); 743 666 744 667 /* write to manufacturer access with sleep command */ 745 668 ret = bq20z75_write_word_data(client,
+3
include/linux/power/bq20z75.h
··· 29 29 * @battery_detect: GPIO which is used to detect battery presence 30 30 * @battery_detect_present: gpio state when battery is present (0 / 1) 31 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 35 struct bq20z75_platform_data { 34 36 int battery_detect; 35 37 int battery_detect_present; 36 38 int i2c_retry_count; 39 + int poll_retry_count; 37 40 }; 38 41 39 42 #endif