diff options
author | Shaohua Li <shaohua.li@intel.com> | 2007-10-11 23:53:58 +0200 |
---|---|---|
committer | Bartlomiej Zolnierkiewicz <bzolnier@gmail.com> | 2007-10-11 23:53:58 +0200 |
commit | 5e32132befa5d2cefadf3141fee0bbb40cd11f0e (patch) | |
tree | 7bf2002e49b169d24b87a8fe71364875e04d1e90 | |
parent | 8cb1f567f4c0a2fde9cbf77c2af888a28cab3423 (diff) |
ide: hook ACPI _PSx method to IDE power on/off
ACPI spec defines the sequence of IDE power on/off:
Powering down:
Call _GTM.
Power down drive (calls _PS3 method and turns off power planes).
Powering up:
Power up drive (calls _PS0 method if present and turns on power planes).
Call _STM passing info from _GTM (possibly modified), with ID data from
each drive.
Initialize the channel.
May modify the results of _GTF.
For each drive:
Call _GTF.
Execute task file (possibly modified).
This patch adds the missed _PS0/_PS3 methods call.
Signed-off-by: Shaohua Li <shaohua.li@intel.com>
Acked-by: Len Brown <len.brown@intel.com>
Signed-off-by: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
-rw-r--r-- | drivers/acpi/bus.c | 4 | ||||
-rw-r--r-- | drivers/ide/ide-acpi.c | 42 | ||||
-rw-r--r-- | drivers/ide/ide.c | 14 | ||||
-rw-r--r-- | include/linux/ide.h | 2 |
4 files changed, 59 insertions, 3 deletions
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 9ba778a2b484..feab124d8e05 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -262,10 +262,12 @@ int acpi_bus_set_power(acpi_handle handle, int state) printk(KERN_WARNING PREFIX "Transitioning device [%s] to D%d\n", device->pnp.bus_id, state); - else + else { + device->power.state = state; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] transitioned to D%d\n", device->pnp.bus_id, state)); + } return result; } diff --git a/drivers/ide/ide-acpi.c b/drivers/ide/ide-acpi.c index 17aea65d7dd2..6bff81a58bf3 100644 --- a/drivers/ide/ide-acpi.c +++ b/drivers/ide/ide-acpi.c @@ -612,6 +612,46 @@ void ide_acpi_push_timing(ide_hwif_t *hwif) EXPORT_SYMBOL_GPL(ide_acpi_push_timing); /** + * ide_acpi_set_state - set the channel power state + * @hwif: target IDE interface + * @on: 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 ide_acpi_set_state(ide_hwif_t *hwif, int on) +{ + int unit; + + if (ide_noacpi) + return; + + DEBPRINT("ENTER:\n"); + + if (!hwif->acpidata) { + DEBPRINT("no ACPI data for %s\n", hwif->name); + return; + } + /* channel first and then drives for power on and verse versa for power off */ + if (on) + acpi_bus_set_power(hwif->acpidata->obj_handle, ACPI_STATE_D0); + for (unit = 0; unit < MAX_DRIVES; ++unit) { + ide_drive_t *drive = &hwif->drives[unit]; + + if (!drive->acpidata->obj_handle) + drive->acpidata->obj_handle = ide_acpi_drive_get_handle(drive); + + if (drive->acpidata->obj_handle && drive->present) { + acpi_bus_set_power(drive->acpidata->obj_handle, + on? ACPI_STATE_D0: ACPI_STATE_D3); + } + } + if (!on) + acpi_bus_set_power(hwif->acpidata->obj_handle, ACPI_STATE_D3); +} +EXPORT_SYMBOL_GPL(ide_acpi_set_state); + +/** * ide_acpi_init - initialize the ACPI link for an IDE interface * @hwif: target IDE interface (channel) * @@ -679,6 +719,8 @@ void ide_acpi_init(ide_hwif_t *hwif) return; } + /* ACPI _PS0 before _STM */ + ide_acpi_set_state(hwif, 1); /* * ACPI requires us to call _STM on startup */ diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index de54306789a1..9fdc1fe1b299 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -915,6 +915,7 @@ static int generic_ide_suspend(struct device *dev, pm_message_t mesg) struct request rq; struct request_pm_state rqpm; ide_task_t args; + int ret; /* Call ACPI _GTM only once */ if (!(drive->dn % 2)) @@ -931,7 +932,14 @@ static int generic_ide_suspend(struct device *dev, pm_message_t mesg) mesg.event = PM_EVENT_FREEZE; rqpm.pm_state = mesg.event; - return ide_do_drive_cmd(drive, &rq, ide_wait); + ret = ide_do_drive_cmd(drive, &rq, ide_wait); + /* only call ACPI _PS3 after both drivers are suspended */ + if (!ret && (((drive->dn % 2) && hwif->drives[0].present + && hwif->drives[1].present) + || !hwif->drives[0].present + || !hwif->drives[1].present)) + ide_acpi_set_state(hwif, 0); + return ret; } static int generic_ide_resume(struct device *dev) @@ -944,8 +952,10 @@ static int generic_ide_resume(struct device *dev) int err; /* Call ACPI _STM only once */ - if (!(drive->dn % 2)) + if (!(drive->dn % 2)) { + ide_acpi_set_state(hwif, 1); ide_acpi_push_timing(hwif); + } ide_acpi_exec_tfs(drive); diff --git a/include/linux/ide.h b/include/linux/ide.h index 0665428356d3..80ea946282d7 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1338,11 +1338,13 @@ extern int ide_acpi_exec_tfs(ide_drive_t *drive); extern void ide_acpi_get_timing(ide_hwif_t *hwif); extern void ide_acpi_push_timing(ide_hwif_t *hwif); extern void ide_acpi_init(ide_hwif_t *hwif); +extern void ide_acpi_set_state(ide_hwif_t *hwif, int on); #else static inline int ide_acpi_exec_tfs(ide_drive_t *drive) { return 0; } static inline void ide_acpi_get_timing(ide_hwif_t *hwif) { ; } static inline void ide_acpi_push_timing(ide_hwif_t *hwif) { ; } static inline void ide_acpi_init(ide_hwif_t *hwif) { ; } +static inline void ide_acpi_set_state(ide_hwif_t *hwif, int on) {} #endif extern int ide_hwif_request_regions(ide_hwif_t *hwif); |