From 2835f327bd1240508db2c89fe94a056faa53c49a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Almeida?= Date: Fri, 8 Oct 2021 00:05:29 -0300 Subject: [PATCH 1/7] ACPI: battery: Accept charges over the design capacity as full MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some buggy firmware and/or brand new batteries can support a charge that's slightly over the reported design capacity. In such cases, the kernel will report to userspace that the charging state of the battery is "Unknown", when in reality the battery charge is "Full", at least from the design capacity point of view. Make the fallback condition accepts capacities over the designed capacity so userspace knows that is full. Signed-off-by: André Almeida Reviewed-by: Hans de Goede Reviewed-by: Sebastian Reichel Signed-off-by: Rafael J. Wysocki --- drivers/acpi/battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index dae91f906cea..8afa85d6eb6a 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -169,7 +169,7 @@ static int acpi_battery_is_charged(struct acpi_battery *battery) return 1; /* fallback to using design values for broken batteries */ - if (battery->design_capacity == battery->capacity_now) + if (battery->design_capacity <= battery->capacity_now) return 1; /* we don't do any sort of metric based on percentages */ From a1224f34d72a103829d6953935d6c6621f135b83 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 15 Oct 2021 19:14:10 +0200 Subject: [PATCH 2/7] ACPI: PM: Check states of power resources during initialization To avoid situations in which the actual states of certain ACPI power resources are not known just because they have never been referenced by any device configuration objects, check the initial states of all power resources as soon as they are found in the ACPI namespace (and fall back to turning them on if the state check fails). Signed-off-by: Rafael J. Wysocki Tested-by: Andreas K. Huettel --- drivers/acpi/power.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index f0ed4414edb1..bb03fb0eaa0e 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -943,6 +943,7 @@ struct acpi_device *acpi_add_power_resource(acpi_handle handle) union acpi_object acpi_object; struct acpi_buffer buffer = { sizeof(acpi_object), &acpi_object }; acpi_status status; + u8 state_dummy; int result; acpi_bus_get_device(handle, &device); @@ -971,6 +972,10 @@ struct acpi_device *acpi_add_power_resource(acpi_handle handle) resource->order = acpi_object.power_resource.resource_order; resource->state = ACPI_POWER_RESOURCE_STATE_UNKNOWN; + /* Get the initial state or just flip it on if that fails. */ + if (acpi_power_get_state(resource, &state_dummy)) + __acpi_power_on(resource); + pr_info("%s [%s]\n", acpi_device_name(device), acpi_device_bid(device)); device->flags.match_driver = true; From 7a63296d6f579a02b2675b4b0fe5b1cd3235e8d3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 15 Oct 2021 19:01:28 +0200 Subject: [PATCH 3/7] ACPI: PM: Turn off unused wakeup power resources If an ACPI power resource is found to be "on" during the initialization of the list of wakeup power resources of a device, it is reference counted and its wakeup_enabled flag is set, which is problematic if the deivce in question is the only user of the given power resource, it is never runtime-suspended and it is not allowed to wake up the system from sleep, because in that case the given power resource will stay "on" until the system reboots and energy will be wasted. It is better to simply turn off wakeup power resources that are "on" during the initialization unless their reference counters are not zero, because that may be the only opportunity to prevent them from staying in the "on" state all the time. Fixes: b5d667eb392e ("ACPI / PM: Take unusual configurations of power resources into account") Signed-off-by: Rafael J. Wysocki --- drivers/acpi/power.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index bb03fb0eaa0e..1b7431fb5fb2 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -615,20 +615,19 @@ int acpi_power_wakeup_list_init(struct list_head *list, int *system_level_p) list_for_each_entry(entry, list, node) { struct acpi_power_resource *resource = entry->resource; - int result; u8 state; mutex_lock(&resource->resource_lock); - result = acpi_power_get_state(resource, &state); - if (result) { - mutex_unlock(&resource->resource_lock); - return result; - } - if (state == ACPI_POWER_RESOURCE_STATE_ON) { - resource->ref_count++; - resource->wakeup_enabled = true; - } + /* + * Make sure that the power resource state and its reference + * counter value are consistent with each other. + */ + if (!resource->ref_count && + !acpi_power_get_state(resource, &state) && + state == ACPI_POWER_RESOURCE_STATE_ON) + __acpi_power_off(resource); + if (system_level > resource->system_level) system_level = resource->system_level; From a2d7b2e004af6b09f21ac3d10f8f4456c16a8ddf Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 16 Oct 2021 12:11:08 +0200 Subject: [PATCH 4/7] ACPI: PM: Fix sharing of wakeup power resources If an ACPI wakeup power resource is shared between multiple devices, it may not be managed correctly. Suppose, for example, that two devices, A and B, share a wakeup power resource P whose wakeup_enabled flag is 0 initially. Next, suppose that wakeup power is enabled for A and B, in this order, and disabled for B. When wakeup power is enabled for A, P will be turned on and its wakeup_enabled flag will be set. Next, when wakeup power is enabled for B, P will not be touched, because its wakeup_enabled flag is set. Now, when wakeup power is disabled for B, P will be turned off which is incorrect, because A will still need P in order to signal wakeup. Moreover, if wakeup power is enabled for A and then disabled for B, the latter will cause P to be turned off incorrectly (it will be still needed by A), because acpi_disable_wakeup_device_power() is allowed to manipulate power resources when the wakeup.prepare_count counter of the given device is 0. While the first issue could be addressed by changing the wakeup_enabled power resource flag into a counter, addressing the second one requires modifying acpi_disable_wakeup_device_power() to do nothing when the target device's wakeup.prepare_count reference counter is zero and that would cause the new counter to be redundant. Namely, if acpi_disable_wakeup_device_power() is modified as per the above, every change of the new counter following a wakeup.prepare_count change would be reflected by the analogous change of the main reference counter of the given power resource. Accordingly, modify acpi_disable_wakeup_device_power() to do nothing when the target device's wakeup.prepare_count reference counter is zero and drop the power resource wakeup_enabled flag altogether. While at it, ensure that all of the power resources that can be turned off will be turned off when disabling device wakeup due to a power resource manipulation error, to prevent energy from being wasted. Fixes: b5d667eb392e ("ACPI / PM: Take unusual configurations of power resources into account") Signed-off-by: Rafael J. Wysocki --- drivers/acpi/power.c | 69 +++++++++++++++----------------------------- 1 file changed, 24 insertions(+), 45 deletions(-) diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 1b7431fb5fb2..ba6aaf7224a1 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -52,7 +52,6 @@ struct acpi_power_resource { u32 order; unsigned int ref_count; u8 state; - bool wakeup_enabled; struct mutex resource_lock; struct list_head dependents; }; @@ -710,7 +709,6 @@ int acpi_device_sleep_wake(struct acpi_device *dev, */ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state) { - struct acpi_power_resource_entry *entry; int err = 0; if (!dev || !dev->wakeup.flags.valid) @@ -721,26 +719,13 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state) if (dev->wakeup.prepare_count++) goto out; - list_for_each_entry(entry, &dev->wakeup.resources, node) { - struct acpi_power_resource *resource = entry->resource; - - mutex_lock(&resource->resource_lock); - - if (!resource->wakeup_enabled) { - err = acpi_power_on_unlocked(resource); - if (!err) - resource->wakeup_enabled = true; - } - - mutex_unlock(&resource->resource_lock); - - if (err) { - dev_err(&dev->dev, - "Cannot turn wakeup power resources on\n"); - dev->wakeup.flags.valid = 0; - goto out; - } + err = acpi_power_on_list(&dev->wakeup.resources); + if (err) { + dev_err(&dev->dev, "Cannot turn on wakeup power resources\n"); + dev->wakeup.flags.valid = 0; + goto out; } + /* * Passing 3 as the third argument below means the device may be * put into arbitrary power state afterward. @@ -770,39 +755,33 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev) mutex_lock(&acpi_device_lock); - if (--dev->wakeup.prepare_count > 0) + if (dev->wakeup.prepare_count > 1) { + dev->wakeup.prepare_count--; goto out; + } - /* - * Executing the code below even if prepare_count is already zero when - * the function is called may be useful, for example for initialisation. - */ - if (dev->wakeup.prepare_count < 0) - dev->wakeup.prepare_count = 0; + /* Do nothing if wakeup power has not been enabled for this device. */ + if (!dev->wakeup.prepare_count) + goto out; err = acpi_device_sleep_wake(dev, 0, 0, 0); if (err) goto out; + /* + * All of the power resources in the list need to be turned off even if + * there are errors. + */ list_for_each_entry(entry, &dev->wakeup.resources, node) { - struct acpi_power_resource *resource = entry->resource; + int ret; - mutex_lock(&resource->resource_lock); - - if (resource->wakeup_enabled) { - err = acpi_power_off_unlocked(resource); - if (!err) - resource->wakeup_enabled = false; - } - - mutex_unlock(&resource->resource_lock); - - if (err) { - dev_err(&dev->dev, - "Cannot turn wakeup power resources off\n"); - dev->wakeup.flags.valid = 0; - break; - } + ret = acpi_power_off(entry->resource); + if (ret && !err) + err = ret; + } + if (err) { + dev_err(&dev->dev, "Cannot turn off wakeup power resources\n"); + dev->wakeup.flags.valid = 0; } out: From a9a8f827f9e81dc8506d3283d166cd6efe822302 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 15 Oct 2021 19:04:26 +0200 Subject: [PATCH 5/7] ACPI: PM: Turn off wakeup power resources on _DSW/_PSW errors If acpi_device_sleep_wake() called by acpi_enable_wakeup_device_power() returns an error which means that the evaluation of either _DWS or _PSW has failed, turn off all of the device's wakeup power resources to be consistent with the clearing of dev->wakeup.prepare_count. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/power.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index ba6aaf7224a1..112256154880 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -731,8 +731,10 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state) * put into arbitrary power state afterward. */ err = acpi_device_sleep_wake(dev, 1, sleep_state, 3); - if (err) + if (err) { + acpi_power_off_list(&dev->wakeup.resources); dev->wakeup.prepare_count = 0; + } out: mutex_unlock(&acpi_device_lock); From d69d1f708093347c085699cc0b547982e48c47b8 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 20 Oct 2021 21:10:17 +0200 Subject: [PATCH 6/7] ACPI: PM: sleep: Do not set suspend_ops unnecessarily If none of the S1 - S3 sleep states is supported, it is not necessary to register suspend_ops, so don't do that then. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/sleep.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 3023224515ab..eaa47753b758 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -815,14 +815,18 @@ void __weak acpi_s2idle_setup(void) static void acpi_sleep_suspend_setup(void) { + bool suspend_ops_needed = false; int i; for (i = ACPI_STATE_S1; i < ACPI_STATE_S4; i++) - if (acpi_sleep_state_supported(i)) + if (acpi_sleep_state_supported(i)) { sleep_states[i] = 1; + suspend_ops_needed = true; + } - suspend_set_ops(old_suspend_ordering ? - &acpi_suspend_ops_old : &acpi_suspend_ops); + if (suspend_ops_needed) + suspend_set_ops(old_suspend_ordering ? + &acpi_suspend_ops_old : &acpi_suspend_ops); acpi_s2idle_setup(); } From 3d730ee686800d71ecc5c3cb8460dcdcdeaf38a3 Mon Sep 17 00:00:00 2001 From: Stefan Schaeckeler Date: Sun, 24 Oct 2021 15:04:45 -0700 Subject: [PATCH 7/7] ACPI: AC: Quirk GK45 to skip reading _PSR Let GK45 not go into BIOS for determining the AC power state. The BIOS wrongly returns 0, so hardcode the power state to 1. The mini PC GK45 by Besstar Tech Lld. (aka Kodlix) just runs off AC. It does not include any batteries. Nevertheless BIOS reports AC off: root@kodlix:/usr/src/linux# cat /sys/class/power_supply/ADP1/online 0 root@kodlix:/usr/src/linux# modprobe acpi_dbg root@kodlix:/usr/src/linux# tools/power/acpi/acpidbg - find _PSR \_SB.PCI0.SBRG.H_EC.ADP1._PSR Method 000000009283cee8 001 Args 0 Len 001C Aml 00000000f54e5f67 - execute \_SB.PCI0.SBRG.H_EC.ADP1._PSR Evaluating \_SB.PCI0.SBRG.H_EC.ADP1._PSR Evaluation of \_SB.PCI0.SBRG.H_EC.ADP1._PSR returned object 00000000dc08c187, external buffer length 18 [Integer] = 0000000000000000 that should be [Integer] = 0000000000000001 Signed-off-by: Stefan Schaeckeler Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ac.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index b0cb662233f1..81aff651a0d4 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -61,6 +61,7 @@ static SIMPLE_DEV_PM_OPS(acpi_ac_pm, NULL, acpi_ac_resume); static int ac_sleep_before_get_state_ms; static int ac_check_pmic = 1; +static int ac_only; static struct acpi_driver acpi_ac_driver = { .name = "ac", @@ -93,6 +94,11 @@ static int acpi_ac_get_state(struct acpi_ac *ac) if (!ac) return -EINVAL; + if (ac_only) { + ac->state = 1; + return 0; + } + status = acpi_evaluate_integer(ac->device->handle, "_PSR", NULL, &ac->state); if (ACPI_FAILURE(status)) { @@ -200,6 +206,12 @@ static int __init ac_do_not_check_pmic_quirk(const struct dmi_system_id *d) return 0; } +static int __init ac_only_quirk(const struct dmi_system_id *d) +{ + ac_only = 1; + return 0; +} + /* Please keep this list alphabetically sorted */ static const struct dmi_system_id ac_dmi_table[] __initconst = { { @@ -209,6 +221,13 @@ static const struct dmi_system_id ac_dmi_table[] __initconst = { DMI_MATCH(DMI_PRODUCT_NAME, "EF20EA"), }, }, + { + /* Kodlix GK45 returning incorrect state */ + .callback = ac_only_quirk, + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "GK45"), + }, + }, { /* Lenovo Ideapad Miix 320, AXP288 PMIC, separate fuel-gauge */ .callback = ac_do_not_check_pmic_quirk,