mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
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>
This commit is contained in:
1 parent
a37186e1c2
commit
62d1a69a4e
7 files changed
+149
-25
No files matched your search
@@ -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
|
||||
|
||||
|
||||
+62
-17
@@ -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,19 +366,33 @@ 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_sinfo.sleep_extended )
|
||||
{
|
||||
/*
|
||||
* 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);
|
||||
|
||||
if ( 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() )
|
||||
continue;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||