// SPDX-License-Identifier: GPL-2.0-or-later /* * drivers/acpi/power.c - ACPI Power Resources management. * * Copyright (C) 2001 - 2015 Intel Corp. * Author: Andy Grover <andrew.grover@intel.com> * Author: Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
*/
/* * ACPI power-managed devices may be controlled in two ways: * 1. via "Device Specific (D-State) Control" * 2. via "Power Resource Control". * The code below deals with ACPI Power Resources control. * * An ACPI "power resource object" represents a software controllable power * plane, clock plane, or other resource depended on by a device. * * A device may rely on multiple power resources, and a power resource * may be shared by multiple devices.
*/
/* The caller is expected to check the package element types */
rhandle = package->package.elements[i].reference.handle; for (j = start; j < i; j++) {
dup = package->package.elements[j].reference.handle; if (dup == rhandle) returntrue;
}
returnfalse;
}
int acpi_extract_power_resources(union acpi_object *package, unsignedint start, struct list_head *list)
{ unsignedint i; int err = 0;
for (i = start; i < package->package.count; i++) { union acpi_object *element = &package->package.elements[i]; struct acpi_device *rdev;
acpi_handle rhandle;
/* The state of the list is 'on' IFF all resources are 'on'. */
list_for_each_entry(entry, list, node) { struct acpi_power_resource *resource = entry->resource; int result;
mutex_lock(&resource->resource_lock);
result = acpi_power_get_state(resource, &cur_state);
mutex_unlock(&resource->resource_lock); if (result) return result;
if (cur_state != ACPI_POWER_RESOURCE_STATE_ON) break;
}
pr_debug("Power resource list is %s\n", str_on_off(cur_state));
*state = cur_state; return 0;
}
staticint
acpi_power_resource_add_dependent(struct acpi_power_resource *resource, struct device *dev)
{ struct acpi_power_dependent_device *dep; int ret = 0;
mutex_lock(&resource->resource_lock);
list_for_each_entry(dep, &resource->dependents, node) { /* Only add it once */ if (dep->dev == dev) goto unlock;
}
dep = kzalloc(sizeof(*dep), GFP_KERNEL); if (!dep) {
ret = -ENOMEM; goto unlock;
}
dep->dev = dev;
list_add_tail(&dep->node, &resource->dependents);
dev_dbg(dev, "added power dependency to [%s]\n",
resource_dev_name(resource));
mutex_lock(&resource->resource_lock);
list_for_each_entry(dep, &resource->dependents, node) { if (dep->dev == dev) {
list_del(&dep->node);
kfree(dep);
dev_dbg(dev, "removed power dependency to [%s]\n",
resource_dev_name(resource)); break;
}
}
mutex_unlock(&resource->resource_lock);
}
/** * acpi_device_power_add_dependent - Add dependent device of this ACPI device * @adev: ACPI device pointer * @dev: Dependent device * * If @adev has non-empty _PR0 the @dev is added as dependent device to all * power resources returned by it. This means that whenever these power * resources are turned _ON the dependent devices get runtime resumed. This * is needed for devices such as PCI to allow its driver to re-initialize * it after it went to D0uninitialized. * * If @adev does not have _PR0 this does nothing. * * Returns %0 in case of success and negative errno otherwise.
*/ int acpi_device_power_add_dependent(struct acpi_device *adev, struct device *dev)
{ struct acpi_power_resource_entry *entry; struct list_head *resources; int ret;
if (!adev->flags.power_manageable) return 0;
resources = &adev->power.states[ACPI_STATE_D0].resources;
list_for_each_entry(entry, resources, node) {
ret = acpi_power_resource_add_dependent(entry->resource, dev); if (ret) goto err;
}
/** * acpi_device_power_remove_dependent - Remove dependent device * @adev: ACPI device pointer * @dev: Dependent device * * Does the opposite of acpi_device_power_add_dependent() and removes the * dependent device if it is found. Can be called to @adev that does not * have _PR0 as well.
*/ void acpi_device_power_remove_dependent(struct acpi_device *adev, struct device *dev)
{ struct acpi_power_resource_entry *entry; struct list_head *resources;
/* * If there are other dependents on this power resource we need to * resume them now so that their drivers can re-initialize the * hardware properly after it went back to D0.
*/ if (list_empty(&resource->dependents) ||
list_is_singular(&resource->dependents)) return 0;
if (--resource->ref_count) {
acpi_handle_debug(resource->device.handle, "Power resource still in use\n");
} else {
result = __acpi_power_off(resource); if (result)
resource->ref_count++;
} return result;
}
staticint acpi_power_off(struct acpi_power_resource *resource)
{ int result;
mutex_lock(&resource->resource_lock);
result = acpi_power_off_unlocked(resource);
mutex_unlock(&resource->resource_lock); return result;
}
staticint acpi_power_off_list(struct list_head *list)
{ struct acpi_power_resource_entry *entry; int result = 0;
list_for_each_entry_reverse(entry, list, node) {
result = acpi_power_off(entry->resource); if (result) goto err;
} return 0;
/* * 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;
/* -------------------------------------------------------------------------- Device Power Management
-------------------------------------------------------------------------- */
/** * acpi_device_sleep_wake - execute _DSW (Device Sleep Wake) or (deprecated in * ACPI 3.0) _PSW (Power State Wake) * @dev: Device to handle. * @enable: 0 - disable, 1 - enable the wake capabilities of the device. * @sleep_state: Target sleep state of the system. * @dev_state: Target power state of the device. * * Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power * State Wake) for the device, if present. On failure reset the device's * wakeup.flags.valid flag. * * RETURN VALUE: * 0 if either _DSW or _PSW has been successfully executed * 0 if neither _DSW nor _PSW has been found * -ENODEV if the execution of either _DSW or _PSW has failed
*/ int acpi_device_sleep_wake(struct acpi_device *dev, int enable, int sleep_state, int dev_state)
{ union acpi_object in_arg[3]; struct acpi_object_list arg_list = { 3, in_arg };
acpi_status status = AE_OK;
/* * Try to execute _DSW first. * * Three arguments are needed for the _DSW object: * Argument 0: enable/disable the wake capabilities * Argument 1: target system state * Argument 2: target device state * When _DSW object is called to disable the wake capabilities, maybe * the first argument is filled. The values of the other two arguments * are meaningless.
*/
in_arg[0].type = ACPI_TYPE_INTEGER;
in_arg[0].integer.value = enable;
in_arg[1].type = ACPI_TYPE_INTEGER;
in_arg[1].integer.value = sleep_state;
in_arg[2].type = ACPI_TYPE_INTEGER;
in_arg[2].integer.value = dev_state;
status = acpi_evaluate_object(dev->handle, "_DSW", &arg_list, NULL); if (ACPI_SUCCESS(status)) { return 0;
} elseif (status != AE_NOT_FOUND) {
acpi_handle_info(dev->handle, "_DSW execution failed\n");
dev->wakeup.flags.valid = 0; return -ENODEV;
}
/* * Prepare a wakeup device, two steps (Ref ACPI 2.0:P229): * 1. Power on the power resources required for the wakeup device * 2. Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power * State Wake) for the device, if present
*/ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state)
{ int err = 0;
if (!dev || !dev->wakeup.flags.valid) return -EINVAL;
mutex_lock(&acpi_device_lock);
dev_dbg(&dev->dev, "Enabling wakeup power (count %d)\n",
dev->wakeup.prepare_count);
if (dev->wakeup.prepare_count++) 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.
*/
err = acpi_device_sleep_wake(dev, 1, sleep_state, 3); if (err) {
acpi_power_off_list(&dev->wakeup.resources);
dev->wakeup.prepare_count = 0; goto out;
}
/* * Shutdown a wakeup device, counterpart of above method * 1. Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power * State Wake) for the device, if present * 2. Shutdown down the power resources
*/ int acpi_disable_wakeup_device_power(struct acpi_device *dev)
{ struct acpi_power_resource_entry *entry; int err = 0;
if (!dev || !dev->wakeup.flags.valid) return -EINVAL;
mutex_lock(&acpi_device_lock);
dev_dbg(&dev->dev, "Disabling wakeup power (count %d)\n",
dev->wakeup.prepare_count);
/* Do nothing if wakeup power has not been enabled for this device. */ if (dev->wakeup.prepare_count <= 0) goto out;
if (--dev->wakeup.prepare_count > 0) 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) { int ret;
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; goto out;
}
int acpi_power_get_inferred_state(struct acpi_device *device, int *state)
{
u8 list_state = ACPI_POWER_RESOURCE_STATE_OFF; int result = 0; int i = 0;
if (!device || !state) return -EINVAL;
/* * We know a device's inferred power state when all the resources * required for a given D-state are 'on'.
*/ for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) { struct list_head *list = &device->power.states[i].resources;
if (list_empty(list)) continue;
result = acpi_power_get_list_state(list, &list_state); if (result) return result;
int acpi_power_on_resources(struct acpi_device *device, int state)
{ if (!device || state < ACPI_STATE_D0 || state > ACPI_STATE_D3_HOT) return -EINVAL;
if (device->power.state == state || !device->flags.power_manageable) return 0;
if ((device->power.state < ACPI_STATE_D0)
|| (device->power.state > ACPI_STATE_D3_COLD)) return -ENODEV;
/* * First we reference all power resources required in the target list * (e.g. so the device doesn't lose power while transitioning). Then, * we dereference all power resources used in the current list.
*/ if (state < ACPI_STATE_D3_COLD)
result = acpi_power_on_list(
&device->power.states[state].resources);
if (!result && device->power.state < ACPI_STATE_D3_COLD)
acpi_power_off_list(
&device->power.states[device->power.state].resources);
/* We shouldn't change the state unless the above operations succeed. */
device->power.state = result ? ACPI_STATE_UNKNOWN : state;
/* Evaluate the object to get the system level and resource order. */
status = acpi_evaluate_object(handle, NULL, NULL, &buffer); if (ACPI_FAILURE(status)) goto err;
staticconststruct dmi_system_id dmi_leave_unused_power_resources_on[] = {
{ /* * The Toshiba Click Mini has a CPR3 power-resource which must * be on for the touchscreen to work, but which is not in any * _PR? lists. The other 2 affected power-resources are no-ops.
*/
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
DMI_MATCH(DMI_PRODUCT_NAME, "SATELLITE Click Mini L9W-B"),
},
},
{}
};
/** * acpi_turn_off_unused_power_resources - Turn off power resources not in use.
*/ void acpi_turn_off_unused_power_resources(void)
{ struct acpi_power_resource *resource;
if (dmi_check_system(dmi_leave_unused_power_resources_on)) return;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.