From 3031cddea633ea5328162d3d712a582e4205dbed Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Sat, 20 Sep 2014 13:19:45 +0200 Subject: ACPI / SBS: Don't assume the existence of an SBS charger Apple hardware continues to expose an ACPI AC charger even when using SBS to report battery state. The charger status byte returns all 0s in this case. Since the spec requires that bit 4 be 1 at all times, assume that there's not really a charger if it's set to zero. Signed-off-by: Matthew Garrett Signed-off-by: Andreas Noever Signed-off-by: Rafael J. Wysocki --- drivers/acpi/sbs.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index 366ca40a6f70..b1df4eee8657 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -109,6 +109,7 @@ struct acpi_sbs { u8 batteries_supported:4; u8 manager_present:1; u8 charger_present:1; + u8 charger_exists:1; }; #define to_acpi_sbs(x) container_of(x, struct acpi_sbs, charger) @@ -429,9 +430,19 @@ static int acpi_ac_get_present(struct acpi_sbs *sbs) result = acpi_smbus_read(sbs->hc, SMBUS_READ_WORD, ACPI_SBS_CHARGER, 0x13, (u8 *) & status); - if (!result) - sbs->charger_present = (status >> 15) & 0x1; - return result; + + if (result) + return result; + + /* + * The spec requires that bit 4 always be 1. If it's not set, assume + * that the implementation doesn't support an SBS charger + */ + if (!(status >> 4) & 0x1) + return -ENODEV; + + sbs->charger_present = (status >> 15) & 0x1; + return 0; } static ssize_t acpi_battery_alarm_show(struct device *dev, @@ -554,6 +565,7 @@ static int acpi_charger_add(struct acpi_sbs *sbs) if (result) goto end; + sbs->charger_exists = 1; sbs->charger.name = "sbs-charger"; sbs->charger.type = POWER_SUPPLY_TYPE_MAINS; sbs->charger.properties = sbs_ac_props; @@ -580,9 +592,12 @@ static void acpi_sbs_callback(void *context) struct acpi_battery *bat; u8 saved_charger_state = sbs->charger_present; u8 saved_battery_state; - acpi_ac_get_present(sbs); - if (sbs->charger_present != saved_charger_state) - kobject_uevent(&sbs->charger.dev->kobj, KOBJ_CHANGE); + + if (sbs->charger_exists) { + acpi_ac_get_present(sbs); + if (sbs->charger_present != saved_charger_state) + kobject_uevent(&sbs->charger.dev->kobj, KOBJ_CHANGE); + } if (sbs->manager_present) { for (id = 0; id < MAX_SBS_BAT; ++id) { @@ -619,7 +634,7 @@ static int acpi_sbs_add(struct acpi_device *device) device->driver_data = sbs; result = acpi_charger_add(sbs); - if (result) + if (result && result != -ENODEV) goto end; result = acpi_manager_get_info(sbs); -- cgit v1.2.3 From 9faf6136ff4647452580b019f4b16f8c5082e589 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Sat, 20 Sep 2014 13:19:46 +0200 Subject: ACPI / SBS: Disable smart battery manager on Apple Touching the smart battery manager at all on Apple hardware appears to make it unhappy - unplugging the AC adapter triggers accesses that hang the controller for several minutes. Quirk it out via DMI in order to avoid this. Compensate by changing battery presence if we fail to communicate with the battery. Signed-off-by: Matthew Garrett Signed-off-by: Andreas Noever Signed-off-by: Rafael J. Wysocki --- drivers/acpi/sbs.c | 51 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 8 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index b1df4eee8657..32aeceae10e3 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "sbshc.h" #include "battery.h" @@ -61,6 +62,8 @@ static unsigned int cache_time = 1000; module_param(cache_time, uint, 0644); MODULE_PARM_DESC(cache_time, "cache time in milliseconds"); +static bool sbs_manager_broken; + #define MAX_SBS_BAT 4 #define ACPI_SBS_BLOCK_MAX 32 @@ -494,16 +497,21 @@ static int acpi_battery_read(struct acpi_battery *battery) ACPI_SBS_MANAGER, 0x01, (u8 *)&state, 2); } else if (battery->id == 0) battery->present = 1; + if (result || !battery->present) return result; if (saved_present != battery->present) { battery->update_time = 0; result = acpi_battery_get_info(battery); - if (result) + if (result) { + battery->present = 0; return result; + } } result = acpi_battery_get_state(battery); + if (result) + battery->present = 0; return result; } @@ -535,6 +543,7 @@ static int acpi_battery_add(struct acpi_sbs *sbs, int id) result = power_supply_register(&sbs->device->dev, &battery->bat); if (result) goto end; + result = device_create_file(battery->bat.dev, &alarm_attr); if (result) goto end; @@ -613,12 +622,31 @@ static void acpi_sbs_callback(void *context) } } +static int disable_sbs_manager(const struct dmi_system_id *d) +{ + sbs_manager_broken = true; + return 0; +} + +static struct dmi_system_id acpi_sbs_dmi_table[] = { + { + .callback = disable_sbs_manager, + .ident = "Apple", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc.") + }, + }, + { }, +}; + static int acpi_sbs_add(struct acpi_device *device) { struct acpi_sbs *sbs; int result = 0; int id; + dmi_check_system(acpi_sbs_dmi_table); + sbs = kzalloc(sizeof(struct acpi_sbs), GFP_KERNEL); if (!sbs) { result = -ENOMEM; @@ -637,14 +665,21 @@ static int acpi_sbs_add(struct acpi_device *device) if (result && result != -ENODEV) goto end; - result = acpi_manager_get_info(sbs); - if (!result) { - sbs->manager_present = 1; - for (id = 0; id < MAX_SBS_BAT; ++id) - if ((sbs->batteries_supported & (1 << id))) - acpi_battery_add(sbs, id); - } else + result = 0; + + if (!sbs_manager_broken) { + result = acpi_manager_get_info(sbs); + if (!result) { + sbs->manager_present = 0; + for (id = 0; id < MAX_SBS_BAT; ++id) + if ((sbs->batteries_supported & (1 << id))) + acpi_battery_add(sbs, id); + } + } + + if (!sbs->manager_present) acpi_battery_add(sbs, 0); + acpi_smbus_register_callback(sbs->hc, acpi_sbs_callback, sbs); end: if (result) -- cgit v1.2.3 From 7bc5a2bad0b8d9d1ac9f7b8b33150e4ddf197334 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Sat, 20 Sep 2014 13:19:47 +0200 Subject: ACPI: Support _OSI("Darwin") correctly Apple hardware queries _OSI("Darwin") in order to determine whether the system is running OS X, and changes firmware behaviour based on the answer. The most obvious difference in behaviour is that Thunderbolt hardware is forcibly powered down unless the system is running OS X. The obvious solution would be to simply add Darwin to the list of supported _OSI strings, but this causes problems. Recent Apple hardware includes two separate methods for checking _OSI strings. The first will check whether Darwin is supported, and if so will exit. The second will check whether Darwin is supported, but will then continue to check for further operating systems. If a further operating system is found then later firmware code will assume that the OS is not OS X. This results in the unfortunate situation where the Thunderbolt controller is available at boot time but remains powered down after suspend. The easiest way to handle this is to special-case it in the Linux-specific OSI handling code. If we see Darwin, we should answer true and then disable all other _OSI vendor strings. The next problem is that the Apple PCI _OSC method has the following code: if (LEqual (0x01, OSDW ())) if (LAnd (LEqual (Arg0, GUID), NEXP) (do stuff) else (fail) NEXP is a value in high memory and is presumably under the control of the firmware. No methods sets it. The methods that are called in the "do stuff" path are dummies. Unless there's some additional firmware call in early boot, there's no way for this call to succeed - and even if it does, it doesn't do anything. The easiest way to handle this is simply to ignore it. We know which flags would be set, so just set them by hand if the platform is running in Darwin mode. Signed-off-by: Matthew Garrett [andreas.noever@gmail.com: merged two patches, do not touch ACPICA] Signed-off-by: Andreas Noever Signed-off-by: Rafael J. Wysocki --- drivers/acpi/osl.c | 10 ++++++++++ drivers/acpi/pci_root.c | 14 ++++++++++++++ 2 files changed, 24 insertions(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 3abe9b223ba7..938b6ac71dde 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -152,6 +152,16 @@ static u32 acpi_osi_handler(acpi_string interface, u32 supported) osi_linux.dmi ? " via DMI" : ""); } + if (!strcmp("Darwin", interface)) { + /* + * Apple firmware will behave poorly if it receives positive + * answers to "Darwin" and any other OS. Respond positively + * to Darwin and then disable all other vendor strings. + */ + acpi_update_interfaces(ACPI_DISABLE_ALL_VENDOR_STRINGS); + supported = ACPI_UINT32_MAX; + } + return supported; } diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index e6ae603ed1a1..cd4de7e038ea 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -35,6 +35,7 @@ #include #include #include +#include #include /* for acpi_hest_init() */ #include "internal.h" @@ -429,6 +430,19 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm, struct acpi_device *device = root->device; acpi_handle handle = device->handle; + /* + * Apple always return failure on _OSC calls when _OSI("Darwin") has + * been called successfully. We know the feature set supported by the + * platform, so avoid calling _OSC at all + */ + + if (dmi_match(DMI_SYS_VENDOR, "Apple Inc.")) { + root->osc_control_set = ~OSC_PCI_EXPRESS_PME_CONTROL; + decode_osc_control(root, "OS assumes control of", + root->osc_control_set); + return; + } + /* * All supported architectures that use ACPI have support for * PCI domains, so we indicate this in _OSC support capabilities. -- cgit v1.2.3 From 5159e39bdf119613adc6556901ae2d2e5e027551 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Sun, 28 Sep 2014 01:27:42 +0200 Subject: ACPI / SBS: Fix check in acpi_ac_get_present() Parentheses are missing under an if () statement in acpi_ac_get_present() which makes the check work differently from what was intended (at least according to the comment right above it). Add the missing parens. The problem was found by sparse. Signed-off-by: Rafael J. Wysocki Cc: All applicable --- drivers/acpi/sbs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index 32aeceae10e3..a7a3edd28beb 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -441,7 +441,7 @@ static int acpi_ac_get_present(struct acpi_sbs *sbs) * The spec requires that bit 4 always be 1. If it's not set, assume * that the implementation doesn't support an SBS charger */ - if (!(status >> 4) & 0x1) + if (!((status >> 4) & 0x1)) return -ENODEV; sbs->charger_present = (status >> 15) & 0x1; -- cgit v1.2.3