Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e615bf5b authored by Myron Stowe's avatar Myron Stowe Committed by Len Brown
Browse files

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
04c25997, 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: default avatarMyron Stowe <myron.stowe@redhat.com>
Signed-off-by: default avatarLen Brown <len.brown@intel.com>
parent dcd6c922
Loading
Loading
Loading
Loading
+4 −31
Original line number Diff line number Diff line
@@ -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);
+116 −0
Original line number Diff line number Diff line
@@ -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)
+4 −0
Original line number Diff line number Diff line
@@ -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