[PATCH v4 2/3] PCI: endpoint: Add API pci_epf_assign_bar_space()

Frank Li posted 3 patches 4 months, 1 week ago
There is a newer version of this series
[PATCH v4 2/3] PCI: endpoint: Add API pci_epf_assign_bar_space()
Posted by Frank Li 4 months, 1 week ago
Add pci_epf_assign_bar_space() to allow setting any physical address as
inbound memory space, such as an MSI message base address.

Since PCI BAR size must be a power of two, the input MMIO range
[inbound_addr, inbound_addr + size) is mapped by finding n such that
[base, base + 2^n) covers the range. The base address is also required
to be aligned to 2^n.

Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v4
- use size for bar size.

chagne in v3.
- update function name to pci_epf_assign_bar_space()
- s/allocated/assigned/
- add check when align down input address to memory align require, may not
bar's size can't cover required ragion.

change in v2
- add new API pci_epf_set_inbound_space()
- fix bits 8 * size_of(dma_addr_t);
---
 drivers/pci/endpoint/pci-epf-core.c | 80 +++++++++++++++++++++++++++++++++++++
 include/linux/pci-epf.h             |  6 +++
 2 files changed, 86 insertions(+)

diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c
index db0420f601d81de426a3e805c7fc309de58d54b7..d0b8a02c1de988873d28ede76800d5d4e9810827 100644
--- a/drivers/pci/endpoint/pci-epf-core.c
+++ b/drivers/pci/endpoint/pci-epf-core.c
@@ -349,6 +349,86 @@ void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar,
 }
 EXPORT_SYMBOL_GPL(pci_epf_alloc_space);
 
+/**
+ * pci_epf_assign_bar_space() - Assign PCI EPF BAR space
+ * @epf: the EPF device to whom allocate the memory
+ * @size: the size of the memory that has to be assigned
+ * @bar: the BAR number corresponding to the assigned register space
+ * @epc_features: the 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 assigned memory for the PCI EPF register space.
+ * 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;
+	struct pci_epc *epc;
+	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
+	 *
+	 *  U means address bits have not change in Range [bar_addr, limit]
+	 *  X means bit 0 or 1.
+	 *
+	 *  bar_addr^limit	0  0  0  0  0  0  1 X X X X X X X X X
+	 *  Find first bit 1 pos from MSB, 2 ^ pos windows will cover
+	 *  [bar_Addr, limit] 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_bar_required_size(epf, &bar_size, &aligned_mem_size,
+					  bar, epc_features, type))
+		return -ENOMEM;
+
+	if (type == PRIMARY_INTERFACE) {
+		epc = epf->epc;
+		epf_bar = epf->bar;
+	} else {
+		epc = epf->sec_epc;
+		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].aligned_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)
 {
 	struct config_group *group, *tmp;
diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h
index 2e85504ba2baf93827224884ca19ae2bd0e6906b..d2cdcc06d366ce61db0d7633105965e50cb49a7b 100644
--- a/include/linux/pci-epf.h
+++ b/include/linux/pci-epf.h
@@ -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,
 			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 inbound_addr);
+
 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);

-- 
2.34.1
Re: [External] : [PATCH v4 2/3] PCI: endpoint: Add API pci_epf_assign_bar_space()
Posted by ALOK TIWARI 4 months ago

On 10/1/2025 2:09 AM, Frank Li wrote:
> +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 inbound_addr);

nit: Any particular reason for using inbound_addr instead of bar_addr ?

> +
>   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);


Thanks,
Alok
Re: [External] : [PATCH v4 2/3] PCI: endpoint: Add API pci_epf_assign_bar_space()
Posted by Frank Li 4 months ago
On Tue, Oct 07, 2025 at 12:25:59AM +0530, ALOK TIWARI wrote:
>
>
> On 10/1/2025 2:09 AM, Frank Li wrote:
> > +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 inbound_addr);
>
> nit: Any particular reason for using inbound_addr instead of bar_addr ?

bar_addr is easy to confuse with RC side's bar windows's address. This one
means the ATU convert bar's inbound transfer to what EP side's address.

The below API also use term 'inbound_addr'.

It is not big deal. Manivannan or Niklas Cassel, do you have any perfer?

I am okay for both names.

Frank
>
> > +
> >   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);
>
>
> Thanks,
> Alok
Re: [External] : [PATCH v4 2/3] PCI: endpoint: Add API pci_epf_assign_bar_space()
Posted by ALOK TIWARI 4 months ago

On 10/7/2025 8:41 PM, Frank Li wrote:
> On Tue, Oct 07, 2025 at 12:25:59AM +0530, ALOK TIWARI wrote:
>>
>> On 10/1/2025 2:09 AM, Frank Li wrote:
>>> +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 inbound_addr);
>> nit: Any particular reason for using inbound_addr instead of bar_addr ?
> bar_addr is easy to confuse with RC side's bar windows's address. This one
> means the ATU convert bar's inbound transfer to what EP side's address.
> 
> The below API also use term 'inbound_addr'.
> 
> It is not big deal. Manivannan or Niklas Cassel, do you have any perfer?
> 
> I am okay for both names.

It is not a major issue, just noticed that the .c file refers to 
dma_addr_t bar_addr while the .h file uses inbound_addr.
Just curious to know the reason.

> 
> Frank
>>> +
>>>    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);
>>
>> Thanks,
>> Alok



Thanks,
Alok