From: Alexey Starikovskiy - Create "acpi" system device in order to be notified of shutdown preparation. - Move some functionality from main.c, register as a system device to be notified of shutdown preparation - Move some functionality to poweroff.c Signed-off-by: Andrew Morton --- 25-akpm/drivers/acpi/sleep/main.c | 33 ++++++-------- 25-akpm/drivers/acpi/sleep/poweroff.c | 76 +++++++++++++++++++++++++++++----- 25-akpm/drivers/base/sys.c | 1 25-akpm/include/linux/pm.h | 2 25-akpm/kernel/power/main.c | 2 5 files changed, 82 insertions(+), 32 deletions(-) diff -puN drivers/acpi/sleep/main.c~acpi-poweroff-fix drivers/acpi/sleep/main.c --- 25/drivers/acpi/sleep/main.c~acpi-poweroff-fix Fri Mar 11 14:17:52 2005 +++ 25-akpm/drivers/acpi/sleep/main.c Fri Mar 11 14:17:52 2005 @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include "sleep.h" @@ -31,6 +30,7 @@ static u32 acpi_suspend_states[] = { [PM_SUSPEND_STANDBY] = ACPI_STATE_S1, [PM_SUSPEND_MEM] = ACPI_STATE_S3, [PM_SUSPEND_DISK] = ACPI_STATE_S4, + [PM_SUSPEND_MAX] = ACPI_STATE_S5 }; static int init_8259A_after_S1; @@ -44,27 +44,18 @@ static int init_8259A_after_S1; * wakeup code to the waking vector. */ +extern int acpi_sleep_prepare(u32 acpi_state); +extern void acpi_power_off (void); + static int acpi_pm_prepare(suspend_state_t pm_state) { u32 acpi_state = acpi_suspend_states[pm_state]; - if (!sleep_states[acpi_state]) + if (!sleep_states[acpi_state]) { + printk("acpi_pm_prepare does not support %d \n", pm_state); return -EPERM; - - /* do we have a wakeup address for S2 and S3? */ - /* Here, we support only S4BIOS, those we set the wakeup address */ - /* S4OS is only supported for now via swsusp.. */ - if (pm_state == PM_SUSPEND_MEM || pm_state == PM_SUSPEND_DISK) { - if (!acpi_wakeup_address) - return -EFAULT; - acpi_set_firmware_waking_vector( - (acpi_physical_address) virt_to_phys( - (void *)acpi_wakeup_address)); } - ACPI_FLUSH_CPU_CACHE(); - acpi_enable_wakeup_device_prep(acpi_state); - acpi_enter_sleep_state_prep(acpi_state); - return 0; + return acpi_sleep_prepare(acpi_state); } @@ -92,7 +83,6 @@ static int acpi_pm_enter(suspend_state_t return error; } - local_irq_save(flags); acpi_enable_wakeup_device(acpi_state); switch (pm_state) @@ -112,7 +102,11 @@ static int acpi_pm_enter(suspend_state_t else do_suspend_lowlevel_s4bios(); break; - default: + case PM_SUSPEND_MAX: + acpi_power_off(); + break; + + default: return -EINVAL; } local_irq_restore(flags); @@ -163,9 +157,10 @@ int acpi_suspend(u32 acpi_state) [1] = PM_SUSPEND_STANDBY, [3] = PM_SUSPEND_MEM, [4] = PM_SUSPEND_DISK, + [5] = PM_SUSPEND_MAX }; - if (acpi_state <= 4 && states[acpi_state]) + if (acpi_state < 6 && states[acpi_state]) return pm_suspend(states[acpi_state]); return -EINVAL; } diff -puN drivers/acpi/sleep/poweroff.c~acpi-poweroff-fix drivers/acpi/sleep/poweroff.c --- 25/drivers/acpi/sleep/poweroff.c~acpi-poweroff-fix Fri Mar 11 14:17:52 2005 +++ 25-akpm/drivers/acpi/sleep/poweroff.c Fri Mar 11 14:17:52 2005 @@ -9,20 +9,69 @@ #include #include #include +#include +#include #include "sleep.h" -static void -acpi_power_off (void) +int acpi_sleep_prepare(u32 acpi_state) +{ + /* Flag to do not allow second time invocation for S5 state */ + static int shutdown_prepared = 0; +#ifdef CONFIG_ACPI_SLEEP + /* do we have a wakeup address for S2 and S3? */ + /* Here, we support only S4BIOS, those we set the wakeup address */ + /* S4OS is only supported for now via swsusp.. */ + if (acpi_state == ACPI_STATE_S3 || acpi_state == ACPI_STATE_S4) { + if (!acpi_wakeup_address) { + return -EFAULT; + } + acpi_set_firmware_waking_vector( + (acpi_physical_address) virt_to_phys( + (void *)acpi_wakeup_address)); + + } + ACPI_FLUSH_CPU_CACHE(); + acpi_enable_wakeup_device_prep(acpi_state); +#endif + if (acpi_state == ACPI_STATE_S5) { + /* Check if we were already called */ + if (shutdown_prepared) return 0; + acpi_wakeup_gpe_poweroff_prepare(); + shutdown_prepared = 1; + } + acpi_enter_sleep_state_prep(acpi_state); + return 0; +} + +void acpi_power_off (void) { printk("%s called\n",__FUNCTION__); - /* Some SMP machines only can poweroff in boot CPU */ - set_cpus_allowed(current, cpumask_of_cpu(0)); - acpi_wakeup_gpe_poweroff_prepare(); - acpi_enter_sleep_state_prep(ACPI_STATE_S5); - ACPI_DISABLE_IRQS(); - acpi_enter_sleep_state(ACPI_STATE_S5); + acpi_sleep_prepare(ACPI_STATE_S5); + local_irq_disable(); + /* Some SMP machines only can poweroff in boot CPU */ + set_cpus_allowed(current, cpumask_of_cpu(0)); + acpi_enter_sleep_state(ACPI_STATE_S5); +} + +#ifdef CONFIG_PM + +static int acpi_shutdown(struct sys_device *x) +{ + return acpi_sleep_prepare(ACPI_STATE_S5); } +static struct sysdev_class acpi_sysclass = { + set_kset_name("acpi"), + .shutdown = acpi_shutdown +}; + +static struct sys_device device_acpi = { + .id = 0, + .cls = &acpi_sysclass, +}; + +#endif + static int acpi_poweroff_init(void) { if (!acpi_disabled) { @@ -30,9 +79,16 @@ static int acpi_poweroff_init(void) acpi_status status; status = acpi_get_sleep_type_data(ACPI_STATE_S5, &type_a, &type_b); - if (ACPI_SUCCESS(status)) + if (ACPI_SUCCESS(status)) { pm_power_off = acpi_power_off; - } +#ifdef CONFIG_PM + int error = sysdev_class_register(&acpi_sysclass); + if (!error) + error = sysdev_register(&device_acpi); + return error; +#endif + } + } return 0; } diff -puN drivers/base/sys.c~acpi-poweroff-fix drivers/base/sys.c --- 25/drivers/base/sys.c~acpi-poweroff-fix Fri Mar 11 14:17:52 2005 +++ 25-akpm/drivers/base/sys.c Fri Mar 11 14:17:52 2005 @@ -21,7 +21,6 @@ #include #include - extern struct subsystem devices_subsys; #define to_sysdev(k) container_of(k, struct sys_device, kobj) diff -puN include/linux/pm.h~acpi-poweroff-fix include/linux/pm.h --- 25/include/linux/pm.h~acpi-poweroff-fix Fri Mar 11 14:17:52 2005 +++ 25-akpm/include/linux/pm.h Fri Mar 11 14:17:52 2005 @@ -175,7 +175,7 @@ struct pm_ops { }; extern void pm_set_ops(struct pm_ops *); - +extern struct pm_ops *pm_ops; extern int pm_suspend(suspend_state_t state); diff -puN kernel/power/main.c~acpi-poweroff-fix kernel/power/main.c --- 25/kernel/power/main.c~acpi-poweroff-fix Fri Mar 11 14:17:52 2005 +++ 25-akpm/kernel/power/main.c Fri Mar 11 14:17:52 2005 @@ -186,7 +186,7 @@ int software_suspend(void) int pm_suspend(suspend_state_t state) { - if (state > PM_SUSPEND_ON && state < PM_SUSPEND_MAX) + if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX) return enter_state(state); return -EINVAL; } _