From: Alexey Starikovskiy <alexey.y.starikovskiy@intel.com>

- 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 <akpm@osdl.org>
---

 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 <linux/dmi.h>
 #include <linux/device.h>
 #include <linux/suspend.h>
-#include <asm/io.h>
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_drivers.h>
 #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 <linux/init.h>
 #include <acpi/acpi_bus.h>
 #include <linux/sched.h>
+#include <linux/sysdev.h>
+#include <asm/io.h>
 #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 <linux/slab.h>
 #include <linux/string.h>
 
-
 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;
 }
_