[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <aS3tOi39GEqLMdzR@lizhi-Precision-Tower-5810>
Date: Mon, 1 Dec 2025 14:32:10 -0500
From: Frank Li <Frank.li@....com>
To: Koichiro Den <den@...inux.co.jp>
Cc: ntb@...ts.linux.dev, linux-pci@...r.kernel.org,
dmaengine@...r.kernel.org, linux-kernel@...r.kernel.org,
mani@...nel.org, kwilczynski@...nel.org, kishon@...nel.org,
bhelgaas@...gle.com, corbet@....net, vkoul@...nel.org,
jdmason@...zu.us, dave.jiang@...el.com, allenbh@...il.com,
Basavaraj.Natikar@....com, Shyam-sundar.S-k@....com,
kurt.schwemmer@...rosemi.com, logang@...tatee.com,
jingoohan1@...il.com, lpieralisi@...nel.org, robh@...nel.org,
jbrunet@...libre.com, fancer.lancer@...il.com, arnd@...db.de,
pstanner@...hat.com, elfring@...rs.sourceforge.net
Subject: Re: [RFC PATCH v2 05/27] PCI: dwc: ep: Implement EPC inbound mapping
support
On Sun, Nov 30, 2025 at 01:03:43AM +0900, Koichiro Den wrote:
> Implement map_inbound() and unmap_inbound() for DesignWare endpoint
> controllers (Address Match mode). Allows subrange mappings within a BAR,
> enabling advanced endpoint functions such as NTB with offset-based
> windows.
>
> Signed-off-by: Koichiro Den <den@...inux.co.jp>
> ---
> .../pci/controller/dwc/pcie-designware-ep.c | 239 +++++++++++++++---
> drivers/pci/controller/dwc/pcie-designware.h | 2 +
> 2 files changed, 212 insertions(+), 29 deletions(-)
>
> diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
> index 19571ac2b961..3780a9bd6f79 100644
> --- a/drivers/pci/controller/dwc/pcie-designware-ep.c
> +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
...
>
> +static int dw_pcie_ep_set_bar_init(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> + struct pci_epf_bar *epf_bar)
> +{
> + struct dw_pcie_ep *ep = epc_get_drvdata(epc);
> + struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> + enum pci_barno bar = epf_bar->barno;
> + enum pci_epc_bar_type bar_type;
> + int ret;
> +
> + bar_type = dw_pcie_ep_get_bar_type(ep, bar);
> + switch (bar_type) {
> + case BAR_FIXED:
> + /*
> + * There is no need to write a BAR mask for a fixed BAR (except
> + * to write 1 to the LSB of the BAR mask register, to enable the
> + * BAR). Write the BAR mask regardless. (The fixed bits in the
> + * BAR mask register will be read-only anyway.)
> + */
> + fallthrough;
> + case BAR_PROGRAMMABLE:
> + ret = dw_pcie_ep_set_bar_programmable(ep, func_no, epf_bar);
> + break;
> + case BAR_RESIZABLE:
> + ret = dw_pcie_ep_set_bar_resizable(ep, func_no, epf_bar);
> + break;
> + default:
> + ret = -EINVAL;
> + dev_err(pci->dev, "Invalid BAR type\n");
> + break;
> + }
> +
> + return ret;
> +}
> +
Create new patch for above code movement.
> static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> struct pci_epf_bar *epf_bar)
> {
> struct dw_pcie_ep *ep = epc_get_drvdata(epc);
> - struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> enum pci_barno bar = epf_bar->barno;
> size_t size = epf_bar->size;
> - enum pci_epc_bar_type bar_type;
> int flags = epf_bar->flags;
> int ret, type;
>
> @@ -374,35 +429,12 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> * When dynamically changing a BAR, skip writing the BAR reg, as
> * that would clear the BAR's PCI address assigned by the host.
> */
> - goto config_atu;
> - }
> -
> - bar_type = dw_pcie_ep_get_bar_type(ep, bar);
> - switch (bar_type) {
> - case BAR_FIXED:
> - /*
> - * There is no need to write a BAR mask for a fixed BAR (except
> - * to write 1 to the LSB of the BAR mask register, to enable the
> - * BAR). Write the BAR mask regardless. (The fixed bits in the
> - * BAR mask register will be read-only anyway.)
> - */
> - fallthrough;
> - case BAR_PROGRAMMABLE:
> - ret = dw_pcie_ep_set_bar_programmable(ep, func_no, epf_bar);
> - break;
> - case BAR_RESIZABLE:
> - ret = dw_pcie_ep_set_bar_resizable(ep, func_no, epf_bar);
> - break;
> - default:
> - ret = -EINVAL;
> - dev_err(pci->dev, "Invalid BAR type\n");
> - break;
> + } else {
> + ret = dw_pcie_ep_set_bar_init(epc, func_no, vfunc_no, epf_bar);
> + if (ret)
> + return ret;
> }
>
> - if (ret)
> - return ret;
> -
> -config_atu:
> if (!(flags & PCI_BASE_ADDRESS_SPACE))
> type = PCIE_ATU_TYPE_MEM;
> else
> @@ -488,6 +520,151 @@ static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> return 0;
> }
>
...
> +
> +static int dw_pcie_ep_map_inbound(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> + struct pci_epf_bar *epf_bar, u64 offset)
> +{
> + struct dw_pcie_ep *ep = epc_get_drvdata(epc);
> + struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> + enum pci_barno bar = epf_bar->barno;
> + size_t size = epf_bar->size;
> + int flags = epf_bar->flags;
> + struct dw_pcie_ib_map *m;
> + u64 base, pci_addr;
> + int ret, type, win;
> +
> + /*
> + * DWC does not allow BAR pairs to overlap, e.g. you cannot combine BARs
> + * 1 and 2 to form a 64-bit BAR.
> + */
> + if ((flags & PCI_BASE_ADDRESS_MEM_TYPE_64) && (bar & 1))
> + return -EINVAL;
> +
> + /*
> + * Certain EPF drivers dynamically change the physical address of a BAR
> + * (i.e. they call set_bar() twice, without ever calling clear_bar(), as
> + * calling clear_bar() would clear the BAR's PCI address assigned by the
> + * host).
> + */
> + if (epf_bar->phys_addr && ep->epf_bar[bar]) {
> + /*
> + * We can only dynamically add a whole or partial mapping if the
> + * BAR flags do not differ from the existing configuration.
> + */
> + if (ep->epf_bar[bar]->barno != bar ||
> + ep->epf_bar[bar]->flags != flags)
> + return -EINVAL;
> +
> + /*
> + * When dynamically changing a BAR, skip writing the BAR reg, as
> + * that would clear the BAR's PCI address assigned by the host.
> + */
> + }
> +
> + /*
> + * Skip programming the inbound translation if phys_addr is 0.
> + * In this case, the caller only intends to initialize the BAR.
> + */
> + if (!epf_bar->phys_addr) {
> + ret = dw_pcie_ep_set_bar_init(epc, func_no, vfunc_no, epf_bar);
> + ep->epf_bar[bar] = epf_bar;
> + return ret;
> + }
> +
> + base = dw_pcie_ep_read_bar_assigned(ep, func_no, bar,
> + flags & PCI_BASE_ADDRESS_SPACE,
> + flags & PCI_BASE_ADDRESS_MEM_TYPE_64);
> + if (!(flags & PCI_BASE_ADDRESS_SPACE))
> + type = PCIE_ATU_TYPE_MEM;
> + else
> + type = PCIE_ATU_TYPE_IO;
> + pci_addr = base + offset;
> +
> + /* Allocate an inbound iATU window */
> + win = find_first_zero_bit(ep->ib_window_map, pci->num_ib_windows);
> + if (win >= pci->num_ib_windows)
> + return -ENOSPC;
> +
> + /* Program address-match inbound iATU */
> + ret = dw_pcie_prog_inbound_atu(pci, win, type,
> + epf_bar->phys_addr - pci->parent_bus_offset,
> + pci_addr, size);
> + if (ret)
> + return ret;
> +
> + m = kzalloc(sizeof(*m), GFP_KERNEL);
> + if (!m) {
> + dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, win);
> + return -ENOMEM;
> + }
at least this should be above dw_pcie_prog_inbound_atu(). if return error
here, atu already prog.
> + m->bar = bar;
> + m->pci_addr = pci_addr;
> + m->cpu_addr = epf_bar->phys_addr;
> + m->size = size;
> + m->index = win;
> +
> + guard(spinlock_irqsave)(&ep->ib_map_lock);
> + set_bit(win, ep->ib_window_map);
> + list_add(&m->node, &ep->ib_map_list);
> +
> + return 0;
> +}
> +
...
>
> INIT_LIST_HEAD(&ep->func_list);
> + INIT_LIST_HEAD(&ep->ib_map_list);
> + spin_lock_init(&ep->ib_map_lock);
>
> epc = devm_pci_epc_create(dev, &epc_ops);
> if (IS_ERR(epc)) {
> diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
> index 31685951a080..269a9fe0501f 100644
> --- a/drivers/pci/controller/dwc/pcie-designware.h
> +++ b/drivers/pci/controller/dwc/pcie-designware.h
> @@ -476,6 +476,8 @@ struct dw_pcie_ep {
> phys_addr_t *outbound_addr;
> unsigned long *ib_window_map;
> unsigned long *ob_window_map;
> + struct list_head ib_map_list;
> + spinlock_t ib_map_lock;
need comments for lock
Frank
> void __iomem *msi_mem;
> phys_addr_t msi_mem_phys;
> struct pci_epf_bar *epf_bar[PCI_STD_NUM_BARS];
> --
> 2.48.1
>
Powered by blists - more mailing lists