* [PATCH v4 0/2] PCI: endpoint: BAR subrange mapping support
@ 2026-01-08 4:41 Koichiro Den
2026-01-08 4:41 ` [PATCH v4 1/2] PCI: endpoint: Add " Koichiro Den
2026-01-08 4:41 ` [PATCH v4 2/2] PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU Koichiro Den
0 siblings, 2 replies; 7+ messages in thread
From: Koichiro Den @ 2026-01-08 4:41 UTC (permalink / raw)
To: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas, cassel
Cc: Frank.Li, linux-pci, linux-kernel
This series proposes support for mapping subranges within a PCIe endpoint
BAR and enables controllers to program inbound address translation for
those subranges.
The first patch introduces generic BAR subrange mapping support in the
PCI endpoint core. The second patch adds an implementation for the
DesignWare PCIe endpoint controller using Address Match Mode IB iATU.
This series is a spin-off from a larger RFC series posted earlier:
https://lore.kernel.org/all/20251217151609.3162665-4-den@valinux.co.jp/
The first user will likely be Remote eDMA-backed NTB transport,
demonstrated in that RFC series.
Kernel base:
- repo: git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git
- branch: controller/dwc
- commit: 68ac85fb42cf ("PCI: dwc: Use cfg0_base as iMSI-RX target address
to support 32-bit MSI devices")
Changelog:
* v3->v4 changes:
- Drop unused includes that should have been removed in v3
* v2->v3 changes:
- Remove submap copying and sorting from dw_pcie_ep_ib_atu_addr(), and
require callers to pass a sorted submap. The related source code
comments are updated accordingly.
- Refine source code comments and commit messages, including normalizing
"Address Match Mode" wording.
- Add const qualifiers where applicable.
* v1->v2 changes:
- Introduced stricter submap validation: no holes/overlaps and the
subranges must exactly cover the whole BAR. Added
dw_pcie_ep_validate_submap() to enforce alignment and full-coverage
constraints.
- Enforced one-shot (all-or-nothing) submap programming to avoid leaving
half-programmed BAR state:
* Dropped incremental/overwrite logic that is no longer needed with the
one-shot design.
* Added dw_pcie_ep_clear_ib_maps() and used it from multiple places to
tear down BAR match / address match inbound mappings without code
duplication.
- Updated kernel source code comments and commit messages, including a
small refinement made along the way.
- Changed num_submap type to unsigned int.
v3: https://lore.kernel.org/all/20260108024829.2255501-1-den@valinux.co.jp/
v2: https://lore.kernel.org/all/20260107041358.1986701-1-den@valinux.co.jp/
v1: https://lore.kernel.org/all/20260105080214.1254325-1-den@valinux.co.jp/
Thank you for reviewing,
Koichiro Den (2):
PCI: endpoint: Add BAR subrange mapping support
PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match
Mode iATU
.../pci/controller/dwc/pcie-designware-ep.c | 232 +++++++++++++++++-
drivers/pci/controller/dwc/pcie-designware.h | 2 +
include/linux/pci-epf.h | 31 +++
3 files changed, 254 insertions(+), 11 deletions(-)
--
2.51.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v4 1/2] PCI: endpoint: Add BAR subrange mapping support 2026-01-08 4:41 [PATCH v4 0/2] PCI: endpoint: BAR subrange mapping support Koichiro Den @ 2026-01-08 4:41 ` Koichiro Den 2026-01-08 9:37 ` Niklas Cassel 2026-01-08 4:41 ` [PATCH v4 2/2] PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU Koichiro Den 1 sibling, 1 reply; 7+ messages in thread From: Koichiro Den @ 2026-01-08 4:41 UTC (permalink / raw) To: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas, cassel Cc: Frank.Li, linux-pci, linux-kernel Extend the PCI endpoint core to support mapping subranges within a BAR. Introduce a new 'submap' field and a 'use_submap' flag in struct pci_epf_bar so an endpoint function driver can request inbound mappings that fully cover the BAR. The submap array describes the complete BAR layout (no overlaps and no gaps are allowed to avoid exposing untranslated address ranges). This provides the generic infrastructure needed to map multiple logical regions into a single BAR at different offsets, without assuming a controller-specific inbound address translation mechanism. Also, the array must be sorted in ascending order by offset. No controller-specific implementation is added in this commit. Signed-off-by: Koichiro Den <den@valinux.co.jp> --- include/linux/pci-epf.h | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h index 48f68c4dcfa5..91f2e3489cda 100644 --- a/include/linux/pci-epf.h +++ b/include/linux/pci-epf.h @@ -110,6 +110,28 @@ struct pci_epf_driver { #define to_pci_epf_driver(drv) container_of_const((drv), struct pci_epf_driver, driver) +/** + * struct pci_epf_bar_submap - BAR subrange for inbound mapping + * @phys_addr: target physical/DMA address for this subrange + * @size: the size of the subrange to be mapped + * @offset: byte offset within the BAR base + * + * When pci_epf_bar.use_submap is set, pci_epf_bar.submap describes the + * complete BAR layout. This allows an EPC driver to program multiple + * inbound translation windows for a single BAR when supported by the + * controller. + * + * Note that the subranges: + * - must be non-overlapping + * - must exactly cover the BAR (i.e. no holes) + * - must be sorted (in ascending order by offset) + */ +struct pci_epf_bar_submap { + dma_addr_t phys_addr; + size_t size; + size_t offset; +}; + /** * struct pci_epf_bar - represents the BAR of EPF device * @phys_addr: physical address that should be mapped to the BAR @@ -119,6 +141,10 @@ struct pci_epf_driver { * requirement * @barno: BAR number * @flags: flags that are set for the BAR + * @use_submap: set true to request subrange mappings within this BAR + * @num_submap: number of entries in @submap + * @submap: array of subrange descriptors allocated by the caller. See + * struct pci_epf_bar_submap for the restrictions in detail. */ struct pci_epf_bar { dma_addr_t phys_addr; @@ -127,6 +153,11 @@ struct pci_epf_bar { size_t mem_size; enum pci_barno barno; int flags; + + /* Optional sub-range mapping */ + bool use_submap; + unsigned int num_submap; + struct pci_epf_bar_submap *submap; }; /** -- 2.51.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v4 1/2] PCI: endpoint: Add BAR subrange mapping support 2026-01-08 4:41 ` [PATCH v4 1/2] PCI: endpoint: Add " Koichiro Den @ 2026-01-08 9:37 ` Niklas Cassel 2026-01-08 9:41 ` Niklas Cassel 2026-01-08 9:59 ` Niklas Cassel 0 siblings, 2 replies; 7+ messages in thread From: Niklas Cassel @ 2026-01-08 9:37 UTC (permalink / raw) To: Koichiro Den Cc: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas, Frank.Li, linux-pci, linux-kernel On Thu, Jan 08, 2026 at 01:41:47PM +0900, Koichiro Den wrote: > Extend the PCI endpoint core to support mapping subranges within a BAR. > Introduce a new 'submap' field and a 'use_submap' flag in struct > pci_epf_bar so an endpoint function driver can request inbound mappings > that fully cover the BAR. > > The submap array describes the complete BAR layout (no overlaps and no > gaps are allowed to avoid exposing untranslated address ranges). This > provides the generic infrastructure needed to map multiple logical > regions into a single BAR at different offsets, without assuming a > controller-specific inbound address translation mechanism. Also, the > array must be sorted in ascending order by offset. > > No controller-specific implementation is added in this commit. > > Signed-off-by: Koichiro Den <den@valinux.co.jp> What I don't really like is that you don't have any checks at all in: pci_epc_set_bar() if the controller actually supports the submap feature. AFAICT, for non-DWC drivers, e.g.: drivers/pci/controller/cadence/pcie-cadence-ep.c drivers/pci/controller/pcie-rcar-ep.c drivers/pci/controller/pcie-rockchip-ep.c They have no idea about submapping, so they will silently ignore pci_epf_bar.use_submap. I think that we should somehow expose that an EndPoint Controller(EPC) supports the submap feature. That way pci_epc_set_bar() can return an error for an EPC that does not support it. Perhaps something like: diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 1195d401df19..b8eb069f6d57 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -622,15 +622,26 @@ static int dw_pcie_ep_start(struct pci_epc *epc) return dw_pcie_start_link(pci); } +static struct pci_epc_features dw_pcie_ep_features; + static const struct pci_epc_features* dw_pcie_ep_get_features(struct pci_epc *epc, u8 func_no, u8 vfunc_no) { struct dw_pcie_ep *ep = epc_get_drvdata(epc); + const struct pci_epc_features *glue_epc_features; if (!ep->ops->get_features) return NULL; - return ep->ops->get_features(ep); + glue_epc_features = ep->ops->get_features(ep); + + memcpy(&dw_pcie_ep_features, glue_epc_features, + sizeof(dw_pcie_ep_features)); + + /* All DWC based glue drivers support inbound subrange mapping */ + dw_pcie_ep_features.subrange_mapping = true; + + return &dw_pcie_ep_features; } static const struct pci_epc_ops epc_ops = { diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index ca7f19cc973a..8804daaf8376 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -596,6 +596,9 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, if (!epc_features) return -EINVAL; + if (epf_bar->flags && !epc_features->subrange_mapping) + return -EINVAL; + if (epc_features->bar[bar].type == BAR_RESIZABLE && (epf_bar->size < SZ_1M || (u64)epf_bar->size > (SZ_128G * 1024))) return -EINVAL; diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index 4286bfdbfdfa..898a29e7d6f7 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -223,6 +223,8 @@ struct pci_epc_bar_desc { /** * struct pci_epc_features - features supported by a EPC device per function * @linkup_notifier: indicate if the EPC device can notify EPF driver on link up + * @subrange_mapping: indicate if the EPC device can map inbound subranges for a + * BAR * @msi_capable: indicate if the endpoint function has MSI capability * @msix_capable: indicate if the endpoint function has MSI-X capability * @intx_capable: indicate if the endpoint can raise INTx interrupts @@ -231,6 +233,7 @@ struct pci_epc_bar_desc { */ struct pci_epc_features { unsigned int linkup_notifier : 1; + unsigned int subrange_mapping : 1; unsigned int msi_capable : 1; unsigned int msix_capable : 1; unsigned int intx_capable : 1; The memcpy in dw_pcie_ep_get_features() is a bit ugly. I guess the alternative is to change all the DWC based glue drivers to return a "struct pci_epc_features*" instead of a "const struct pci_epc_features*" such that dw_pcie_ep_get_features() can simply set subrange_mapping = true in the struct pci_epc_features returned by the glue driver. Kind regards, Niklas ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v4 1/2] PCI: endpoint: Add BAR subrange mapping support 2026-01-08 9:37 ` Niklas Cassel @ 2026-01-08 9:41 ` Niklas Cassel 2026-01-08 9:59 ` Niklas Cassel 1 sibling, 0 replies; 7+ messages in thread From: Niklas Cassel @ 2026-01-08 9:41 UTC (permalink / raw) To: Koichiro Den Cc: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas, Frank.Li, linux-pci, linux-kernel On Thu, Jan 08, 2026 at 10:37:35AM +0100, Niklas Cassel wrote: > @@ -596,6 +596,9 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, > if (!epc_features) > return -EINVAL; > > + if (epf_bar->flags && !epc_features->subrange_mapping) > + return -EINVAL; This should of course have been: if (epf_bar->use_submap && !epc_features->subrange_mapping) return -EINVAL; (I simply used flags in order to compile test without applying your series.) Kind regards, Niklas ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v4 1/2] PCI: endpoint: Add BAR subrange mapping support 2026-01-08 9:37 ` Niklas Cassel 2026-01-08 9:41 ` Niklas Cassel @ 2026-01-08 9:59 ` Niklas Cassel 2026-01-08 16:39 ` Koichiro Den 1 sibling, 1 reply; 7+ messages in thread From: Niklas Cassel @ 2026-01-08 9:59 UTC (permalink / raw) To: Koichiro Den Cc: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas, Frank.Li, linux-pci, linux-kernel On Thu, Jan 08, 2026 at 10:37:35AM +0100, Niklas Cassel wrote: > The memcpy in dw_pcie_ep_get_features() is a bit ugly. > I guess the alternative is to change all the DWC based glue drivers > to return a "struct pci_epc_features*" instead of a "const struct pci_epc_features*" > such that dw_pcie_ep_get_features() can simply set subrange_mapping = true in the > struct pci_epc_features returned by the glue driver. I think the best way it probably to create another patch, that will be patch 2 out of 3 in the series, which changes: https://github.com/torvalds/linux/blob/v6.19-rc4/drivers/pci/controller/dwc/pcie-designware.h#L449 from: const struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep); to: struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep); and which does the equivalent change in all the DWC based glue drivers. That way, dw_pcie_ep_get_features() can simply set subrange_mapping = true in the struct pci_epc_features returned by the glue driver. Note that the function dw_pcie_ep_get_features() itself should still return: "static const struct pci_epc_features*" (Since this represents the DWC midlayer driver level.) It is only the DWC based glue drivers (lower level drivers) that should drop the const. Kind regards, Niklas ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v4 1/2] PCI: endpoint: Add BAR subrange mapping support 2026-01-08 9:59 ` Niklas Cassel @ 2026-01-08 16:39 ` Koichiro Den 0 siblings, 0 replies; 7+ messages in thread From: Koichiro Den @ 2026-01-08 16:39 UTC (permalink / raw) To: Niklas Cassel Cc: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas, Frank.Li, linux-pci, linux-kernel On Thu, Jan 08, 2026 at 10:59:30AM +0100, Niklas Cassel wrote: > On Thu, Jan 08, 2026 at 10:37:35AM +0100, Niklas Cassel wrote: > > The memcpy in dw_pcie_ep_get_features() is a bit ugly. > > I guess the alternative is to change all the DWC based glue drivers > > to return a "struct pci_epc_features*" instead of a "const struct pci_epc_features*" > > such that dw_pcie_ep_get_features() can simply set subrange_mapping = true in the > > struct pci_epc_features returned by the glue driver. > > I think the best way it probably to create another patch, > that will be patch 2 out of 3 in the series, which changes: > https://github.com/torvalds/linux/blob/v6.19-rc4/drivers/pci/controller/dwc/pcie-designware.h#L449 > > from: > const struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep); > > to: > struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep); > > and which does the equivalent change in all the DWC based glue drivers. > > That way, dw_pcie_ep_get_features() can simply set subrange_mapping = true > in the struct pci_epc_features returned by the glue driver. > > > > Note that the function dw_pcie_ep_get_features() itself should still return: > "static const struct pci_epc_features*" > > (Since this represents the DWC midlayer driver level.) > > It is only the DWC based glue drivers (lower level drivers) that should drop > the const. Hi Niklas, Thanks again for the detailed feedback. I agree we should not let controllers that do not support subrange mapping silently ignore epf_bar->use_submap, as that could potentially lead to an unexpected and hard-to-debug state without returning an error. Adding a subrange_mapping bit to struct pci_epc_features makes sense. I also considered setting .subrange_mapping = 1 in every DWC-based glue driver and keeping the const qualifiers, but that would duplicate the same information across drivers and add unnecessary maintenance burden. So I'll follow your suggestion and drop const only at the glue-driver level. I'll send v5 shortly. Thanks, Koichiro > > > Kind regards, > Niklas ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v4 2/2] PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU 2026-01-08 4:41 [PATCH v4 0/2] PCI: endpoint: BAR subrange mapping support Koichiro Den 2026-01-08 4:41 ` [PATCH v4 1/2] PCI: endpoint: Add " Koichiro Den @ 2026-01-08 4:41 ` Koichiro Den 1 sibling, 0 replies; 7+ messages in thread From: Koichiro Den @ 2026-01-08 4:41 UTC (permalink / raw) To: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas, cassel Cc: Frank.Li, linux-pci, linux-kernel Extend dw_pcie_ep_set_bar() to support inbound mappings for BAR subranges using Address Match Mode IB iATU. Rename the existing BAR-match helper into dw_pcie_ep_ib_atu_bar() and introduce dw_pcie_ep_ib_atu_addr() for Address Match Mode. When use_submap is set, read the assigned BAR base address and program one inbound iATU window per subrange. Validate the submap array before programming: - each subrange is aligned to pci->region_align - subranges cover the whole BAR (no gaps and no overlaps) - subranges are sorted in ascending order by offset Track Address Match Mode mappings and tear them down on clear_bar() and on set_bar() error paths to avoid leaving half-programmed state or untranslated BAR holes. Signed-off-by: Koichiro Den <den@valinux.co.jp> --- .../pci/controller/dwc/pcie-designware-ep.c | 232 +++++++++++++++++- drivers/pci/controller/dwc/pcie-designware.h | 2 + 2 files changed, 223 insertions(+), 11 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 1195d401df19..d9f9ad7855b4 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -139,9 +139,10 @@ static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no, return 0; } -static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type, - dma_addr_t parent_bus_addr, enum pci_barno bar, - size_t size) +/* Bar Match Mode inbound iATU mapping */ +static int dw_pcie_ep_ib_atu_bar(struct dw_pcie_ep *ep, u8 func_no, int type, + dma_addr_t parent_bus_addr, enum pci_barno bar, + size_t size) { int ret; u32 free_win; @@ -174,6 +175,208 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type, return 0; } +/* Inbound mapping bookkeeping for Address Match Mode */ +struct dw_pcie_ib_map { + struct list_head list; + enum pci_barno bar; + u64 pci_addr; + u64 parent_bus_addr; + u64 size; + u32 index; +}; + +static void dw_pcie_ep_clear_ib_maps(struct dw_pcie_ep *ep, enum pci_barno bar) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + struct dw_pcie_ib_map *m, *tmp; + struct device *dev = pci->dev; + u32 atu_index; + + /* Tear down the BAR Match Mode mapping, if any. */ + if (ep->bar_to_atu[bar]) { + atu_index = ep->bar_to_atu[bar] - 1; + dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, atu_index); + clear_bit(atu_index, ep->ib_window_map); + ep->bar_to_atu[bar] = 0; + } + + /* Tear down all Address Match Mode mappings, if any. */ + guard(spinlock_irqsave)(&ep->ib_map_lock); + list_for_each_entry_safe(m, tmp, &ep->ib_map_list, list) { + if (m->bar != bar) + continue; + dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, m->index); + clear_bit(m->index, ep->ib_window_map); + list_del(&m->list); + devm_kfree(dev, m); + } +} + +static u64 dw_pcie_ep_read_bar_assigned(struct dw_pcie_ep *ep, u8 func_no, + enum pci_barno bar, int flags) +{ + u32 reg = PCI_BASE_ADDRESS_0 + (4 * bar); + u32 lo, hi; + u64 addr; + + lo = dw_pcie_ep_readl_dbi(ep, func_no, reg); + + if (flags & PCI_BASE_ADDRESS_SPACE) + return lo & PCI_BASE_ADDRESS_IO_MASK; + + addr = lo & PCI_BASE_ADDRESS_MEM_MASK; + if (!(flags & PCI_BASE_ADDRESS_MEM_TYPE_64)) + return addr; + + hi = dw_pcie_ep_readl_dbi(ep, func_no, reg + 4); + return addr | ((u64)hi << 32); +} + +static int dw_pcie_ep_validate_submap(struct dw_pcie_ep *ep, + const struct pci_epf_bar_submap *submap, + unsigned int num_submap, size_t bar_size) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + u32 align = pci->region_align; + size_t expected = 0; + size_t size, off; + unsigned int i; + + if (!align || !IS_ALIGNED(bar_size, align)) + return -EINVAL; + + /* + * The array is expected to be sorted by offset before calling this + * helper. With sorted entries, we can enforce a strict, gapless + * decomposition of the BAR: + * - each entry has a non-zero size + * - offset/size/phys_addr are aligned to pci->region_align + * - each entry lies within the BAR range + * - entries are contiguous (no overlaps, no holes) + * - the entries exactly cover the whole BAR + * + * Note: dw_pcie_prog_inbound_atu() also checks alignment for + * offset/phys_addr, but validating up-front avoids partially + * programming iATU windows in vain. + */ + for (i = 0; i < num_submap; i++) { + off = submap[i].offset; + size = submap[i].size; + + if (!size) + return -EINVAL; + + if (!IS_ALIGNED(size, align) || !IS_ALIGNED(off, align)) + return -EINVAL; + + if (!IS_ALIGNED(submap[i].phys_addr, align)) + return -EINVAL; + + if (off > bar_size || size > bar_size - off) + return -EINVAL; + + /* Enforce contiguity (no overlaps, no holes). */ + if (off != expected) + return -EINVAL; + + expected += size; + } + if (expected != bar_size) + return -EINVAL; + + return 0; +} + +/* Address Match Mode inbound iATU mapping */ +static int dw_pcie_ep_ib_atu_addr(struct dw_pcie_ep *ep, u8 func_no, int type, + const struct pci_epf_bar *epf_bar) +{ + const struct pci_epf_bar_submap *submap = epf_bar->submap; + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + enum pci_barno bar = epf_bar->barno; + struct device *dev = pci->dev; + u64 pci_addr, parent_bus_addr; + struct dw_pcie_ib_map *new; + u64 size, off, base; + unsigned long flags; + int free_win, ret; + unsigned int i; + + if (!epf_bar->num_submap || !submap || !epf_bar->size) + return -EINVAL; + + ret = dw_pcie_ep_validate_submap(ep, submap, epf_bar->num_submap, + epf_bar->size); + if (ret) + return ret; + + base = dw_pcie_ep_read_bar_assigned(ep, func_no, bar, epf_bar->flags); + if (!base) { + dev_err(dev, + "BAR%u not assigned, cannot set up sub-range mappings\n", + bar); + return -EINVAL; + } + + /* Tear down any existing mappings before (re)programming. */ + dw_pcie_ep_clear_ib_maps(ep, bar); + + for (i = 0; i < epf_bar->num_submap; i++) { + off = submap[i].offset; + size = submap[i].size; + parent_bus_addr = submap[i].phys_addr; + + if (off > (~0ULL) - base) { + ret = -EINVAL; + goto err; + } + + pci_addr = base + off; + + new = devm_kzalloc(dev, sizeof(*new), GFP_KERNEL); + if (!new) { + ret = -ENOMEM; + goto err; + } + + spin_lock_irqsave(&ep->ib_map_lock, flags); + + free_win = find_first_zero_bit(ep->ib_window_map, + pci->num_ib_windows); + if (free_win >= pci->num_ib_windows) { + spin_unlock_irqrestore(&ep->ib_map_lock, flags); + devm_kfree(dev, new); + ret = -ENOSPC; + goto err; + } + set_bit(free_win, ep->ib_window_map); + + new->bar = bar; + new->index = free_win; + new->pci_addr = pci_addr; + new->parent_bus_addr = parent_bus_addr; + new->size = size; + list_add_tail(&new->list, &ep->ib_map_list); + + spin_unlock_irqrestore(&ep->ib_map_lock, flags); + + ret = dw_pcie_prog_inbound_atu(pci, free_win, type, + parent_bus_addr, pci_addr, size); + if (ret) { + spin_lock_irqsave(&ep->ib_map_lock, flags); + list_del(&new->list); + clear_bit(free_win, ep->ib_window_map); + spin_unlock_irqrestore(&ep->ib_map_lock, flags); + devm_kfree(dev, new); + goto err; + } + } + return 0; +err: + dw_pcie_ep_clear_ib_maps(ep, bar); + return ret; +} + static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, struct dw_pcie_ob_atu_cfg *atu) { @@ -204,17 +407,15 @@ static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, 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; - u32 atu_index = ep->bar_to_atu[bar] - 1; - if (!ep->bar_to_atu[bar]) + if (!ep->epf_bar[bar]) return; __dw_pcie_ep_reset_bar(pci, func_no, bar, epf_bar->flags); - dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, atu_index); - clear_bit(atu_index, ep->ib_window_map); + dw_pcie_ep_clear_ib_maps(ep, bar); + ep->epf_bar[bar] = NULL; - ep->bar_to_atu[bar] = 0; } static unsigned int dw_pcie_ep_get_rebar_offset(struct dw_pcie *pci, @@ -408,10 +609,17 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, else type = PCIE_ATU_TYPE_IO; - ret = dw_pcie_ep_inbound_atu(ep, func_no, type, epf_bar->phys_addr, bar, - size); - if (ret) + if (epf_bar->use_submap) + ret = dw_pcie_ep_ib_atu_addr(ep, func_no, type, epf_bar); + else + ret = dw_pcie_ep_ib_atu_bar(ep, func_no, type, + epf_bar->phys_addr, bar, size); + + if (ret) { + if (epf_bar->use_submap) + dw_pcie_ep_clear_bar(epc, func_no, vfunc_no, epf_bar); return ret; + } ep->epf_bar[bar] = epf_bar; @@ -1120,6 +1328,8 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) struct device *dev = pci->dev; INIT_LIST_HEAD(&ep->func_list); + INIT_LIST_HEAD(&ep->ib_map_list); + spin_lock_init(&ep->ib_map_lock); ep->msi_iatu_mapped = false; ep->msi_msg_addr = 0; ep->msi_map_size = 0; diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index f87c67a7a482..1ebe8a9ee139 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -479,6 +479,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; void __iomem *msi_mem; phys_addr_t msi_mem_phys; struct pci_epf_bar *epf_bar[PCI_STD_NUM_BARS]; -- 2.51.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-01-08 16:39 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-01-08 4:41 [PATCH v4 0/2] PCI: endpoint: BAR subrange mapping support Koichiro Den 2026-01-08 4:41 ` [PATCH v4 1/2] PCI: endpoint: Add " Koichiro Den 2026-01-08 9:37 ` Niklas Cassel 2026-01-08 9:41 ` Niklas Cassel 2026-01-08 9:59 ` Niklas Cassel 2026-01-08 16:39 ` Koichiro Den 2026-01-08 4:41 ` [PATCH v4 2/2] PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU Koichiro Den
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox