diff options
author | Jan Beulich <jbeulich@suse.com> | 2013-02-22 11:48:57 +0100 |
---|---|---|
committer | Jan Beulich <jbeulich@suse.com> | 2013-02-22 11:48:57 +0100 |
commit | 62d1a69a4e9f435999af22b7e31d7ee8910f5de0 (patch) | |
tree | 18ccac8cf815b8175cdaffeecd7c27509f86096c /xen/arch/x86/acpi | |
parent | a37186e1c2b3e9462cce65d330a71c3afbde4b9b (diff) | |
download | xen-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.c | 49 | ||||
-rw-r--r-- | xen/arch/x86/acpi/power.c | 79 |
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() ) |