Define pm interfaces between dom0 and Xen, with one
to register sleep info and the other for triggering
sleep state. "acpi_sleep=s3_mode/s3_bios" option is
also supported by piggybacking video flag/mode to
xen at trigger point.

Signed-off-by Ke Yu <ke.yu@intel.com>
Signed-off-by Kevin Tian <kevin.tian@intel.com>

diff -r 169fcd328fd9 xen/arch/x86/acpi/power.c
--- a/xen/arch/x86/acpi/power.c	Tue Feb 13 17:30:14 2007 +0800
+++ b/xen/arch/x86/acpi/power.c	Tue Feb 13 17:30:34 2007 +0800
@@ -27,6 +27,16 @@ u8 sleep_states[ACPI_S_STATE_COUNT];
 u8 sleep_states[ACPI_S_STATE_COUNT];
 DEFINE_SPINLOCK(pm_lock);
 
+struct acpi_sleep_info {
+    uint16_t pm1a_cnt;
+    uint16_t pm1b_cnt;
+    uint16_t pm1a_evt;
+    uint16_t pm1b_evt;
+    uint16_t pm1a_cnt_val;
+    uint16_t pm1b_cnt_val;
+    uint32_t sleep_state;
+} acpi_sinfo;
+
 extern void do_suspend_lowlevel(void);
 
 static char *acpi_states[ACPI_S_STATE_COUNT] =
@@ -36,7 +46,7 @@ static char *acpi_states[ACPI_S_STATE_CO
     [ACPI_STATE_S4] = "disk",
 };
 
-/* Add suspend failure recover later */
+/* XXX: Add suspend failure recover later */
 static int device_power_down(void)
 {
     console_suspend();
@@ -65,6 +75,7 @@ static void device_power_up(void)
     console_resume();
 }
 
+/* Main interface to do xen specific suspend/resume */
 int enter_state(u32 state)
 {
     struct domain *d;
@@ -92,7 +103,6 @@ int enter_state(u32 state)
 
     ACPI_FLUSH_CPU_CACHE();
 
-    /* Do arch specific saving of state. */
     if (state > ACPI_STATE_S1) {
         error = acpi_save_state_mem();
         if (error)
@@ -105,7 +115,7 @@ int enter_state(u32 state)
             break;
         default:
             error = -EINVAL;
-            goto Powerup;
+            break;
     }
 
     printk("Back to C!\n");
@@ -125,6 +135,97 @@ int enter_state(u32 state)
     spin_unlock(&pm_lock);
     return error;
 
+}
+
+/*
+ * Xen just requires address of pm1x_cnt, and ACPI interpreter
+ * is still kept in dom0. Address of xen wakeup stub will be
+ * returned, and then dom0 writes that address to FACS.
+ */
+int set_acpi_sleep_info(struct xenpf_set_acpi_sleep *info)
+{
+    if (acpi_sinfo.pm1a_cnt)
+        printk(XENLOG_WARNING "Multiple setting on acpi sleep info\n");
+
+    acpi_sinfo.pm1a_cnt = info->pm1a_cnt_port;
+    acpi_sinfo.pm1b_cnt = info->pm1b_cnt_port;
+    acpi_sinfo.pm1a_evt = info->pm1a_evt_port;
+    acpi_sinfo.pm1b_evt = info->pm1b_evt_port;
+    info->xen_waking_vec = (uint64_t)__pa(acpi_wakeup_address);
+
+    printk(XENLOG_INFO "pm1a[%x],pm1b[%x],pm1a_e[%x],pm1b_e[%x]"
+                       "wake[%"PRIx64"]",
+                       acpi_sinfo.pm1a_cnt, acpi_sinfo.pm1b_cnt,
+                       acpi_sinfo.pm1a_evt, acpi_sinfo.pm1b_evt,
+                       info->xen_waking_vec);
+    return 0;
+}
+
+/*
+ * Dom0 issues this hypercall in place of writing pm1a_cnt. Xen then
+ * takes over the control and put the system into sleep state really.
+ * Also video flags and mode are passed here, in case user may use
+ * "acpi_sleep=***" for video resume.
+ *
+ * Guest may issue a two-phases write to PM1x_CNT, to work
+ * around poorly implemented hardware. It's better to keep
+ * this logic here. Two writes can be differentiated by 
+ * enable bit setting.
+ */
+int acpi_enter_sleep(struct xenpf_enter_acpi_sleep *sleep)
+{
+    if (!acpi_sinfo.pm1a_cnt)
+        return -EPERM;
+
+    /* Sanity check */
+    if (acpi_sinfo.pm1b_cnt_val &&
+        ((sleep->pm1a_cnt_val ^ sleep->pm1b_cnt_val) &
+         ACPI_BITMASK_SLEEP_ENABLE)) {
+        printk(XENLOG_ERR "Mismatched pm1a/pm1b setting\n");
+        return -EINVAL;
+    }
+
+    /* Write #1 */
+    if (!(sleep->pm1a_cnt_val & ACPI_BITMASK_SLEEP_ENABLE)) {
+        outw((u16)sleep->pm1a_cnt_val, acpi_sinfo.pm1a_cnt);
+        if (acpi_sinfo.pm1b_cnt)
+            outw((u16)sleep->pm1b_cnt_val, acpi_sinfo.pm1b_cnt);
+        return 0;
+    }
+
+    /* Write #2 */
+    acpi_sinfo.pm1a_cnt_val = sleep->pm1a_cnt_val;
+    acpi_sinfo.pm1b_cnt_val = sleep->pm1b_cnt_val;
+    acpi_sinfo.sleep_state = sleep->sleep_state;
+    acpi_video_flags = sleep->video_flags;
+    saved_videomode = sleep->video_mode;
+
+    return enter_state(acpi_sinfo.sleep_state);
+}
+
+static int acpi_get_wake_status(void)
+{
+    uint16_t val;
+
+    /* Wake status is the 15th bit of PM1 status register. (ACPI spec 3.0) */
+    val = inw(acpi_sinfo.pm1a_evt) | inw(acpi_sinfo.pm1b_evt);
+    val &= ACPI_BITMASK_WAKE_STATUS;
+    val >>= ACPI_BITPOSITION_WAKE_STATUS;
+    return val;
+}
+
+/* System is really put into sleep state by this stub */
+acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state)
+{
+    ACPI_FLUSH_CPU_CACHE();
+
+    outw((u16)acpi_sinfo.pm1a_cnt_val, acpi_sinfo.pm1a_cnt);
+    if (acpi_sinfo.pm1b_cnt)
+        outw((u16)acpi_sinfo.pm1b_cnt_val, acpi_sinfo.pm1b_cnt);
+    
+    /* Wait until we enter sleep state, and spin until we wake */
+    while (!acpi_get_wake_status());
+    return_ACPI_STATUS(AE_OK);
 }
 
 static int __init acpi_sleep_init(void)
diff -r 169fcd328fd9 xen/arch/x86/platform_hypercall.c
--- a/xen/arch/x86/platform_hypercall.c	Tue Feb 13 17:30:14 2007 +0800
+++ b/xen/arch/x86/platform_hypercall.c	Tue Feb 13 17:30:21 2007 +0800
@@ -18,6 +18,7 @@
 #include <xen/console.h>
 #include <xen/iocap.h>
 #include <xen/guest_access.h>
+#include <xen/acpi.h>
 #include <asm/current.h>
 #include <public/platform.h>
 #include <asm/mtrr.h>
@@ -151,6 +152,20 @@ ret_t do_platform_op(XEN_GUEST_HANDLE(xe
     }
     break;
 
+    case XENPF_set_acpi_sleep:
+    {
+        ret = set_acpi_sleep_info(&op->u.set_acpi_sleep);
+        if (!ret && copy_to_guest(u_xenpf_op, op, 1))
+            ret = -EFAULT;
+    }
+    break;
+
+    case XENPF_enter_acpi_sleep:
+    {
+        ret = acpi_enter_sleep(&op->u.enter_acpi_sleep);
+    }
+    break;
+
     default:
         ret = -ENOSYS;
         break;
diff -r 169fcd328fd9 xen/include/public/platform.h
--- a/xen/include/public/platform.h	Tue Feb 13 17:30:14 2007 +0800
+++ b/xen/include/public/platform.h	Tue Feb 13 17:30:21 2007 +0800
@@ -114,6 +114,31 @@ typedef struct xenpf_platform_quirk xenp
 typedef struct xenpf_platform_quirk xenpf_platform_quirk_t;
 DEFINE_XEN_GUEST_HANDLE(xenpf_platform_quirk_t);
 
+#define XENPF_set_acpi_sleep      40
+struct xenpf_set_acpi_sleep {
+    /* IN variables. */
+    uint16_t pm1a_cnt_port;
+    uint16_t pm1b_cnt_port;
+    uint16_t pm1a_evt_port;
+    uint16_t pm1b_evt_port;
+    /* OUT variables */
+    uint64_t xen_waking_vec;   /* Tell dom0 to set FACS waking vector */
+};
+typedef struct xenpf_set_acpi_sleep xenpf_set_acpi_sleep_t;
+DEFINE_XEN_GUEST_HANDLE(xenpf_set_acpi_sleep_t);
+
+#define XENPF_enter_acpi_sleep    41
+struct xenpf_enter_acpi_sleep {
+    /* IN variables */
+    uint16_t pm1a_cnt_val;
+    uint16_t pm1b_cnt_val;
+    uint32_t sleep_state;       /* Which state to enter */
+    uint32_t video_flags;       /* S3_bios or s3_mode */
+    uint32_t video_mode;        /* Mode setting for s3_mode */
+};
+typedef struct xenpf_enter_acpi_sleep xenpf_enter_acpi_sleep_t;
+DEFINE_XEN_GUEST_HANDLE(xenpf_enter_acpi_sleep_t);
+
 struct xen_platform_op {
     uint32_t cmd;
     uint32_t interface_version; /* XENPF_INTERFACE_VERSION */
@@ -124,6 +149,8 @@ struct xen_platform_op {
         struct xenpf_read_memtype      read_memtype;
         struct xenpf_microcode_update  microcode;
         struct xenpf_platform_quirk    platform_quirk;
+        struct xenpf_set_acpi_sleep    set_acpi_sleep;
+        struct xenpf_enter_acpi_sleep  enter_acpi_sleep;
         uint8_t                        pad[128];
     } u;
 };
diff -r 169fcd328fd9 xen/include/xen/acpi.h
--- a/xen/include/xen/acpi.h	Tue Feb 13 17:30:14 2007 +0800
+++ b/xen/include/xen/acpi.h	Tue Feb 13 17:30:21 2007 +0800
@@ -535,4 +535,10 @@ static inline int acpi_get_pxm(acpi_hand
 
 extern int pnpacpi_disabled;
 
+#include <public/platform.h>
+extern unsigned long acpi_video_flags;
+extern unsigned long saved_videomode;
+extern int set_acpi_sleep_info(struct xenpf_set_acpi_sleep *info);
+extern int acpi_enter_sleep(struct xenpf_enter_acpi_sleep *sleep);
+extern int acpi_enter_state(u32 state);
 #endif /*_LINUX_ACPI_H*/
