|
@@ -195,17 +195,19 @@ acpi_hw_validate_register(struct acpi_generic_address *reg,
|
|
|
* 64-bit values is not needed.
|
|
|
*
|
|
|
* LIMITATIONS: <These limitations also apply to acpi_hw_write>
|
|
|
- * bit_width must be exactly 8, 16, or 32.
|
|
|
* space_ID must be system_memory or system_IO.
|
|
|
- * bit_offset and access_width are currently ignored, as there has
|
|
|
- * not been a need to implement these.
|
|
|
*
|
|
|
******************************************************************************/
|
|
|
|
|
|
acpi_status acpi_hw_read(u32 *value, struct acpi_generic_address *reg)
|
|
|
{
|
|
|
u64 address;
|
|
|
+ u8 access_width;
|
|
|
+ u32 bit_width;
|
|
|
+ u8 bit_offset;
|
|
|
u64 value64;
|
|
|
+ u32 value32;
|
|
|
+ u8 index;
|
|
|
acpi_status status;
|
|
|
|
|
|
ACPI_FUNCTION_NAME(hw_read);
|
|
@@ -217,28 +219,75 @@ acpi_status acpi_hw_read(u32 *value, struct acpi_generic_address *reg)
|
|
|
return (status);
|
|
|
}
|
|
|
|
|
|
- /* Initialize entire 32-bit return value to zero */
|
|
|
-
|
|
|
+ /*
|
|
|
+ * Initialize entire 32-bit return value to zero, convert access_width
|
|
|
+ * into number of bits based
|
|
|
+ */
|
|
|
*value = 0;
|
|
|
+ access_width = acpi_hw_get_access_bit_width(reg, 32);
|
|
|
+ bit_width = reg->bit_offset + reg->bit_width;
|
|
|
+ bit_offset = reg->bit_offset;
|
|
|
|
|
|
/*
|
|
|
* Two address spaces supported: Memory or IO. PCI_Config is
|
|
|
* not supported here because the GAS structure is insufficient
|
|
|
*/
|
|
|
- if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
|
|
|
- status = acpi_os_read_memory((acpi_physical_address)
|
|
|
- address, &value64, reg->bit_width);
|
|
|
+ index = 0;
|
|
|
+ while (bit_width) {
|
|
|
+ if (bit_offset >= access_width) {
|
|
|
+ value32 = 0;
|
|
|
+ bit_offset -= access_width;
|
|
|
+ } else {
|
|
|
+ if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
|
|
|
+ status =
|
|
|
+ acpi_os_read_memory((acpi_physical_address)
|
|
|
+ address +
|
|
|
+ index *
|
|
|
+ ACPI_DIV_8
|
|
|
+ (access_width),
|
|
|
+ &value64, access_width);
|
|
|
+ value32 = (u32)value64;
|
|
|
+ } else { /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
|
|
|
+
|
|
|
+ status = acpi_hw_read_port((acpi_io_address)
|
|
|
+ address +
|
|
|
+ index *
|
|
|
+ ACPI_DIV_8
|
|
|
+ (access_width),
|
|
|
+ &value32,
|
|
|
+ access_width);
|
|
|
+ }
|
|
|
|
|
|
- *value = (u32)value64;
|
|
|
- } else { /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
|
|
|
+ /*
|
|
|
+ * Use offset style bit masks because:
|
|
|
+ * bit_offset < access_width/bit_width < access_width, and
|
|
|
+ * access_width is ensured to be less than 32-bits by
|
|
|
+ * acpi_hw_validate_register().
|
|
|
+ */
|
|
|
+ if (bit_offset) {
|
|
|
+ value32 &= ACPI_MASK_BITS_BELOW(bit_offset);
|
|
|
+ bit_offset = 0;
|
|
|
+ }
|
|
|
+ if (bit_width < access_width) {
|
|
|
+ value32 &= ACPI_MASK_BITS_ABOVE(bit_width);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Use offset style bit writes because "Index * AccessWidth" is
|
|
|
+ * ensured to be less than 32-bits by acpi_hw_validate_register().
|
|
|
+ */
|
|
|
+ ACPI_SET_BITS(value, index * access_width,
|
|
|
+ ACPI_MASK_BITS_ABOVE_32(access_width), value32);
|
|
|
|
|
|
- status = acpi_hw_read_port((acpi_io_address)
|
|
|
- address, value, reg->bit_width);
|
|
|
+ bit_width -=
|
|
|
+ bit_width > access_width ? access_width : bit_width;
|
|
|
+ index++;
|
|
|
}
|
|
|
|
|
|
ACPI_DEBUG_PRINT((ACPI_DB_IO,
|
|
|
"Read: %8.8X width %2d from %8.8X%8.8X (%s)\n",
|
|
|
- *value, reg->bit_width, ACPI_FORMAT_UINT64(address),
|
|
|
+ *value, access_width, ACPI_FORMAT_UINT64(address),
|
|
|
acpi_ut_get_region_name(reg->space_id)));
|
|
|
|
|
|
return (status);
|