[PATCH 1/5] pci: Add pci_generic_mmap_write_config32() helper

Pranav Sanwal <[email protected]>
Newsgroups org.u-boot-project.lists.u-boot
Message-ID <dfa217d94694c145439be412fd815b44a7617882.1787554549.git.pranav.sanwal@amd.com>
Some memory-mapped PCI host bridges only decode 32-bit accesses to
their configuration space and either drop or corrupt neighbouring
bytes on a narrower access. Because such hardware only ever reliably
commits a full 32-bit transaction, the only safe way to perform a
narrower write is to synthesize it in software: read the containing
word, patch in the requested bits, and write the whole word back.

Add pci_generic_mmap_write_config32(), a counterpart to
pci_generic_mmap_write_config(), that does exactly this: it
transparently promotes 8-bit and 16-bit writes to a 32-bit
read-modify-write of the word containing the requested offset,
leaving 32-bit writes untouched. This mirrors Linux's
pci_generic_config_write32() (drivers/pci/access.c), used for the
same class of host bridge.

Signed-off-by: Pranav Sanwal <[email protected]>
---
 drivers/pci/pci-uclass.c | 42 ++++++++++++++++++++++++++++++++++++++++
 include/pci.h            | 26 +++++++++++++++++++++++++
 2 files changed, 68 insertions(+)

diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c
index 4bdd1f7477f..f4ce630118f 100644
--- a/drivers/pci/pci-uclass.c
+++ b/drivers/pci/pci-uclass.c
@@ -618,6 +618,48 @@ int pci_generic_mmap_write_config(
 	}
 }
 
+int pci_generic_mmap_write_config32(
+	const struct udevice *bus,
+	int (*addr_f)(const struct udevice *bus, pci_dev_t bdf, uint offset,
+		      void **addrp),
+	pci_dev_t bdf,
+	uint offset,
+	ulong value,
+	enum pci_size_t size)
+{
+	u32 shift, mask, tmp;
+	void *address;
+
+	if (addr_f(bus, bdf, ALIGN_DOWN(offset, 4), &address) < 0) {
+		debug("%s: failed to get config address for offset 0x%x\n",
+		      __func__, offset);
+		return 0;
+	}
+
+	switch (size) {
+	case PCI_SIZE_32:
+		writel(value, address);
+		return 0;
+	case PCI_SIZE_8:
+	case PCI_SIZE_16:
+		/*
+		 * Some host bridges only decode 32-bit accesses to their
+		 * config space and silently corrupt neighbouring bytes on a
+		 * narrower write. Widen the access to a 32-bit
+		 * read-modify-write of the word containing the requested
+		 * offset.
+		 */
+		shift = (offset % 4) * BITS_PER_BYTE;
+		mask = pci_get_ff(size) << shift;
+		tmp = readl(address) & ~mask;
+		tmp |= (value << shift) & mask;
+		writel(tmp, address);
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
 int pci_generic_mmap_read_config(
 	const struct udevice *bus,
 	int (*addr_f)(const struct udevice *bus, pci_dev_t bdf, uint offset,
diff --git a/include/pci.h b/include/pci.h
index 4b0facd6dcf..108d97a5d54 100644
--- a/include/pci.h
+++ b/include/pci.h
@@ -1151,6 +1151,32 @@ int pci_generic_mmap_write_config(
 	ulong value,
 	enum pci_size_t size);
 
+/**
+ * pci_generic_mmap_write_config32() - Generic helper for writing to
+ * memory-mapped PCI configuration space that only supports 32-bit accesses.
+ * @bus: Pointer to the PCI bus
+ * @addr_f: Callback for calculating the config space address
+ * @bdf: Identifies the PCI device to access
+ * @offset: The offset into the device's configuration space
+ * @value: The value to write
+ * @size: Indicates the size of access to perform
+ *
+ * Like pci_generic_mmap_write_config(), but for host bridges whose
+ * configuration space does not decode 8-bit or 16-bit accesses correctly.
+ * Accesses narrower than 32 bits are promoted to a 32-bit
+ * read-modify-write of the word containing @offset.
+ *
+ * Return: 0 on success, else -EINVAL
+ */
+int pci_generic_mmap_write_config32(
+	const struct udevice *bus,
+	int (*addr_f)(const struct udevice *bus, pci_dev_t bdf, uint offset,
+		      void **addrp),
+	pci_dev_t bdf,
+	uint offset,
+	ulong value,
+	enum pci_size_t size);
+
 /**
  * pci_generic_mmap_read_config() - Generic helper for reading from
  * memory-mapped PCI configuration space.
-- 
2.43.7
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.