Commit 5e32132befa5d2cefadf3141fee0bbb40cd11f0e
Committed by
Bartlomiej Zolnierkiewicz
1 parent
8cb1f567f4
Exists in
master
and in
4 other branches
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>
Showing 4 changed files with 59 additions and 3 deletions Side-by-side Diff
drivers/acpi/bus.c
... | ... | @@ -262,10 +262,12 @@ |
262 | 262 | printk(KERN_WARNING PREFIX |
263 | 263 | "Transitioning device [%s] to D%d\n", |
264 | 264 | device->pnp.bus_id, state); |
265 | - else | |
265 | + else { | |
266 | + device->power.state = state; | |
266 | 267 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
267 | 268 | "Device [%s] transitioned to D%d\n", |
268 | 269 | device->pnp.bus_id, state)); |
270 | + } | |
269 | 271 | |
270 | 272 | return result; |
271 | 273 | } |
drivers/ide/ide-acpi.c
... | ... | @@ -612,6 +612,46 @@ |
612 | 612 | EXPORT_SYMBOL_GPL(ide_acpi_push_timing); |
613 | 613 | |
614 | 614 | /** |
615 | + * ide_acpi_set_state - set the channel power state | |
616 | + * @hwif: target IDE interface | |
617 | + * @on: state, on/off | |
618 | + * | |
619 | + * This function executes the _PS0/_PS3 ACPI method to set the power state. | |
620 | + * ACPI spec requires _PS0 when IDE power on and _PS3 when power off | |
621 | + */ | |
622 | +void ide_acpi_set_state(ide_hwif_t *hwif, int on) | |
623 | +{ | |
624 | + int unit; | |
625 | + | |
626 | + if (ide_noacpi) | |
627 | + return; | |
628 | + | |
629 | + DEBPRINT("ENTER:\n"); | |
630 | + | |
631 | + if (!hwif->acpidata) { | |
632 | + DEBPRINT("no ACPI data for %s\n", hwif->name); | |
633 | + return; | |
634 | + } | |
635 | + /* channel first and then drives for power on and verse versa for power off */ | |
636 | + if (on) | |
637 | + acpi_bus_set_power(hwif->acpidata->obj_handle, ACPI_STATE_D0); | |
638 | + for (unit = 0; unit < MAX_DRIVES; ++unit) { | |
639 | + ide_drive_t *drive = &hwif->drives[unit]; | |
640 | + | |
641 | + if (!drive->acpidata->obj_handle) | |
642 | + drive->acpidata->obj_handle = ide_acpi_drive_get_handle(drive); | |
643 | + | |
644 | + if (drive->acpidata->obj_handle && drive->present) { | |
645 | + acpi_bus_set_power(drive->acpidata->obj_handle, | |
646 | + on? ACPI_STATE_D0: ACPI_STATE_D3); | |
647 | + } | |
648 | + } | |
649 | + if (!on) | |
650 | + acpi_bus_set_power(hwif->acpidata->obj_handle, ACPI_STATE_D3); | |
651 | +} | |
652 | +EXPORT_SYMBOL_GPL(ide_acpi_set_state); | |
653 | + | |
654 | +/** | |
615 | 655 | * ide_acpi_init - initialize the ACPI link for an IDE interface |
616 | 656 | * @hwif: target IDE interface (channel) |
617 | 657 | * |
... | ... | @@ -679,6 +719,8 @@ |
679 | 719 | return; |
680 | 720 | } |
681 | 721 | |
722 | + /* ACPI _PS0 before _STM */ | |
723 | + ide_acpi_set_state(hwif, 1); | |
682 | 724 | /* |
683 | 725 | * ACPI requires us to call _STM on startup |
684 | 726 | */ |
drivers/ide/ide.c
... | ... | @@ -915,6 +915,7 @@ |
915 | 915 | struct request rq; |
916 | 916 | struct request_pm_state rqpm; |
917 | 917 | ide_task_t args; |
918 | + int ret; | |
918 | 919 | |
919 | 920 | /* Call ACPI _GTM only once */ |
920 | 921 | if (!(drive->dn % 2)) |
... | ... | @@ -931,7 +932,14 @@ |
931 | 932 | mesg.event = PM_EVENT_FREEZE; |
932 | 933 | rqpm.pm_state = mesg.event; |
933 | 934 | |
934 | - return ide_do_drive_cmd(drive, &rq, ide_wait); | |
935 | + ret = ide_do_drive_cmd(drive, &rq, ide_wait); | |
936 | + /* only call ACPI _PS3 after both drivers are suspended */ | |
937 | + if (!ret && (((drive->dn % 2) && hwif->drives[0].present | |
938 | + && hwif->drives[1].present) | |
939 | + || !hwif->drives[0].present | |
940 | + || !hwif->drives[1].present)) | |
941 | + ide_acpi_set_state(hwif, 0); | |
942 | + return ret; | |
935 | 943 | } |
936 | 944 | |
937 | 945 | static int generic_ide_resume(struct device *dev) |
938 | 946 | |
... | ... | @@ -944,8 +952,10 @@ |
944 | 952 | int err; |
945 | 953 | |
946 | 954 | /* Call ACPI _STM only once */ |
947 | - if (!(drive->dn % 2)) | |
955 | + if (!(drive->dn % 2)) { | |
956 | + ide_acpi_set_state(hwif, 1); | |
948 | 957 | ide_acpi_push_timing(hwif); |
958 | + } | |
949 | 959 | |
950 | 960 | ide_acpi_exec_tfs(drive); |
951 | 961 |
include/linux/ide.h
... | ... | @@ -1338,11 +1338,13 @@ |
1338 | 1338 | extern void ide_acpi_get_timing(ide_hwif_t *hwif); |
1339 | 1339 | extern void ide_acpi_push_timing(ide_hwif_t *hwif); |
1340 | 1340 | extern void ide_acpi_init(ide_hwif_t *hwif); |
1341 | +extern void ide_acpi_set_state(ide_hwif_t *hwif, int on); | |
1341 | 1342 | #else |
1342 | 1343 | static inline int ide_acpi_exec_tfs(ide_drive_t *drive) { return 0; } |
1343 | 1344 | static inline void ide_acpi_get_timing(ide_hwif_t *hwif) { ; } |
1344 | 1345 | static inline void ide_acpi_push_timing(ide_hwif_t *hwif) { ; } |
1345 | 1346 | static inline void ide_acpi_init(ide_hwif_t *hwif) { ; } |
1347 | +static inline void ide_acpi_set_state(ide_hwif_t *hwif, int on) {} | |
1346 | 1348 | #endif |
1347 | 1349 | |
1348 | 1350 | extern int ide_hwif_request_regions(ide_hwif_t *hwif); |