* [PATCH v9 1/5] PCI: endpoint: Add dynamic_inbound_mapping EPC feature
2026-01-22 8:49 [PATCH v9 0/5] PCI: endpoint: BAR subrange mapping support Koichiro Den
@ 2026-01-22 8:49 ` Koichiro Den
2026-01-22 8:49 ` [PATCH v9 2/5] PCI: endpoint: Add BAR subrange mapping support Koichiro Den
` (3 subsequent siblings)
4 siblings, 0 replies; 16+ messages in thread
From: Koichiro Den @ 2026-01-22 8:49 UTC (permalink / raw)
To: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas, cassel
Cc: vigneshr, s-vadapalli, hongxing.zhu, l.stach, shawnguo, s.hauer,
kernel, festevam, minghuan.Lian, mingkai.hu, roy.zang,
jesper.nilsson, heiko, srikanth.thokala, marek.vasut+renesas,
yoshihiro.shimoda.uh, geert+renesas, magnus.damm, christian.bruel,
mcoquelin.stm32, alexandre.torgue, thierry.reding, jonathanh,
hayashi.kunihiko, mhiramat, kishon, jirislaby, rongqianfeng,
18255117159, shawn.lin, nicolas.frattaroli, linux.amoon, vidyas,
Frank.Li, linux-omap, linux-pci, linux-arm-kernel, linux-kernel,
imx, linuxppc-dev, linux-arm-kernel, linux-rockchip,
linux-arm-msm, linux-renesas-soc, linux-stm32, linux-tegra
Introduce a new EPC feature bit (dynamic_inbound_mapping) that indicates
whether an Endpoint Controller can update the inbound address
translation for a BAR without requiring the EPF driver to clear/reset
the BAR first.
Endpoint Function drivers (e.g. vNTB) can use this information to decide
whether it really is safe to call pci_epc_set_bar() multiple times to
update inbound mappings for the BAR.
Suggested-by: Niklas Cassel <cassel@kernel.org>
Reviewed-by: Niklas Cassel <cassel@kernel.org>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
include/linux/pci-epc.h | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h
index 4286bfdbfdfa..4c8516756c56 100644
--- a/include/linux/pci-epc.h
+++ b/include/linux/pci-epc.h
@@ -223,6 +223,10 @@ 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
+ * @dynamic_inbound_mapping: indicate if the EPC device supports updating
+ * inbound mappings for an already configured BAR
+ * (i.e. allow calling pci_epc_set_bar() again
+ * without first calling pci_epc_clear_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 +235,7 @@ struct pci_epc_bar_desc {
*/
struct pci_epc_features {
unsigned int linkup_notifier : 1;
+ unsigned int dynamic_inbound_mapping : 1;
unsigned int msi_capable : 1;
unsigned int msix_capable : 1;
unsigned int intx_capable : 1;
--
2.51.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v9 2/5] PCI: endpoint: Add BAR subrange mapping support
2026-01-22 8:49 [PATCH v9 0/5] PCI: endpoint: BAR subrange mapping support Koichiro Den
2026-01-22 8:49 ` [PATCH v9 1/5] PCI: endpoint: Add dynamic_inbound_mapping EPC feature Koichiro Den
@ 2026-01-22 8:49 ` Koichiro Den
2026-01-22 9:03 ` Niklas Cassel
2026-01-22 8:49 ` [PATCH v9 3/5] PCI: dwc: Advertise dynamic inbound " Koichiro Den
` (2 subsequent siblings)
4 siblings, 1 reply; 16+ messages in thread
From: Koichiro Den @ 2026-01-22 8:49 UTC (permalink / raw)
To: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas, cassel
Cc: vigneshr, s-vadapalli, hongxing.zhu, l.stach, shawnguo, s.hauer,
kernel, festevam, minghuan.Lian, mingkai.hu, roy.zang,
jesper.nilsson, heiko, srikanth.thokala, marek.vasut+renesas,
yoshihiro.shimoda.uh, geert+renesas, magnus.damm, christian.bruel,
mcoquelin.stm32, alexandre.torgue, thierry.reding, jonathanh,
hayashi.kunihiko, mhiramat, kishon, jirislaby, rongqianfeng,
18255117159, shawn.lin, nicolas.frattaroli, linux.amoon, vidyas,
Frank.Li, linux-omap, linux-pci, linux-arm-kernel, linux-kernel,
imx, linuxppc-dev, linux-arm-kernel, linux-rockchip,
linux-arm-msm, linux-renesas-soc, linux-stm32, linux-tegra
Some endpoint platforms have only a small number of usable BARs. At the
same time, EPF drivers (e.g. vNTB) may need multiple independent inbound
regions (control/scratchpad, one or more memory windows, and optionally
MSI or other feature-related regions). Subrange mapping allows these to
share a single BAR without consuming additional BARs that may not be
available, or forcing a fragile layout by aggressively packing into a
single contiguous memory range.
Extend the PCI endpoint core to support mapping subranges within a BAR.
Add an optional 'submap' field in struct pci_epf_bar so an endpoint
function driver can request inbound mappings that fully cover the BAR.
Introduce a new EPC feature bit, subrange_mapping, and reject submap
requests from pci_epc_set_bar() unless the controller advertises both
subrange_mapping and dynamic_inbound_mapping features.
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.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
drivers/pci/endpoint/pci-epc-core.c | 8 ++++++++
include/linux/pci-epc.h | 4 ++++
include/linux/pci-epf.h | 23 +++++++++++++++++++++++
3 files changed, 35 insertions(+)
diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
index ca7f19cc973a..068155819c57 100644
--- a/drivers/pci/endpoint/pci-epc-core.c
+++ b/drivers/pci/endpoint/pci-epc-core.c
@@ -596,6 +596,14 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
if (!epc_features)
return -EINVAL;
+ if (epf_bar->num_submap && !epf_bar->submap)
+ return -EINVAL;
+
+ if (epf_bar->num_submap &&
+ !(epc_features->dynamic_inbound_mapping &&
+ 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 4c8516756c56..c021c7af175f 100644
--- a/include/linux/pci-epc.h
+++ b/include/linux/pci-epc.h
@@ -227,6 +227,9 @@ struct pci_epc_bar_desc {
* inbound mappings for an already configured BAR
* (i.e. allow calling pci_epc_set_bar() again
* without first calling pci_epc_clear_bar())
+ * @subrange_mapping: indicate if the EPC device can map inbound subranges for a
+ * BAR. This feature depends on @dynamic_inbound_mapping
+ * feature.
* @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
@@ -236,6 +239,7 @@ struct pci_epc_bar_desc {
struct pci_epc_features {
unsigned int linkup_notifier : 1;
unsigned int dynamic_inbound_mapping : 1;
+ unsigned int subrange_mapping : 1;
unsigned int msi_capable : 1;
unsigned int msix_capable : 1;
unsigned int intx_capable : 1;
diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h
index 48f68c4dcfa5..7737a7c03260 100644
--- a/include/linux/pci-epf.h
+++ b/include/linux/pci-epf.h
@@ -110,6 +110,22 @@ 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
+ *
+ * When pci_epf_bar.num_submap is >0, 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. The array order defines the BAR layout (submap[0] at offset
+ * 0, and each immediately follows the previous one).
+ */
+struct pci_epf_bar_submap {
+ dma_addr_t phys_addr;
+ size_t size;
+};
+
/**
* struct pci_epf_bar - represents the BAR of EPF device
* @phys_addr: physical address that should be mapped to the BAR
@@ -119,6 +135,9 @@ struct pci_epf_driver {
* requirement
* @barno: BAR number
* @flags: flags that are set for the 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 semantics in detail.
*/
struct pci_epf_bar {
dma_addr_t phys_addr;
@@ -127,6 +146,10 @@ struct pci_epf_bar {
size_t mem_size;
enum pci_barno barno;
int flags;
+
+ /* Optional sub-range mapping */
+ unsigned int num_submap;
+ struct pci_epf_bar_submap *submap;
};
/**
--
2.51.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH v9 2/5] PCI: endpoint: Add BAR subrange mapping support
2026-01-22 8:49 ` [PATCH v9 2/5] PCI: endpoint: Add BAR subrange mapping support Koichiro Den
@ 2026-01-22 9:03 ` Niklas Cassel
0 siblings, 0 replies; 16+ messages in thread
From: Niklas Cassel @ 2026-01-22 9:03 UTC (permalink / raw)
To: Koichiro Den
Cc: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas,
vigneshr, s-vadapalli, hongxing.zhu, l.stach, shawnguo, s.hauer,
kernel, festevam, minghuan.Lian, mingkai.hu, roy.zang,
jesper.nilsson, heiko, srikanth.thokala, marek.vasut+renesas,
yoshihiro.shimoda.uh, geert+renesas, magnus.damm, christian.bruel,
mcoquelin.stm32, alexandre.torgue, thierry.reding, jonathanh,
hayashi.kunihiko, mhiramat, kishon, jirislaby, rongqianfeng,
18255117159, shawn.lin, nicolas.frattaroli, linux.amoon, vidyas,
Frank.Li, linux-omap, linux-pci, linux-arm-kernel, linux-kernel,
imx, linuxppc-dev, linux-arm-kernel, linux-rockchip,
linux-arm-msm, linux-renesas-soc, linux-stm32, linux-tegra
On Thu, Jan 22, 2026 at 05:49:06PM +0900, Koichiro Den wrote:
> Some endpoint platforms have only a small number of usable BARs. At the
> same time, EPF drivers (e.g. vNTB) may need multiple independent inbound
> regions (control/scratchpad, one or more memory windows, and optionally
> MSI or other feature-related regions). Subrange mapping allows these to
> share a single BAR without consuming additional BARs that may not be
> available, or forcing a fragile layout by aggressively packing into a
> single contiguous memory range.
>
> Extend the PCI endpoint core to support mapping subranges within a BAR.
> Add an optional 'submap' field in struct pci_epf_bar so an endpoint
> function driver can request inbound mappings that fully cover the BAR.
>
> Introduce a new EPC feature bit, subrange_mapping, and reject submap
> requests from pci_epc_set_bar() unless the controller advertises both
> subrange_mapping and dynamic_inbound_mapping features.
>
> 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.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
Reviewed-by: Niklas Cassel <cassel@kernel.org>
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v9 3/5] PCI: dwc: Advertise dynamic inbound mapping support
2026-01-22 8:49 [PATCH v9 0/5] PCI: endpoint: BAR subrange mapping support Koichiro Den
2026-01-22 8:49 ` [PATCH v9 1/5] PCI: endpoint: Add dynamic_inbound_mapping EPC feature Koichiro Den
2026-01-22 8:49 ` [PATCH v9 2/5] PCI: endpoint: Add BAR subrange mapping support Koichiro Den
@ 2026-01-22 8:49 ` Koichiro Den
2026-01-22 9:00 ` Niklas Cassel
2026-01-22 8:49 ` [PATCH v9 4/5] PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU Koichiro Den
2026-01-22 8:49 ` [PATCH v9 5/5] Documentation: PCI: endpoint: Clarify pci_epc_set_bar() usage Koichiro Den
4 siblings, 1 reply; 16+ messages in thread
From: Koichiro Den @ 2026-01-22 8:49 UTC (permalink / raw)
To: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas, cassel
Cc: vigneshr, s-vadapalli, hongxing.zhu, l.stach, shawnguo, s.hauer,
kernel, festevam, minghuan.Lian, mingkai.hu, roy.zang,
jesper.nilsson, heiko, srikanth.thokala, marek.vasut+renesas,
yoshihiro.shimoda.uh, geert+renesas, magnus.damm, christian.bruel,
mcoquelin.stm32, alexandre.torgue, thierry.reding, jonathanh,
hayashi.kunihiko, mhiramat, kishon, jirislaby, rongqianfeng,
18255117159, shawn.lin, nicolas.frattaroli, linux.amoon, vidyas,
Frank.Li, linux-omap, linux-pci, linux-arm-kernel, linux-kernel,
imx, linuxppc-dev, linux-arm-kernel, linux-rockchip,
linux-arm-msm, linux-renesas-soc, linux-stm32, linux-tegra
The DesignWare EP core has supported updating the inbound iATU mapping
for an already configured BAR (i.e. allowing pci_epc_set_bar() to be
called again without a prior pci_epc_clear_bar()) since commit
4284c88fff0e ("PCI: designware-ep: Allow pci_epc_set_bar() update
inbound map address").
Now that this capability is exposed via the dynamic_inbound_mapping EPC
feature bit, set it for DWC-based EP glue drivers using a common
initializer macro to avoid duplicating the same flag in each driver.
Note that pci-layerscape-ep.c is untouched. It currently constructs the
feature struct dynamically in ls_pcie_ep_init(). Once converted to a
static feature definition, it will use DWC_EPC_COMMON_FEATURES as well.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
drivers/pci/controller/dwc/pci-dra7xx.c | 1 +
drivers/pci/controller/dwc/pci-imx6.c | 3 +++
drivers/pci/controller/dwc/pci-keystone.c | 1 +
drivers/pci/controller/dwc/pcie-artpec6.c | 1 +
drivers/pci/controller/dwc/pcie-designware-plat.c | 1 +
drivers/pci/controller/dwc/pcie-designware.h | 3 +++
drivers/pci/controller/dwc/pcie-dw-rockchip.c | 2 ++
drivers/pci/controller/dwc/pcie-keembay.c | 1 +
drivers/pci/controller/dwc/pcie-qcom-ep.c | 1 +
drivers/pci/controller/dwc/pcie-rcar-gen4.c | 1 +
drivers/pci/controller/dwc/pcie-stm32-ep.c | 1 +
drivers/pci/controller/dwc/pcie-tegra194.c | 1 +
drivers/pci/controller/dwc/pcie-uniphier-ep.c | 2 ++
13 files changed, 19 insertions(+)
diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c
index 01cfd9aeb0b8..d5d26229063f 100644
--- a/drivers/pci/controller/dwc/pci-dra7xx.c
+++ b/drivers/pci/controller/dwc/pci-dra7xx.c
@@ -424,6 +424,7 @@ static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
}
static const struct pci_epc_features dra7xx_pcie_epc_features = {
+ DWC_EPC_COMMON_FEATURES,
.linkup_notifier = true,
.msi_capable = true,
};
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index dfe814469993..06f45e009d7d 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -1388,6 +1388,7 @@ static int imx_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
}
static const struct pci_epc_features imx8m_pcie_epc_features = {
+ DWC_EPC_COMMON_FEATURES,
.msi_capable = true,
.bar[BAR_1] = { .type = BAR_RESERVED, },
.bar[BAR_3] = { .type = BAR_RESERVED, },
@@ -1397,6 +1398,7 @@ static const struct pci_epc_features imx8m_pcie_epc_features = {
};
static const struct pci_epc_features imx8q_pcie_epc_features = {
+ DWC_EPC_COMMON_FEATURES,
.msi_capable = true,
.bar[BAR_1] = { .type = BAR_RESERVED, },
.bar[BAR_3] = { .type = BAR_RESERVED, },
@@ -1417,6 +1419,7 @@ static const struct pci_epc_features imx8q_pcie_epc_features = {
* BAR5 | Enable | 32-bit | 64 KB | Programmable Size
*/
static const struct pci_epc_features imx95_pcie_epc_features = {
+ DWC_EPC_COMMON_FEATURES,
.msi_capable = true,
.bar[BAR_1] = { .type = BAR_FIXED, .fixed_size = SZ_64K, },
.align = SZ_4K,
diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c
index f86d9111f863..20fa4dadb82a 100644
--- a/drivers/pci/controller/dwc/pci-keystone.c
+++ b/drivers/pci/controller/dwc/pci-keystone.c
@@ -930,6 +930,7 @@ static int ks_pcie_am654_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
}
static const struct pci_epc_features ks_pcie_am654_epc_features = {
+ DWC_EPC_COMMON_FEATURES,
.msi_capable = true,
.msix_capable = true,
.bar[BAR_0] = { .type = BAR_RESERVED, },
diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c
index f4a136ee2daf..e994b75986c3 100644
--- a/drivers/pci/controller/dwc/pcie-artpec6.c
+++ b/drivers/pci/controller/dwc/pcie-artpec6.c
@@ -370,6 +370,7 @@ static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
}
static const struct pci_epc_features artpec6_pcie_epc_features = {
+ DWC_EPC_COMMON_FEATURES,
.msi_capable = true,
};
diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c
index 12f41886c65d..8530746ec5cb 100644
--- a/drivers/pci/controller/dwc/pcie-designware-plat.c
+++ b/drivers/pci/controller/dwc/pcie-designware-plat.c
@@ -61,6 +61,7 @@ static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
}
static const struct pci_epc_features dw_plat_pcie_epc_features = {
+ DWC_EPC_COMMON_FEATURES,
.msi_capable = true,
.msix_capable = true,
};
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index 53b65428fadb..be47f34b49ca 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -305,6 +305,9 @@
/* Default eDMA LLP memory size */
#define DMA_LLP_MEM_SIZE PAGE_SIZE
+/* Common struct pci_epc_feature bits among DWC EP glue drivers */
+#define DWC_EPC_COMMON_FEATURES .dynamic_inbound_mapping = true
+
struct dw_pcie;
struct dw_pcie_rp;
struct dw_pcie_ep;
diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
index 77c4e6a4ddea..03ad8c242366 100644
--- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c
+++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
@@ -382,6 +382,7 @@ static int rockchip_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
}
static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = {
+ DWC_EPC_COMMON_FEATURES,
.linkup_notifier = true,
.msi_capable = true,
.msix_capable = true,
@@ -402,6 +403,7 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = {
* BARs) would be overwritten, resulting in (all other BARs) no longer working.
*/
static const struct pci_epc_features rockchip_pcie_epc_features_rk3588 = {
+ DWC_EPC_COMMON_FEATURES,
.linkup_notifier = true,
.msi_capable = true,
.msix_capable = true,
diff --git a/drivers/pci/controller/dwc/pcie-keembay.c b/drivers/pci/controller/dwc/pcie-keembay.c
index 60e74ac782af..2666a9c3d67e 100644
--- a/drivers/pci/controller/dwc/pcie-keembay.c
+++ b/drivers/pci/controller/dwc/pcie-keembay.c
@@ -309,6 +309,7 @@ static int keembay_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
}
static const struct pci_epc_features keembay_pcie_epc_features = {
+ DWC_EPC_COMMON_FEATURES,
.msi_capable = true,
.msix_capable = true,
.bar[BAR_0] = { .only_64bit = true, },
diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c
index f1bc0ac81a92..5e990c7a5879 100644
--- a/drivers/pci/controller/dwc/pcie-qcom-ep.c
+++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c
@@ -820,6 +820,7 @@ static void qcom_pcie_ep_init_debugfs(struct qcom_pcie_ep *pcie_ep)
}
static const struct pci_epc_features qcom_pcie_epc_features = {
+ DWC_EPC_COMMON_FEATURES,
.linkup_notifier = true,
.msi_capable = true,
.align = SZ_4K,
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index 80778917d2dd..a6912e85e4dd 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -420,6 +420,7 @@ static int rcar_gen4_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
}
static const struct pci_epc_features rcar_gen4_pcie_epc_features = {
+ DWC_EPC_COMMON_FEATURES,
.msi_capable = true,
.bar[BAR_1] = { .type = BAR_RESERVED, },
.bar[BAR_3] = { .type = BAR_RESERVED, },
diff --git a/drivers/pci/controller/dwc/pcie-stm32-ep.c b/drivers/pci/controller/dwc/pcie-stm32-ep.c
index 2cecf32d2b0f..c1944b40ce02 100644
--- a/drivers/pci/controller/dwc/pcie-stm32-ep.c
+++ b/drivers/pci/controller/dwc/pcie-stm32-ep.c
@@ -70,6 +70,7 @@ static int stm32_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
}
static const struct pci_epc_features stm32_pcie_epc_features = {
+ DWC_EPC_COMMON_FEATURES,
.msi_capable = true,
.align = SZ_64K,
};
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index 0ddeef70726d..06571d806ab3 100644
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
@@ -1988,6 +1988,7 @@ static int tegra_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
}
static const struct pci_epc_features tegra_pcie_epc_features = {
+ DWC_EPC_COMMON_FEATURES,
.linkup_notifier = true,
.msi_capable = true,
.bar[BAR_0] = { .type = BAR_FIXED, .fixed_size = SZ_1M,
diff --git a/drivers/pci/controller/dwc/pcie-uniphier-ep.c b/drivers/pci/controller/dwc/pcie-uniphier-ep.c
index d6e73811216e..d52753060970 100644
--- a/drivers/pci/controller/dwc/pcie-uniphier-ep.c
+++ b/drivers/pci/controller/dwc/pcie-uniphier-ep.c
@@ -420,6 +420,7 @@ static const struct uniphier_pcie_ep_soc_data uniphier_pro5_data = {
.init = uniphier_pcie_pro5_init_ep,
.wait = NULL,
.features = {
+ DWC_EPC_COMMON_FEATURES,
.linkup_notifier = false,
.msi_capable = true,
.msix_capable = false,
@@ -438,6 +439,7 @@ static const struct uniphier_pcie_ep_soc_data uniphier_nx1_data = {
.init = uniphier_pcie_nx1_init_ep,
.wait = uniphier_pcie_nx1_wait_ep,
.features = {
+ DWC_EPC_COMMON_FEATURES,
.linkup_notifier = false,
.msi_capable = true,
.msix_capable = false,
--
2.51.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH v9 3/5] PCI: dwc: Advertise dynamic inbound mapping support
2026-01-22 8:49 ` [PATCH v9 3/5] PCI: dwc: Advertise dynamic inbound " Koichiro Den
@ 2026-01-22 9:00 ` Niklas Cassel
0 siblings, 0 replies; 16+ messages in thread
From: Niklas Cassel @ 2026-01-22 9:00 UTC (permalink / raw)
To: Koichiro Den
Cc: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas,
vigneshr, s-vadapalli, hongxing.zhu, l.stach, shawnguo, s.hauer,
kernel, festevam, minghuan.Lian, mingkai.hu, roy.zang,
jesper.nilsson, heiko, srikanth.thokala, marek.vasut+renesas,
yoshihiro.shimoda.uh, geert+renesas, magnus.damm, christian.bruel,
mcoquelin.stm32, alexandre.torgue, thierry.reding, jonathanh,
hayashi.kunihiko, mhiramat, kishon, jirislaby, rongqianfeng,
18255117159, shawn.lin, nicolas.frattaroli, linux.amoon, vidyas,
Frank.Li, linux-omap, linux-pci, linux-arm-kernel, linux-kernel,
imx, linuxppc-dev, linux-arm-kernel, linux-rockchip,
linux-arm-msm, linux-renesas-soc, linux-stm32, linux-tegra
On Thu, Jan 22, 2026 at 05:49:07PM +0900, Koichiro Den wrote:
> The DesignWare EP core has supported updating the inbound iATU mapping
> for an already configured BAR (i.e. allowing pci_epc_set_bar() to be
> called again without a prior pci_epc_clear_bar()) since commit
> 4284c88fff0e ("PCI: designware-ep: Allow pci_epc_set_bar() update
> inbound map address").
>
> Now that this capability is exposed via the dynamic_inbound_mapping EPC
> feature bit, set it for DWC-based EP glue drivers using a common
> initializer macro to avoid duplicating the same flag in each driver.
>
> Note that pci-layerscape-ep.c is untouched. It currently constructs the
> feature struct dynamically in ls_pcie_ep_init(). Once converted to a
> static feature definition, it will use DWC_EPC_COMMON_FEATURES as well.
>
> Reviewed-by: Frank Li <Frank.Li@nxp.com>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
Reviewed-by: Niklas Cassel <cassel@kernel.org>
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v9 4/5] PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU
2026-01-22 8:49 [PATCH v9 0/5] PCI: endpoint: BAR subrange mapping support Koichiro Den
` (2 preceding siblings ...)
2026-01-22 8:49 ` [PATCH v9 3/5] PCI: dwc: Advertise dynamic inbound " Koichiro Den
@ 2026-01-22 8:49 ` Koichiro Den
2026-01-22 9:23 ` Niklas Cassel
2026-01-22 8:49 ` [PATCH v9 5/5] Documentation: PCI: endpoint: Clarify pci_epc_set_bar() usage Koichiro Den
4 siblings, 1 reply; 16+ messages in thread
From: Koichiro Den @ 2026-01-22 8:49 UTC (permalink / raw)
To: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas, cassel
Cc: vigneshr, s-vadapalli, hongxing.zhu, l.stach, shawnguo, s.hauer,
kernel, festevam, minghuan.Lian, mingkai.hu, roy.zang,
jesper.nilsson, heiko, srikanth.thokala, marek.vasut+renesas,
yoshihiro.shimoda.uh, geert+renesas, magnus.damm, christian.bruel,
mcoquelin.stm32, alexandre.torgue, thierry.reding, jonathanh,
hayashi.kunihiko, mhiramat, kishon, jirislaby, rongqianfeng,
18255117159, shawn.lin, nicolas.frattaroli, linux.amoon, vidyas,
Frank.Li, linux-omap, linux-pci, linux-arm-kernel, linux-kernel,
imx, linuxppc-dev, linux-arm-kernel, linux-rockchip,
linux-arm-msm, linux-renesas-soc, linux-stm32, linux-tegra
Extend dw_pcie_ep_set_bar() to support inbound mappings for BAR
subranges using Address Match Mode IB iATU when pci_epf_bar.num_submap
is non-zero.
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
num_submap is non-zero, 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.
Advertise this capability by extending the common feature bit
initializer macro (DWC_EPC_COMMON_FEATURES).
This enables multiple inbound windows within a single BAR, which is
useful on platforms where usable BARs are scarce but EPFs need multiple
inbound regions.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
.../pci/controller/dwc/pcie-designware-ep.c | 203 +++++++++++++++++-
drivers/pci/controller/dwc/pcie-designware.h | 7 +-
2 files changed, 199 insertions(+), 11 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index cfd59899c7b8..0567552b784c 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -100,9 +100,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;
@@ -135,6 +136,179 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type,
return 0;
}
+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 device *dev = pci->dev;
+ unsigned int i, num;
+ u32 atu_index;
+ u32 *indexes;
+
+ /* 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. */
+ indexes = ep->ib_atu_indexes[bar];
+ num = ep->num_ib_atu_indexes[bar];
+ ep->ib_atu_indexes[bar] = NULL;
+ ep->num_ib_atu_indexes[bar] = 0;
+ if (!indexes)
+ return;
+ for (i = 0; i < num; i++) {
+ dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, indexes[i]);
+ clear_bit(indexes[i], ep->ib_window_map);
+ }
+ devm_kfree(dev, indexes);
+}
+
+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 off = 0;
+ unsigned int i;
+ size_t size;
+
+ if (!align || !IS_ALIGNED(bar_size, align))
+ return -EINVAL;
+
+ /*
+ * The submap array order defines the BAR layout (submap[0] starts
+ * at offset 0 and each entry immediately follows the previous
+ * one). Here, validate that it forms a strict, gapless
+ * decomposition of the BAR:
+ * - each entry has a non-zero size
+ * - sizes, implicit offsets and phys_addr are aligned to
+ * pci->region_align
+ * - each entry lies within the BAR range
+ * - the entries exactly cover the whole BAR
+ *
+ * Note: dw_pcie_prog_inbound_atu() also checks alignment for the
+ * PCI address and the target phys_addr, but validating up-front
+ * avoids partially programming iATU windows in vain.
+ */
+ for (i = 0; i < num_submap; i++) {
+ 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;
+
+ off += size;
+ }
+ if (off != 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;
+ u64 size, base, off = 0;
+ int free_win, ret;
+ unsigned int i;
+ u32 *indexes;
+
+ 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;
+ }
+
+ indexes = devm_kcalloc(dev, epf_bar->num_submap, sizeof(*indexes),
+ GFP_KERNEL);
+ if (!indexes)
+ return -ENOMEM;
+
+ ep->ib_atu_indexes[bar] = indexes;
+ ep->num_ib_atu_indexes[bar] = 0;
+
+ for (i = 0; i < epf_bar->num_submap; i++) {
+ size = submap[i].size;
+ parent_bus_addr = submap[i].phys_addr;
+
+ if (off > (~0ULL) - base) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ pci_addr = base + off;
+ off += size;
+
+ free_win = find_first_zero_bit(ep->ib_window_map,
+ pci->num_ib_windows);
+ if (free_win >= pci->num_ib_windows) {
+ ret = -ENOSPC;
+ goto err;
+ }
+
+ ret = dw_pcie_prog_inbound_atu(pci, free_win, type,
+ parent_bus_addr, pci_addr, size);
+ if (ret)
+ goto err;
+
+ set_bit(free_win, ep->ib_window_map);
+ indexes[i] = free_win;
+ ep->num_ib_atu_indexes[bar] = i + 1;
+ }
+ 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)
{
@@ -165,17 +339,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,
@@ -331,6 +503,13 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
ep->epf_bar[bar]->flags != flags)
return -EINVAL;
+ /*
+ * When dynamically changing a BAR, tear down any existing
+ * mappings before re-programming.
+ */
+ if (ep->epf_bar[bar]->num_submap || epf_bar->num_submap)
+ dw_pcie_ep_clear_ib_maps(ep, bar);
+
/*
* When dynamically changing a BAR, skip writing the BAR reg, as
* that would clear the BAR's PCI address assigned by the host.
@@ -369,8 +548,12 @@ 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 (epf_bar->num_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)
return ret;
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index be47f34b49ca..938dcb541ce3 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -306,7 +306,8 @@
#define DMA_LLP_MEM_SIZE PAGE_SIZE
/* Common struct pci_epc_feature bits among DWC EP glue drivers */
-#define DWC_EPC_COMMON_FEATURES .dynamic_inbound_mapping = true
+#define DWC_EPC_COMMON_FEATURES .dynamic_inbound_mapping = true, \
+ .subrange_mapping = true
struct dw_pcie;
struct dw_pcie_rp;
@@ -487,6 +488,10 @@ struct dw_pcie_ep {
phys_addr_t msi_mem_phys;
struct pci_epf_bar *epf_bar[PCI_STD_NUM_BARS];
+ /* Only for Address Match Mode inbound iATU */
+ u32 *ib_atu_indexes[PCI_STD_NUM_BARS];
+ unsigned int num_ib_atu_indexes[PCI_STD_NUM_BARS];
+
/* MSI outbound iATU state */
bool msi_iatu_mapped;
u64 msi_msg_addr;
--
2.51.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH v9 4/5] PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU
2026-01-22 8:49 ` [PATCH v9 4/5] PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU Koichiro Den
@ 2026-01-22 9:23 ` Niklas Cassel
2026-01-22 14:29 ` Koichiro Den
0 siblings, 1 reply; 16+ messages in thread
From: Niklas Cassel @ 2026-01-22 9:23 UTC (permalink / raw)
To: Koichiro Den
Cc: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas,
vigneshr, s-vadapalli, hongxing.zhu, l.stach, shawnguo, s.hauer,
kernel, festevam, minghuan.Lian, mingkai.hu, roy.zang,
jesper.nilsson, heiko, srikanth.thokala, marek.vasut+renesas,
yoshihiro.shimoda.uh, geert+renesas, magnus.damm, christian.bruel,
mcoquelin.stm32, alexandre.torgue, thierry.reding, jonathanh,
hayashi.kunihiko, mhiramat, kishon, jirislaby, rongqianfeng,
18255117159, shawn.lin, nicolas.frattaroli, linux.amoon, vidyas,
Frank.Li, linux-omap, linux-pci, linux-arm-kernel, linux-kernel,
imx, linuxppc-dev, linux-arm-kernel, linux-rockchip,
linux-arm-msm, linux-renesas-soc, linux-stm32, linux-tegra
On Thu, Jan 22, 2026 at 05:49:08PM +0900, Koichiro Den wrote:
> Extend dw_pcie_ep_set_bar() to support inbound mappings for BAR
> subranges using Address Match Mode IB iATU when pci_epf_bar.num_submap
> is non-zero.
>
> 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
> num_submap is non-zero, 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.
>
> Advertise this capability by extending the common feature bit
> initializer macro (DWC_EPC_COMMON_FEATURES).
>
> This enables multiple inbound windows within a single BAR, which is
> useful on platforms where usable BARs are scarce but EPFs need multiple
> inbound regions.
>
> Reviewed-by: Frank Li <Frank.Li@nxp.com>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
> @@ -331,6 +503,13 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> ep->epf_bar[bar]->flags != flags)
> return -EINVAL;
>
> + /*
> + * When dynamically changing a BAR, tear down any existing
> + * mappings before re-programming.
> + */
> + if (ep->epf_bar[bar]->num_submap || epf_bar->num_submap)
> + dw_pcie_ep_clear_ib_maps(ep, bar);
> +
> /*
> * When dynamically changing a BAR, skip writing the BAR reg, as
> * that would clear the BAR's PCI address assigned by the host.
> @@ -369,8 +548,12 @@ 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 (epf_bar->num_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 someone calls set_bar() with a submap, without having called set_bar() first
without a submap, we will still call dw_pcie_ep_ib_atu_addr() here.
To make sure that dw_pcie_ep_ib_atu_addr() cannot be called without already
having a BAR configured, to we perhaps want something like:
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 0567552b784c..fe26b7f7b212 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -487,6 +487,9 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
if ((flags & PCI_BASE_ADDRESS_MEM_TYPE_64) && (bar & 1))
return -EINVAL;
+ if (!ep->epf_bar[bar] && epf_bar->num_submap)
+ 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
or
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 0567552b784c..8aeaa6fe53f9 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -475,6 +475,7 @@ static int dw_pcie_ep_set_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;
+ bool use_addr_match_mode = false;
size_t size = epf_bar->size;
enum pci_epc_bar_type bar_type;
int flags = epf_bar->flags;
@@ -510,6 +511,9 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
if (ep->epf_bar[bar]->num_submap || epf_bar->num_submap)
dw_pcie_ep_clear_ib_maps(ep, bar);
+ if (epf_bar->num_submap)
+ use_addr_match_mode = true;
+
/*
* When dynamically changing a BAR, skip writing the BAR reg, as
* that would clear the BAR's PCI address assigned by the host.
@@ -548,7 +552,7 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
else
type = PCIE_ATU_TYPE_IO;
- if (epf_bar->num_submap)
+ if (use_addr_match_mode)
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,
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH v9 4/5] PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU
2026-01-22 9:23 ` Niklas Cassel
@ 2026-01-22 14:29 ` Koichiro Den
2026-01-22 16:59 ` Niklas Cassel
0 siblings, 1 reply; 16+ messages in thread
From: Koichiro Den @ 2026-01-22 14:29 UTC (permalink / raw)
To: Niklas Cassel
Cc: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas,
vigneshr, s-vadapalli, hongxing.zhu, l.stach, shawnguo, s.hauer,
kernel, festevam, minghuan.Lian, mingkai.hu, roy.zang,
jesper.nilsson, heiko, srikanth.thokala, marek.vasut+renesas,
yoshihiro.shimoda.uh, geert+renesas, magnus.damm, christian.bruel,
mcoquelin.stm32, alexandre.torgue, thierry.reding, jonathanh,
hayashi.kunihiko, mhiramat, kishon, jirislaby, rongqianfeng,
18255117159, shawn.lin, nicolas.frattaroli, linux.amoon, vidyas,
Frank.Li, linux-omap, linux-pci, linux-arm-kernel, linux-kernel,
imx, linuxppc-dev, linux-arm-kernel, linux-rockchip,
linux-arm-msm, linux-renesas-soc, linux-stm32, linux-tegra
On Thu, Jan 22, 2026 at 10:23:03AM +0100, Niklas Cassel wrote:
> On Thu, Jan 22, 2026 at 05:49:08PM +0900, Koichiro Den wrote:
> > Extend dw_pcie_ep_set_bar() to support inbound mappings for BAR
> > subranges using Address Match Mode IB iATU when pci_epf_bar.num_submap
> > is non-zero.
> >
> > 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
> > num_submap is non-zero, 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.
> >
> > Advertise this capability by extending the common feature bit
> > initializer macro (DWC_EPC_COMMON_FEATURES).
> >
> > This enables multiple inbound windows within a single BAR, which is
> > useful on platforms where usable BARs are scarce but EPFs need multiple
> > inbound regions.
> >
> > Reviewed-by: Frank Li <Frank.Li@nxp.com>
> > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> > ---
>
>
> > @@ -331,6 +503,13 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> > ep->epf_bar[bar]->flags != flags)
> > return -EINVAL;
> >
> > + /*
> > + * When dynamically changing a BAR, tear down any existing
> > + * mappings before re-programming.
> > + */
> > + if (ep->epf_bar[bar]->num_submap || epf_bar->num_submap)
> > + dw_pcie_ep_clear_ib_maps(ep, bar);
> > +
> > /*
> > * When dynamically changing a BAR, skip writing the BAR reg, as
> > * that would clear the BAR's PCI address assigned by the host.
> > @@ -369,8 +548,12 @@ 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 (epf_bar->num_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 someone calls set_bar() with a submap, without having called set_bar() first
> without a submap, we will still call dw_pcie_ep_ib_atu_addr() here.
>
> To make sure that dw_pcie_ep_ib_atu_addr() cannot be called without already
> having a BAR configured, to we perhaps want something like:
Thanks for the review.
Isn't the existing guard in dw_pcie_ep_ib_atu_addr sufficient?
[...]
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;
}
Koichiro
>
> diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
> index 0567552b784c..fe26b7f7b212 100644
> --- a/drivers/pci/controller/dwc/pcie-designware-ep.c
> +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
> @@ -487,6 +487,9 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> if ((flags & PCI_BASE_ADDRESS_MEM_TYPE_64) && (bar & 1))
> return -EINVAL;
>
> + if (!ep->epf_bar[bar] && epf_bar->num_submap)
> + 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
>
>
> or
>
>
> diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
> index 0567552b784c..8aeaa6fe53f9 100644
> --- a/drivers/pci/controller/dwc/pcie-designware-ep.c
> +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
> @@ -475,6 +475,7 @@ static int dw_pcie_ep_set_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;
> + bool use_addr_match_mode = false;
> size_t size = epf_bar->size;
> enum pci_epc_bar_type bar_type;
> int flags = epf_bar->flags;
> @@ -510,6 +511,9 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> if (ep->epf_bar[bar]->num_submap || epf_bar->num_submap)
> dw_pcie_ep_clear_ib_maps(ep, bar);
>
> + if (epf_bar->num_submap)
> + use_addr_match_mode = true;
> +
> /*
> * When dynamically changing a BAR, skip writing the BAR reg, as
> * that would clear the BAR's PCI address assigned by the host.
> @@ -548,7 +552,7 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> else
> type = PCIE_ATU_TYPE_IO;
>
> - if (epf_bar->num_submap)
> + if (use_addr_match_mode)
> 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,
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [PATCH v9 4/5] PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU
2026-01-22 14:29 ` Koichiro Den
@ 2026-01-22 16:59 ` Niklas Cassel
2026-01-23 1:16 ` Koichiro Den
0 siblings, 1 reply; 16+ messages in thread
From: Niklas Cassel @ 2026-01-22 16:59 UTC (permalink / raw)
To: Koichiro Den
Cc: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas,
vigneshr, s-vadapalli, hongxing.zhu, l.stach, shawnguo, s.hauer,
kernel, festevam, minghuan.Lian, mingkai.hu, roy.zang,
jesper.nilsson, heiko, srikanth.thokala, marek.vasut+renesas,
yoshihiro.shimoda.uh, geert+renesas, magnus.damm, christian.bruel,
mcoquelin.stm32, alexandre.torgue, thierry.reding, jonathanh,
hayashi.kunihiko, mhiramat, kishon, jirislaby, rongqianfeng,
18255117159, shawn.lin, nicolas.frattaroli, linux.amoon, vidyas,
Frank.Li, linux-omap, linux-pci, linux-arm-kernel, linux-kernel,
imx, linuxppc-dev, linux-arm-kernel, linux-rockchip,
linux-arm-msm, linux-renesas-soc, linux-stm32, linux-tegra
On 22 January 2026 15:29:02 CET, Koichiro Den <den@valinux.co.jp> wrote:
>
>> To make sure that dw_pcie_ep_ib_atu_addr() cannot be called without already
>> having a BAR configured, to we perhaps want something like:
>
>Thanks for the review.
>Isn't the existing guard in dw_pcie_ep_ib_atu_addr sufficient?
>
> [...]
> 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;
> }
>
Well, for a driver that does not call dw_pcie_ep_reset_bar() in their .init() to disable all BARs that are enabled in the controller by default, the host side will assign an PCI address even if no EPF has called set_bar() on that BAR.
See e.g.
https://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git/commit/drivers/pci/controller/dwc/pcie-tegra194.c?h=controller/dwc&id=42f9c66a6d0cc45758dab77233c5460e1cf003df
There might be other EPC drivers that don't disable all BARs in their .init(), so I would say that simply checking if the BAR has an address is not sufficient to guarantee that an EPF driver has called set_bar().
I think the safest option is my second suggestion because then we know that we will only call
dw_pcie_ep_ib_atu_addr()
When:
1) If ep->epf_bar[bar] is set:
https://github.com/torvalds/linux/blob/v6.19-rc6/drivers/pci/controller/dwc/pcie-designware-ep.c#L363
2) All the other requirements to dynamically update a BAR is also met:
https://github.com/torvalds/linux/blob/v6.19-rc6/drivers/pci/controller/dwc/pcie-designware-ep.c#L368-L370
Kind regards,
Niklas
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [PATCH v9 4/5] PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU
2026-01-22 16:59 ` Niklas Cassel
@ 2026-01-23 1:16 ` Koichiro Den
2026-01-23 8:51 ` Niklas Cassel
0 siblings, 1 reply; 16+ messages in thread
From: Koichiro Den @ 2026-01-23 1:16 UTC (permalink / raw)
To: Niklas Cassel
Cc: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas,
vigneshr, s-vadapalli, hongxing.zhu, l.stach, shawnguo, s.hauer,
kernel, festevam, minghuan.Lian, mingkai.hu, roy.zang,
jesper.nilsson, heiko, srikanth.thokala, marek.vasut+renesas,
yoshihiro.shimoda.uh, geert+renesas, magnus.damm, christian.bruel,
mcoquelin.stm32, alexandre.torgue, thierry.reding, jonathanh,
hayashi.kunihiko, mhiramat, kishon, jirislaby, rongqianfeng,
18255117159, shawn.lin, nicolas.frattaroli, linux.amoon, vidyas,
Frank.Li, linux-omap, linux-pci, linux-arm-kernel, linux-kernel,
imx, linuxppc-dev, linux-arm-kernel, linux-rockchip,
linux-arm-msm, linux-renesas-soc, linux-stm32, linux-tegra
On Thu, Jan 22, 2026 at 05:59:18PM +0100, Niklas Cassel wrote:
> On 22 January 2026 15:29:02 CET, Koichiro Den <den@valinux.co.jp> wrote:
> >
> >> To make sure that dw_pcie_ep_ib_atu_addr() cannot be called without already
> >> having a BAR configured, to we perhaps want something like:
> >
> >Thanks for the review.
> >Isn't the existing guard in dw_pcie_ep_ib_atu_addr sufficient?
> >
> > [...]
> > 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;
> > }
> >
>
> Well, for a driver that does not call dw_pcie_ep_reset_bar() in their .init() to disable all BARs that are enabled in the controller by default, the host side will assign an PCI address even if no EPF has called set_bar() on that BAR.
Thanks for the explanation.
>
> See e.g.
> https://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git/commit/drivers/pci/controller/dwc/pcie-tegra194.c?h=controller/dwc&id=42f9c66a6d0cc45758dab77233c5460e1cf003df
>
> There might be other EPC drivers that don't disable all BARs in their .init(), so I would say that simply checking if the BAR has an address is not sufficient to guarantee that an EPF driver has called set_bar().
>
Even if an EPC driver does not reset the BAR in their .init() and some
default translation is left exposed, wouldn't it be safe as long as
dw_pcie_ep_ib_atu_addr() succeeds in programming inbound mappings for the
entire BAR?
That said, such usage apparently contradicts the documented usage (1st
set_bar with no submap, then with submap) described in the docs and commit
messages in this series, and allowing it would make things unnecessarily
complicated. So I agree that adding such a safeguard is the right approach.
>
> I think the safest option is my second suggestion because then we know that we will only call
> dw_pcie_ep_ib_atu_addr()
>
> When:
>
> 1) If ep->epf_bar[bar] is set:
> https://github.com/torvalds/linux/blob/v6.19-rc6/drivers/pci/controller/dwc/pcie-designware-ep.c#L363
>
>
> 2) All the other requirements to dynamically update a BAR is also met:
>
> https://github.com/torvalds/linux/blob/v6.19-rc6/drivers/pci/controller/dwc/pcie-designware-ep.c#L368-L370
>
That makes sense, and it ensures that the behavior always accords with the
docs and commit messages in this series.
Thanks a lot for the careful review,
Koichiro
>
>
> Kind regards,
> Niklas
>
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [PATCH v9 4/5] PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU
2026-01-23 1:16 ` Koichiro Den
@ 2026-01-23 8:51 ` Niklas Cassel
2026-01-23 14:28 ` Koichiro Den
0 siblings, 1 reply; 16+ messages in thread
From: Niklas Cassel @ 2026-01-23 8:51 UTC (permalink / raw)
To: Koichiro Den
Cc: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas,
vigneshr, s-vadapalli, hongxing.zhu, l.stach, shawnguo, s.hauer,
kernel, festevam, minghuan.Lian, mingkai.hu, roy.zang,
jesper.nilsson, heiko, srikanth.thokala, marek.vasut+renesas,
yoshihiro.shimoda.uh, geert+renesas, magnus.damm, christian.bruel,
mcoquelin.stm32, alexandre.torgue, thierry.reding, jonathanh,
hayashi.kunihiko, mhiramat, kishon, jirislaby, rongqianfeng,
18255117159, shawn.lin, nicolas.frattaroli, linux.amoon, vidyas,
Frank.Li, linux-omap, linux-pci, linux-arm-kernel, linux-kernel,
imx, linuxppc-dev, linux-arm-kernel, linux-rockchip,
linux-arm-msm, linux-renesas-soc, linux-stm32, linux-tegra
On Fri, Jan 23, 2026 at 10:16:21AM +0900, Koichiro Den wrote:
> >
> > There might be other EPC drivers that don't disable all BARs in their .init(), so I would say that simply checking if the BAR has an address is not sufficient to guarantee that an EPF driver has called set_bar().
> >
>
> Even if an EPC driver does not reset the BAR in their .init() and some
> default translation is left exposed, wouldn't it be safe as long as
> dw_pcie_ep_ib_atu_addr() succeeds in programming inbound mappings for the
> entire BAR?
For e.g. on RK3588, the default HW configuration of the DWC controller has
all 5 BARs as enabled, with a size of 1 GB.
There is no inbound address translation for these BARs by default.
So for it to be safe, the size of the set_bar() call would have to
match the current size of the BAR, but how should the EPF driver know
that when it has not called set_bar() yet?
dw_pcie_ep_read_bar_assigned() does not return the current size of the
BAR. So you can't verify that the set_bar() call has the same size as
the BARs "default size".
>
> That said, such usage apparently contradicts the documented usage (1st
> set_bar with no submap, then with submap) described in the docs and commit
> messages in this series, and allowing it would make things unnecessarily
> complicated. So I agree that adding such a safeguard is the right approach.
>
> >
> > I think the safest option is my second suggestion because then we know that we will only call
> > dw_pcie_ep_ib_atu_addr()
> >
> > When:
> >
> > 1) If ep->epf_bar[bar] is set:
> > https://github.com/torvalds/linux/blob/v6.19-rc6/drivers/pci/controller/dwc/pcie-designware-ep.c#L363
> >
> >
> > 2) All the other requirements to dynamically update a BAR is also met:
> >
> > https://github.com/torvalds/linux/blob/v6.19-rc6/drivers/pci/controller/dwc/pcie-designware-ep.c#L368-L370
> >
>
> That makes sense, and it ensures that the behavior always accords with the
> docs and commit messages in this series.
I think it makes most sense to put the "use_addr_translation = true"
after the check:
/*
* We can only dynamically change a BAR if the new BAR size and
* BAR flags do not differ from the existing configuration.
*/
if (ep->epf_bar[bar]->barno != bar ||
ep->epf_bar[bar]->size != size ||
ep->epf_bar[bar]->flags != flags)
return -EINVAL;
So we know that dw_pcie_ep_ib_atu_addr() is only called when the size is the
same.
Kind regards,
Niklas
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v9 4/5] PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU
2026-01-23 8:51 ` Niklas Cassel
@ 2026-01-23 14:28 ` Koichiro Den
2026-01-24 14:22 ` Koichiro Den
0 siblings, 1 reply; 16+ messages in thread
From: Koichiro Den @ 2026-01-23 14:28 UTC (permalink / raw)
To: Niklas Cassel
Cc: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas,
vigneshr, s-vadapalli, hongxing.zhu, l.stach, shawnguo, s.hauer,
kernel, festevam, minghuan.Lian, mingkai.hu, roy.zang,
jesper.nilsson, heiko, srikanth.thokala, marek.vasut+renesas,
yoshihiro.shimoda.uh, geert+renesas, magnus.damm, christian.bruel,
mcoquelin.stm32, alexandre.torgue, thierry.reding, jonathanh,
hayashi.kunihiko, mhiramat, kishon, jirislaby, rongqianfeng,
18255117159, shawn.lin, nicolas.frattaroli, linux.amoon, vidyas,
Frank.Li, linux-omap, linux-pci, linux-arm-kernel, linux-kernel,
imx, linuxppc-dev, linux-arm-kernel, linux-rockchip,
linux-arm-msm, linux-renesas-soc, linux-stm32, linux-tegra
On Fri, Jan 23, 2026 at 09:51:19AM +0100, Niklas Cassel wrote:
> On Fri, Jan 23, 2026 at 10:16:21AM +0900, Koichiro Den wrote:
> > >
> > > There might be other EPC drivers that don't disable all BARs in their .init(), so I would say that simply checking if the BAR has an address is not sufficient to guarantee that an EPF driver has called set_bar().
> > >
> >
> > Even if an EPC driver does not reset the BAR in their .init() and some
> > default translation is left exposed, wouldn't it be safe as long as
> > dw_pcie_ep_ib_atu_addr() succeeds in programming inbound mappings for the
> > entire BAR?
>
> For e.g. on RK3588, the default HW configuration of the DWC controller has
> all 5 BARs as enabled, with a size of 1 GB.
>
> There is no inbound address translation for these BARs by default.
>
> So for it to be safe, the size of the set_bar() call would have to
> match the current size of the BAR, but how should the EPF driver know
> that when it has not called set_bar() yet?
>
> dw_pcie_ep_read_bar_assigned() does not return the current size of the
> BAR. So you can't verify that the set_bar() call has the same size as
> the BARs "default size".
I wasn't considering either of the following cases as unsafe:
- succeeding by chance in programming via a one-shot set_bar() with submaps
- such a set_bar() failing (due to incorrect size recognition)
while as I mentioned in my previous reply, the first case effectively
becomes a loophole that contradicts the docs and git commit messages.
However, since v8, the second case clears any existing mappings, which
could indeed lead to an unsafe situtation.
>
>
> >
> > That said, such usage apparently contradicts the documented usage (1st
> > set_bar with no submap, then with submap) described in the docs and commit
> > messages in this series, and allowing it would make things unnecessarily
> > complicated. So I agree that adding such a safeguard is the right approach.
> >
> > >
> > > I think the safest option is my second suggestion because then we know that we will only call
> > > dw_pcie_ep_ib_atu_addr()
> > >
> > > When:
> > >
> > > 1) If ep->epf_bar[bar] is set:
> > > https://github.com/torvalds/linux/blob/v6.19-rc6/drivers/pci/controller/dwc/pcie-designware-ep.c#L363
> > >
> > >
> > > 2) All the other requirements to dynamically update a BAR is also met:
> > >
> > > https://github.com/torvalds/linux/blob/v6.19-rc6/drivers/pci/controller/dwc/pcie-designware-ep.c#L368-L370
> > >
> >
> > That makes sense, and it ensures that the behavior always accords with the
> > docs and commit messages in this series.
>
> I think it makes most sense to put the "use_addr_translation = true"
>
> after the check:
>
> /*
> * We can only dynamically change a BAR if the new BAR size and
> * BAR flags do not differ from the existing configuration.
> */
> if (ep->epf_bar[bar]->barno != bar ||
> ep->epf_bar[bar]->size != size ||
> ep->epf_bar[bar]->flags != flags)
> return -EINVAL;
>
>
> So we know that dw_pcie_ep_ib_atu_addr() is only called when the size is the
> same.
I'll send v10 with the fix, possibly adding a BAR_SUBRANGE_TEST to pci
endpoint test as well.
Kind regards,
Koichiro
>
>
> Kind regards,
> Niklas
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v9 4/5] PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU
2026-01-23 14:28 ` Koichiro Den
@ 2026-01-24 14:22 ` Koichiro Den
0 siblings, 0 replies; 16+ messages in thread
From: Koichiro Den @ 2026-01-24 14:22 UTC (permalink / raw)
To: Niklas Cassel
Cc: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas,
vigneshr, s-vadapalli, hongxing.zhu, l.stach, shawnguo, s.hauer,
kernel, festevam, minghuan.Lian, mingkai.hu, roy.zang,
jesper.nilsson, heiko, srikanth.thokala, marek.vasut+renesas,
yoshihiro.shimoda.uh, geert+renesas, magnus.damm, christian.bruel,
mcoquelin.stm32, alexandre.torgue, thierry.reding, jonathanh,
hayashi.kunihiko, mhiramat, kishon, jirislaby, rongqianfeng,
18255117159, shawn.lin, nicolas.frattaroli, linux.amoon, vidyas,
Frank.Li, linux-omap, linux-pci, linux-arm-kernel, linux-kernel,
imx, linuxppc-dev, linux-arm-kernel, linux-rockchip,
linux-arm-msm, linux-renesas-soc, linux-stm32, linux-tegra
On Fri, Jan 23, 2026 at 11:28:30PM +0900, Koichiro Den wrote:
> On Fri, Jan 23, 2026 at 09:51:19AM +0100, Niklas Cassel wrote:
> > On Fri, Jan 23, 2026 at 10:16:21AM +0900, Koichiro Den wrote:
> > > >
> > > > There might be other EPC drivers that don't disable all BARs in their .init(), so I would say that simply checking if the BAR has an address is not sufficient to guarantee that an EPF driver has called set_bar().
> > > >
> > >
> > > Even if an EPC driver does not reset the BAR in their .init() and some
> > > default translation is left exposed, wouldn't it be safe as long as
> > > dw_pcie_ep_ib_atu_addr() succeeds in programming inbound mappings for the
> > > entire BAR?
> >
> > For e.g. on RK3588, the default HW configuration of the DWC controller has
> > all 5 BARs as enabled, with a size of 1 GB.
> >
> > There is no inbound address translation for these BARs by default.
> >
> > So for it to be safe, the size of the set_bar() call would have to
> > match the current size of the BAR, but how should the EPF driver know
> > that when it has not called set_bar() yet?
> >
> > dw_pcie_ep_read_bar_assigned() does not return the current size of the
> > BAR. So you can't verify that the set_bar() call has the same size as
> > the BARs "default size".
>
> I wasn't considering either of the following cases as unsafe:
> - succeeding by chance in programming via a one-shot set_bar() with submaps
> - such a set_bar() failing (due to incorrect size recognition)
>
> while as I mentioned in my previous reply, the first case effectively
> becomes a loophole that contradicts the docs and git commit messages.
>
> However, since v8, the second case clears any existing mappings, which
> could indeed lead to an unsafe situtation.
>
> >
> >
> > >
> > > That said, such usage apparently contradicts the documented usage (1st
> > > set_bar with no submap, then with submap) described in the docs and commit
> > > messages in this series, and allowing it would make things unnecessarily
> > > complicated. So I agree that adding such a safeguard is the right approach.
> > >
> > > >
> > > > I think the safest option is my second suggestion because then we know that we will only call
> > > > dw_pcie_ep_ib_atu_addr()
> > > >
> > > > When:
> > > >
> > > > 1) If ep->epf_bar[bar] is set:
> > > > https://github.com/torvalds/linux/blob/v6.19-rc6/drivers/pci/controller/dwc/pcie-designware-ep.c#L363
> > > >
> > > >
> > > > 2) All the other requirements to dynamically update a BAR is also met:
> > > >
> > > > https://github.com/torvalds/linux/blob/v6.19-rc6/drivers/pci/controller/dwc/pcie-designware-ep.c#L368-L370
> > > >
> > >
> > > That makes sense, and it ensures that the behavior always accords with the
> > > docs and commit messages in this series.
> >
> > I think it makes most sense to put the "use_addr_translation = true"
> >
> > after the check:
> >
> > /*
> > * We can only dynamically change a BAR if the new BAR size and
> > * BAR flags do not differ from the existing configuration.
> > */
> > if (ep->epf_bar[bar]->barno != bar ||
> > ep->epf_bar[bar]->size != size ||
> > ep->epf_bar[bar]->flags != flags)
> > return -EINVAL;
> >
> >
> > So we know that dw_pcie_ep_ib_atu_addr() is only called when the size is the
> > same.
>
> I'll send v10 with the fix, possibly adding a BAR_SUBRANGE_TEST to pci
> endpoint test as well.
After thinking again, I believe just the following is the most robust and
safest approach, as it makes subrange mapping strictly update-only and
avoids any silent success on invalid first-time calls.
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -508,20 +508,29 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
* mappings before re-programming.
*/
if (ep->epf_bar[bar]->num_submap || epf_bar->num_submap)
dw_pcie_ep_clear_ib_maps(ep, bar);
/*
* 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;
+ } else {
+ /*
+ * Subrange mapping is an update-only operation.
+ * The BAR must have been configured once without submaps so that
+ * subsequent set_bar() calls can update inbound mappings without
+ * touching the BAR register (and clobbering the host-assigned address).
+ */
+ if (epf_bar->num_submap)
+ return -EINVAL;
}
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.)
This is close to your first suggestion at:
https://lore.kernel.org/linux-pci/aXHsd7-WWAGyhy_w@ryzen/
but it avoids even performing BAR sizing when set_bar() is called in an invalid manner.
With this, we still guarantee dw_pcie_ep_ib_atu_addr() is only reached when:
1) ep->epf_bar[bar] is set
2) All the other requirements to dynamically update a BAR is also met
The resulting behavior matrix becomes:
| num_submap > 0 | num_submap == 0 |
-------------------------+--------------------------+--------------------------+
ep->epf_bar[bar] == NULL | returns -EINVAL | always try BAR Match |
ep->epf_bar[bar] != NULL | always try Address Match | always try BAR Match |
By contrast, with the latest idea that relies on the local
"use_addr_translation" variable, the case marked as [1] below possibly
leads to an unexpected success in BAR Match Mode, .submap/.num_submap are
silently ignored, and the caller has no way to notice the mistake.
| num_submap > 0 | num_submap == 0 |
-------------------------+--------------------------+--------------------------+
ep->epf_bar[bar] == NULL | always try BAR Match [1] | always try BAR Match |
ep->epf_bar[bar] != NULL | always try Address Match | always try BAR Match |
Kind regards,
Koichiro
>
> Kind regards,
> Koichiro
>
> >
> >
> > Kind regards,
> > Niklas
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v9 5/5] Documentation: PCI: endpoint: Clarify pci_epc_set_bar() usage
2026-01-22 8:49 [PATCH v9 0/5] PCI: endpoint: BAR subrange mapping support Koichiro Den
` (3 preceding siblings ...)
2026-01-22 8:49 ` [PATCH v9 4/5] PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU Koichiro Den
@ 2026-01-22 8:49 ` Koichiro Den
2026-01-22 9:07 ` Niklas Cassel
4 siblings, 1 reply; 16+ messages in thread
From: Koichiro Den @ 2026-01-22 8:49 UTC (permalink / raw)
To: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas, cassel
Cc: vigneshr, s-vadapalli, hongxing.zhu, l.stach, shawnguo, s.hauer,
kernel, festevam, minghuan.Lian, mingkai.hu, roy.zang,
jesper.nilsson, heiko, srikanth.thokala, marek.vasut+renesas,
yoshihiro.shimoda.uh, geert+renesas, magnus.damm, christian.bruel,
mcoquelin.stm32, alexandre.torgue, thierry.reding, jonathanh,
hayashi.kunihiko, mhiramat, kishon, jirislaby, rongqianfeng,
18255117159, shawn.lin, nicolas.frattaroli, linux.amoon, vidyas,
Frank.Li, linux-omap, linux-pci, linux-arm-kernel, linux-kernel,
imx, linuxppc-dev, linux-arm-kernel, linux-rockchip,
linux-arm-msm, linux-renesas-soc, linux-stm32, linux-tegra
The current documentation implies that pci_epc_set_bar() is only used
before the host enumerates the endpoint.
In practice, some Endpoint Controllers support calling pci_epc_set_bar()
multiple times for the same BAR (without clearing it) in order to update
inbound address translations after the host has programmed the BAR base
address, which some Endpoint Functions such as vNTB already rely on.
Add document text for that.
Also document the expected call flow for BAR subrange mapping
(pci_epf_bar.num_submap / pci_epf_bar.submap), which may require a
second pci_epc_set_bar() call after the host has programmed the BAR base
address.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Documentation/PCI/endpoint/pci-endpoint.rst | 24 +++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/Documentation/PCI/endpoint/pci-endpoint.rst b/Documentation/PCI/endpoint/pci-endpoint.rst
index 0741c8cbd74e..4697377adeae 100644
--- a/Documentation/PCI/endpoint/pci-endpoint.rst
+++ b/Documentation/PCI/endpoint/pci-endpoint.rst
@@ -95,6 +95,30 @@ by the PCI endpoint function driver.
Register space of the function driver is usually configured
using this API.
+ Some endpoint controllers also support calling pci_epc_set_bar() again
+ for the same BAR (without calling pci_epc_clear_bar()) to update inbound
+ address translations after the host has programmed the BAR base address.
+ Endpoint function drivers can check this capability via the
+ dynamic_inbound_mapping EPC feature bit.
+
+ When pci_epf_bar.num_submap is non-zero, the endpoint function driver is
+ requesting BAR subrange mapping using pci_epf_bar.submap. This requires
+ the EPC to advertise support via the subrange_mapping EPC feature bit.
+
+ When an EPF driver wants to make use of the inbound subrange mapping
+ feature, it requires that the BAR base address has been programmed by
+ the host during enumeration. Thus, it needs to call pci_epc_set_bar()
+ twice for the same BAR (requires dynamic_inbound_mapping): first with
+ num_submap set to zero and configuring the BAR size, then after the PCIe
+ link is up and the host enumerates the endpoint and programs the BAR
+ base address, again with num_submap set to non-zero value.
+
+ Note that when making use of the inbound subrange mapping feature, the
+ EPF driver must not call pci_epc_clear_bar() between the two
+ pci_epc_set_bar() calls, because clearing the BAR can clear/disable the
+ BAR register or BAR decode on the endpoint while the host still expects
+ the assigned BAR address to remain valid.
+
* pci_epc_clear_bar()
The PCI endpoint function driver should use pci_epc_clear_bar() to reset
--
2.51.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH v9 5/5] Documentation: PCI: endpoint: Clarify pci_epc_set_bar() usage
2026-01-22 8:49 ` [PATCH v9 5/5] Documentation: PCI: endpoint: Clarify pci_epc_set_bar() usage Koichiro Den
@ 2026-01-22 9:07 ` Niklas Cassel
0 siblings, 0 replies; 16+ messages in thread
From: Niklas Cassel @ 2026-01-22 9:07 UTC (permalink / raw)
To: Koichiro Den
Cc: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas,
vigneshr, s-vadapalli, hongxing.zhu, l.stach, shawnguo, s.hauer,
kernel, festevam, minghuan.Lian, mingkai.hu, roy.zang,
jesper.nilsson, heiko, srikanth.thokala, marek.vasut+renesas,
yoshihiro.shimoda.uh, geert+renesas, magnus.damm, christian.bruel,
mcoquelin.stm32, alexandre.torgue, thierry.reding, jonathanh,
hayashi.kunihiko, mhiramat, kishon, jirislaby, rongqianfeng,
18255117159, shawn.lin, nicolas.frattaroli, linux.amoon, vidyas,
Frank.Li, linux-omap, linux-pci, linux-arm-kernel, linux-kernel,
imx, linuxppc-dev, linux-arm-kernel, linux-rockchip,
linux-arm-msm, linux-renesas-soc, linux-stm32, linux-tegra
On Thu, Jan 22, 2026 at 05:49:09PM +0900, Koichiro Den wrote:
> The current documentation implies that pci_epc_set_bar() is only used
> before the host enumerates the endpoint.
>
> In practice, some Endpoint Controllers support calling pci_epc_set_bar()
> multiple times for the same BAR (without clearing it) in order to update
> inbound address translations after the host has programmed the BAR base
> address, which some Endpoint Functions such as vNTB already rely on.
> Add document text for that.
>
> Also document the expected call flow for BAR subrange mapping
> (pci_epf_bar.num_submap / pci_epf_bar.submap), which may require a
> second pci_epc_set_bar() call after the host has programmed the BAR base
> address.
>
> Reviewed-by: Frank Li <Frank.Li@nxp.com>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
Reviewed-by: Niklas Cassel <cassel@kernel.org>
^ permalink raw reply [flat|nested] 16+ messages in thread