[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