ACPI: support v5 (reduced HW) sleep interface
authorJan Beulich <jbeulich@suse.com>
Fri, 22 Feb 2013 10:48:57 +0000 (11:48 +0100)
committerJan Beulich <jbeulich@suse.com>
Fri, 22 Feb 2013 10:48:57 +0000 (11:48 +0100)
Note that this also fixes a broken input check in acpi_enter_sleep()
(previously validating the sleep->pm1[ab]_cnt_val relationship based
on acpi_sinfo.pm1b_cnt_val, which however gets set only subsequently).

Also adjust a few minor issues with the pre-v5 handling in
acpi_fadt_parse_sleep_info().

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Keir Fraser <keir@xen.org>
xen/arch/x86/acpi/boot.c
xen/arch/x86/acpi/power.c
xen/drivers/acpi/hwregs.c
xen/include/acpi/aclocal.h
xen/include/acpi/actbl.h
xen/include/asm-x86/acpi.h
xen/include/public/platform.h

index b2dc35eadeeb236cd104c3e066102b51f201738f..0e1d570c29eed03cd714f005474fdd334c2b6bcb 100644 (file)
@@ -310,14 +310,15 @@ static int __init acpi_invalidate_bgrt(struct acpi_table_header *table)
 
 #ifdef CONFIG_ACPI_SLEEP
 #define acpi_fadt_copy_address(dst, src, len) do {                     \
-       if (fadt->header.revision >= FADT2_REVISION_ID)                 \
+       if (fadt->header.revision >= FADT2_REVISION_ID &&               \
+           fadt->header.length >= ACPI_FADT_V2_SIZE)                   \
                acpi_sinfo.dst##_blk = fadt->x##src##_block;            \
        if (!acpi_sinfo.dst##_blk.address) {                            \
                acpi_sinfo.dst##_blk.address      = fadt->src##_block;  \
                acpi_sinfo.dst##_blk.space_id     = ACPI_ADR_SPACE_SYSTEM_IO; \
                acpi_sinfo.dst##_blk.bit_width    = fadt->len##_length << 3; \
                acpi_sinfo.dst##_blk.bit_offset   = 0;                  \
-               acpi_sinfo.dst##_blk.access_width = 0;                  \
+               acpi_sinfo.dst##_blk.access_width = fadt->len##_length; \
        } \
 } while (0)
 
@@ -328,6 +329,41 @@ acpi_fadt_parse_sleep_info(struct acpi_table_fadt *fadt)
        struct acpi_table_facs *facs = NULL;
        uint64_t facs_pa;
 
+       if (fadt->header.revision >= 5 &&
+           fadt->header.length >= ACPI_FADT_V5_SIZE) {
+               acpi_sinfo.sleep_control = fadt->sleep_control;
+               acpi_sinfo.sleep_status = fadt->sleep_status;
+
+               printk(KERN_INFO PREFIX
+                      "v5 SLEEP INFO: control[%d:%"PRIx64"],"
+                      " status[%d:%"PRIx64"]\n",
+                      acpi_sinfo.sleep_control.space_id,
+                      acpi_sinfo.sleep_control.address,
+                      acpi_sinfo.sleep_status.space_id,
+                      acpi_sinfo.sleep_status.address);
+
+               if ((fadt->sleep_control.address &&
+                    (fadt->sleep_control.bit_offset ||
+                     fadt->sleep_control.bit_width !=
+                     fadt->sleep_control.access_width * 8)) ||
+                   (fadt->sleep_status.address &&
+                    (fadt->sleep_status.bit_offset ||
+                     fadt->sleep_status.bit_width !=
+                     fadt->sleep_status.access_width * 8))) {
+                       printk(KERN_WARNING PREFIX
+                              "Invalid sleep control/status register data:"
+                              " %#x:%#x:%#x %#x:%#x:%#x\n",
+                              fadt->sleep_control.bit_offset,
+                              fadt->sleep_control.bit_width,
+                              fadt->sleep_control.access_width,
+                              fadt->sleep_status.bit_offset,
+                              fadt->sleep_status.bit_width,
+                              fadt->sleep_status.access_width);
+                       fadt->sleep_control.address = 0;
+                       fadt->sleep_status.address = 0;
+               }
+       }
+
        if (fadt->flags & ACPI_FADT_HW_REDUCED)
                goto bad;
 
@@ -337,7 +373,7 @@ acpi_fadt_parse_sleep_info(struct acpi_table_fadt *fadt)
        acpi_fadt_copy_address(pm1b_evt, pm1b_event, pm1_event);
 
        printk(KERN_INFO PREFIX
-              "ACPI SLEEP INFO: pm1x_cnt[%"PRIx64",%"PRIx64"], "
+              "SLEEP INFO: pm1x_cnt[%"PRIx64",%"PRIx64"], "
               "pm1x_evt[%"PRIx64",%"PRIx64"]\n",
               acpi_sinfo.pm1a_cnt_blk.address,
               acpi_sinfo.pm1b_cnt_blk.address,
@@ -384,11 +420,14 @@ acpi_fadt_parse_sleep_info(struct acpi_table_fadt *fadt)
        acpi_sinfo.vector_width = 32;
 
        printk(KERN_INFO PREFIX
-              "                 wakeup_vec[%"PRIx64"], vec_size[%x]\n",
+              "            wakeup_vec[%"PRIx64"], vec_size[%x]\n",
               acpi_sinfo.wakeup_vector, acpi_sinfo.vector_width);
        return;
 bad:
-       memset(&acpi_sinfo, 0, sizeof(acpi_sinfo));
+       memset(&acpi_sinfo, 0,
+              offsetof(struct acpi_sleep_info, sleep_control));
+       memset(&acpi_sinfo.sleep_status + 1, 0,
+              (long)(&acpi_sinfo + 1) - (long)(&acpi_sinfo.sleep_status + 1));
 }
 #endif
 
index c693bd9dd415c16bba4cd4bde3cf7cc0a82a2944..3c2585cfb815d253387ba4706c7334c62459f708 100644 (file)
@@ -239,23 +239,47 @@ static long enter_state_helper(void *data)
  */
 int acpi_enter_sleep(struct xenpf_enter_acpi_sleep *sleep)
 {
-    if ( !acpi_sinfo.pm1a_cnt_blk.address )
+    if ( sleep->flags & XENPF_ACPI_SLEEP_EXTENDED )
+    {
+        if ( !acpi_sinfo.sleep_control.address ||
+             !acpi_sinfo.sleep_status.address )
+            return -EPERM;
+
+        if ( sleep->flags & ~XENPF_ACPI_SLEEP_EXTENDED )
+            return -EINVAL;
+
+        if ( sleep->val_a > ACPI_SLEEP_TYPE_MAX ||
+             (sleep->val_b != ACPI_SLEEP_TYPE_INVALID &&
+              sleep->val_b > ACPI_SLEEP_TYPE_MAX) )
+            return -ERANGE;
+
+        acpi_sinfo.sleep_type_a = sleep->val_a;
+        acpi_sinfo.sleep_type_b = sleep->val_b;
+
+        acpi_sinfo.sleep_extended = 1;
+    }
+
+    else if ( !acpi_sinfo.pm1a_cnt_blk.address )
         return -EPERM;
 
     /* Sanity check */
-    if ( acpi_sinfo.pm1b_cnt_val &&
-         ((sleep->pm1a_cnt_val ^ sleep->pm1b_cnt_val) &
-          ACPI_BITMASK_SLEEP_ENABLE) )
+    else if ( sleep->val_b &&
+              ((sleep->val_a ^ sleep->val_b) & ACPI_BITMASK_SLEEP_ENABLE) )
     {
         gdprintk(XENLOG_ERR, "Mismatched pm1a/pm1b setting.");
         return -EINVAL;
     }
 
-    if ( sleep->flags )
+    else if ( sleep->flags )
         return -EINVAL;
 
-    acpi_sinfo.pm1a_cnt_val = sleep->pm1a_cnt_val;
-    acpi_sinfo.pm1b_cnt_val = sleep->pm1b_cnt_val;
+    else
+    {
+        acpi_sinfo.pm1a_cnt_val = sleep->val_a;
+        acpi_sinfo.pm1b_cnt_val = sleep->val_b;
+        acpi_sinfo.sleep_extended = 0;
+    }
+
     acpi_sinfo.sleep_state = sleep->sleep_state;
 
     return continue_hypercall_on_cpu(0, enter_state_helper, &acpi_sinfo);
@@ -266,6 +290,13 @@ static int acpi_get_wake_status(void)
     uint32_t val;
     acpi_status status;
 
+    if ( acpi_sinfo.sleep_extended )
+    {
+        status = acpi_hw_register_read(ACPI_REGISTER_SLEEP_STATUS, &val);
+
+        return ACPI_FAILURE(status) ? 0 : val & ACPI_X_WAKE_STATUS;
+    }
+
     /* Wake status is the 15th bit of PM1 status register. (ACPI spec 3.0) */
     status = acpi_hw_register_read(ACPI_REGISTER_PM1_STATUS, &val);
     if ( ACPI_FAILURE(status) )
@@ -335,18 +366,32 @@ acpi_status acpi_enter_sleep_state(u8 sleep_state)
 
     ACPI_FLUSH_CPU_CACHE();
 
-    status = acpi_hw_register_write(ACPI_REGISTER_PM1A_CONTROL, 
-                                    acpi_sinfo.pm1a_cnt_val);
-    if ( ACPI_FAILURE(status) )
-        return_ACPI_STATUS(AE_ERROR);
-
-    if ( acpi_sinfo.pm1b_cnt_blk.address )
+    if ( acpi_sinfo.sleep_extended )
     {
-        status = acpi_hw_register_write(ACPI_REGISTER_PM1B_CONTROL, 
-                                        acpi_sinfo.pm1b_cnt_val);
-        if ( ACPI_FAILURE(status) )
-            return_ACPI_STATUS(AE_ERROR);
+        /*
+         * Set the SLP_TYP and SLP_EN bits.
+         *
+         * Note: We only use the first value returned by the \_Sx method
+         * (acpi_sinfo.sleep_type_a) - As per ACPI specification.
+         */
+        u8 sleep_type_value =
+            ((acpi_sinfo.sleep_type_a << ACPI_X_SLEEP_TYPE_POSITION) &
+             ACPI_X_SLEEP_TYPE_MASK) | ACPI_X_SLEEP_ENABLE;
+
+        status = acpi_hw_register_write(ACPI_REGISTER_SLEEP_CONTROL,
+                                        sleep_type_value);
     }
+    else
+    {
+        status = acpi_hw_register_write(ACPI_REGISTER_PM1A_CONTROL,
+                                        acpi_sinfo.pm1a_cnt_val);
+        if ( !ACPI_FAILURE(status) && acpi_sinfo.pm1b_cnt_blk.address )
+            status = acpi_hw_register_write(ACPI_REGISTER_PM1B_CONTROL,
+                                            acpi_sinfo.pm1b_cnt_val);
+    }
+
+    if ( ACPI_FAILURE(status) )
+        return_ACPI_STATUS(AE_ERROR);
 
     /* Wait until we enter sleep state, and spin until we wake */
     while ( !acpi_get_wake_status() )
index 67a3c51624d64c7c4cf593f3648a03f883f7f0c0..164fd61ab3d4ab87dda1b53c4f5e3234612005b7 100644 (file)
@@ -365,6 +365,14 @@ acpi_hw_register_read(u32 register_id, u32 * return_value)
                    acpi_os_read_port(acpi_gbl_FADT.smi_command, &value1, 8);
                break;
 
+       case ACPI_REGISTER_SLEEP_STATUS:
+
+               status =
+                   acpi_hw_low_level_read(acpi_gbl_FADT.sleep_status.bit_width,
+                                          &value1,
+                                          &acpi_gbl_FADT.sleep_status);
+               break;
+
        default:
                ACPI_DEBUG_PRINT((AE_INFO, "Unknown Register ID: %X", register_id));
                status = AE_BAD_PARAMETER;
@@ -525,6 +533,14 @@ acpi_status acpi_hw_register_write(u32 register_id, u32 value)
                    acpi_os_write_port(acpi_gbl_FADT.smi_command, value, 8);
                break;
 
+       case ACPI_REGISTER_SLEEP_CONTROL:
+
+               status =
+                   acpi_hw_low_level_write(acpi_gbl_FADT.sleep_control.bit_width,
+                                           value,
+                                           &acpi_gbl_FADT.sleep_control);
+               break;
+
        default:
                status = AE_BAD_PARAMETER;
                break;
index afa4f6409c734c30a8bd2f162282c639f43d840f..16e234f0de14226b474bd3307cdc8fb4c602c05e 100644 (file)
@@ -128,6 +128,8 @@ struct acpi_bit_register_info {
 #define ACPI_REGISTER_PM_TIMER                  0x07
 #define ACPI_REGISTER_PROCESSOR_BLOCK           0x08
 #define ACPI_REGISTER_SMI_COMMAND_BLOCK         0x09
+#define ACPI_REGISTER_SLEEP_CONTROL             0x0a
+#define ACPI_REGISTER_SLEEP_STATUS              0x0b
 
 /* Masks used to access the bit_registers */
 
index 2f31d3a0c82e5b6594f3cb191bb99ec23520c4d4..856945d3812e12381d7e9d3b21c858418588b4d8 100644 (file)
@@ -309,6 +309,13 @@ enum acpi_prefered_pm_profiles {
        PM_TABLET = 8
 };
 
+/* Values for sleep_status and sleep_control registers (V5 FADT) */
+
+#define ACPI_X_WAKE_STATUS          0x80
+#define ACPI_X_SLEEP_TYPE_MASK      0x1C
+#define ACPI_X_SLEEP_TYPE_POSITION  0x02
+#define ACPI_X_SLEEP_ENABLE         0x20
+
 /* Reset to default packing */
 
 #pragma pack()
index 41322e26109f32321cfa7682bc9b25517370babe..a7b11b8960f7d999319786695a6d979d0112a612 100644 (file)
@@ -126,11 +126,20 @@ struct acpi_sleep_info {
     struct acpi_generic_address pm1b_cnt_blk;
     struct acpi_generic_address pm1a_evt_blk;
     struct acpi_generic_address pm1b_evt_blk;
-    uint16_t pm1a_cnt_val;
-    uint16_t pm1b_cnt_val;
+    struct acpi_generic_address sleep_control;
+    struct acpi_generic_address sleep_status;
+    union {
+        uint16_t pm1a_cnt_val;
+        uint8_t sleep_type_a;
+    };
+    union {
+        uint16_t pm1b_cnt_val;
+        uint8_t sleep_type_b;
+    };
     uint32_t sleep_state;
     uint64_t wakeup_vector;
     uint32_t vector_width;
+    bool_t sleep_extended;
 };
 
 #endif /* CONFIG_ACPI_SLEEP */
index 215e571bcb4a43e2440b7a4420a9550ae4282b9d..d7d2a5bddffb79ae8ac11d4ffbe5e4795f76fd59 100644 (file)
@@ -290,10 +290,16 @@ DEFINE_XEN_GUEST_HANDLE(xenpf_firmware_info_t);
 #define XENPF_enter_acpi_sleep    51
 struct xenpf_enter_acpi_sleep {
     /* IN variables */
+#if __XEN_INTERFACE_VERSION__ < 0x00040300
     uint16_t pm1a_cnt_val;      /* PM1a control value. */
     uint16_t pm1b_cnt_val;      /* PM1b control value. */
+#else
+    uint16_t val_a;             /* PM1a control / sleep type A. */
+    uint16_t val_b;             /* PM1b control / sleep type B. */
+#endif
     uint32_t sleep_state;       /* Which state to enter (Sn). */
-    uint32_t flags;             /* Must be zero. */
+#define XENPF_ACPI_SLEEP_EXTENDED 0x00000001
+    uint32_t flags;             /* XENPF_ACPI_SLEEP_*. */
 };
 typedef struct xenpf_enter_acpi_sleep xenpf_enter_acpi_sleep_t;
 DEFINE_XEN_GUEST_HANDLE(xenpf_enter_acpi_sleep_t);