diff options
author | Myron Stowe <mstowe@redhat.com> | 2012-01-20 19:13:24 -0700 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2012-01-21 01:08:17 -0500 |
commit | e615bf5b5519862ab66172f4dec7455d6543a578 (patch) | |
tree | c6d37861dc376b55bc5c4ece88f57faf9d51e99a /drivers/acpi | |
parent | dcd6c92267155e70a94b3927bce681ce74b80d1f (diff) |
ACPI, APEI: Add 64-bit read/write support for APEI on i386
Base ACPI (CA) currently does not support atomic 64-bit reads and writes
(acpi_read() and acpi_write() split 64-bit loads/stores into two
32-bit transfers) yet APEI expects 64-bit transfer capability, even
when running on 32-bit systems.
This patch implements 64-bit read and write routines for APEI usage.
This patch re-factors similar functionality introduced in commit
04c25997c97, bringing it into the ACPI subsystem in preparation for
removing ./drivers/acpi/atomicio.[ch]. In the implementation I have
replicated acpi_os_read_memory() and acpi_os_write_memory(), creating
64-bit versions for APEI to utilize, as opposed to something more
elegant. My thinking is that we should attempt to see if we can get
ACPI's CA/OSL changed so that the existing acpi_read() and acpi_write()
interfaces are natively 64-bit capable and then subsequently remove the
replication.
Signed-off-by: Myron Stowe <myron.stowe@redhat.com>
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/apei/apei-base.c | 35 | ||||
-rw-r--r-- | drivers/acpi/osl.c | 116 |
2 files changed, 120 insertions, 31 deletions
diff --git a/drivers/acpi/apei/apei-base.c b/drivers/acpi/apei/apei-base.c index e45350cb6ac8..e5d53b7ddc7e 100644 --- a/drivers/acpi/apei/apei-base.c +++ b/drivers/acpi/apei/apei-base.c @@ -596,33 +596,19 @@ int apei_read(u64 *val, struct acpi_generic_address *reg) { int rc; u64 address; - u32 tmp, width = reg->bit_width; acpi_status status; rc = apei_check_gar(reg, &address); if (rc) return rc; - if (width == 64) - width = 32; /* Break into two 32-bit transfers */ - *val = 0; switch(reg->space_id) { case ACPI_ADR_SPACE_SYSTEM_MEMORY: - status = acpi_os_read_memory((acpi_physical_address) - address, &tmp, width); + status = acpi_os_read_memory64((acpi_physical_address) + address, val, reg->bit_width); if (ACPI_FAILURE(status)) return -EIO; - *val = tmp; - - if (reg->bit_width == 64) { - /* Read the top 32 bits */ - status = acpi_os_read_memory((acpi_physical_address) - (address + 4), &tmp, 32); - if (ACPI_FAILURE(status)) - return -EIO; - *val |= ((u64)tmp << 32); - } break; case ACPI_ADR_SPACE_SYSTEM_IO: status = acpi_os_read_port(address, (u32 *)val, reg->bit_width); @@ -642,31 +628,18 @@ int apei_write(u64 val, struct acpi_generic_address *reg) { int rc; u64 address; - u32 width = reg->bit_width; acpi_status status; rc = apei_check_gar(reg, &address); if (rc) return rc; - if (width == 64) - width = 32; /* Break into two 32-bit transfers */ - switch (reg->space_id) { case ACPI_ADR_SPACE_SYSTEM_MEMORY: - status = acpi_os_write_memory((acpi_physical_address) - address, ACPI_LODWORD(val), - width); + status = acpi_os_write_memory64((acpi_physical_address) + address, val, reg->bit_width); if (ACPI_FAILURE(status)) return -EIO; - - if (reg->bit_width == 64) { - status = acpi_os_write_memory((acpi_physical_address) - (address + 4), - ACPI_HIDWORD(val), 32); - if (ACPI_FAILURE(status)) - return -EIO; - } break; case ACPI_ADR_SPACE_SYSTEM_IO: status = acpi_os_write_port(address, val, reg->bit_width); diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index fcc12d842bcc..5498a6d88ba2 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -710,6 +710,67 @@ acpi_os_read_memory(acpi_physical_address phys_addr, u32 * value, u32 width) return AE_OK; } +#ifdef readq +static inline u64 read64(const volatile void __iomem *addr) +{ + return readq(addr); +} +#else +static inline u64 read64(const volatile void __iomem *addr) +{ + u64 l, h; + l = readl(addr); + h = readl(addr+4); + return l | (h << 32); +} +#endif + +acpi_status +acpi_os_read_memory64(acpi_physical_address phys_addr, u64 *value, u32 width) +{ + void __iomem *virt_addr; + unsigned int size = width / 8; + bool unmap = false; + u64 dummy; + + rcu_read_lock(); + virt_addr = acpi_map_vaddr_lookup(phys_addr, size); + if (!virt_addr) { + rcu_read_unlock(); + virt_addr = acpi_os_ioremap(phys_addr, size); + if (!virt_addr) + return AE_BAD_ADDRESS; + unmap = true; + } + + if (!value) + value = &dummy; + + switch (width) { + case 8: + *(u8 *) value = readb(virt_addr); + break; + case 16: + *(u16 *) value = readw(virt_addr); + break; + case 32: + *(u32 *) value = readl(virt_addr); + break; + case 64: + *(u64 *) value = read64(virt_addr); + break; + default: + BUG(); + } + + if (unmap) + iounmap(virt_addr); + else + rcu_read_unlock(); + + return AE_OK; +} + acpi_status acpi_os_write_memory(acpi_physical_address phys_addr, u32 value, u32 width) { @@ -749,6 +810,61 @@ acpi_os_write_memory(acpi_physical_address phys_addr, u32 value, u32 width) return AE_OK; } +#ifdef writeq +static inline void write64(u64 val, volatile void __iomem *addr) +{ + writeq(val, addr); +} +#else +static inline void write64(u64 val, volatile void __iomem *addr) +{ + writel(val, addr); + writel(val>>32, addr+4); +} +#endif + +acpi_status +acpi_os_write_memory64(acpi_physical_address phys_addr, u64 value, u32 width) +{ + void __iomem *virt_addr; + unsigned int size = width / 8; + bool unmap = false; + + rcu_read_lock(); + virt_addr = acpi_map_vaddr_lookup(phys_addr, size); + if (!virt_addr) { + rcu_read_unlock(); + virt_addr = acpi_os_ioremap(phys_addr, size); + if (!virt_addr) + return AE_BAD_ADDRESS; + unmap = true; + } + + switch (width) { + case 8: + writeb(value, virt_addr); + break; + case 16: + writew(value, virt_addr); + break; + case 32: + writel(value, virt_addr); + break; + case 64: + write64(value, virt_addr); + break; + default: + BUG(); + } + + if (unmap) + iounmap(virt_addr); + else + rcu_read_unlock(); + + return AE_OK; +} + acpi_status acpi_os_read_pci_configuration(struct acpi_pci_id * pci_id, u32 reg, u64 *value, u32 width) |