aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/battery.c
diff options
context:
space:
mode:
authorKamil Iskra <kamil@iskra.name>2012-11-16 16:28:58 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2012-11-16 16:28:58 -0500
commit4000e626156935dfb626321ce09cae2c833eabbb (patch)
tree3cc20a54a0fee5b7bf5594fa81ea8c8602adc0b2 /drivers/acpi/battery.c
parentbacaf7cd092a2c42a904bce437e64690e04aaa10 (diff)
ACPI / battery: Correct battery capacity values on Thinkpads
Add a quirk to correctly report battery capacity on 2010 and 2011 Lenovo Thinkpad models. The affected models that I tested (x201, t410, t410s, and x220) exhibit a problem where, when battery capacity reporting unit is mAh, the values being reported are wrong. Pre-2010 and 2012 models appear to always report in mWh and are thus unaffected. Also, in mid-2012 Lenovo issued a BIOS update for the 2011 models that fixes the issue (tested on x220 with a post-1.29 BIOS). No such update is available for the 2010 models, so those still need this patch. Problem description: for some reason, the affected Thinkpads switch the reporting unit between mAh and mWh; generally, mAh is used when a laptop is plugged in and mWh when it's unplugged, although a suspend/resume or rmmod/modprobe is needed for the switch to take effect. The values reported in mAh are *always* wrong. This does not appear to be a kernel regression; I believe that the values were never reported correctly. I tested back to kernel 2.6.34, with multiple machines and BIOS versions. Simply plugging a laptop into mains before turning it on is enough to reproduce the problem. Here's a sample /proc/acpi/battery/BAT0/info from Thinkpad x220 (before a BIOS update) with a 4-cell battery: present: yes design capacity: 2886 mAh last full capacity: 2909 mAh battery technology: rechargeable design voltage: 14800 mV design capacity warning: 145 mAh design capacity low: 13 mAh cycle count: 0 capacity granularity 1: 1 mAh capacity granularity 2: 1 mAh model number: 42T4899 serial number: 21064 battery type: LION OEM info: SANYO Once the laptop switches the unit to mWh (unplug from mains, suspend, resume), the output changes to: present: yes design capacity: 28860 mWh last full capacity: 29090 mWh battery technology: rechargeable design voltage: 14800 mV design capacity warning: 1454 mWh design capacity low: 200 mWh cycle count: 0 capacity granularity 1: 1 mWh capacity granularity 2: 1 mWh model number: 42T4899 serial number: 21064 battery type: LION OEM info: SANYO Can you see how the values for "design capacity", etc., differ by a factor of 10 instead of 14.8 (the design voltage of this battery)? On the battery itself it says: 14.8V, 1.95Ah, 29Wh, so clearly the values reported in mWh are correct and the ones in mAh are not. My guess is that this problem has been around ever since those machines were released, but because the most common Thinkpad batteries are rated at 10.8V, the error (8%) is small enough that it simply hasn't been noticed or at least nobody could be bothered to look into it. My patch works around the problem by adjusting the incorrectly reported mAh values by "10000 / design_voltage". The patch also has code to figure out if it should be activated or not. It only activates on Lenovo Thinkpads, only when the unit is mAh, and, as an extra precaution, only when the battery capacity reported through ACPI does not match what is reported through DMI (I've never encountered a machine where the first two conditions would be true but the last would not, but better safe than sorry). I've been using this patch for close to a year on several systems without any problems. References: https://bugzilla.kernel.org/show_bug.cgi?id=41062 Acked-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br> Cc: <stable@vger.kernel.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/acpi/battery.c')
-rw-r--r--drivers/acpi/battery.c77
1 files changed, 77 insertions, 0 deletions
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index 45e3e1759fb..7efaeaa53b8 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -34,6 +34,7 @@
34#include <linux/dmi.h> 34#include <linux/dmi.h>
35#include <linux/slab.h> 35#include <linux/slab.h>
36#include <linux/suspend.h> 36#include <linux/suspend.h>
37#include <asm/unaligned.h>
37 38
38#ifdef CONFIG_ACPI_PROCFS_POWER 39#ifdef CONFIG_ACPI_PROCFS_POWER
39#include <linux/proc_fs.h> 40#include <linux/proc_fs.h>
@@ -95,6 +96,18 @@ enum {
95 ACPI_BATTERY_ALARM_PRESENT, 96 ACPI_BATTERY_ALARM_PRESENT,
96 ACPI_BATTERY_XINFO_PRESENT, 97 ACPI_BATTERY_XINFO_PRESENT,
97 ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, 98 ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY,
99 /* On Lenovo Thinkpad models from 2010 and 2011, the power unit
100 switches between mWh and mAh depending on whether the system
101 is running on battery or not. When mAh is the unit, most
102 reported values are incorrect and need to be adjusted by
103 10000/design_voltage. Verified on x201, t410, t410s, and x220.
104 Pre-2010 and 2012 models appear to always report in mWh and
105 are thus unaffected (tested with t42, t61, t500, x200, x300,
106 and x230). Also, in mid-2012 Lenovo issued a BIOS update for
107 the 2011 models that fixes the issue (tested on x220 with a
108 post-1.29 BIOS), but as of Nov. 2012, no such update is
109 available for the 2010 models. */
110 ACPI_BATTERY_QUIRK_THINKPAD_MAH,
98}; 111};
99 112
100struct acpi_battery { 113struct acpi_battery {
@@ -438,6 +451,21 @@ static int acpi_battery_get_info(struct acpi_battery *battery)
438 kfree(buffer.pointer); 451 kfree(buffer.pointer);
439 if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags)) 452 if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags))
440 battery->full_charge_capacity = battery->design_capacity; 453 battery->full_charge_capacity = battery->design_capacity;
454 if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags) &&
455 battery->power_unit && battery->design_voltage) {
456 battery->design_capacity = battery->design_capacity *
457 10000 / battery->design_voltage;
458 battery->full_charge_capacity = battery->full_charge_capacity *
459 10000 / battery->design_voltage;
460 battery->design_capacity_warning =
461 battery->design_capacity_warning *
462 10000 / battery->design_voltage;
463 /* Curiously, design_capacity_low, unlike the rest of them,
464 is correct. */
465 /* capacity_granularity_* equal 1 on the systems tested, so
466 it's impossible to tell if they would need an adjustment
467 or not if their values were higher. */
468 }
441 return result; 469 return result;
442} 470}
443 471
@@ -486,6 +514,11 @@ static int acpi_battery_get_state(struct acpi_battery *battery)
486 && battery->capacity_now >= 0 && battery->capacity_now <= 100) 514 && battery->capacity_now >= 0 && battery->capacity_now <= 100)
487 battery->capacity_now = (battery->capacity_now * 515 battery->capacity_now = (battery->capacity_now *
488 battery->full_charge_capacity) / 100; 516 battery->full_charge_capacity) / 100;
517 if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags) &&
518 battery->power_unit && battery->design_voltage) {
519 battery->capacity_now = battery->capacity_now *
520 10000 / battery->design_voltage;
521 }
489 return result; 522 return result;
490} 523}
491 524
@@ -595,6 +628,24 @@ static void sysfs_remove_battery(struct acpi_battery *battery)
595 mutex_unlock(&battery->sysfs_lock); 628 mutex_unlock(&battery->sysfs_lock);
596} 629}
597 630
631static void find_battery(const struct dmi_header *dm, void *private)
632{
633 struct acpi_battery *battery = (struct acpi_battery *)private;
634 /* Note: the hardcoded offsets below have been extracted from
635 the source code of dmidecode. */
636 if (dm->type == DMI_ENTRY_PORTABLE_BATTERY && dm->length >= 8) {
637 const u8 *dmi_data = (const u8 *)(dm + 1);
638 int dmi_capacity = get_unaligned((const u16 *)(dmi_data + 6));
639 if (dm->length >= 18)
640 dmi_capacity *= dmi_data[17];
641 if (battery->design_capacity * battery->design_voltage / 1000
642 != dmi_capacity &&
643 battery->design_capacity * 10 == dmi_capacity)
644 set_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH,
645 &battery->flags);
646 }
647}
648
598/* 649/*
599 * According to the ACPI spec, some kinds of primary batteries can 650 * According to the ACPI spec, some kinds of primary batteries can
600 * report percentage battery remaining capacity directly to OS. 651 * report percentage battery remaining capacity directly to OS.
@@ -620,6 +671,32 @@ static void acpi_battery_quirks(struct acpi_battery *battery)
620 battery->capacity_now = (battery->capacity_now * 671 battery->capacity_now = (battery->capacity_now *
621 battery->full_charge_capacity) / 100; 672 battery->full_charge_capacity) / 100;
622 } 673 }
674
675 if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags))
676 return ;
677
678 if (battery->power_unit && dmi_name_in_vendors("LENOVO")) {
679 const char *s;
680 s = dmi_get_system_info(DMI_PRODUCT_VERSION);
681 if (s && !strnicmp(s, "ThinkPad", 8)) {
682 dmi_walk(find_battery, battery);
683 if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH,
684 &battery->flags) &&
685 battery->design_voltage) {
686 battery->design_capacity =
687 battery->design_capacity *
688 10000 / battery->design_voltage;
689 battery->full_charge_capacity =
690 battery->full_charge_capacity *
691 10000 / battery->design_voltage;
692 battery->design_capacity_warning =
693 battery->design_capacity_warning *
694 10000 / battery->design_voltage;
695 battery->capacity_now = battery->capacity_now *
696 10000 / battery->design_voltage;
697 }
698 }
699 }
623} 700}
624 701
625static int acpi_battery_update(struct acpi_battery *battery) 702static int acpi_battery_update(struct acpi_battery *battery)