diff options
Diffstat (limited to 'drivers/ata/libata-acpi.c')
-rw-r--r-- | drivers/ata/libata-acpi.c | 76 |
1 files changed, 53 insertions, 23 deletions
diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index 97094496127e..dfd529a30c20 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -836,9 +836,11 @@ void ata_acpi_on_resume(struct ata_port *ap) } } -static int ata_acpi_choose_suspend_state(struct ata_device *dev) +static int ata_acpi_choose_suspend_state(struct ata_device *dev, bool runtime) { int d_max_in = ACPI_STATE_D3_COLD; + if (!runtime) + goto out; /* * For ATAPI, runtime D3 cold is only allowed @@ -848,53 +850,81 @@ static int ata_acpi_choose_suspend_state(struct ata_device *dev) !(zpodd_dev_enabled(dev) && zpodd_zpready(dev))) d_max_in = ACPI_STATE_D3_HOT; +out: return acpi_pm_device_sleep_state(&dev->sdev->sdev_gendev, NULL, d_max_in); } -/** - * ata_acpi_set_state - set the port power state - * @ap: target ATA port - * @state: state, on/off - * - * This function executes the _PS0/_PS3 ACPI method to set the power state. - * ACPI spec requires _PS0 when IDE power on and _PS3 when power off - */ -void ata_acpi_set_state(struct ata_port *ap, pm_message_t state) +static void sata_acpi_set_state(struct ata_port *ap, pm_message_t state) { + bool runtime = PMSG_IS_AUTO(state); struct ata_device *dev; acpi_handle handle; int acpi_state; - /* channel first and then drives for power on and vica versa - for power off */ - handle = ata_ap_acpi_handle(ap); - if (handle && state.event == PM_EVENT_ON) - acpi_bus_set_power(handle, ACPI_STATE_D0); - ata_for_each_dev(dev, &ap->link, ENABLED) { handle = ata_dev_acpi_handle(dev); if (!handle) continue; - if (state.event != PM_EVENT_ON) { - acpi_state = ata_acpi_choose_suspend_state(dev); + if (!(state.event & PM_EVENT_RESUME)) { + acpi_state = ata_acpi_choose_suspend_state(dev, runtime); if (acpi_state == ACPI_STATE_D0) continue; - if (zpodd_dev_enabled(dev) && + if (runtime && zpodd_dev_enabled(dev) && acpi_state == ACPI_STATE_D3_COLD) zpodd_enable_run_wake(dev); acpi_bus_set_power(handle, acpi_state); } else { - if (zpodd_dev_enabled(dev)) + if (runtime && zpodd_dev_enabled(dev)) zpodd_disable_run_wake(dev); acpi_bus_set_power(handle, ACPI_STATE_D0); } } +} + +/* ACPI spec requires _PS0 when IDE power on and _PS3 when power off */ +static void pata_acpi_set_state(struct ata_port *ap, pm_message_t state) +{ + struct ata_device *dev; + acpi_handle port_handle; + + port_handle = ata_ap_acpi_handle(ap); + if (!port_handle) + return; + + /* channel first and then drives for power on and vica versa + for power off */ + if (state.event & PM_EVENT_RESUME) + acpi_bus_set_power(port_handle, ACPI_STATE_D0); + + ata_for_each_dev(dev, &ap->link, ENABLED) { + acpi_handle dev_handle = ata_dev_acpi_handle(dev); + if (!dev_handle) + continue; + + acpi_bus_set_power(dev_handle, state.event & PM_EVENT_RESUME ? + ACPI_STATE_D0 : ACPI_STATE_D3); + } + + if (!(state.event & PM_EVENT_RESUME)) + acpi_bus_set_power(port_handle, ACPI_STATE_D3); +} - handle = ata_ap_acpi_handle(ap); - if (handle && state.event != PM_EVENT_ON) - acpi_bus_set_power(handle, ACPI_STATE_D3); +/** + * ata_acpi_set_state - set the port power state + * @ap: target ATA port + * @state: state, on/off + * + * This function sets a proper ACPI D state for the device on + * system and runtime PM operations. + */ +void ata_acpi_set_state(struct ata_port *ap, pm_message_t state) +{ + if (ap->flags & ATA_FLAG_ACPI_SATA) + sata_acpi_set_state(ap, state); + else + pata_acpi_set_state(ap, state); } /** |