]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ACPI, APEI: Add 64-bit read/write support for APEI on i386
authorMyron Stowe <mstowe@redhat.com>
Sat, 21 Jan 2012 02:13:24 +0000 (19:13 -0700)
committerLen Brown <len.brown@intel.com>
Sat, 21 Jan 2012 06:08:17 +0000 (01:08 -0500)
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>
drivers/acpi/apei/apei-base.c
drivers/acpi/osl.c
include/acpi/acpiosxf.h

index e45350cb6ac8356f9bbc161344a5875a976f430b..e5d53b7ddc7e0eb024f7cdc6863619bd2c3dc0a2 100644 (file)
@@ -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);
index fcc12d842bcc51cf0413e7316ac6dcf9e4cf0a1f..5498a6d88ba23a5c409b46c200d781cfb7118f0e 100644 (file)
@@ -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)
index 2fe8639b3ae74f8ef8ed93626391256d0528c991..7c9aebe8a7aa27ebf091c1a0ec2470e1e23d7f0a 100644 (file)
@@ -218,9 +218,13 @@ acpi_status acpi_os_write_port(acpi_io_address address, u32 value, u32 width);
  */
 acpi_status
 acpi_os_read_memory(acpi_physical_address address, u32 * value, u32 width);
+acpi_status
+acpi_os_read_memory64(acpi_physical_address address, u64 *value, u32 width);
 
 acpi_status
 acpi_os_write_memory(acpi_physical_address address, u32 value, u32 width);
+acpi_status
+acpi_os_write_memory64(acpi_physical_address address, u64 value, u32 width);
 
 /*
  * Platform and hardware-independent PCI configuration space access