]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
PCI: endpoint: Add pci_epf_align_inbound_addr() helper for inbound address alignment
authorFrank Li <Frank.Li@nxp.com>
Thu, 10 Jul 2025 19:13:51 +0000 (15:13 -0400)
committerBjorn Helgaas <bhelgaas@google.com>
Thu, 24 Jul 2025 21:51:41 +0000 (16:51 -0500)
Add pci_epf_align_inbound_addr() to align the inbound addresses according
to PCI BAR alignment requirements. The aligned base address and offset are
returned via 'base' and 'off' parameters.

Signed-off-by: Frank Li <Frank.Li@nxp.com>
[mani: reworded kernel-doc and commit message]
Signed-off-by: Manivannan Sadhasivam <mani@kernel.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Tested-by: Niklas Cassel <cassel@kernel.org>
Link: https://patch.msgid.link/20250710-ep-msi-v21-5-57683fc7fb25@nxp.com
drivers/pci/endpoint/pci-epf-core.c
include/linux/pci-epf.h

index 577a9e490115c9dd5d7fb624c4a3001f39b97e78..09b90e1631d549e6a39d7a6ef887db7f9f3e2674 100644 (file)
@@ -477,6 +477,44 @@ struct pci_epf *pci_epf_create(const char *name)
 }
 EXPORT_SYMBOL_GPL(pci_epf_create);
 
+/**
+ * pci_epf_align_inbound_addr() - Align the given address based on the BAR
+ *                               alignment requirement
+ * @epf: the EPF device
+ * @addr: inbound address to be aligned
+ * @bar: the BAR number corresponding to the given addr
+ * @base: base address matching the @bar alignment requirement
+ * @off: offset to be added to the @base address
+ *
+ * Helper function to align input @addr based on BAR's alignment requirement.
+ * The aligned base address and offset are returned via @base and @off.
+ *
+ * NOTE: The pci_epf_alloc_space() function already accounts for alignment.
+ * This API is primarily intended for use with other memory regions not
+ * allocated by pci_epf_alloc_space(), such as peripheral register spaces or
+ * the message address of a platform MSI controller.
+ *
+ * Return: 0 on success, errno otherwise.
+ */
+int pci_epf_align_inbound_addr(struct pci_epf *epf, enum pci_barno bar,
+                              u64 addr, dma_addr_t *base, size_t *off)
+{
+       /*
+        * Most EP controllers require the BAR start address to be aligned to
+        * the BAR size, because they mask off the lower bits.
+        *
+        * Alignment to BAR size also works for controllers that support
+        * unaligned addresses.
+        */
+       u64 align = epf->bar[bar].size;
+
+       *base = round_down(addr, align);
+       *off = addr & (align - 1);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(pci_epf_align_inbound_addr);
+
 static void pci_epf_dev_release(struct device *dev)
 {
        struct pci_epf *epf = to_pci_epf(dev);
index 52e07602f08e8a6ea8716af367e27ead64e61309..2e85504ba2baf93827224884ca19ae2bd0e6906b 100644 (file)
@@ -241,6 +241,9 @@ void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar,
                          enum pci_epc_interface_type type);
 void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar,
                        enum pci_epc_interface_type type);
+
+int pci_epf_align_inbound_addr(struct pci_epf *epf, enum pci_barno bar,
+                              u64 addr, dma_addr_t *base, size_t *off);
 int pci_epf_bind(struct pci_epf *epf);
 void pci_epf_unbind(struct pci_epf *epf);
 int pci_epf_add_vepf(struct pci_epf *epf_pf, struct pci_epf *epf_vf);