aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/power
diff options
context:
space:
mode:
authorDaniel Mack <daniel@caiaq.de>2009-07-23 14:35:54 -0400
committerAnton Vorontsov <cbouatmailru@gmail.com>2009-07-30 09:49:16 -0400
commit8d631ccff8d90fce77b42f01b3872595c599cbf9 (patch)
tree7a3d3e2e1eefc930d216a8998eed8c05fb8a39ea /drivers/power
parente5f5ccb646bc6009572b5c23201b5e81638ff150 (diff)
ds2760_battery: implement set_charged() feature
The ds2760's internal current meter is not reliable enough as it has an inacurracy of around ~15%. Without any correction for that error, the current accumulator is couting up all the time, even though the battery is already fully charged and hence destroys the static information. The longer it is connected, the worse is the aberration. Fortunately, this can be corrected by the DS2760_CURRENT_OFFSET_BIAS register. Using the external power_supply_set_battery_charged() function, this register is now gauging the measurement. A delayed work is used to debounce flaky GPIO signals and to let the current value settle. Also see Maxim's application note AN4188. Signed-off-by: Daniel Mack <daniel@caiaq.de> Cc: Szabolcs Gyurko <szabolcs.gyurko@tlt.hu> Cc: Matt Reimer <mreimer@vpop.net> Cc: Anton Vorontsov <cbou@mail.ru> Signed-off-by: Anton Vorontsov <cbouatmailru@gmail.com>
Diffstat (limited to 'drivers/power')
-rw-r--r--drivers/power/ds2760_battery.c52
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
61static unsigned int cache_time = 1000; 62static 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
332static 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
367static 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
330static int ds2760_battery_get_property(struct power_supply *psy, 377static 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