summaryrefslogtreecommitdiffstats
path: root/drivers/acpi/battery.c
diff options
context:
space:
mode:
authorDave Lambley <linux@davel.me.uk>2016-11-04 02:05:40 +0100
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2016-11-16 23:09:45 +0100
commit2d09af4a88d99a8dd0429263451b7b88e6d92738 (patch)
treeabaee425ecd9e7dfc0611c8754c5c6419b943268 /drivers/acpi/battery.c
parentLinux 4.9-rc5 (diff)
downloadlinux-2d09af4a88d99a8dd0429263451b7b88e6d92738.tar.xz
linux-2d09af4a88d99a8dd0429263451b7b88e6d92738.zip
ACPI / battery: If _BIX fails, retry with _BIF
The Lenovo Yoga 300 laptop's firmware advertises that it provides the _BIX the method to retrieve battery information. Unfortunately (some versions of?) the implementation return with an error. [ 21.712228] ACPI Exception: AE_AML_PACKAGE_LIMIT, Index (0x000000010) is beyond end of object (length 0xD) (20160422/exoparg2-427) [ 21.712244] ACPI Error: Method parse/execution failed [\_SB.PCI0.LPCB.H_EC.BAT1._BIX] (Node ffff95f8ff0b20f0), AE_AML_PACKAGE_LIMIT (20160422/psparse-542) The _BIF method does succeed and returns convincing data. We detect _BIX failing and automatically retry with _BIF. Signed-off-by: Dave Lambley <linux@davel.me.uk> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/acpi/battery.c')
-rw-r--r--drivers/acpi/battery.c72
1 files changed, 48 insertions, 24 deletions
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index 93ecae55fe6a..05fe9ebfb9b5 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -430,39 +430,24 @@ static int acpi_battery_get_status(struct acpi_battery *battery)
return 0;
}
-static int acpi_battery_get_info(struct acpi_battery *battery)
+
+static int extract_battery_info(const int use_bix,
+ struct acpi_battery *battery,
+ const struct acpi_buffer *buffer)
{
int result = -EFAULT;
- acpi_status status = 0;
- char *name = test_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags) ?
- "_BIX" : "_BIF";
-
- struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
- if (!acpi_battery_present(battery))
- return 0;
- mutex_lock(&battery->lock);
- status = acpi_evaluate_object(battery->device->handle, name,
- NULL, &buffer);
- mutex_unlock(&battery->lock);
-
- if (ACPI_FAILURE(status)) {
- ACPI_EXCEPTION((AE_INFO, status, "Evaluating %s", name));
- return -ENODEV;
- }
-
- if (battery_bix_broken_package)
- result = extract_package(battery, buffer.pointer,
+ if (use_bix && battery_bix_broken_package)
+ result = extract_package(battery, buffer->pointer,
extended_info_offsets + 1,
ARRAY_SIZE(extended_info_offsets) - 1);
- else if (test_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags))
- result = extract_package(battery, buffer.pointer,
+ else if (use_bix)
+ result = extract_package(battery, buffer->pointer,
extended_info_offsets,
ARRAY_SIZE(extended_info_offsets));
else
- result = extract_package(battery, buffer.pointer,
+ result = extract_package(battery, buffer->pointer,
info_offsets, ARRAY_SIZE(info_offsets));
- kfree(buffer.pointer);
if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags))
battery->full_charge_capacity = battery->design_capacity;
if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags) &&
@@ -483,6 +468,45 @@ static int acpi_battery_get_info(struct acpi_battery *battery)
return result;
}
+static int acpi_battery_get_info(struct acpi_battery *battery)
+{
+ const int xinfo = test_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags);
+ int use_bix;
+ int result = -ENODEV;
+
+ if (!acpi_battery_present(battery))
+ return 0;
+
+
+ for (use_bix = xinfo ? 1 : 0; use_bix >= 0; use_bix--) {
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ acpi_status status = AE_ERROR;
+
+ mutex_lock(&battery->lock);
+ status = acpi_evaluate_object(battery->device->handle,
+ use_bix ? "_BIX":"_BIF",
+ NULL, &buffer);
+ mutex_unlock(&battery->lock);
+
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status, "Evaluating %s",
+ use_bix ? "_BIX":"_BIF"));
+ } else {
+ result = extract_battery_info(use_bix,
+ battery,
+ &buffer);
+
+ kfree(buffer.pointer);
+ break;
+ }
+ }
+
+ if (!result && !use_bix && xinfo)
+ pr_warn(FW_BUG "The _BIX method is broken, using _BIF.\n");
+
+ return result;
+}
+
static int acpi_battery_get_state(struct acpi_battery *battery)
{
int result = 0;