]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/acpi/sleep/main.c
PM: Separate hibernation code from suspend code
[karo-tx-linux.git] / drivers / acpi / sleep / main.c
index 930427fc0c4b33ff923a5623038adcdf7073ea1c..52b23471dd69b08e01b868db425f2390ed5631b9 100644 (file)
@@ -29,7 +29,6 @@ static u32 acpi_suspend_states[] = {
        [PM_SUSPEND_ON] = ACPI_STATE_S0,
        [PM_SUSPEND_STANDBY] = ACPI_STATE_S1,
        [PM_SUSPEND_MEM] = ACPI_STATE_S3,
-       [PM_SUSPEND_DISK] = ACPI_STATE_S4,
        [PM_SUSPEND_MAX] = ACPI_STATE_S5
 };
 
@@ -94,17 +93,17 @@ static int acpi_pm_enter(suspend_state_t pm_state)
                do_suspend_lowlevel();
                break;
 
-       case PM_SUSPEND_DISK:
-               if (acpi_pm_ops.pm_disk_mode == PM_DISK_PLATFORM)
-                       status = acpi_enter_sleep_state(acpi_state);
-               break;
-       case PM_SUSPEND_MAX:
-               acpi_power_off();
-               break;
-
        default:
                return -EINVAL;
        }
+
+       /* ACPI 3.0 specs (P62) says that it's the responsabilty
+        * of the OSPM to clear the status bit [ implying that the
+        * POWER_BUTTON event should not reach userspace ]
+        */
+       if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3))
+               acpi_clear_event(ACPI_EVENT_POWER_BUTTON);
+
        local_irq_restore(flags);
        printk(KERN_DEBUG "Back to C!\n");
 
@@ -149,20 +148,30 @@ int acpi_suspend(u32 acpi_state)
        suspend_state_t states[] = {
                [1] = PM_SUSPEND_STANDBY,
                [3] = PM_SUSPEND_MEM,
-               [4] = PM_SUSPEND_DISK,
                [5] = PM_SUSPEND_MAX
        };
 
        if (acpi_state < 6 && states[acpi_state])
                return pm_suspend(states[acpi_state]);
+       if (acpi_state == 4)
+               return hibernate();
        return -EINVAL;
 }
 
 static int acpi_pm_state_valid(suspend_state_t pm_state)
 {
-       u32 acpi_state = acpi_suspend_states[pm_state];
+       u32 acpi_state;
+
+       switch (pm_state) {
+       case PM_SUSPEND_ON:
+       case PM_SUSPEND_STANDBY:
+       case PM_SUSPEND_MEM:
+               acpi_state = acpi_suspend_states[pm_state];
 
-       return sleep_states[acpi_state];
+               return sleep_states[acpi_state];
+       default:
+               return 0;
+       }
 }
 
 static struct pm_ops acpi_pm_ops = {
@@ -172,6 +181,49 @@ static struct pm_ops acpi_pm_ops = {
        .finish = acpi_pm_finish,
 };
 
+#ifdef CONFIG_SOFTWARE_SUSPEND
+static int acpi_hibernation_prepare(void)
+{
+       return acpi_sleep_prepare(ACPI_STATE_S4);
+}
+
+static int acpi_hibernation_enter(void)
+{
+       acpi_status status = AE_OK;
+       unsigned long flags = 0;
+
+       ACPI_FLUSH_CPU_CACHE();
+
+       local_irq_save(flags);
+       acpi_enable_wakeup_device(ACPI_STATE_S4);
+       /* This shouldn't return.  If it returns, we have a problem */
+       status = acpi_enter_sleep_state(ACPI_STATE_S4);
+       local_irq_restore(flags);
+
+       return ACPI_SUCCESS(status) ? 0 : -EFAULT;
+}
+
+static void acpi_hibernation_finish(void)
+{
+       acpi_leave_sleep_state(ACPI_STATE_S4);
+       acpi_disable_wakeup_device(ACPI_STATE_S4);
+
+       /* reset firmware waking vector */
+       acpi_set_firmware_waking_vector((acpi_physical_address) 0);
+
+       if (init_8259A_after_S1) {
+               printk("Broken toshiba laptop -> kicking interrupts\n");
+               init_8259A(0);
+       }
+}
+
+static struct hibernation_ops acpi_hibernation_ops = {
+       .prepare = acpi_hibernation_prepare,
+       .enter = acpi_hibernation_enter,
+       .finish = acpi_hibernation_finish,
+};
+#endif /* CONFIG_SOFTWARE_SUSPEND */
+
 /*
  * Toshiba fails to preserve interrupts over S1, reinitialization
  * of 8259 is needed after S1 resume.
@@ -192,7 +244,7 @@ static struct dmi_system_id __initdata acpisleep_dmi_table[] = {
        {},
 };
 
-static int __init acpi_sleep_init(void)
+int __init acpi_sleep_init(void)
 {
        int i = 0;
 
@@ -210,15 +262,18 @@ static int __init acpi_sleep_init(void)
                        sleep_states[i] = 1;
                        printk(" S%d", i);
                }
-               if (i == ACPI_STATE_S4) {
-                       if (sleep_states[i])
-                               acpi_pm_ops.pm_disk_mode = PM_DISK_PLATFORM;
-               }
        }
        printk(")\n");
 
        pm_set_ops(&acpi_pm_ops);
+
+#ifdef CONFIG_SOFTWARE_SUSPEND
+       if (sleep_states[ACPI_STATE_S4])
+               hibernation_set_ops(&acpi_hibernation_ops);
+#else
+       sleep_states[ACPI_STATE_S4] = 0;
+#endif
+
        return 0;
 }
 
-late_initcall(acpi_sleep_init);