diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 4269735aadde..76104f715efa 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -23,6 +23,8 @@ #include "fan.h" #include "internal.h" +#define ACPI_D_STATE_INVALID ACPI_D_STATE_COUNT + /** * acpi_power_state_string - String representation of ACPI device power state. * @state: ACPI device power state to return the string representation of. @@ -293,24 +295,28 @@ int acpi_bus_set_power(acpi_handle handle, int state) } EXPORT_SYMBOL(acpi_bus_set_power); -int acpi_bus_init_power(struct acpi_device *device) +static int acpi_device_init_power(struct acpi_device *device) { int state; int result; - if (!device) - return -EINVAL; - - device->power.state = ACPI_STATE_UNKNOWN; - if (!acpi_device_is_present(device)) { - device->flags.initialized = false; - return -ENXIO; - } - result = acpi_device_get_power(device, &state); if (result) return result; + /* + * If the current power state of the device is D0 and it has a parent + * whose power state is not ignored, and the parent's power state + * initialization has failed, the parent's power state can be updated to + * D0 for consistency. + */ + if (!device->power.flags.ignore_parent && state == ACPI_STATE_D0) { + struct acpi_device *parent = acpi_dev_parent(device); + + if (parent && parent->power.state == ACPI_D_STATE_INVALID) + parent->power.state = ACPI_STATE_D0; + } + if (state < ACPI_STATE_D3_COLD && device->power.flags.power_resources) { /* Reference count the power resources. */ result = acpi_power_on_resources(device, state); @@ -340,9 +346,36 @@ int acpi_bus_init_power(struct acpi_device *device) state = ACPI_STATE_D0; } device->power.state = state; + + acpi_handle_debug(device->handle, "Initial power state: %s\n", + acpi_power_state_string(state)); + return 0; } +int acpi_bus_init_power(struct acpi_device *device) +{ + int result; + + if (device->power.state != ACPI_STATE_UNKNOWN) + return 0; + + /* + * The ACPI device power state can be only initialized once. If this + * fails, ACPI power management will not be used for the device going + * forward. + */ + result = acpi_device_init_power(device); + if (result) { + device->flags.power_manageable = 0; + device->power.state = ACPI_D_STATE_INVALID; + acpi_handle_info(device->handle, + "Initial power state undetermined, ACPI PM disabled\n"); + } + + return result; +} + /** * acpi_device_fix_up_power - Force device with missing _PSC into D0. * @device: Device object whose power state is to be fixed up. @@ -464,8 +497,13 @@ static int acpi_power_up_if_adr_present(struct acpi_device *adev, void *not_used if (!(adev->flags.power_manageable && adev->pnp.type.bus_address)) return 0; - acpi_handle_debug(adev->handle, "Power state: %s\n", - acpi_power_state_string(adev->power.state)); + /* + * This is done during the PCI root initialization which occurs before + * acpi_bus_attach() is called for the device, so the ACPI power state + * of the device needs to be initialized here. + */ + if (acpi_bus_init_power(adev)) + return 0; if (adev->power.state == ACPI_STATE_D3_COLD) return acpi_device_set_power(adev, ACPI_STATE_D0); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index ec3a802e9238..13d1e77c9973 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -1142,8 +1143,7 @@ static void acpi_bus_get_power_flags(struct acpi_device *device) device->power.states[ACPI_STATE_D3_COLD].flags.valid = 1; } - if (acpi_bus_init_power(device)) - device->flags.power_manageable = 0; + device->power.state = ACPI_STATE_UNKNOWN; } static void acpi_bus_get_flags(struct acpi_device *device) @@ -2337,38 +2337,50 @@ static int acpi_scan_attach_handler(struct acpi_device *device) static int acpi_bus_attach(struct acpi_device *device, void *first_pass) { bool skip = !first_pass && device->flags.visited; + struct pci_dev *pci; acpi_handle ejd; int ret; if (skip) goto ok; + device->flags.initialized = true; + if (ACPI_SUCCESS(acpi_bus_get_ejd(device->handle, &ejd))) register_dock_dependent_device(device, ejd); acpi_bus_get_status(device); - /* Skip devices that are not ready for enumeration (e.g. not present) */ - if (!acpi_dev_ready_for_enumeration(device)) { - device->flags.initialized = false; + /* + * If the given ACPI device object has been already associated with a + * PCI device found on the bus, its status is effectively "present and + * enabled", and dependencies are not tracked for PCI devices, so it is + * not necessary or even useful to check the device's readiness in that + * case. + */ + pci = acpi_dev_get_pci_dev(device); + if (pci) { + acpi_handle_debug(device->handle, "PCI companion %s found\n", + pci_name(pci)); + + if (!acpi_device_is_present(device)) + pci_info(pci, FW_BUG "ACPI status differs from reality\n"); + + pci_dev_put(pci); + } else if (!acpi_dev_ready_for_enumeration(device)) { + /* The device is not ready (e.g. not present), so skip it. */ acpi_device_clear_enumerated(device); - device->flags.power_manageable = 0; return 0; } + if (device->handler) goto ok; acpi_ec_register_opregions(device); - if (!device->flags.initialized) { - device->flags.power_manageable = - device->power.states[ACPI_STATE_D0].flags.valid; - if (acpi_bus_init_power(device)) - device->flags.power_manageable = 0; + acpi_bus_init_power(device); - device->flags.initialized = true; - } else if (device->flags.visited) { + if (device->flags.visited) goto ok; - } ret = acpi_scan_attach_handler(device); if (ret < 0)