Skip to content

Commit 58ddafa

Browse files
rhkleinenomsg
authored andcommitted
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>
1 parent 906649d commit 58ddafa

2 files changed

Lines changed: 95 additions & 9 deletions

File tree

drivers/power/bq20z75.c

Lines changed: 92 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,10 @@ struct bq20z75_info {
152152
bool gpio_detect;
153153
bool enable_detection;
154154
int irq;
155+
int last_state;
156+
int poll_time;
157+
struct delayed_work work;
158+
int ignore_changes;
155159
};
156160

157161
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,
279283
int reg_offset, enum power_supply_property psp,
280284
union power_supply_propval *val)
281285
{
286+
struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client);
282287
s32 ret;
283288

284289
ret = bq20z75_read_word_data(client,
@@ -293,15 +298,24 @@ static int bq20z75_get_battery_property(struct i2c_client *client,
293298
if (ret >= bq20z75_data[reg_offset].min_value &&
294299
ret <= bq20z75_data[reg_offset].max_value) {
295300
val->intval = ret;
296-
if (psp == POWER_SUPPLY_PROP_STATUS) {
297-
if (ret & BATTERY_FULL_CHARGED)
298-
val->intval = POWER_SUPPLY_STATUS_FULL;
299-
else if (ret & BATTERY_FULL_DISCHARGED)
300-
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
301-
else if (ret & BATTERY_DISCHARGING)
302-
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
303-
else
304-
val->intval = POWER_SUPPLY_STATUS_CHARGING;
301+
if (psp != POWER_SUPPLY_PROP_STATUS)
302+
return 0;
303+
304+
if (ret & BATTERY_FULL_CHARGED)
305+
val->intval = POWER_SUPPLY_STATUS_FULL;
306+
else if (ret & BATTERY_FULL_DISCHARGED)
307+
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
308+
else if (ret & BATTERY_DISCHARGING)
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;
305319
}
306320
} else {
307321
if (psp == POWER_SUPPLY_PROP_STATUS)
@@ -545,6 +559,60 @@ static irqreturn_t bq20z75_irq(int irq, void *devid)
545559
return IRQ_HANDLED;
546560
}
547561

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+
548616
static int __devinit bq20z75_probe(struct i2c_client *client,
549617
const struct i2c_device_id *id)
550618
{
@@ -566,6 +634,13 @@ static int __devinit bq20z75_probe(struct i2c_client *client,
566634
bq20z75_device->power_supply.num_properties =
567635
ARRAY_SIZE(bq20z75_properties);
568636
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;
569644

570645
if (pdata) {
571646
bq20z75_device->gpio_detect =
@@ -625,6 +700,8 @@ static int __devinit bq20z75_probe(struct i2c_client *client,
625700
dev_info(&client->dev,
626701
"%s: battery gas gauge device registered\n", client->name);
627702

703+
INIT_DELAYED_WORK(&bq20z75_device->work, bq20z75_delayed_work);
704+
628705
return 0;
629706

630707
exit_psupply:
@@ -648,6 +725,9 @@ static int __devexit bq20z75_remove(struct i2c_client *client)
648725
gpio_free(bq20z75_device->pdata->battery_detect);
649726

650727
power_supply_unregister(&bq20z75_device->power_supply);
728+
729+
cancel_delayed_work_sync(&bq20z75_device->work);
730+
651731
kfree(bq20z75_device);
652732
bq20z75_device = NULL;
653733

@@ -661,6 +741,9 @@ static int bq20z75_suspend(struct i2c_client *client,
661741
struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client);
662742
s32 ret;
663743

744+
if (bq20z75_device->poll_time > 0)
745+
cancel_delayed_work_sync(&bq20z75_device->work);
746+
664747
/* write to manufacturer access with sleep command */
665748
ret = bq20z75_write_word_data(client,
666749
bq20z75_data[REG_MANUFACTURER_DATA].addr,

include/linux/power/bq20z75.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,14 @@
2929
* @battery_detect: GPIO which is used to detect battery presence
3030
* @battery_detect_present: gpio state when battery is present (0 / 1)
3131
* @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
3234
*/
3335
struct bq20z75_platform_data {
3436
int battery_detect;
3537
int battery_detect_present;
3638
int i2c_retry_count;
39+
int poll_retry_count;
3740
};
3841

3942
#endif

0 commit comments

Comments
 (0)