aboutsummaryrefslogtreecommitdiffstats
path: root/xen/arch/x86/acpi
diff options
context:
space:
mode:
authorJan Beulich <jbeulich@suse.com>2013-02-22 11:48:57 +0100
committerJan Beulich <jbeulich@suse.com>2013-02-22 11:48:57 +0100
commit62d1a69a4e9f435999af22b7e31d7ee8910f5de0 (patch)
tree18ccac8cf815b8175cdaffeecd7c27509f86096c /xen/arch/x86/acpi
parenta37186e1c2b3e9462cce65d330a71c3afbde4b9b (diff)
downloadxen-62d1a69a4e9f435999af22b7e31d7ee8910f5de0.tar.gz
xen-62d1a69a4e9f435999af22b7e31d7ee8910f5de0.tar.bz2
xen-62d1a69a4e9f435999af22b7e31d7ee8910f5de0.zip
ACPI: support v5 (reduced HW) sleep interface
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>
Diffstat (limited to 'xen/arch/x86/acpi')
-rw-r--r--xen/arch/x86/acpi/boot.c49
-rw-r--r--xen/arch/x86/acpi/power.c79
2 files changed, 106 insertions, 22 deletions
diff --git a/xen/arch/x86/acpi/boot.c b/xen/arch/x86/acpi/boot.c
index b2dc35eade..0e1d570c29 100644
--- a/xen/arch/x86/acpi/boot.c
+++ b/xen/arch/x86/acpi/boot.c
@@ -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
diff --git a/xen/arch/x86/acpi/power.c b/xen/arch/x86/acpi/power.c
index c693bd9dd4..3c2585cfb8 100644
--- a/xen/arch/x86/acpi/power.c
+++ b/xen/arch/x86/acpi/power.c
@@ -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() )