|
@@ -52,7 +52,8 @@ ACPI_MODULE_NAME("hwregs")
|
|
|
#if (!ACPI_REDUCED_HARDWARE)
|
|
|
/* Local Prototypes */
|
|
|
static u8
|
|
|
-acpi_hw_get_access_bit_width(struct acpi_generic_address *reg,
|
|
|
+acpi_hw_get_access_bit_width(u64 address,
|
|
|
+ struct acpi_generic_address *reg,
|
|
|
u8 max_bit_width);
|
|
|
|
|
|
static acpi_status
|
|
@@ -71,7 +72,8 @@ acpi_hw_write_multiple(u32 value,
|
|
|
*
|
|
|
* FUNCTION: acpi_hw_get_access_bit_width
|
|
|
*
|
|
|
- * PARAMETERS: reg - GAS register structure
|
|
|
+ * PARAMETERS: address - GAS register address
|
|
|
+ * reg - GAS register structure
|
|
|
* max_bit_width - Max bit_width supported (32 or 64)
|
|
|
*
|
|
|
* RETURN: Status
|
|
@@ -81,27 +83,59 @@ acpi_hw_write_multiple(u32 value,
|
|
|
******************************************************************************/
|
|
|
|
|
|
static u8
|
|
|
-acpi_hw_get_access_bit_width(struct acpi_generic_address *reg, u8 max_bit_width)
|
|
|
+acpi_hw_get_access_bit_width(u64 address,
|
|
|
+ struct acpi_generic_address *reg, u8 max_bit_width)
|
|
|
{
|
|
|
- if (!reg->access_width) {
|
|
|
- if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
|
|
|
- max_bit_width = 32;
|
|
|
- }
|
|
|
+ u8 access_bit_width;
|
|
|
|
|
|
- /*
|
|
|
- * Detect old register descriptors where only the bit_width field
|
|
|
- * makes senses.
|
|
|
- */
|
|
|
- if (reg->bit_width < max_bit_width &&
|
|
|
- !reg->bit_offset && reg->bit_width &&
|
|
|
- ACPI_IS_POWER_OF_TWO(reg->bit_width) &&
|
|
|
- ACPI_IS_ALIGNED(reg->bit_width, 8)) {
|
|
|
- return (reg->bit_width);
|
|
|
- }
|
|
|
- return (max_bit_width);
|
|
|
+ /*
|
|
|
+ * GAS format "register", used by FADT:
|
|
|
+ * 1. Detected if bit_offset is 0 and bit_width is 8/16/32/64;
|
|
|
+ * 2. access_size field is ignored and bit_width field is used for
|
|
|
+ * determining the boundary of the IO accesses.
|
|
|
+ * GAS format "region", used by APEI registers:
|
|
|
+ * 1. Detected if bit_offset is not 0 or bit_width is not 8/16/32/64;
|
|
|
+ * 2. access_size field is used for determining the boundary of the
|
|
|
+ * IO accesses;
|
|
|
+ * 3. bit_offset/bit_width fields are used to describe the "region".
|
|
|
+ *
|
|
|
+ * Note: This algorithm assumes that the "Address" fields should always
|
|
|
+ * contain aligned values.
|
|
|
+ */
|
|
|
+ if (!reg->bit_offset && reg->bit_width &&
|
|
|
+ ACPI_IS_POWER_OF_TWO(reg->bit_width) &&
|
|
|
+ ACPI_IS_ALIGNED(reg->bit_width, 8)) {
|
|
|
+ access_bit_width = reg->bit_width;
|
|
|
+ } else if (reg->access_width) {
|
|
|
+ access_bit_width = (1 << (reg->access_width + 2));
|
|
|
} else {
|
|
|
- return (1 << (reg->access_width + 2));
|
|
|
+ access_bit_width =
|
|
|
+ ACPI_ROUND_UP_POWER_OF_TWO_8(reg->bit_offset +
|
|
|
+ reg->bit_width);
|
|
|
+ if (access_bit_width <= 8) {
|
|
|
+ access_bit_width = 8;
|
|
|
+ } else {
|
|
|
+ while (!ACPI_IS_ALIGNED(address, access_bit_width >> 3)) {
|
|
|
+ access_bit_width >>= 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Maximum IO port access bit width is 32 */
|
|
|
+
|
|
|
+ if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
|
|
|
+ max_bit_width = 32;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Return access width according to the requested maximum access bit width,
|
|
|
+ * as the caller should know the format of the register and may enforce
|
|
|
+ * a 32-bit accesses.
|
|
|
+ */
|
|
|
+ if (access_bit_width < max_bit_width) {
|
|
|
+ return (access_bit_width);
|
|
|
}
|
|
|
+ return (max_bit_width);
|
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
@@ -163,7 +197,8 @@ acpi_hw_validate_register(struct acpi_generic_address *reg,
|
|
|
|
|
|
/* Validate the bit_width, convert access_width into number of bits */
|
|
|
|
|
|
- access_width = acpi_hw_get_access_bit_width(reg, max_bit_width);
|
|
|
+ access_width =
|
|
|
+ acpi_hw_get_access_bit_width(*address, reg, max_bit_width);
|
|
|
bit_width =
|
|
|
ACPI_ROUND_UP(reg->bit_offset + reg->bit_width, access_width);
|
|
|
if (max_bit_width < bit_width) {
|
|
@@ -219,7 +254,7 @@ acpi_status acpi_hw_read(u32 *value, struct acpi_generic_address *reg)
|
|
|
* into number of bits based
|
|
|
*/
|
|
|
*value = 0;
|
|
|
- access_width = acpi_hw_get_access_bit_width(reg, 32);
|
|
|
+ access_width = acpi_hw_get_access_bit_width(address, reg, 32);
|
|
|
bit_width = reg->bit_offset + reg->bit_width;
|
|
|
bit_offset = reg->bit_offset;
|
|
|
|
|
@@ -311,7 +346,7 @@ acpi_status acpi_hw_write(u32 value, struct acpi_generic_address *reg)
|
|
|
|
|
|
/* Convert access_width into number of bits based */
|
|
|
|
|
|
- access_width = acpi_hw_get_access_bit_width(reg, 32);
|
|
|
+ access_width = acpi_hw_get_access_bit_width(address, reg, 32);
|
|
|
bit_width = reg->bit_offset + reg->bit_width;
|
|
|
bit_offset = reg->bit_offset;
|
|
|
|