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 /drivers/power | |
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>
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/bq20z75.c | 101 |
1 files changed, 92 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, |