diff options
Diffstat (limited to 'drivers/acpi/acpica/hwregs.c')
-rw-r--r-- | drivers/acpi/acpica/hwregs.c | 322 |
1 files changed, 223 insertions, 99 deletions
diff --git a/drivers/acpi/acpica/hwregs.c b/drivers/acpi/acpica/hwregs.c index 4dc43b018517..7b2fb602b5cb 100644 --- a/drivers/acpi/acpica/hwregs.c +++ b/drivers/acpi/acpica/hwregs.c @@ -51,6 +51,17 @@ #define _COMPONENT ACPI_HARDWARE ACPI_MODULE_NAME("hwregs") +/* Local Prototypes */ +static acpi_status +acpi_hw_read_multiple(u32 *value, + struct acpi_generic_address *register_a, + struct acpi_generic_address *register_b); + +static acpi_status +acpi_hw_write_multiple(u32 value, + struct acpi_generic_address *register_a, + struct acpi_generic_address *register_b); + /******************************************************************************* * * FUNCTION: acpi_hw_clear_acpi_status @@ -60,9 +71,9 @@ ACPI_MODULE_NAME("hwregs") * RETURN: Status * * DESCRIPTION: Clears all fixed and general purpose status bits - * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED * ******************************************************************************/ + acpi_status acpi_hw_clear_acpi_status(void) { acpi_status status; @@ -70,28 +81,20 @@ acpi_status acpi_hw_clear_acpi_status(void) ACPI_FUNCTION_TRACE(hw_clear_acpi_status); - ACPI_DEBUG_PRINT((ACPI_DB_IO, "About to write %04X to %04X\n", + ACPI_DEBUG_PRINT((ACPI_DB_IO, "About to write %04X to %0llX\n", ACPI_BITMASK_ALL_FIXED_STATUS, - (u16) acpi_gbl_FADT.xpm1a_event_block.address)); + acpi_gbl_xpm1a_status.address)); lock_flags = acpi_os_acquire_lock(acpi_gbl_hardware_lock); + /* Clear the fixed events in PM1 A/B */ + status = acpi_hw_register_write(ACPI_REGISTER_PM1_STATUS, ACPI_BITMASK_ALL_FIXED_STATUS); if (ACPI_FAILURE(status)) { goto unlock_and_exit; } - /* Clear the fixed events */ - - if (acpi_gbl_FADT.xpm1b_event_block.address) { - status = acpi_write(ACPI_BITMASK_ALL_FIXED_STATUS, - &acpi_gbl_FADT.xpm1b_event_block); - if (ACPI_FAILURE(status)) { - goto unlock_and_exit; - } - } - /* Clear the GPE Bits in all GPE registers in all GPE blocks */ status = acpi_ev_walk_gpe_list(acpi_hw_clear_gpe_block, NULL); @@ -128,6 +131,42 @@ struct acpi_bit_register_info *acpi_hw_get_bit_register_info(u32 register_id) /****************************************************************************** * + * FUNCTION: acpi_hw_write_pm1_control + * + * PARAMETERS: pm1a_control - Value to be written to PM1A control + * pm1b_control - Value to be written to PM1B control + * + * RETURN: Status + * + * DESCRIPTION: Write the PM1 A/B control registers. These registers are + * different than than the PM1 A/B status and enable registers + * in that different values can be written to the A/B registers. + * Most notably, the SLP_TYP bits can be different, as per the + * values returned from the _Sx predefined methods. + * + ******************************************************************************/ + +acpi_status acpi_hw_write_pm1_control(u32 pm1a_control, u32 pm1b_control) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(hw_write_pm1_control); + + status = acpi_write(pm1a_control, &acpi_gbl_FADT.xpm1a_control_block); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + if (acpi_gbl_FADT.xpm1b_control_block.address) { + status = + acpi_write(pm1b_control, + &acpi_gbl_FADT.xpm1b_control_block); + } + return_ACPI_STATUS(status); +} + +/****************************************************************************** + * * FUNCTION: acpi_hw_register_read * * PARAMETERS: register_id - ACPI Register ID @@ -141,64 +180,56 @@ struct acpi_bit_register_info *acpi_hw_get_bit_register_info(u32 register_id) acpi_status acpi_hw_register_read(u32 register_id, u32 * return_value) { - u32 value1 = 0; - u32 value2 = 0; + u32 value = 0; acpi_status status; ACPI_FUNCTION_TRACE(hw_register_read); switch (register_id) { - case ACPI_REGISTER_PM1_STATUS: /* 16-bit access */ - - status = acpi_read(&value1, &acpi_gbl_FADT.xpm1a_event_block); - if (ACPI_FAILURE(status)) { - goto exit; - } - - /* PM1B is optional */ + case ACPI_REGISTER_PM1_STATUS: /* PM1 A/B: 16-bit access each */ - status = acpi_read(&value2, &acpi_gbl_FADT.xpm1b_event_block); - value1 |= value2; + status = acpi_hw_read_multiple(&value, + &acpi_gbl_xpm1a_status, + &acpi_gbl_xpm1b_status); break; - case ACPI_REGISTER_PM1_ENABLE: /* 16-bit access */ + case ACPI_REGISTER_PM1_ENABLE: /* PM1 A/B: 16-bit access each */ - status = acpi_read(&value1, &acpi_gbl_xpm1a_enable); - if (ACPI_FAILURE(status)) { - goto exit; - } - - /* PM1B is optional */ - - status = acpi_read(&value2, &acpi_gbl_xpm1b_enable); - value1 |= value2; + status = acpi_hw_read_multiple(&value, + &acpi_gbl_xpm1a_enable, + &acpi_gbl_xpm1b_enable); break; - case ACPI_REGISTER_PM1_CONTROL: /* 16-bit access */ + case ACPI_REGISTER_PM1_CONTROL: /* PM1 A/B: 16-bit access each */ - status = acpi_read(&value1, &acpi_gbl_FADT.xpm1a_control_block); - if (ACPI_FAILURE(status)) { - goto exit; - } + status = acpi_hw_read_multiple(&value, + &acpi_gbl_FADT. + xpm1a_control_block, + &acpi_gbl_FADT. + xpm1b_control_block); - status = acpi_read(&value2, &acpi_gbl_FADT.xpm1b_control_block); - value1 |= value2; + /* + * Zero the write-only bits. From the ACPI specification, "Hardware + * Write-Only Bits": "Upon reads to registers with write-only bits, + * software masks out all write-only bits." + */ + value &= ~ACPI_PM1_CONTROL_WRITEONLY_BITS; break; case ACPI_REGISTER_PM2_CONTROL: /* 8-bit access */ - status = acpi_read(&value1, &acpi_gbl_FADT.xpm2_control_block); + status = acpi_read(&value, &acpi_gbl_FADT.xpm2_control_block); break; case ACPI_REGISTER_PM_TIMER: /* 32-bit access */ - status = acpi_read(&value1, &acpi_gbl_FADT.xpm_timer_block); + status = acpi_read(&value, &acpi_gbl_FADT.xpm_timer_block); break; case ACPI_REGISTER_SMI_COMMAND_BLOCK: /* 8-bit access */ status = - acpi_os_read_port(acpi_gbl_FADT.smi_command, &value1, 8); + acpi_hw_read_port(acpi_gbl_FADT.smi_command, &value, 8); break; default: @@ -207,10 +238,8 @@ acpi_hw_register_read(u32 register_id, u32 * return_value) break; } - exit: - if (ACPI_SUCCESS(status)) { - *return_value = value1; + *return_value = value; } return_ACPI_STATUS(status); @@ -250,52 +279,42 @@ acpi_status acpi_hw_register_write(u32 register_id, u32 value) ACPI_FUNCTION_TRACE(hw_register_write); switch (register_id) { - case ACPI_REGISTER_PM1_STATUS: /* 16-bit access */ - - /* Perform a read first to preserve certain bits (per ACPI spec) */ - - status = acpi_hw_register_read(ACPI_REGISTER_PM1_STATUS, - &read_value); - if (ACPI_FAILURE(status)) { - goto exit; - } - - /* Insert the bits to be preserved */ - - ACPI_INSERT_BITS(value, ACPI_PM1_STATUS_PRESERVED_BITS, - read_value); - - /* Now we can write the data */ - - status = acpi_write(value, &acpi_gbl_FADT.xpm1a_event_block); - if (ACPI_FAILURE(status)) { - goto exit; - } - - /* PM1B is optional */ + case ACPI_REGISTER_PM1_STATUS: /* PM1 A/B: 16-bit access each */ + /* + * Handle the "ignored" bit in PM1 Status. According to the ACPI + * specification, ignored bits are to be preserved when writing. + * Normally, this would mean a read/modify/write sequence. However, + * preserving a bit in the status register is different. Writing a + * one clears the status, and writing a zero preserves the status. + * Therefore, we must always write zero to the ignored bit. + * + * This behavior is clarified in the ACPI 4.0 specification. + */ + value &= ~ACPI_PM1_STATUS_PRESERVED_BITS; - status = acpi_write(value, &acpi_gbl_FADT.xpm1b_event_block); + status = acpi_hw_write_multiple(value, + &acpi_gbl_xpm1a_status, + &acpi_gbl_xpm1b_status); break; - case ACPI_REGISTER_PM1_ENABLE: /* 16-bit access */ + case ACPI_REGISTER_PM1_ENABLE: /* PM1 A/B: 16-bit access */ - status = acpi_write(value, &acpi_gbl_xpm1a_enable); - if (ACPI_FAILURE(status)) { - goto exit; - } - - /* PM1B is optional */ - - status = acpi_write(value, &acpi_gbl_xpm1b_enable); + status = acpi_hw_write_multiple(value, + &acpi_gbl_xpm1a_enable, + &acpi_gbl_xpm1b_enable); break; - case ACPI_REGISTER_PM1_CONTROL: /* 16-bit access */ + case ACPI_REGISTER_PM1_CONTROL: /* PM1 A/B: 16-bit access each */ /* * Perform a read first to preserve certain bits (per ACPI spec) + * Note: This includes SCI_EN, we never want to change this bit */ - status = acpi_hw_register_read(ACPI_REGISTER_PM1_CONTROL, - &read_value); + status = acpi_hw_read_multiple(&read_value, + &acpi_gbl_FADT. + xpm1a_control_block, + &acpi_gbl_FADT. + xpm1b_control_block); if (ACPI_FAILURE(status)) { goto exit; } @@ -307,25 +326,29 @@ acpi_status acpi_hw_register_write(u32 register_id, u32 value) /* Now we can write the data */ - status = acpi_write(value, &acpi_gbl_FADT.xpm1a_control_block); - if (ACPI_FAILURE(status)) { - goto exit; - } - - status = acpi_write(value, &acpi_gbl_FADT.xpm1b_control_block); + status = acpi_hw_write_multiple(value, + &acpi_gbl_FADT. + xpm1a_control_block, + &acpi_gbl_FADT. + xpm1b_control_block); break; - case ACPI_REGISTER_PM1A_CONTROL: /* 16-bit access */ - - status = acpi_write(value, &acpi_gbl_FADT.xpm1a_control_block); - break; + case ACPI_REGISTER_PM2_CONTROL: /* 8-bit access */ - case ACPI_REGISTER_PM1B_CONTROL: /* 16-bit access */ + /* + * For control registers, all reserved bits must be preserved, + * as per the ACPI spec. + */ + status = + acpi_read(&read_value, &acpi_gbl_FADT.xpm2_control_block); + if (ACPI_FAILURE(status)) { + goto exit; + } - status = acpi_write(value, &acpi_gbl_FADT.xpm1b_control_block); - break; + /* Insert the bits to be preserved */ - case ACPI_REGISTER_PM2_CONTROL: /* 8-bit access */ + ACPI_INSERT_BITS(value, ACPI_PM2_CONTROL_PRESERVED_BITS, + read_value); status = acpi_write(value, &acpi_gbl_FADT.xpm2_control_block); break; @@ -340,10 +363,11 @@ acpi_status acpi_hw_register_write(u32 register_id, u32 value) /* SMI_CMD is currently always in IO space */ status = - acpi_os_write_port(acpi_gbl_FADT.smi_command, value, 8); + acpi_hw_write_port(acpi_gbl_FADT.smi_command, value, 8); break; default: + ACPI_ERROR((AE_INFO, "Unknown Register ID: %X", register_id)); status = AE_BAD_PARAMETER; break; } @@ -351,3 +375,103 @@ acpi_status acpi_hw_register_write(u32 register_id, u32 value) exit: return_ACPI_STATUS(status); } + +/****************************************************************************** + * + * FUNCTION: acpi_hw_read_multiple + * + * PARAMETERS: Value - Where the register value is returned + * register_a - First ACPI register (required) + * register_b - Second ACPI register (optional) + * + * RETURN: Status + * + * DESCRIPTION: Read from the specified two-part ACPI register (such as PM1 A/B) + * + ******************************************************************************/ + +static acpi_status +acpi_hw_read_multiple(u32 *value, + struct acpi_generic_address *register_a, + struct acpi_generic_address *register_b) +{ + u32 value_a = 0; + u32 value_b = 0; + acpi_status status; + + /* The first register is always required */ + + status = acpi_read(&value_a, register_a); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Second register is optional */ + + if (register_b->address) { + status = acpi_read(&value_b, register_b); + if (ACPI_FAILURE(status)) { + return (status); + } + } + + /* + * OR the two return values together. No shifting or masking is necessary, + * because of how the PM1 registers are defined in the ACPI specification: + * + * "Although the bits can be split between the two register blocks (each + * register block has a unique pointer within the FADT), the bit positions + * are maintained. The register block with unimplemented bits (that is, + * those implemented in the other register block) always returns zeros, + * and writes have no side effects" + */ + *value = (value_a | value_b); + return (AE_OK); +} + +/****************************************************************************** + * + * FUNCTION: acpi_hw_write_multiple + * + * PARAMETERS: Value - The value to write + * register_a - First ACPI register (required) + * register_b - Second ACPI register (optional) + * + * RETURN: Status + * + * DESCRIPTION: Write to the specified two-part ACPI register (such as PM1 A/B) + * + ******************************************************************************/ + +static acpi_status +acpi_hw_write_multiple(u32 value, + struct acpi_generic_address *register_a, + struct acpi_generic_address *register_b) +{ + acpi_status status; + + /* The first register is always required */ + + status = acpi_write(value, register_a); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* + * Second register is optional + * + * No bit shifting or clearing is necessary, because of how the PM1 + * registers are defined in the ACPI specification: + * + * "Although the bits can be split between the two register blocks (each + * register block has a unique pointer within the FADT), the bit positions + * are maintained. The register block with unimplemented bits (that is, + * those implemented in the other register block) always returns zeros, + * and writes have no side effects" + */ + if (register_b->address) { + status = acpi_write(value, register_b); + } + + return (status); +} |