aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/power
diff options
context:
space:
mode:
authorRhyland Klein <rklein@nvidia.com>2011-05-24 15:05:59 -0400
committerAnton Vorontsov <cbouatmailru@gmail.com>2011-07-08 08:53:07 -0400
commit58ddafae2d7102d8f493840cb7aca0b4b2326b2a (patch)
tree33490b6266c2e9367bcf3549860aae7ee18ed857 /drivers/power
parent906649de0eacde7a643b5a0750de6207441ad54f (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.c101
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
157static int bq20z75_read_word_data(struct i2c_client *client, u8 address) 161static 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
562static 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
580static 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
548static int __devinit bq20z75_probe(struct i2c_client *client, 616static 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
630exit_psupply: 707exit_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,