diff options
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/ds2760_battery.c | 52 |
1 files changed, 52 insertions, 0 deletions
diff --git a/drivers/power/ds2760_battery.c b/drivers/power/ds2760_battery.c index f4a9258aa9d0..1bb8498f14be 100644 --- a/drivers/power/ds2760_battery.c +++ b/drivers/power/ds2760_battery.c | |||
@@ -56,6 +56,7 @@ struct ds2760_device_info { | |||
56 | struct device *w1_dev; | 56 | struct device *w1_dev; |
57 | struct workqueue_struct *monitor_wqueue; | 57 | struct workqueue_struct *monitor_wqueue; |
58 | struct delayed_work monitor_work; | 58 | struct delayed_work monitor_work; |
59 | struct delayed_work set_charged_work; | ||
59 | }; | 60 | }; |
60 | 61 | ||
61 | static unsigned int cache_time = 1000; | 62 | static unsigned int cache_time = 1000; |
@@ -327,6 +328,52 @@ static void ds2760_battery_external_power_changed(struct power_supply *psy) | |||
327 | queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ/10); | 328 | queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ/10); |
328 | } | 329 | } |
329 | 330 | ||
331 | |||
332 | static void ds2760_battery_set_charged_work(struct work_struct *work) | ||
333 | { | ||
334 | char bias; | ||
335 | struct ds2760_device_info *di = container_of(work, | ||
336 | struct ds2760_device_info, set_charged_work.work); | ||
337 | |||
338 | dev_dbg(di->dev, "%s\n", __func__); | ||
339 | |||
340 | ds2760_battery_read_status(di); | ||
341 | |||
342 | /* When we get notified by external circuitry that the battery is | ||
343 | * considered fully charged now, we know that there is no current | ||
344 | * flow any more. However, the ds2760's internal current meter is | ||
345 | * too inaccurate to rely on - spec say something ~15% failure. | ||
346 | * Hence, we use the current offset bias register to compensate | ||
347 | * that error. | ||
348 | */ | ||
349 | |||
350 | if (!power_supply_am_i_supplied(&di->bat)) | ||
351 | return; | ||
352 | |||
353 | bias = (signed char) di->current_raw + | ||
354 | (signed char) di->raw[DS2760_CURRENT_OFFSET_BIAS]; | ||
355 | |||
356 | dev_dbg(di->dev, "%s: bias = %d\n", __func__, bias); | ||
357 | |||
358 | w1_ds2760_write(di->w1_dev, &bias, DS2760_CURRENT_OFFSET_BIAS, 1); | ||
359 | w1_ds2760_store_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1); | ||
360 | w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1); | ||
361 | |||
362 | /* Write to the di->raw[] buffer directly - the CURRENT_OFFSET_BIAS | ||
363 | * value won't be read back by ds2760_battery_read_status() */ | ||
364 | di->raw[DS2760_CURRENT_OFFSET_BIAS] = bias; | ||
365 | } | ||
366 | |||
367 | static void ds2760_battery_set_charged(struct power_supply *psy) | ||
368 | { | ||
369 | struct ds2760_device_info *di = to_ds2760_device_info(psy); | ||
370 | |||
371 | /* postpone the actual work by 20 secs. This is for debouncing GPIO | ||
372 | * signals and to let the current value settle. See AN4188. */ | ||
373 | cancel_delayed_work(&di->set_charged_work); | ||
374 | queue_delayed_work(di->monitor_wqueue, &di->set_charged_work, HZ * 20); | ||
375 | } | ||
376 | |||
330 | static int ds2760_battery_get_property(struct power_supply *psy, | 377 | static int ds2760_battery_get_property(struct power_supply *psy, |
331 | enum power_supply_property psp, | 378 | enum power_supply_property psp, |
332 | union power_supply_propval *val) | 379 | union power_supply_propval *val) |
@@ -412,6 +459,7 @@ static int ds2760_battery_probe(struct platform_device *pdev) | |||
412 | di->bat.properties = ds2760_battery_props; | 459 | di->bat.properties = ds2760_battery_props; |
413 | di->bat.num_properties = ARRAY_SIZE(ds2760_battery_props); | 460 | di->bat.num_properties = ARRAY_SIZE(ds2760_battery_props); |
414 | di->bat.get_property = ds2760_battery_get_property; | 461 | di->bat.get_property = ds2760_battery_get_property; |
462 | di->bat.set_charged = ds2760_battery_set_charged; | ||
415 | di->bat.external_power_changed = | 463 | di->bat.external_power_changed = |
416 | ds2760_battery_external_power_changed; | 464 | ds2760_battery_external_power_changed; |
417 | 465 | ||
@@ -443,6 +491,8 @@ static int ds2760_battery_probe(struct platform_device *pdev) | |||
443 | } | 491 | } |
444 | 492 | ||
445 | INIT_DELAYED_WORK(&di->monitor_work, ds2760_battery_work); | 493 | INIT_DELAYED_WORK(&di->monitor_work, ds2760_battery_work); |
494 | INIT_DELAYED_WORK(&di->set_charged_work, | ||
495 | ds2760_battery_set_charged_work); | ||
446 | di->monitor_wqueue = create_singlethread_workqueue(dev_name(&pdev->dev)); | 496 | di->monitor_wqueue = create_singlethread_workqueue(dev_name(&pdev->dev)); |
447 | if (!di->monitor_wqueue) { | 497 | if (!di->monitor_wqueue) { |
448 | retval = -ESRCH; | 498 | retval = -ESRCH; |
@@ -467,6 +517,8 @@ static int ds2760_battery_remove(struct platform_device *pdev) | |||
467 | 517 | ||
468 | cancel_rearming_delayed_workqueue(di->monitor_wqueue, | 518 | cancel_rearming_delayed_workqueue(di->monitor_wqueue, |
469 | &di->monitor_work); | 519 | &di->monitor_work); |
520 | cancel_rearming_delayed_workqueue(di->monitor_wqueue, | ||
521 | &di->set_charged_work); | ||
470 | destroy_workqueue(di->monitor_wqueue); | 522 | destroy_workqueue(di->monitor_wqueue); |
471 | power_supply_unregister(&di->bat); | 523 | power_supply_unregister(&di->bat); |
472 | 524 | ||