PCI: endpoint: Add pci_epf_assign_bar_space() API

Add pci_epf_assign_bar_space() API to allow setting any MMIO address as
the BAR memory space, such as an MSI message base address.

This API also conforms to the BAR base address and size alignment
restrictions enforced by the PCI spec r6.0, sec 7.5.1.2.1.

Signed-off-by: Frank Li <Frank.Li@nxp.com>
[mani: removed unused epc var, reworded kdoc, comments and description]
Signed-off-by: Manivannan Sadhasivam <mani@kernel.org>
Link: https://patch.msgid.link/20251015-vntb_msi_doorbell-v6-3-9230298b1910@nxp.com
This commit is contained in:
Frank Li 2025-10-15 11:27:30 -04:00 committed by Manivannan Sadhasivam
parent f71e2b67b5
commit 0bfc6758f2
2 changed files with 83 additions and 0 deletions

View File

@ -346,6 +346,83 @@ void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar,
} }
EXPORT_SYMBOL_GPL(pci_epf_alloc_space); EXPORT_SYMBOL_GPL(pci_epf_alloc_space);
/**
* pci_epf_assign_bar_space() - Assign PCI EPF BAR space
* @epf: EPF device to assign the BAR memory
* @size: Size of the memory that has to be assigned
* @bar: BAR number for which the memory is assigned
* @epc_features: Features provided by the EPC specific to this EPF
* @type: Identifies if the assignment is for primary EPC or secondary EPC
* @bar_addr: Address to be assigned for the @bar
*
* Invoke to assign memory for the PCI EPF BAR.
* Flag PCI_BASE_ADDRESS_MEM_TYPE_64 will automatically get set if the BAR
* can only be a 64-bit BAR, or if the requested size is larger than 2 GB.
*/
int pci_epf_assign_bar_space(struct pci_epf *epf, size_t size,
enum pci_barno bar,
const struct pci_epc_features *epc_features,
enum pci_epc_interface_type type,
dma_addr_t bar_addr)
{
size_t bar_size, aligned_mem_size;
struct pci_epf_bar *epf_bar;
dma_addr_t limit;
int pos;
if (!size)
return -EINVAL;
limit = bar_addr + size - 1;
/*
* Bits: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
* bar_addr: U U U U U U 0 X X X X X X X X X
* limit: U U U U U U 1 X X X X X X X X X
*
* bar_addr^limit 0 0 0 0 0 0 1 X X X X X X X X X
*
* U: unchanged address bits in range [bar_addr, limit]
* X: bit 0 or 1
*
* (bar_addr^limit) & BIT_ULL(pos) will find the first set bit from MSB
* (pos). And value of (2 ^ pos) should be able to cover the BAR range.
*/
for (pos = 8 * sizeof(dma_addr_t) - 1; pos > 0; pos--)
if ((limit ^ bar_addr) & BIT_ULL(pos))
break;
if (pos == 8 * sizeof(dma_addr_t) - 1)
return -EINVAL;
bar_size = BIT_ULL(pos + 1);
if (pci_epf_get_required_bar_size(epf, &bar_size, &aligned_mem_size,
bar, epc_features, type))
return -ENOMEM;
if (type == PRIMARY_INTERFACE)
epf_bar = epf->bar;
else
epf_bar = epf->sec_epc_bar;
epf_bar[bar].phys_addr = ALIGN_DOWN(bar_addr, aligned_mem_size);
if (epf_bar[bar].phys_addr + bar_size < limit)
return -ENOMEM;
epf_bar[bar].addr = NULL;
epf_bar[bar].size = bar_size;
epf_bar[bar].mem_size = aligned_mem_size;
epf_bar[bar].barno = bar;
if (upper_32_bits(size) || epc_features->bar[bar].only_64bit)
epf_bar[bar].flags |= PCI_BASE_ADDRESS_MEM_TYPE_64;
else
epf_bar[bar].flags |= PCI_BASE_ADDRESS_MEM_TYPE_32;
return 0;
}
EXPORT_SYMBOL_GPL(pci_epf_assign_bar_space);
static void pci_epf_remove_cfs(struct pci_epf_driver *driver) static void pci_epf_remove_cfs(struct pci_epf_driver *driver)
{ {
struct config_group *group, *tmp; struct config_group *group, *tmp;

View File

@ -242,6 +242,12 @@ void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar,
void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar, void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar,
enum pci_epc_interface_type type); enum pci_epc_interface_type type);
int pci_epf_assign_bar_space(struct pci_epf *epf, size_t size,
enum pci_barno bar,
const struct pci_epc_features *epc_features,
enum pci_epc_interface_type type,
dma_addr_t bar_addr);
int pci_epf_align_inbound_addr(struct pci_epf *epf, enum pci_barno bar, int pci_epf_align_inbound_addr(struct pci_epf *epf, enum pci_barno bar,
u64 addr, dma_addr_t *base, size_t *off); u64 addr, dma_addr_t *base, size_t *off);
int pci_epf_bind(struct pci_epf *epf); int pci_epf_bind(struct pci_epf *epf);