* [PATCH v7 0/6] PCI: endpoint: BAR subrange mapping support
@ 2026-01-13 16:27 Koichiro Den
2026-01-13 16:27 ` [PATCH v7 1/6] PCI: endpoint: Add dynamic_inbound_mapping EPC feature Koichiro Den
` (5 more replies)
0 siblings, 6 replies; 23+ messages in thread
From: Koichiro Den @ 2026-01-13 16:27 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
This series proposes support for mapping subranges within a PCIe endpoint
BAR and enables controllers to program inbound address translation for
those subranges.
- Patch 1/6 introduces dynamic_inbound_mapping feature bit. This can be
used as a safeguard to check whether a BAR can really be reconfigured
without clearing/resetting it.
- Patch 2/6 introduces generic BAR subrange mapping support in the PCI
endpoint core.
- Patch 3/6 changes dw_pcie_ep_ops.get_features() to return a mutable
struct pci_epc_features * and updates all DWC-based glue drivers
accordingly. This is preparatory work for subsequent patches.
- Patch 4/6 advertises dynamic inbound mapping support centrally for all
DWC-based glue drivers.
- Patch 5/6 adds an implementation for the DesignWare PCIe endpoint
controller using Address Match Mode IB iATU. It also advertises
subrange_mapping support from the DWC EP midlayer.
- Patch 6/6 updates a documentation for pci_epc_set_bar().
This series is originally 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:
* v6->v7 changes:
- Added missing check of dynamic_inbound_mapping feature bit in
pci_epc_set_bar() when use_submap is set true.
- Addressed the remaining review comments from Niklas (patch reordering,
splitting, and source code comment/documentation refinements).
* v5->v6 changes:
- Added a new feature bit dynamic_inbound_mapping and set it centrally
in dw_pcie_ep_get_features() for all DWC-based glue drivers.
- Updated documentation for pci_epc_set_bar().
- Dropped a needless and harmful dw_pcie_ep_clear_bar() call on the error
path.
- Fixed "Bar Match Mode" to "BAR Match Mode" in a source code comment.
* v4->v5 changes:
- Added subrange_mapping to struct pci_epc_features and enforced a
strict capability check in pci_epc_set_bar() (reject use_submap when
unsupported).
- Changed DWC-based glue drivers to return a mutable features pointer
and set subrange_mapping centrally at the DWC midlayer.
- Split the series into 3 patches accordingly.
* 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.
v6: https://lore.kernel.org/all/20260113023715.3463724-1-den@valinux.co.jp/
v5: https://lore.kernel.org/all/20260108172403.2629671-1-den@valinux.co.jp/
v4: https://lore.kernel.org/all/20260108044148.2352800-1-den@valinux.co.jp/
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 (6):
PCI: endpoint: Add dynamic_inbound_mapping EPC feature
PCI: endpoint: Add BAR subrange mapping support
PCI: dwc: Allow glue drivers to return mutable EPC features
PCI: dwc: Advertise dynamic inbound mapping support
PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match
Mode iATU
Documentation: PCI: endpoint: Clarify pci_epc_set_bar() usage
Documentation/PCI/endpoint/pci-endpoint.rst | 24 ++
drivers/pci/controller/dwc/pci-dra7xx.c | 4 +-
drivers/pci/controller/dwc/pci-imx6.c | 10 +-
drivers/pci/controller/dwc/pci-keystone.c | 4 +-
.../pci/controller/dwc/pci-layerscape-ep.c | 2 +-
drivers/pci/controller/dwc/pcie-artpec6.c | 4 +-
.../pci/controller/dwc/pcie-designware-ep.c | 240 +++++++++++++++++-
.../pci/controller/dwc/pcie-designware-plat.c | 4 +-
drivers/pci/controller/dwc/pcie-designware.h | 4 +-
drivers/pci/controller/dwc/pcie-dw-rockchip.c | 8 +-
drivers/pci/controller/dwc/pcie-keembay.c | 4 +-
drivers/pci/controller/dwc/pcie-qcom-ep.c | 4 +-
drivers/pci/controller/dwc/pcie-rcar-gen4.c | 4 +-
drivers/pci/controller/dwc/pcie-stm32-ep.c | 4 +-
drivers/pci/controller/dwc/pcie-tegra194.c | 4 +-
drivers/pci/controller/dwc/pcie-uniphier-ep.c | 58 +++--
drivers/pci/endpoint/pci-epc-core.c | 5 +
include/linux/pci-epc.h | 9 +
include/linux/pci-epf.h | 31 +++
19 files changed, 360 insertions(+), 67 deletions(-)
--
2.51.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v7 1/6] PCI: endpoint: Add dynamic_inbound_mapping EPC feature
2026-01-13 16:27 [PATCH v7 0/6] PCI: endpoint: BAR subrange mapping support Koichiro Den
@ 2026-01-13 16:27 ` Koichiro Den
2026-01-13 20:19 ` Frank Li
2026-01-13 16:27 ` [PATCH v7 2/6] PCI: endpoint: Add BAR subrange mapping support Koichiro Den
` (4 subsequent siblings)
5 siblings, 1 reply; 23+ messages in thread
From: Koichiro Den @ 2026-01-13 16:27 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>
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] 23+ messages in thread
* [PATCH v7 2/6] PCI: endpoint: Add BAR subrange mapping support
2026-01-13 16:27 [PATCH v7 0/6] PCI: endpoint: BAR subrange mapping support Koichiro Den
2026-01-13 16:27 ` [PATCH v7 1/6] PCI: endpoint: Add dynamic_inbound_mapping EPC feature Koichiro Den
@ 2026-01-13 16:27 ` Koichiro Den
2026-01-13 20:26 ` Frank Li
2026-01-13 16:27 ` [PATCH v7 3/6] PCI: dwc: Allow glue drivers to return mutable EPC features Koichiro Den
` (3 subsequent siblings)
5 siblings, 1 reply; 23+ messages in thread
From: Koichiro Den @ 2026-01-13 16:27 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 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.
Add a subrange_mapping feature bit to struct pci_epc_features so EPC
drivers can explicitly advertise support. Make pci_epc_set_bar() reject
use_submap requests (-EINVAL) when the EPC does not advertise
subrange_mapping, to avoid silently accepting a configuration that the
controller cannot implement.
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.
Reviewed-by: Niklas Cassel <cassel@kernel.org>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
drivers/pci/endpoint/pci-epc-core.c | 5 +++++
include/linux/pci-epc.h | 4 ++++
include/linux/pci-epf.h | 31 +++++++++++++++++++++++++++++
3 files changed, 40 insertions(+)
diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
index ca7f19cc973a..4870e6cdb458 100644
--- a/drivers/pci/endpoint/pci-epc-core.c
+++ b/drivers/pci/endpoint/pci-epc-core.c
@@ -596,6 +596,11 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
if (!epc_features)
return -EINVAL;
+ if (epf_bar->use_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..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] 23+ messages in thread
* [PATCH v7 3/6] PCI: dwc: Allow glue drivers to return mutable EPC features
2026-01-13 16:27 [PATCH v7 0/6] PCI: endpoint: BAR subrange mapping support Koichiro Den
2026-01-13 16:27 ` [PATCH v7 1/6] PCI: endpoint: Add dynamic_inbound_mapping EPC feature Koichiro Den
2026-01-13 16:27 ` [PATCH v7 2/6] PCI: endpoint: Add BAR subrange mapping support Koichiro Den
@ 2026-01-13 16:27 ` Koichiro Den
2026-01-13 20:38 ` Frank Li
2026-01-13 16:27 ` [PATCH v7 4/6] PCI: dwc: Advertise dynamic inbound mapping support Koichiro Den
` (2 subsequent siblings)
5 siblings, 1 reply; 23+ messages in thread
From: Koichiro Den @ 2026-01-13 16:27 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 midlayer needs to advertise additional capabilities at
the DWC layer (e.g. subrange_mapping) without duplicating the same bit
in every DWC-based glue driver and without copying feature structures.
Change dw_pcie_ep_ops.get_features() to return a mutable
struct pci_epc_features * and update all DWC-based glue drivers
accordingly. The DWC midlayer can then adjust/augment the returned
features while still exposing a const struct pci_epc_features * to the
PCI EPC core.
No functional change on its own.
Suggested-by: Niklas Cassel <cassel@kernel.org>
Reviewed-by: Niklas Cassel <cassel@kernel.org>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
drivers/pci/controller/dwc/pci-dra7xx.c | 4 +-
drivers/pci/controller/dwc/pci-imx6.c | 10 ++--
drivers/pci/controller/dwc/pci-keystone.c | 4 +-
.../pci/controller/dwc/pci-layerscape-ep.c | 2 +-
drivers/pci/controller/dwc/pcie-artpec6.c | 4 +-
.../pci/controller/dwc/pcie-designware-plat.c | 4 +-
drivers/pci/controller/dwc/pcie-designware.h | 2 +-
drivers/pci/controller/dwc/pcie-dw-rockchip.c | 8 +--
drivers/pci/controller/dwc/pcie-keembay.c | 4 +-
drivers/pci/controller/dwc/pcie-qcom-ep.c | 4 +-
drivers/pci/controller/dwc/pcie-rcar-gen4.c | 4 +-
drivers/pci/controller/dwc/pcie-stm32-ep.c | 4 +-
drivers/pci/controller/dwc/pcie-tegra194.c | 4 +-
drivers/pci/controller/dwc/pcie-uniphier-ep.c | 58 ++++++++++---------
14 files changed, 60 insertions(+), 56 deletions(-)
diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c
index 01cfd9aeb0b8..e67f8b7b56cb 100644
--- a/drivers/pci/controller/dwc/pci-dra7xx.c
+++ b/drivers/pci/controller/dwc/pci-dra7xx.c
@@ -423,12 +423,12 @@ static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
return 0;
}
-static const struct pci_epc_features dra7xx_pcie_epc_features = {
+static struct pci_epc_features dra7xx_pcie_epc_features = {
.linkup_notifier = true,
.msi_capable = true,
};
-static const struct pci_epc_features*
+static struct pci_epc_features*
dra7xx_pcie_get_features(struct dw_pcie_ep *ep)
{
return &dra7xx_pcie_epc_features;
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index 4668fc9648bf..fe1de30b3df6 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -131,7 +131,7 @@ struct imx_pcie_drvdata {
const u32 ltssm_mask;
const u32 mode_off[IMX_PCIE_MAX_INSTANCES];
const u32 mode_mask[IMX_PCIE_MAX_INSTANCES];
- const struct pci_epc_features *epc_features;
+ struct pci_epc_features *epc_features;
int (*init_phy)(struct imx_pcie *pcie);
int (*enable_ref_clk)(struct imx_pcie *pcie, bool enable);
int (*core_reset)(struct imx_pcie *pcie, bool assert);
@@ -1386,7 +1386,7 @@ static int imx_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
return 0;
}
-static const struct pci_epc_features imx8m_pcie_epc_features = {
+static struct pci_epc_features imx8m_pcie_epc_features = {
.msi_capable = true,
.bar[BAR_1] = { .type = BAR_RESERVED, },
.bar[BAR_3] = { .type = BAR_RESERVED, },
@@ -1395,7 +1395,7 @@ static const struct pci_epc_features imx8m_pcie_epc_features = {
.align = SZ_64K,
};
-static const struct pci_epc_features imx8q_pcie_epc_features = {
+static struct pci_epc_features imx8q_pcie_epc_features = {
.msi_capable = true,
.bar[BAR_1] = { .type = BAR_RESERVED, },
.bar[BAR_3] = { .type = BAR_RESERVED, },
@@ -1415,13 +1415,13 @@ static const struct pci_epc_features imx8q_pcie_epc_features = {
* BAR4 | Enable | 32-bit | 1 MB | Programmable Size
* BAR5 | Enable | 32-bit | 64 KB | Programmable Size
*/
-static const struct pci_epc_features imx95_pcie_epc_features = {
+static struct pci_epc_features imx95_pcie_epc_features = {
.msi_capable = true,
.bar[BAR_1] = { .type = BAR_FIXED, .fixed_size = SZ_64K, },
.align = SZ_4K,
};
-static const struct pci_epc_features*
+static struct pci_epc_features*
imx_pcie_ep_get_features(struct dw_pcie_ep *ep)
{
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c
index f86d9111f863..4292007a9b3a 100644
--- a/drivers/pci/controller/dwc/pci-keystone.c
+++ b/drivers/pci/controller/dwc/pci-keystone.c
@@ -929,7 +929,7 @@ static int ks_pcie_am654_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
return 0;
}
-static const struct pci_epc_features ks_pcie_am654_epc_features = {
+static struct pci_epc_features ks_pcie_am654_epc_features = {
.msi_capable = true,
.msix_capable = true,
.bar[BAR_0] = { .type = BAR_RESERVED, },
@@ -941,7 +941,7 @@ static const struct pci_epc_features ks_pcie_am654_epc_features = {
.align = SZ_64K,
};
-static const struct pci_epc_features*
+static struct pci_epc_features*
ks_pcie_am654_get_features(struct dw_pcie_ep *ep)
{
return &ks_pcie_am654_epc_features;
diff --git a/drivers/pci/controller/dwc/pci-layerscape-ep.c b/drivers/pci/controller/dwc/pci-layerscape-ep.c
index a4a800699f89..8d48413050ef 100644
--- a/drivers/pci/controller/dwc/pci-layerscape-ep.c
+++ b/drivers/pci/controller/dwc/pci-layerscape-ep.c
@@ -138,7 +138,7 @@ static int ls_pcie_ep_interrupt_init(struct ls_pcie_ep *pcie,
return 0;
}
-static const struct pci_epc_features*
+static struct pci_epc_features*
ls_pcie_ep_get_features(struct dw_pcie_ep *ep)
{
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c
index f4a136ee2daf..84111d8257f2 100644
--- a/drivers/pci/controller/dwc/pcie-artpec6.c
+++ b/drivers/pci/controller/dwc/pcie-artpec6.c
@@ -369,11 +369,11 @@ static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
return 0;
}
-static const struct pci_epc_features artpec6_pcie_epc_features = {
+static struct pci_epc_features artpec6_pcie_epc_features = {
.msi_capable = true,
};
-static const struct pci_epc_features *
+static struct pci_epc_features *
artpec6_pcie_get_features(struct dw_pcie_ep *ep)
{
return &artpec6_pcie_epc_features;
diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c
index 12f41886c65d..60ada0eb838e 100644
--- a/drivers/pci/controller/dwc/pcie-designware-plat.c
+++ b/drivers/pci/controller/dwc/pcie-designware-plat.c
@@ -60,12 +60,12 @@ static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
return 0;
}
-static const struct pci_epc_features dw_plat_pcie_epc_features = {
+static struct pci_epc_features dw_plat_pcie_epc_features = {
.msi_capable = true,
.msix_capable = true,
};
-static const struct pci_epc_features*
+static struct pci_epc_features*
dw_plat_pcie_get_features(struct dw_pcie_ep *ep)
{
return &dw_plat_pcie_epc_features;
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index f87c67a7a482..4dda9a38d46b 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -449,7 +449,7 @@ struct dw_pcie_ep_ops {
void (*init)(struct dw_pcie_ep *ep);
int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no,
unsigned int type, u16 interrupt_num);
- const struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep);
+ struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep);
/*
* Provide a method to implement the different func config space
* access for different platform, if different func have different
diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
index 352f513ebf03..1f3c91368dc3 100644
--- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c
+++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
@@ -100,7 +100,7 @@ struct rockchip_pcie {
struct rockchip_pcie_of_data {
enum dw_pcie_device_mode mode;
- const struct pci_epc_features *epc_features;
+ struct pci_epc_features *epc_features;
};
static int rockchip_pcie_readl_apb(struct rockchip_pcie *rockchip, u32 reg)
@@ -383,7 +383,7 @@ static int rockchip_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
return 0;
}
-static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = {
+static struct pci_epc_features rockchip_pcie_epc_features_rk3568 = {
.linkup_notifier = true,
.msi_capable = true,
.msix_capable = true,
@@ -403,7 +403,7 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = {
* default.) If the host could write to BAR4, the iATU settings (for all other
* BARs) would be overwritten, resulting in (all other BARs) no longer working.
*/
-static const struct pci_epc_features rockchip_pcie_epc_features_rk3588 = {
+static struct pci_epc_features rockchip_pcie_epc_features_rk3588 = {
.linkup_notifier = true,
.msi_capable = true,
.msix_capable = true,
@@ -416,7 +416,7 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3588 = {
.bar[BAR_5] = { .type = BAR_RESIZABLE, },
};
-static const struct pci_epc_features *
+static struct pci_epc_features *
rockchip_pcie_get_features(struct dw_pcie_ep *ep)
{
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
diff --git a/drivers/pci/controller/dwc/pcie-keembay.c b/drivers/pci/controller/dwc/pcie-keembay.c
index 60e74ac782af..e6de5289329f 100644
--- a/drivers/pci/controller/dwc/pcie-keembay.c
+++ b/drivers/pci/controller/dwc/pcie-keembay.c
@@ -308,7 +308,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 = {
+static struct pci_epc_features keembay_pcie_epc_features = {
.msi_capable = true,
.msix_capable = true,
.bar[BAR_0] = { .only_64bit = true, },
@@ -320,7 +320,7 @@ static const struct pci_epc_features keembay_pcie_epc_features = {
.align = SZ_16K,
};
-static const struct pci_epc_features *
+static struct pci_epc_features *
keembay_pcie_get_features(struct dw_pcie_ep *ep)
{
return &keembay_pcie_epc_features;
diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c
index f1bc0ac81a92..6ad033301909 100644
--- a/drivers/pci/controller/dwc/pcie-qcom-ep.c
+++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c
@@ -819,7 +819,7 @@ static void qcom_pcie_ep_init_debugfs(struct qcom_pcie_ep *pcie_ep)
qcom_pcie_ep_link_transition_count);
}
-static const struct pci_epc_features qcom_pcie_epc_features = {
+static struct pci_epc_features qcom_pcie_epc_features = {
.linkup_notifier = true,
.msi_capable = true,
.align = SZ_4K,
@@ -829,7 +829,7 @@ static const struct pci_epc_features qcom_pcie_epc_features = {
.bar[BAR_3] = { .type = BAR_RESERVED, },
};
-static const struct pci_epc_features *
+static struct pci_epc_features *
qcom_pcie_epc_get_features(struct dw_pcie_ep *pci_ep)
{
return &qcom_pcie_epc_features;
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index 80778917d2dd..ff0c4af90eff 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -419,7 +419,7 @@ static int rcar_gen4_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
return 0;
}
-static const struct pci_epc_features rcar_gen4_pcie_epc_features = {
+static struct pci_epc_features rcar_gen4_pcie_epc_features = {
.msi_capable = true,
.bar[BAR_1] = { .type = BAR_RESERVED, },
.bar[BAR_3] = { .type = BAR_RESERVED, },
@@ -428,7 +428,7 @@ static const struct pci_epc_features rcar_gen4_pcie_epc_features = {
.align = SZ_1M,
};
-static const struct pci_epc_features*
+static struct pci_epc_features*
rcar_gen4_pcie_ep_get_features(struct dw_pcie_ep *ep)
{
return &rcar_gen4_pcie_epc_features;
diff --git a/drivers/pci/controller/dwc/pcie-stm32-ep.c b/drivers/pci/controller/dwc/pcie-stm32-ep.c
index 2cecf32d2b0f..8a892def54f5 100644
--- a/drivers/pci/controller/dwc/pcie-stm32-ep.c
+++ b/drivers/pci/controller/dwc/pcie-stm32-ep.c
@@ -69,12 +69,12 @@ static int stm32_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
}
}
-static const struct pci_epc_features stm32_pcie_epc_features = {
+static struct pci_epc_features stm32_pcie_epc_features = {
.msi_capable = true,
.align = SZ_64K,
};
-static const struct pci_epc_features*
+static struct pci_epc_features*
stm32_pcie_get_features(struct dw_pcie_ep *ep)
{
return &stm32_pcie_epc_features;
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index 0ddeef70726d..06f45a17e52c 100644
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
@@ -1987,7 +1987,7 @@ static int tegra_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
return 0;
}
-static const struct pci_epc_features tegra_pcie_epc_features = {
+static struct pci_epc_features tegra_pcie_epc_features = {
.linkup_notifier = true,
.msi_capable = true,
.bar[BAR_0] = { .type = BAR_FIXED, .fixed_size = SZ_1M,
@@ -2000,7 +2000,7 @@ static const struct pci_epc_features tegra_pcie_epc_features = {
.align = SZ_64K,
};
-static const struct pci_epc_features*
+static struct pci_epc_features*
tegra_pcie_ep_get_features(struct dw_pcie_ep *ep)
{
return &tegra_pcie_epc_features;
diff --git a/drivers/pci/controller/dwc/pcie-uniphier-ep.c b/drivers/pci/controller/dwc/pcie-uniphier-ep.c
index d6e73811216e..ddb5ff70340c 100644
--- a/drivers/pci/controller/dwc/pcie-uniphier-ep.c
+++ b/drivers/pci/controller/dwc/pcie-uniphier-ep.c
@@ -82,7 +82,7 @@ struct uniphier_pcie_ep_soc_data {
bool has_gio;
void (*init)(struct uniphier_pcie_ep_priv *priv);
int (*wait)(struct uniphier_pcie_ep_priv *priv);
- const struct pci_epc_features features;
+ struct pci_epc_features *features;
};
#define to_uniphier_pcie(x) dev_get_drvdata((x)->dev)
@@ -273,13 +273,13 @@ static int uniphier_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
return 0;
}
-static const struct pci_epc_features*
+static struct pci_epc_features*
uniphier_pcie_get_features(struct dw_pcie_ep *ep)
{
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
struct uniphier_pcie_ep_priv *priv = to_uniphier_pcie(pci);
- return &priv->data->features;
+ return priv->data->features;
}
static const struct dw_pcie_ep_ops uniphier_pcie_ep_ops = {
@@ -415,40 +415,44 @@ static int uniphier_pcie_ep_probe(struct platform_device *pdev)
return 0;
}
+static struct pci_epc_features uniphier_pro5_features = {
+ .linkup_notifier = false,
+ .msi_capable = true,
+ .msix_capable = false,
+ .align = 1 << 16,
+ .bar[BAR_0] = { .only_64bit = true, },
+ .bar[BAR_1] = { .type = BAR_RESERVED, },
+ .bar[BAR_2] = { .only_64bit = true, },
+ .bar[BAR_3] = { .type = BAR_RESERVED, },
+ .bar[BAR_4] = { .type = BAR_RESERVED, },
+ .bar[BAR_5] = { .type = BAR_RESERVED, },
+};
+
+static struct pci_epc_features uniphier_nx1_features = {
+ .linkup_notifier = false,
+ .msi_capable = true,
+ .msix_capable = false,
+ .align = 1 << 12,
+ .bar[BAR_0] = { .only_64bit = true, },
+ .bar[BAR_1] = { .type = BAR_RESERVED, },
+ .bar[BAR_2] = { .only_64bit = true, },
+ .bar[BAR_3] = { .type = BAR_RESERVED, },
+ .bar[BAR_4] = { .only_64bit = true, },
+ .bar[BAR_5] = { .type = BAR_RESERVED, },
+};
+
static const struct uniphier_pcie_ep_soc_data uniphier_pro5_data = {
.has_gio = true,
.init = uniphier_pcie_pro5_init_ep,
.wait = NULL,
- .features = {
- .linkup_notifier = false,
- .msi_capable = true,
- .msix_capable = false,
- .align = 1 << 16,
- .bar[BAR_0] = { .only_64bit = true, },
- .bar[BAR_1] = { .type = BAR_RESERVED, },
- .bar[BAR_2] = { .only_64bit = true, },
- .bar[BAR_3] = { .type = BAR_RESERVED, },
- .bar[BAR_4] = { .type = BAR_RESERVED, },
- .bar[BAR_5] = { .type = BAR_RESERVED, },
- },
+ .features = &uniphier_pro5_features,
};
static const struct uniphier_pcie_ep_soc_data uniphier_nx1_data = {
.has_gio = false,
.init = uniphier_pcie_nx1_init_ep,
.wait = uniphier_pcie_nx1_wait_ep,
- .features = {
- .linkup_notifier = false,
- .msi_capable = true,
- .msix_capable = false,
- .align = 1 << 12,
- .bar[BAR_0] = { .only_64bit = true, },
- .bar[BAR_1] = { .type = BAR_RESERVED, },
- .bar[BAR_2] = { .only_64bit = true, },
- .bar[BAR_3] = { .type = BAR_RESERVED, },
- .bar[BAR_4] = { .only_64bit = true, },
- .bar[BAR_5] = { .type = BAR_RESERVED, },
- },
+ .features = &uniphier_nx1_features,
};
static const struct of_device_id uniphier_pcie_ep_match[] = {
--
2.51.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v7 4/6] PCI: dwc: Advertise dynamic inbound mapping support
2026-01-13 16:27 [PATCH v7 0/6] PCI: endpoint: BAR subrange mapping support Koichiro Den
` (2 preceding siblings ...)
2026-01-13 16:27 ` [PATCH v7 3/6] PCI: dwc: Allow glue drivers to return mutable EPC features Koichiro Den
@ 2026-01-13 16:27 ` Koichiro Den
2026-01-13 18:44 ` Niklas Cassel
2026-01-13 16:27 ` [PATCH v7 5/6] PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU Koichiro Den
2026-01-13 16:27 ` [PATCH v7 6/6] Documentation: PCI: endpoint: Clarify pci_epc_set_bar() usage Koichiro Den
5 siblings, 1 reply; 23+ messages in thread
From: Koichiro Den @ 2026-01-13 16:27 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 the EPC layer exposes this capability via the
dynamic_inbound_mapping feature bit, set the bit centrally in the
DesignWare EP layer so that all DWC-based EP glue drivers report it
consistently, without duplicating the same flag in each individual
driver.
No functional change intended. This only advertises existing behavior to
EPF drivers.
Suggested-by: Niklas Cassel <cassel@kernel.org>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
drivers/pci/controller/dwc/pcie-designware-ep.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 1195d401df19..0e5a8d200b00 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -626,11 +626,19 @@ 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);
+ struct pci_epc_features *features;
if (!ep->ops->get_features)
return NULL;
- return ep->ops->get_features(ep);
+ features = ep->ops->get_features(ep);
+ if (!features)
+ return NULL;
+
+ /* All DWC-based glue drivers support dynamic inbound mapping */
+ features->dynamic_inbound_mapping = true;
+
+ return features;
}
static const struct pci_epc_ops epc_ops = {
--
2.51.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v7 5/6] PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU
2026-01-13 16:27 [PATCH v7 0/6] PCI: endpoint: BAR subrange mapping support Koichiro Den
` (3 preceding siblings ...)
2026-01-13 16:27 ` [PATCH v7 4/6] PCI: dwc: Advertise dynamic inbound mapping support Koichiro Den
@ 2026-01-13 16:27 ` Koichiro Den
2026-01-13 20:53 ` Frank Li
2026-01-14 3:54 ` Koichiro Den
2026-01-13 16:27 ` [PATCH v7 6/6] Documentation: PCI: endpoint: Clarify pci_epc_set_bar() usage Koichiro Den
5 siblings, 2 replies; 23+ messages in thread
From: Koichiro Den @ 2026-01-13 16:27 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.
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.
Advertise this capability by setting subrange_mapping in the EPC
features returned from dw_pcie_ep_get_features().
Reviewed-by: Niklas Cassel <cassel@kernel.org>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
.../pci/controller/dwc/pcie-designware-ep.c | 230 +++++++++++++++++-
drivers/pci/controller/dwc/pcie-designware.h | 2 +
2 files changed, 222 insertions(+), 10 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 0e5a8d200b00..b2ea2c2c986f 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,8 +609,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->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)
return ret;
@@ -638,6 +843,9 @@ dw_pcie_ep_get_features(struct pci_epc *epc, u8 func_no, u8 vfunc_no)
/* All DWC-based glue drivers support dynamic inbound mapping */
features->dynamic_inbound_mapping = true;
+ /* All DWC-based glue drivers support inbound subrange mapping */
+ features->subrange_mapping = true;
+
return features;
}
@@ -1128,6 +1336,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 4dda9a38d46b..969b1f32dddf 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] 23+ messages in thread
* [PATCH v7 6/6] Documentation: PCI: endpoint: Clarify pci_epc_set_bar() usage
2026-01-13 16:27 [PATCH v7 0/6] PCI: endpoint: BAR subrange mapping support Koichiro Den
` (4 preceding siblings ...)
2026-01-13 16:27 ` [PATCH v7 5/6] PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU Koichiro Den
@ 2026-01-13 16:27 ` Koichiro Den
2026-01-13 20:56 ` Frank Li
5 siblings, 1 reply; 23+ messages in thread
From: Koichiro Den @ 2026-01-13 16:27 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 relies on.
Add document text for that.
Also document the expected call flow for BAR subrange mapping
(pci_epf_bar.use_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: Niklas Cassel <cassel@kernel.org>
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..09b892de9280 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.use_submap is set, 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
+ use_submap cleared to configure the BAR size, then after the PCIe link
+ is up and the host enumerates the endpoint and programs the BAR base
+ address, again with use_submap set.
+
+ 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] 23+ messages in thread
* Re: [PATCH v7 4/6] PCI: dwc: Advertise dynamic inbound mapping support
2026-01-13 16:27 ` [PATCH v7 4/6] PCI: dwc: Advertise dynamic inbound mapping support Koichiro Den
@ 2026-01-13 18:44 ` Niklas Cassel
0 siblings, 0 replies; 23+ messages in thread
From: Niklas Cassel @ 2026-01-13 18:44 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 Wed, Jan 14, 2026 at 01:27:17AM +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 the EPC layer exposes this capability via the
> dynamic_inbound_mapping feature bit, set the bit centrally in the
> DesignWare EP layer so that all DWC-based EP glue drivers report it
> consistently, without duplicating the same flag in each individual
> driver.
>
> No functional change intended. This only advertises existing behavior to
> EPF drivers.
>
> Suggested-by: Niklas Cassel <cassel@kernel.org>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
Reviewed-by: Niklas Cassel <cassel@kernel.org>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v7 1/6] PCI: endpoint: Add dynamic_inbound_mapping EPC feature
2026-01-13 16:27 ` [PATCH v7 1/6] PCI: endpoint: Add dynamic_inbound_mapping EPC feature Koichiro Den
@ 2026-01-13 20:19 ` Frank Li
0 siblings, 0 replies; 23+ messages in thread
From: Frank Li @ 2026-01-13 20:19 UTC (permalink / raw)
To: Koichiro Den
Cc: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas, cassel,
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,
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 Wed, Jan 14, 2026 at 01:27:14AM +0900, Koichiro Den wrote:
> 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>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> 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 [flat|nested] 23+ messages in thread
* Re: [PATCH v7 2/6] PCI: endpoint: Add BAR subrange mapping support
2026-01-13 16:27 ` [PATCH v7 2/6] PCI: endpoint: Add BAR subrange mapping support Koichiro Den
@ 2026-01-13 20:26 ` Frank Li
2026-01-14 3:22 ` Koichiro Den
0 siblings, 1 reply; 23+ messages in thread
From: Frank Li @ 2026-01-13 20:26 UTC (permalink / raw)
To: Koichiro Den
Cc: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas, cassel,
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,
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 Wed, Jan 14, 2026 at 01:27:15AM +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.
>
> Add a subrange_mapping feature bit to struct pci_epc_features so EPC
> drivers can explicitly advertise support. Make pci_epc_set_bar() reject
> use_submap requests (-EINVAL) when the EPC does not advertise
> subrange_mapping, to avoid silently accepting a configuration that the
> controller cannot implement.
>
> 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.
>
> Reviewed-by: Niklas Cassel <cassel@kernel.org>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
...
>
> #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;
> +};
I suppose offset is sum of previous all submap's size? If yes, needn'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;
can we use num_submap != 0 as use_submap?
Frank
> + struct pci_epf_bar_submap *submap;
> };
>
> /**
> --
> 2.51.0
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v7 3/6] PCI: dwc: Allow glue drivers to return mutable EPC features
2026-01-13 16:27 ` [PATCH v7 3/6] PCI: dwc: Allow glue drivers to return mutable EPC features Koichiro Den
@ 2026-01-13 20:38 ` Frank Li
2026-01-14 3:29 ` Koichiro Den
0 siblings, 1 reply; 23+ messages in thread
From: Frank Li @ 2026-01-13 20:38 UTC (permalink / raw)
To: Koichiro Den
Cc: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas, cassel,
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,
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 Wed, Jan 14, 2026 at 01:27:16AM +0900, Koichiro Den wrote:
> The DesignWare EP midlayer needs to advertise additional capabilities at
> the DWC layer (e.g. subrange_mapping) without duplicating the same bit
> in every DWC-based glue driver and without copying feature structures.
>
> Change dw_pcie_ep_ops.get_features() to return a mutable
> struct pci_epc_features * and update all DWC-based glue drivers
> accordingly. The DWC midlayer can then adjust/augment the returned
> features while still exposing a const struct pci_epc_features * to the
> PCI EPC core.
>
> No functional change on its own.
>
> Suggested-by: Niklas Cassel <cassel@kernel.org>
> Reviewed-by: Niklas Cassel <cassel@kernel.org>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
> drivers/pci/controller/dwc/pci-dra7xx.c | 4 +-
> drivers/pci/controller/dwc/pci-imx6.c | 10 ++--
> drivers/pci/controller/dwc/pci-keystone.c | 4 +-
> .../pci/controller/dwc/pci-layerscape-ep.c | 2 +-
> drivers/pci/controller/dwc/pcie-artpec6.c | 4 +-
> .../pci/controller/dwc/pcie-designware-plat.c | 4 +-
> drivers/pci/controller/dwc/pcie-designware.h | 2 +-
> drivers/pci/controller/dwc/pcie-dw-rockchip.c | 8 +--
> drivers/pci/controller/dwc/pcie-keembay.c | 4 +-
> drivers/pci/controller/dwc/pcie-qcom-ep.c | 4 +-
> drivers/pci/controller/dwc/pcie-rcar-gen4.c | 4 +-
> drivers/pci/controller/dwc/pcie-stm32-ep.c | 4 +-
> drivers/pci/controller/dwc/pcie-tegra194.c | 4 +-
> drivers/pci/controller/dwc/pcie-uniphier-ep.c | 58 ++++++++++---------
> 14 files changed, 60 insertions(+), 56 deletions(-)
>
> diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c
> index 01cfd9aeb0b8..e67f8b7b56cb 100644
> --- a/drivers/pci/controller/dwc/pci-dra7xx.c
> +++ b/drivers/pci/controller/dwc/pci-dra7xx.c
> @@ -423,12 +423,12 @@ static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> return 0;
> }
>
> -static const struct pci_epc_features dra7xx_pcie_epc_features = {
> +static struct pci_epc_features dra7xx_pcie_epc_features = {
> .linkup_notifier = true,
> .msi_capable = true,
> };
>
> -static const struct pci_epc_features*
> +static struct pci_epc_features*
> dra7xx_pcie_get_features(struct dw_pcie_ep *ep)
> {
> return &dra7xx_pcie_epc_features;
> diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
> index 4668fc9648bf..fe1de30b3df6 100644
> --- a/drivers/pci/controller/dwc/pci-imx6.c
> +++ b/drivers/pci/controller/dwc/pci-imx6.c
> @@ -131,7 +131,7 @@ struct imx_pcie_drvdata {
> const u32 ltssm_mask;
> const u32 mode_off[IMX_PCIE_MAX_INSTANCES];
> const u32 mode_mask[IMX_PCIE_MAX_INSTANCES];
> - const struct pci_epc_features *epc_features;
> + struct pci_epc_features *epc_features;
> int (*init_phy)(struct imx_pcie *pcie);
> int (*enable_ref_clk)(struct imx_pcie *pcie, bool enable);
> int (*core_reset)(struct imx_pcie *pcie, bool assert);
> @@ -1386,7 +1386,7 @@ static int imx_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> return 0;
> }
>
> -static const struct pci_epc_features imx8m_pcie_epc_features = {
> +static struct pci_epc_features imx8m_pcie_epc_features = {
> .msi_capable = true,
> .bar[BAR_1] = { .type = BAR_RESERVED, },
> .bar[BAR_3] = { .type = BAR_RESERVED, },
> @@ -1395,7 +1395,7 @@ static const struct pci_epc_features imx8m_pcie_epc_features = {
> .align = SZ_64K,
> };
>
> -static const struct pci_epc_features imx8q_pcie_epc_features = {
> +static struct pci_epc_features imx8q_pcie_epc_features = {
Is it more simple if
#define DWC_EPC_DEFAULT .dynamtic_map = true
Add
DWC_EPC_DEFAULT, into every epc_features.
Frank
> .msi_capable = true,
> .bar[BAR_1] = { .type = BAR_RESERVED, },
> .bar[BAR_3] = { .type = BAR_RESERVED, },
> @@ -1415,13 +1415,13 @@ static const struct pci_epc_features imx8q_pcie_epc_features = {
> * BAR4 | Enable | 32-bit | 1 MB | Programmable Size
> * BAR5 | Enable | 32-bit | 64 KB | Programmable Size
> */
> -static const struct pci_epc_features imx95_pcie_epc_features = {
> +static struct pci_epc_features imx95_pcie_epc_features = {
> .msi_capable = true,
> .bar[BAR_1] = { .type = BAR_FIXED, .fixed_size = SZ_64K, },
> .align = SZ_4K,
> };
>
> -static const struct pci_epc_features*
> +static struct pci_epc_features*
> imx_pcie_ep_get_features(struct dw_pcie_ep *ep)
> {
> struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c
> index f86d9111f863..4292007a9b3a 100644
> --- a/drivers/pci/controller/dwc/pci-keystone.c
> +++ b/drivers/pci/controller/dwc/pci-keystone.c
> @@ -929,7 +929,7 @@ static int ks_pcie_am654_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> return 0;
> }
>
> -static const struct pci_epc_features ks_pcie_am654_epc_features = {
> +static struct pci_epc_features ks_pcie_am654_epc_features = {
> .msi_capable = true,
> .msix_capable = true,
> .bar[BAR_0] = { .type = BAR_RESERVED, },
> @@ -941,7 +941,7 @@ static const struct pci_epc_features ks_pcie_am654_epc_features = {
> .align = SZ_64K,
> };
>
> -static const struct pci_epc_features*
> +static struct pci_epc_features*
> ks_pcie_am654_get_features(struct dw_pcie_ep *ep)
> {
> return &ks_pcie_am654_epc_features;
> diff --git a/drivers/pci/controller/dwc/pci-layerscape-ep.c b/drivers/pci/controller/dwc/pci-layerscape-ep.c
> index a4a800699f89..8d48413050ef 100644
> --- a/drivers/pci/controller/dwc/pci-layerscape-ep.c
> +++ b/drivers/pci/controller/dwc/pci-layerscape-ep.c
> @@ -138,7 +138,7 @@ static int ls_pcie_ep_interrupt_init(struct ls_pcie_ep *pcie,
> return 0;
> }
>
> -static const struct pci_epc_features*
> +static struct pci_epc_features*
> ls_pcie_ep_get_features(struct dw_pcie_ep *ep)
> {
> struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c
> index f4a136ee2daf..84111d8257f2 100644
> --- a/drivers/pci/controller/dwc/pcie-artpec6.c
> +++ b/drivers/pci/controller/dwc/pcie-artpec6.c
> @@ -369,11 +369,11 @@ static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> return 0;
> }
>
> -static const struct pci_epc_features artpec6_pcie_epc_features = {
> +static struct pci_epc_features artpec6_pcie_epc_features = {
> .msi_capable = true,
> };
>
> -static const struct pci_epc_features *
> +static struct pci_epc_features *
> artpec6_pcie_get_features(struct dw_pcie_ep *ep)
> {
> return &artpec6_pcie_epc_features;
> diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c
> index 12f41886c65d..60ada0eb838e 100644
> --- a/drivers/pci/controller/dwc/pcie-designware-plat.c
> +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c
> @@ -60,12 +60,12 @@ static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> return 0;
> }
>
> -static const struct pci_epc_features dw_plat_pcie_epc_features = {
> +static struct pci_epc_features dw_plat_pcie_epc_features = {
> .msi_capable = true,
> .msix_capable = true,
> };
>
> -static const struct pci_epc_features*
> +static struct pci_epc_features*
> dw_plat_pcie_get_features(struct dw_pcie_ep *ep)
> {
> return &dw_plat_pcie_epc_features;
> diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
> index f87c67a7a482..4dda9a38d46b 100644
> --- a/drivers/pci/controller/dwc/pcie-designware.h
> +++ b/drivers/pci/controller/dwc/pcie-designware.h
> @@ -449,7 +449,7 @@ struct dw_pcie_ep_ops {
> void (*init)(struct dw_pcie_ep *ep);
> int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no,
> unsigned int type, u16 interrupt_num);
> - const struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep);
> + struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep);
> /*
> * Provide a method to implement the different func config space
> * access for different platform, if different func have different
> diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
> index 352f513ebf03..1f3c91368dc3 100644
> --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c
> +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
> @@ -100,7 +100,7 @@ struct rockchip_pcie {
>
> struct rockchip_pcie_of_data {
> enum dw_pcie_device_mode mode;
> - const struct pci_epc_features *epc_features;
> + struct pci_epc_features *epc_features;
> };
>
> static int rockchip_pcie_readl_apb(struct rockchip_pcie *rockchip, u32 reg)
> @@ -383,7 +383,7 @@ static int rockchip_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> return 0;
> }
>
> -static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = {
> +static struct pci_epc_features rockchip_pcie_epc_features_rk3568 = {
> .linkup_notifier = true,
> .msi_capable = true,
> .msix_capable = true,
> @@ -403,7 +403,7 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = {
> * default.) If the host could write to BAR4, the iATU settings (for all other
> * BARs) would be overwritten, resulting in (all other BARs) no longer working.
> */
> -static const struct pci_epc_features rockchip_pcie_epc_features_rk3588 = {
> +static struct pci_epc_features rockchip_pcie_epc_features_rk3588 = {
> .linkup_notifier = true,
> .msi_capable = true,
> .msix_capable = true,
> @@ -416,7 +416,7 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3588 = {
> .bar[BAR_5] = { .type = BAR_RESIZABLE, },
> };
>
> -static const struct pci_epc_features *
> +static struct pci_epc_features *
> rockchip_pcie_get_features(struct dw_pcie_ep *ep)
> {
> struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> diff --git a/drivers/pci/controller/dwc/pcie-keembay.c b/drivers/pci/controller/dwc/pcie-keembay.c
> index 60e74ac782af..e6de5289329f 100644
> --- a/drivers/pci/controller/dwc/pcie-keembay.c
> +++ b/drivers/pci/controller/dwc/pcie-keembay.c
> @@ -308,7 +308,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 = {
> +static struct pci_epc_features keembay_pcie_epc_features = {
> .msi_capable = true,
> .msix_capable = true,
> .bar[BAR_0] = { .only_64bit = true, },
> @@ -320,7 +320,7 @@ static const struct pci_epc_features keembay_pcie_epc_features = {
> .align = SZ_16K,
> };
>
> -static const struct pci_epc_features *
> +static struct pci_epc_features *
> keembay_pcie_get_features(struct dw_pcie_ep *ep)
> {
> return &keembay_pcie_epc_features;
> diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c
> index f1bc0ac81a92..6ad033301909 100644
> --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c
> +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c
> @@ -819,7 +819,7 @@ static void qcom_pcie_ep_init_debugfs(struct qcom_pcie_ep *pcie_ep)
> qcom_pcie_ep_link_transition_count);
> }
>
> -static const struct pci_epc_features qcom_pcie_epc_features = {
> +static struct pci_epc_features qcom_pcie_epc_features = {
> .linkup_notifier = true,
> .msi_capable = true,
> .align = SZ_4K,
> @@ -829,7 +829,7 @@ static const struct pci_epc_features qcom_pcie_epc_features = {
> .bar[BAR_3] = { .type = BAR_RESERVED, },
> };
>
> -static const struct pci_epc_features *
> +static struct pci_epc_features *
> qcom_pcie_epc_get_features(struct dw_pcie_ep *pci_ep)
> {
> return &qcom_pcie_epc_features;
> diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> index 80778917d2dd..ff0c4af90eff 100644
> --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> @@ -419,7 +419,7 @@ static int rcar_gen4_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> return 0;
> }
>
> -static const struct pci_epc_features rcar_gen4_pcie_epc_features = {
> +static struct pci_epc_features rcar_gen4_pcie_epc_features = {
> .msi_capable = true,
> .bar[BAR_1] = { .type = BAR_RESERVED, },
> .bar[BAR_3] = { .type = BAR_RESERVED, },
> @@ -428,7 +428,7 @@ static const struct pci_epc_features rcar_gen4_pcie_epc_features = {
> .align = SZ_1M,
> };
>
> -static const struct pci_epc_features*
> +static struct pci_epc_features*
> rcar_gen4_pcie_ep_get_features(struct dw_pcie_ep *ep)
> {
> return &rcar_gen4_pcie_epc_features;
> diff --git a/drivers/pci/controller/dwc/pcie-stm32-ep.c b/drivers/pci/controller/dwc/pcie-stm32-ep.c
> index 2cecf32d2b0f..8a892def54f5 100644
> --- a/drivers/pci/controller/dwc/pcie-stm32-ep.c
> +++ b/drivers/pci/controller/dwc/pcie-stm32-ep.c
> @@ -69,12 +69,12 @@ static int stm32_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> }
> }
>
> -static const struct pci_epc_features stm32_pcie_epc_features = {
> +static struct pci_epc_features stm32_pcie_epc_features = {
> .msi_capable = true,
> .align = SZ_64K,
> };
>
> -static const struct pci_epc_features*
> +static struct pci_epc_features*
> stm32_pcie_get_features(struct dw_pcie_ep *ep)
> {
> return &stm32_pcie_epc_features;
> diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
> index 0ddeef70726d..06f45a17e52c 100644
> --- a/drivers/pci/controller/dwc/pcie-tegra194.c
> +++ b/drivers/pci/controller/dwc/pcie-tegra194.c
> @@ -1987,7 +1987,7 @@ static int tegra_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> return 0;
> }
>
> -static const struct pci_epc_features tegra_pcie_epc_features = {
> +static struct pci_epc_features tegra_pcie_epc_features = {
> .linkup_notifier = true,
> .msi_capable = true,
> .bar[BAR_0] = { .type = BAR_FIXED, .fixed_size = SZ_1M,
> @@ -2000,7 +2000,7 @@ static const struct pci_epc_features tegra_pcie_epc_features = {
> .align = SZ_64K,
> };
>
> -static const struct pci_epc_features*
> +static struct pci_epc_features*
> tegra_pcie_ep_get_features(struct dw_pcie_ep *ep)
> {
> return &tegra_pcie_epc_features;
> diff --git a/drivers/pci/controller/dwc/pcie-uniphier-ep.c b/drivers/pci/controller/dwc/pcie-uniphier-ep.c
> index d6e73811216e..ddb5ff70340c 100644
> --- a/drivers/pci/controller/dwc/pcie-uniphier-ep.c
> +++ b/drivers/pci/controller/dwc/pcie-uniphier-ep.c
> @@ -82,7 +82,7 @@ struct uniphier_pcie_ep_soc_data {
> bool has_gio;
> void (*init)(struct uniphier_pcie_ep_priv *priv);
> int (*wait)(struct uniphier_pcie_ep_priv *priv);
> - const struct pci_epc_features features;
> + struct pci_epc_features *features;
> };
>
> #define to_uniphier_pcie(x) dev_get_drvdata((x)->dev)
> @@ -273,13 +273,13 @@ static int uniphier_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> return 0;
> }
>
> -static const struct pci_epc_features*
> +static struct pci_epc_features*
> uniphier_pcie_get_features(struct dw_pcie_ep *ep)
> {
> struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> struct uniphier_pcie_ep_priv *priv = to_uniphier_pcie(pci);
>
> - return &priv->data->features;
> + return priv->data->features;
> }
>
> static const struct dw_pcie_ep_ops uniphier_pcie_ep_ops = {
> @@ -415,40 +415,44 @@ static int uniphier_pcie_ep_probe(struct platform_device *pdev)
> return 0;
> }
>
> +static struct pci_epc_features uniphier_pro5_features = {
> + .linkup_notifier = false,
> + .msi_capable = true,
> + .msix_capable = false,
> + .align = 1 << 16,
> + .bar[BAR_0] = { .only_64bit = true, },
> + .bar[BAR_1] = { .type = BAR_RESERVED, },
> + .bar[BAR_2] = { .only_64bit = true, },
> + .bar[BAR_3] = { .type = BAR_RESERVED, },
> + .bar[BAR_4] = { .type = BAR_RESERVED, },
> + .bar[BAR_5] = { .type = BAR_RESERVED, },
> +};
> +
> +static struct pci_epc_features uniphier_nx1_features = {
> + .linkup_notifier = false,
> + .msi_capable = true,
> + .msix_capable = false,
> + .align = 1 << 12,
> + .bar[BAR_0] = { .only_64bit = true, },
> + .bar[BAR_1] = { .type = BAR_RESERVED, },
> + .bar[BAR_2] = { .only_64bit = true, },
> + .bar[BAR_3] = { .type = BAR_RESERVED, },
> + .bar[BAR_4] = { .only_64bit = true, },
> + .bar[BAR_5] = { .type = BAR_RESERVED, },
> +};
> +
> static const struct uniphier_pcie_ep_soc_data uniphier_pro5_data = {
> .has_gio = true,
> .init = uniphier_pcie_pro5_init_ep,
> .wait = NULL,
> - .features = {
> - .linkup_notifier = false,
> - .msi_capable = true,
> - .msix_capable = false,
> - .align = 1 << 16,
> - .bar[BAR_0] = { .only_64bit = true, },
> - .bar[BAR_1] = { .type = BAR_RESERVED, },
> - .bar[BAR_2] = { .only_64bit = true, },
> - .bar[BAR_3] = { .type = BAR_RESERVED, },
> - .bar[BAR_4] = { .type = BAR_RESERVED, },
> - .bar[BAR_5] = { .type = BAR_RESERVED, },
> - },
> + .features = &uniphier_pro5_features,
> };
>
> static const struct uniphier_pcie_ep_soc_data uniphier_nx1_data = {
> .has_gio = false,
> .init = uniphier_pcie_nx1_init_ep,
> .wait = uniphier_pcie_nx1_wait_ep,
> - .features = {
> - .linkup_notifier = false,
> - .msi_capable = true,
> - .msix_capable = false,
> - .align = 1 << 12,
> - .bar[BAR_0] = { .only_64bit = true, },
> - .bar[BAR_1] = { .type = BAR_RESERVED, },
> - .bar[BAR_2] = { .only_64bit = true, },
> - .bar[BAR_3] = { .type = BAR_RESERVED, },
> - .bar[BAR_4] = { .only_64bit = true, },
> - .bar[BAR_5] = { .type = BAR_RESERVED, },
> - },
> + .features = &uniphier_nx1_features,
> };
>
> static const struct of_device_id uniphier_pcie_ep_match[] = {
> --
> 2.51.0
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v7 5/6] PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU
2026-01-13 16:27 ` [PATCH v7 5/6] PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU Koichiro Den
@ 2026-01-13 20:53 ` Frank Li
2026-01-15 8:48 ` Koichiro Den
2026-01-14 3:54 ` Koichiro Den
1 sibling, 1 reply; 23+ messages in thread
From: Frank Li @ 2026-01-13 20:53 UTC (permalink / raw)
To: Koichiro Den
Cc: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas, cassel,
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,
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 Wed, Jan 14, 2026 at 01:27:18AM +0900, Koichiro Den wrote:
> 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.
>
> Advertise this capability by setting subrange_mapping in the EPC
> features returned from dw_pcie_ep_get_features().
>
> Reviewed-by: Niklas Cassel <cassel@kernel.org>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
> .../pci/controller/dwc/pcie-designware-ep.c | 230 +++++++++++++++++-
> drivers/pci/controller/dwc/pcie-designware.h | 2 +
> 2 files changed, 222 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
> index 0e5a8d200b00..b2ea2c2c986f 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;
> }
>
...
> +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;
submap[i].offset is unnecessary, you can use expected += size as off.
code logic will be simple.
Frank
> +
> + 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;
> + }
Simple alloc an array struct dw_pcie_ib_map[num_submap] should be simpler
than link list and alloc some small news.
Frank
> +
> + 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,8 +609,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->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)
> return ret;
>
> @@ -638,6 +843,9 @@ dw_pcie_ep_get_features(struct pci_epc *epc, u8 func_no, u8 vfunc_no)
> /* All DWC-based glue drivers support dynamic inbound mapping */
> features->dynamic_inbound_mapping = true;
>
> + /* All DWC-based glue drivers support inbound subrange mapping */
> + features->subrange_mapping = true;
> +
> return features;
> }
>
> @@ -1128,6 +1336,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 4dda9a38d46b..969b1f32dddf 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 [flat|nested] 23+ messages in thread
* Re: [PATCH v7 6/6] Documentation: PCI: endpoint: Clarify pci_epc_set_bar() usage
2026-01-13 16:27 ` [PATCH v7 6/6] Documentation: PCI: endpoint: Clarify pci_epc_set_bar() usage Koichiro Den
@ 2026-01-13 20:56 ` Frank Li
0 siblings, 0 replies; 23+ messages in thread
From: Frank Li @ 2026-01-13 20:56 UTC (permalink / raw)
To: Koichiro Den
Cc: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas, cassel,
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,
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 Wed, Jan 14, 2026 at 01:27:19AM +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 relies on.
> Add document text for that.
>
> Also document the expected call flow for BAR subrange mapping
> (pci_epf_bar.use_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: Niklas Cassel <cassel@kernel.org>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> 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..09b892de9280 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.use_submap is set, 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
> + use_submap cleared to configure the BAR size, then after the PCIe link
> + is up and the host enumerates the endpoint and programs the BAR base
> + address, again with use_submap set.
> +
> + 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 [flat|nested] 23+ messages in thread
* Re: [PATCH v7 2/6] PCI: endpoint: Add BAR subrange mapping support
2026-01-13 20:26 ` Frank Li
@ 2026-01-14 3:22 ` Koichiro Den
0 siblings, 0 replies; 23+ messages in thread
From: Koichiro Den @ 2026-01-14 3:22 UTC (permalink / raw)
To: Frank Li
Cc: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas, cassel,
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,
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 Tue, Jan 13, 2026 at 03:26:17PM -0500, Frank Li wrote:
> On Wed, Jan 14, 2026 at 01:27:15AM +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.
> >
> > Add a subrange_mapping feature bit to struct pci_epc_features so EPC
> > drivers can explicitly advertise support. Make pci_epc_set_bar() reject
> > use_submap requests (-EINVAL) when the EPC does not advertise
> > subrange_mapping, to avoid silently accepting a configuration that the
> > controller cannot implement.
> >
> > 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.
> >
> > Reviewed-by: Niklas Cassel <cassel@kernel.org>
> > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> > ---
> ...
> >
> > #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;
> > +};
>
> I suppose offset is sum of previous all submap's size? If yes, needn't
> offset.
Thanks for pointing this out.
Yes, I agree that @offset has become redundant now that the "no holes"
constraint is enforced for submaps. It should indeed have been dropped
earlier (in v2).
>
> > +
> > /**
> > * 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;
>
> can we use num_submap != 0 as use_submap?
Yes. For the same reason, @use_submap has also become redundant.
Calling pci_epc_set_bar() with use_submap == true && num_submap == 0 has
been invalid since v2, so @use_submap no longer adds useful information.
This can likewise be dropped. I'll respin.
Thanks for the review,
Koichiro
>
> Frank
> > + struct pci_epf_bar_submap *submap;
> > };
> >
> > /**
> > --
> > 2.51.0
> >
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v7 3/6] PCI: dwc: Allow glue drivers to return mutable EPC features
2026-01-13 20:38 ` Frank Li
@ 2026-01-14 3:29 ` Koichiro Den
2026-01-14 15:20 ` Frank Li
0 siblings, 1 reply; 23+ messages in thread
From: Koichiro Den @ 2026-01-14 3:29 UTC (permalink / raw)
To: Frank Li
Cc: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas, cassel,
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,
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 Tue, Jan 13, 2026 at 03:38:45PM -0500, Frank Li wrote:
> On Wed, Jan 14, 2026 at 01:27:16AM +0900, Koichiro Den wrote:
> > The DesignWare EP midlayer needs to advertise additional capabilities at
> > the DWC layer (e.g. subrange_mapping) without duplicating the same bit
> > in every DWC-based glue driver and without copying feature structures.
> >
> > Change dw_pcie_ep_ops.get_features() to return a mutable
> > struct pci_epc_features * and update all DWC-based glue drivers
> > accordingly. The DWC midlayer can then adjust/augment the returned
> > features while still exposing a const struct pci_epc_features * to the
> > PCI EPC core.
> >
> > No functional change on its own.
> >
> > Suggested-by: Niklas Cassel <cassel@kernel.org>
> > Reviewed-by: Niklas Cassel <cassel@kernel.org>
> > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> > ---
> > drivers/pci/controller/dwc/pci-dra7xx.c | 4 +-
> > drivers/pci/controller/dwc/pci-imx6.c | 10 ++--
> > drivers/pci/controller/dwc/pci-keystone.c | 4 +-
> > .../pci/controller/dwc/pci-layerscape-ep.c | 2 +-
> > drivers/pci/controller/dwc/pcie-artpec6.c | 4 +-
> > .../pci/controller/dwc/pcie-designware-plat.c | 4 +-
> > drivers/pci/controller/dwc/pcie-designware.h | 2 +-
> > drivers/pci/controller/dwc/pcie-dw-rockchip.c | 8 +--
> > drivers/pci/controller/dwc/pcie-keembay.c | 4 +-
> > drivers/pci/controller/dwc/pcie-qcom-ep.c | 4 +-
> > drivers/pci/controller/dwc/pcie-rcar-gen4.c | 4 +-
> > drivers/pci/controller/dwc/pcie-stm32-ep.c | 4 +-
> > drivers/pci/controller/dwc/pcie-tegra194.c | 4 +-
> > drivers/pci/controller/dwc/pcie-uniphier-ep.c | 58 ++++++++++---------
> > 14 files changed, 60 insertions(+), 56 deletions(-)
> >
> > diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c
> > index 01cfd9aeb0b8..e67f8b7b56cb 100644
> > --- a/drivers/pci/controller/dwc/pci-dra7xx.c
> > +++ b/drivers/pci/controller/dwc/pci-dra7xx.c
> > @@ -423,12 +423,12 @@ static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > return 0;
> > }
> >
> > -static const struct pci_epc_features dra7xx_pcie_epc_features = {
> > +static struct pci_epc_features dra7xx_pcie_epc_features = {
> > .linkup_notifier = true,
> > .msi_capable = true,
> > };
> >
> > -static const struct pci_epc_features*
> > +static struct pci_epc_features*
> > dra7xx_pcie_get_features(struct dw_pcie_ep *ep)
> > {
> > return &dra7xx_pcie_epc_features;
> > diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
> > index 4668fc9648bf..fe1de30b3df6 100644
> > --- a/drivers/pci/controller/dwc/pci-imx6.c
> > +++ b/drivers/pci/controller/dwc/pci-imx6.c
> > @@ -131,7 +131,7 @@ struct imx_pcie_drvdata {
> > const u32 ltssm_mask;
> > const u32 mode_off[IMX_PCIE_MAX_INSTANCES];
> > const u32 mode_mask[IMX_PCIE_MAX_INSTANCES];
> > - const struct pci_epc_features *epc_features;
> > + struct pci_epc_features *epc_features;
> > int (*init_phy)(struct imx_pcie *pcie);
> > int (*enable_ref_clk)(struct imx_pcie *pcie, bool enable);
> > int (*core_reset)(struct imx_pcie *pcie, bool assert);
> > @@ -1386,7 +1386,7 @@ static int imx_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > return 0;
> > }
> >
> > -static const struct pci_epc_features imx8m_pcie_epc_features = {
> > +static struct pci_epc_features imx8m_pcie_epc_features = {
> > .msi_capable = true,
> > .bar[BAR_1] = { .type = BAR_RESERVED, },
> > .bar[BAR_3] = { .type = BAR_RESERVED, },
> > @@ -1395,7 +1395,7 @@ static const struct pci_epc_features imx8m_pcie_epc_features = {
> > .align = SZ_64K,
> > };
> >
> > -static const struct pci_epc_features imx8q_pcie_epc_features = {
> > +static struct pci_epc_features imx8q_pcie_epc_features = {
>
> Is it more simple if
> #define DWC_EPC_DEFAULT .dynamtic_map = true
>
> Add
> DWC_EPC_DEFAULT, into every epc_features.
One corner case is that pci-layerscape-ep.c builds the pci_epc_features
dynamically in ls_pcie_ep_init(), rather than providing a static definition
with an initializer. I think setting (ie. overriding) the bit centrally in
dw_pcie_ep_get_features() keeps things simpler.
Thanks,
Koichiro
>
>
> Frank
>
> > .msi_capable = true,
> > .bar[BAR_1] = { .type = BAR_RESERVED, },
> > .bar[BAR_3] = { .type = BAR_RESERVED, },
> > @@ -1415,13 +1415,13 @@ static const struct pci_epc_features imx8q_pcie_epc_features = {
> > * BAR4 | Enable | 32-bit | 1 MB | Programmable Size
> > * BAR5 | Enable | 32-bit | 64 KB | Programmable Size
> > */
> > -static const struct pci_epc_features imx95_pcie_epc_features = {
> > +static struct pci_epc_features imx95_pcie_epc_features = {
> > .msi_capable = true,
> > .bar[BAR_1] = { .type = BAR_FIXED, .fixed_size = SZ_64K, },
> > .align = SZ_4K,
> > };
> >
> > -static const struct pci_epc_features*
> > +static struct pci_epc_features*
> > imx_pcie_ep_get_features(struct dw_pcie_ep *ep)
> > {
> > struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> > diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c
> > index f86d9111f863..4292007a9b3a 100644
> > --- a/drivers/pci/controller/dwc/pci-keystone.c
> > +++ b/drivers/pci/controller/dwc/pci-keystone.c
> > @@ -929,7 +929,7 @@ static int ks_pcie_am654_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > return 0;
> > }
> >
> > -static const struct pci_epc_features ks_pcie_am654_epc_features = {
> > +static struct pci_epc_features ks_pcie_am654_epc_features = {
> > .msi_capable = true,
> > .msix_capable = true,
> > .bar[BAR_0] = { .type = BAR_RESERVED, },
> > @@ -941,7 +941,7 @@ static const struct pci_epc_features ks_pcie_am654_epc_features = {
> > .align = SZ_64K,
> > };
> >
> > -static const struct pci_epc_features*
> > +static struct pci_epc_features*
> > ks_pcie_am654_get_features(struct dw_pcie_ep *ep)
> > {
> > return &ks_pcie_am654_epc_features;
> > diff --git a/drivers/pci/controller/dwc/pci-layerscape-ep.c b/drivers/pci/controller/dwc/pci-layerscape-ep.c
> > index a4a800699f89..8d48413050ef 100644
> > --- a/drivers/pci/controller/dwc/pci-layerscape-ep.c
> > +++ b/drivers/pci/controller/dwc/pci-layerscape-ep.c
> > @@ -138,7 +138,7 @@ static int ls_pcie_ep_interrupt_init(struct ls_pcie_ep *pcie,
> > return 0;
> > }
> >
> > -static const struct pci_epc_features*
> > +static struct pci_epc_features*
> > ls_pcie_ep_get_features(struct dw_pcie_ep *ep)
> > {
> > struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> > diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c
> > index f4a136ee2daf..84111d8257f2 100644
> > --- a/drivers/pci/controller/dwc/pcie-artpec6.c
> > +++ b/drivers/pci/controller/dwc/pcie-artpec6.c
> > @@ -369,11 +369,11 @@ static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > return 0;
> > }
> >
> > -static const struct pci_epc_features artpec6_pcie_epc_features = {
> > +static struct pci_epc_features artpec6_pcie_epc_features = {
> > .msi_capable = true,
> > };
> >
> > -static const struct pci_epc_features *
> > +static struct pci_epc_features *
> > artpec6_pcie_get_features(struct dw_pcie_ep *ep)
> > {
> > return &artpec6_pcie_epc_features;
> > diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c
> > index 12f41886c65d..60ada0eb838e 100644
> > --- a/drivers/pci/controller/dwc/pcie-designware-plat.c
> > +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c
> > @@ -60,12 +60,12 @@ static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > return 0;
> > }
> >
> > -static const struct pci_epc_features dw_plat_pcie_epc_features = {
> > +static struct pci_epc_features dw_plat_pcie_epc_features = {
> > .msi_capable = true,
> > .msix_capable = true,
> > };
> >
> > -static const struct pci_epc_features*
> > +static struct pci_epc_features*
> > dw_plat_pcie_get_features(struct dw_pcie_ep *ep)
> > {
> > return &dw_plat_pcie_epc_features;
> > diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
> > index f87c67a7a482..4dda9a38d46b 100644
> > --- a/drivers/pci/controller/dwc/pcie-designware.h
> > +++ b/drivers/pci/controller/dwc/pcie-designware.h
> > @@ -449,7 +449,7 @@ struct dw_pcie_ep_ops {
> > void (*init)(struct dw_pcie_ep *ep);
> > int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no,
> > unsigned int type, u16 interrupt_num);
> > - const struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep);
> > + struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep);
> > /*
> > * Provide a method to implement the different func config space
> > * access for different platform, if different func have different
> > diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
> > index 352f513ebf03..1f3c91368dc3 100644
> > --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c
> > +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
> > @@ -100,7 +100,7 @@ struct rockchip_pcie {
> >
> > struct rockchip_pcie_of_data {
> > enum dw_pcie_device_mode mode;
> > - const struct pci_epc_features *epc_features;
> > + struct pci_epc_features *epc_features;
> > };
> >
> > static int rockchip_pcie_readl_apb(struct rockchip_pcie *rockchip, u32 reg)
> > @@ -383,7 +383,7 @@ static int rockchip_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > return 0;
> > }
> >
> > -static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = {
> > +static struct pci_epc_features rockchip_pcie_epc_features_rk3568 = {
> > .linkup_notifier = true,
> > .msi_capable = true,
> > .msix_capable = true,
> > @@ -403,7 +403,7 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = {
> > * default.) If the host could write to BAR4, the iATU settings (for all other
> > * BARs) would be overwritten, resulting in (all other BARs) no longer working.
> > */
> > -static const struct pci_epc_features rockchip_pcie_epc_features_rk3588 = {
> > +static struct pci_epc_features rockchip_pcie_epc_features_rk3588 = {
> > .linkup_notifier = true,
> > .msi_capable = true,
> > .msix_capable = true,
> > @@ -416,7 +416,7 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3588 = {
> > .bar[BAR_5] = { .type = BAR_RESIZABLE, },
> > };
> >
> > -static const struct pci_epc_features *
> > +static struct pci_epc_features *
> > rockchip_pcie_get_features(struct dw_pcie_ep *ep)
> > {
> > struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> > diff --git a/drivers/pci/controller/dwc/pcie-keembay.c b/drivers/pci/controller/dwc/pcie-keembay.c
> > index 60e74ac782af..e6de5289329f 100644
> > --- a/drivers/pci/controller/dwc/pcie-keembay.c
> > +++ b/drivers/pci/controller/dwc/pcie-keembay.c
> > @@ -308,7 +308,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 = {
> > +static struct pci_epc_features keembay_pcie_epc_features = {
> > .msi_capable = true,
> > .msix_capable = true,
> > .bar[BAR_0] = { .only_64bit = true, },
> > @@ -320,7 +320,7 @@ static const struct pci_epc_features keembay_pcie_epc_features = {
> > .align = SZ_16K,
> > };
> >
> > -static const struct pci_epc_features *
> > +static struct pci_epc_features *
> > keembay_pcie_get_features(struct dw_pcie_ep *ep)
> > {
> > return &keembay_pcie_epc_features;
> > diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c
> > index f1bc0ac81a92..6ad033301909 100644
> > --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c
> > +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c
> > @@ -819,7 +819,7 @@ static void qcom_pcie_ep_init_debugfs(struct qcom_pcie_ep *pcie_ep)
> > qcom_pcie_ep_link_transition_count);
> > }
> >
> > -static const struct pci_epc_features qcom_pcie_epc_features = {
> > +static struct pci_epc_features qcom_pcie_epc_features = {
> > .linkup_notifier = true,
> > .msi_capable = true,
> > .align = SZ_4K,
> > @@ -829,7 +829,7 @@ static const struct pci_epc_features qcom_pcie_epc_features = {
> > .bar[BAR_3] = { .type = BAR_RESERVED, },
> > };
> >
> > -static const struct pci_epc_features *
> > +static struct pci_epc_features *
> > qcom_pcie_epc_get_features(struct dw_pcie_ep *pci_ep)
> > {
> > return &qcom_pcie_epc_features;
> > diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> > index 80778917d2dd..ff0c4af90eff 100644
> > --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> > +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> > @@ -419,7 +419,7 @@ static int rcar_gen4_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > return 0;
> > }
> >
> > -static const struct pci_epc_features rcar_gen4_pcie_epc_features = {
> > +static struct pci_epc_features rcar_gen4_pcie_epc_features = {
> > .msi_capable = true,
> > .bar[BAR_1] = { .type = BAR_RESERVED, },
> > .bar[BAR_3] = { .type = BAR_RESERVED, },
> > @@ -428,7 +428,7 @@ static const struct pci_epc_features rcar_gen4_pcie_epc_features = {
> > .align = SZ_1M,
> > };
> >
> > -static const struct pci_epc_features*
> > +static struct pci_epc_features*
> > rcar_gen4_pcie_ep_get_features(struct dw_pcie_ep *ep)
> > {
> > return &rcar_gen4_pcie_epc_features;
> > diff --git a/drivers/pci/controller/dwc/pcie-stm32-ep.c b/drivers/pci/controller/dwc/pcie-stm32-ep.c
> > index 2cecf32d2b0f..8a892def54f5 100644
> > --- a/drivers/pci/controller/dwc/pcie-stm32-ep.c
> > +++ b/drivers/pci/controller/dwc/pcie-stm32-ep.c
> > @@ -69,12 +69,12 @@ static int stm32_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > }
> > }
> >
> > -static const struct pci_epc_features stm32_pcie_epc_features = {
> > +static struct pci_epc_features stm32_pcie_epc_features = {
> > .msi_capable = true,
> > .align = SZ_64K,
> > };
> >
> > -static const struct pci_epc_features*
> > +static struct pci_epc_features*
> > stm32_pcie_get_features(struct dw_pcie_ep *ep)
> > {
> > return &stm32_pcie_epc_features;
> > diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
> > index 0ddeef70726d..06f45a17e52c 100644
> > --- a/drivers/pci/controller/dwc/pcie-tegra194.c
> > +++ b/drivers/pci/controller/dwc/pcie-tegra194.c
> > @@ -1987,7 +1987,7 @@ static int tegra_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > return 0;
> > }
> >
> > -static const struct pci_epc_features tegra_pcie_epc_features = {
> > +static struct pci_epc_features tegra_pcie_epc_features = {
> > .linkup_notifier = true,
> > .msi_capable = true,
> > .bar[BAR_0] = { .type = BAR_FIXED, .fixed_size = SZ_1M,
> > @@ -2000,7 +2000,7 @@ static const struct pci_epc_features tegra_pcie_epc_features = {
> > .align = SZ_64K,
> > };
> >
> > -static const struct pci_epc_features*
> > +static struct pci_epc_features*
> > tegra_pcie_ep_get_features(struct dw_pcie_ep *ep)
> > {
> > return &tegra_pcie_epc_features;
> > diff --git a/drivers/pci/controller/dwc/pcie-uniphier-ep.c b/drivers/pci/controller/dwc/pcie-uniphier-ep.c
> > index d6e73811216e..ddb5ff70340c 100644
> > --- a/drivers/pci/controller/dwc/pcie-uniphier-ep.c
> > +++ b/drivers/pci/controller/dwc/pcie-uniphier-ep.c
> > @@ -82,7 +82,7 @@ struct uniphier_pcie_ep_soc_data {
> > bool has_gio;
> > void (*init)(struct uniphier_pcie_ep_priv *priv);
> > int (*wait)(struct uniphier_pcie_ep_priv *priv);
> > - const struct pci_epc_features features;
> > + struct pci_epc_features *features;
> > };
> >
> > #define to_uniphier_pcie(x) dev_get_drvdata((x)->dev)
> > @@ -273,13 +273,13 @@ static int uniphier_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > return 0;
> > }
> >
> > -static const struct pci_epc_features*
> > +static struct pci_epc_features*
> > uniphier_pcie_get_features(struct dw_pcie_ep *ep)
> > {
> > struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> > struct uniphier_pcie_ep_priv *priv = to_uniphier_pcie(pci);
> >
> > - return &priv->data->features;
> > + return priv->data->features;
> > }
> >
> > static const struct dw_pcie_ep_ops uniphier_pcie_ep_ops = {
> > @@ -415,40 +415,44 @@ static int uniphier_pcie_ep_probe(struct platform_device *pdev)
> > return 0;
> > }
> >
> > +static struct pci_epc_features uniphier_pro5_features = {
> > + .linkup_notifier = false,
> > + .msi_capable = true,
> > + .msix_capable = false,
> > + .align = 1 << 16,
> > + .bar[BAR_0] = { .only_64bit = true, },
> > + .bar[BAR_1] = { .type = BAR_RESERVED, },
> > + .bar[BAR_2] = { .only_64bit = true, },
> > + .bar[BAR_3] = { .type = BAR_RESERVED, },
> > + .bar[BAR_4] = { .type = BAR_RESERVED, },
> > + .bar[BAR_5] = { .type = BAR_RESERVED, },
> > +};
> > +
> > +static struct pci_epc_features uniphier_nx1_features = {
> > + .linkup_notifier = false,
> > + .msi_capable = true,
> > + .msix_capable = false,
> > + .align = 1 << 12,
> > + .bar[BAR_0] = { .only_64bit = true, },
> > + .bar[BAR_1] = { .type = BAR_RESERVED, },
> > + .bar[BAR_2] = { .only_64bit = true, },
> > + .bar[BAR_3] = { .type = BAR_RESERVED, },
> > + .bar[BAR_4] = { .only_64bit = true, },
> > + .bar[BAR_5] = { .type = BAR_RESERVED, },
> > +};
> > +
> > static const struct uniphier_pcie_ep_soc_data uniphier_pro5_data = {
> > .has_gio = true,
> > .init = uniphier_pcie_pro5_init_ep,
> > .wait = NULL,
> > - .features = {
> > - .linkup_notifier = false,
> > - .msi_capable = true,
> > - .msix_capable = false,
> > - .align = 1 << 16,
> > - .bar[BAR_0] = { .only_64bit = true, },
> > - .bar[BAR_1] = { .type = BAR_RESERVED, },
> > - .bar[BAR_2] = { .only_64bit = true, },
> > - .bar[BAR_3] = { .type = BAR_RESERVED, },
> > - .bar[BAR_4] = { .type = BAR_RESERVED, },
> > - .bar[BAR_5] = { .type = BAR_RESERVED, },
> > - },
> > + .features = &uniphier_pro5_features,
> > };
> >
> > static const struct uniphier_pcie_ep_soc_data uniphier_nx1_data = {
> > .has_gio = false,
> > .init = uniphier_pcie_nx1_init_ep,
> > .wait = uniphier_pcie_nx1_wait_ep,
> > - .features = {
> > - .linkup_notifier = false,
> > - .msi_capable = true,
> > - .msix_capable = false,
> > - .align = 1 << 12,
> > - .bar[BAR_0] = { .only_64bit = true, },
> > - .bar[BAR_1] = { .type = BAR_RESERVED, },
> > - .bar[BAR_2] = { .only_64bit = true, },
> > - .bar[BAR_3] = { .type = BAR_RESERVED, },
> > - .bar[BAR_4] = { .only_64bit = true, },
> > - .bar[BAR_5] = { .type = BAR_RESERVED, },
> > - },
> > + .features = &uniphier_nx1_features,
> > };
> >
> > static const struct of_device_id uniphier_pcie_ep_match[] = {
> > --
> > 2.51.0
> >
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v7 5/6] PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU
2026-01-13 16:27 ` [PATCH v7 5/6] PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU Koichiro Den
2026-01-13 20:53 ` Frank Li
@ 2026-01-14 3:54 ` Koichiro Den
2026-01-14 10:39 ` Niklas Cassel
1 sibling, 1 reply; 23+ messages in thread
From: Koichiro Den @ 2026-01-14 3:54 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
On Wed, Jan 14, 2026 at 01:27:18AM +0900, Koichiro Den wrote:
> 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.
>
> Advertise this capability by setting subrange_mapping in the EPC
> features returned from dw_pcie_ep_get_features().
>
> Reviewed-by: Niklas Cassel <cassel@kernel.org>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
> .../pci/controller/dwc/pcie-designware-ep.c | 230 +++++++++++++++++-
> drivers/pci/controller/dwc/pcie-designware.h | 2 +
> 2 files changed, 222 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
> index 0e5a8d200b00..b2ea2c2c986f 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);
> + }
> +}
I realized that I missed one case in v7.
I think dw_pcie_ep_clear_ib_maps() should also be called from
dw_pcie_ep_ib_atu_bar() to tear down any existing inbound mappings for the
same BAR before re-programming it in BAR Match Mode.
This matters when updating inbound mappings for a BAR without resetting the
BAR in between. There are four possible transition patterns, and pattern #4
below was overlooked:
1. BAR Match Mode -> BAR Match Mode
As the current implementation does, the mapping is simply updated
(with the same atu index)
2. BAR Match Mode -> Address Match Mode
This patch series already ensures the old BAR Match mapping is
torn down before reprogramming.
3. Address Match Mode -> Address Match Mode
Likewise, existing Address Match mappings are cleared first.
4. Address Match Mode -> BAR Match Mode
This case was not handled. The change below adds the missing
teardown so that stale Address Match mappings do not remain active.
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -148,9 +148,12 @@ static int dw_pcie_ep_ib_atu_bar(struct dw_pcie_ep *ep, u8 func_no, int type,
u32 free_win;
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
- if (!ep->bar_to_atu[bar])
+ if (!ep->bar_to_atu[bar]) {
+ /* Tear down existing mappings before (re)programming. */
+ dw_pcie_ep_clear_ib_maps(ep, bar);
+
free_win = find_first_zero_bit(ep->ib_window_map,
pci->num_ib_windows);
- else
+ } else
free_win = ep->bar_to_atu[bar] - 1;
Unless there are objections, I'll include this fix in v8.
Thanks,
Koichiro
> +
> +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,8 +609,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->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)
> return ret;
>
> @@ -638,6 +843,9 @@ dw_pcie_ep_get_features(struct pci_epc *epc, u8 func_no, u8 vfunc_no)
> /* All DWC-based glue drivers support dynamic inbound mapping */
> features->dynamic_inbound_mapping = true;
>
> + /* All DWC-based glue drivers support inbound subrange mapping */
> + features->subrange_mapping = true;
> +
> return features;
> }
>
> @@ -1128,6 +1336,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 4dda9a38d46b..969b1f32dddf 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 [flat|nested] 23+ messages in thread
* Re: [PATCH v7 5/6] PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU
2026-01-14 3:54 ` Koichiro Den
@ 2026-01-14 10:39 ` Niklas Cassel
2026-01-14 17:29 ` Koichiro Den
0 siblings, 1 reply; 23+ messages in thread
From: Niklas Cassel @ 2026-01-14 10:39 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 Wed, Jan 14, 2026 at 12:54:37PM +0900, Koichiro Den wrote:
> I realized that I missed one case in v7.
>
> I think dw_pcie_ep_clear_ib_maps() should also be called from
> dw_pcie_ep_ib_atu_bar() to tear down any existing inbound mappings for the
> same BAR before re-programming it in BAR Match Mode.
>
> This matters when updating inbound mappings for a BAR without resetting the
> BAR in between. There are four possible transition patterns, and pattern #4
> below was overlooked:
>
> 1. BAR Match Mode -> BAR Match Mode
> As the current implementation does, the mapping is simply updated
> (with the same atu index)
>
> 2. BAR Match Mode -> Address Match Mode
> This patch series already ensures the old BAR Match mapping is
> torn down before reprogramming.
>
> 3. Address Match Mode -> Address Match Mode
> Likewise, existing Address Match mappings are cleared first.
>
> 4. Address Match Mode -> BAR Match Mode
> This case was not handled. The change below adds the missing
> teardown so that stale Address Match mappings do not remain active.
>
> --- a/drivers/pci/controller/dwc/pcie-designware-ep.c
> +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
> @@ -148,9 +148,12 @@ static int dw_pcie_ep_ib_atu_bar(struct dw_pcie_ep *ep, u8 func_no, int type,
> u32 free_win;
> struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
>
> - if (!ep->bar_to_atu[bar])
> + if (!ep->bar_to_atu[bar]) {
> + /* Tear down existing mappings before (re)programming. */
> + dw_pcie_ep_clear_ib_maps(ep, bar);
> +
> free_win = find_first_zero_bit(ep->ib_window_map,
> pci->num_ib_windows);
> - else
> + } else
> free_win = ep->bar_to_atu[bar] - 1;
If one of the branches has braces, both branches should have braces:
https://www.kernel.org/doc/html/latest/process/coding-style.html#placing-braces-and-spaces
>
> Unless there are objections, I'll include this fix in v8.
Isn't it easier/cleaner if we call dw_pcie_ep_clear_ib_maps() in
dw_pcie_ep_set_bar(), rather than calling it in both dw_pcie_ep_ib_atu_addr()
and dw_pcie_ep_ib_atu_bar() ?
dw_pcie_ep_set_bar() knows the condition if we are dynamically reprogramming
a BAR or not, and all the four cases are when dynamically reprogramming a BAR.
I.e. instead of adding additional code to dw_pcie_ep_ib_atu_bar(), we do
something like:
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index b2ea2c2c986f..63ae5471fe13 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -318,9 +318,6 @@ static int dw_pcie_ep_ib_atu_addr(struct dw_pcie_ep *ep, u8 func_no, int type,
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;
@@ -571,6 +568,9 @@ 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;
+ 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.
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH v7 3/6] PCI: dwc: Allow glue drivers to return mutable EPC features
2026-01-14 3:29 ` Koichiro Den
@ 2026-01-14 15:20 ` Frank Li
2026-01-14 17:17 ` Koichiro Den
0 siblings, 1 reply; 23+ messages in thread
From: Frank Li @ 2026-01-14 15:20 UTC (permalink / raw)
To: Koichiro Den
Cc: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas, cassel,
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,
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 Wed, Jan 14, 2026 at 12:29:00PM +0900, Koichiro Den wrote:
> On Tue, Jan 13, 2026 at 03:38:45PM -0500, Frank Li wrote:
> > On Wed, Jan 14, 2026 at 01:27:16AM +0900, Koichiro Den wrote:
> > > The DesignWare EP midlayer needs to advertise additional capabilities at
> > > the DWC layer (e.g. subrange_mapping) without duplicating the same bit
> > > in every DWC-based glue driver and without copying feature structures.
> > >
> > > Change dw_pcie_ep_ops.get_features() to return a mutable
> > > struct pci_epc_features * and update all DWC-based glue drivers
> > > accordingly. The DWC midlayer can then adjust/augment the returned
> > > features while still exposing a const struct pci_epc_features * to the
> > > PCI EPC core.
> > >
> > > No functional change on its own.
> > >
> > > Suggested-by: Niklas Cassel <cassel@kernel.org>
> > > Reviewed-by: Niklas Cassel <cassel@kernel.org>
> > > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> > > ---
> > > drivers/pci/controller/dwc/pci-dra7xx.c | 4 +-
> > > drivers/pci/controller/dwc/pci-imx6.c | 10 ++--
> > > drivers/pci/controller/dwc/pci-keystone.c | 4 +-
> > > .../pci/controller/dwc/pci-layerscape-ep.c | 2 +-
> > > drivers/pci/controller/dwc/pcie-artpec6.c | 4 +-
> > > .../pci/controller/dwc/pcie-designware-plat.c | 4 +-
> > > drivers/pci/controller/dwc/pcie-designware.h | 2 +-
> > > drivers/pci/controller/dwc/pcie-dw-rockchip.c | 8 +--
> > > drivers/pci/controller/dwc/pcie-keembay.c | 4 +-
> > > drivers/pci/controller/dwc/pcie-qcom-ep.c | 4 +-
> > > drivers/pci/controller/dwc/pcie-rcar-gen4.c | 4 +-
> > > drivers/pci/controller/dwc/pcie-stm32-ep.c | 4 +-
> > > drivers/pci/controller/dwc/pcie-tegra194.c | 4 +-
> > > drivers/pci/controller/dwc/pcie-uniphier-ep.c | 58 ++++++++++---------
> > > 14 files changed, 60 insertions(+), 56 deletions(-)
> > >
> > > diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c
> > > index 01cfd9aeb0b8..e67f8b7b56cb 100644
> > > --- a/drivers/pci/controller/dwc/pci-dra7xx.c
> > > +++ b/drivers/pci/controller/dwc/pci-dra7xx.c
> > > @@ -423,12 +423,12 @@ static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > return 0;
> > > }
> > >
> > > -static const struct pci_epc_features dra7xx_pcie_epc_features = {
> > > +static struct pci_epc_features dra7xx_pcie_epc_features = {
> > > .linkup_notifier = true,
> > > .msi_capable = true,
> > > };
> > >
> > > -static const struct pci_epc_features*
> > > +static struct pci_epc_features*
> > > dra7xx_pcie_get_features(struct dw_pcie_ep *ep)
> > > {
> > > return &dra7xx_pcie_epc_features;
> > > diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
> > > index 4668fc9648bf..fe1de30b3df6 100644
> > > --- a/drivers/pci/controller/dwc/pci-imx6.c
> > > +++ b/drivers/pci/controller/dwc/pci-imx6.c
> > > @@ -131,7 +131,7 @@ struct imx_pcie_drvdata {
> > > const u32 ltssm_mask;
> > > const u32 mode_off[IMX_PCIE_MAX_INSTANCES];
> > > const u32 mode_mask[IMX_PCIE_MAX_INSTANCES];
> > > - const struct pci_epc_features *epc_features;
> > > + struct pci_epc_features *epc_features;
> > > int (*init_phy)(struct imx_pcie *pcie);
> > > int (*enable_ref_clk)(struct imx_pcie *pcie, bool enable);
> > > int (*core_reset)(struct imx_pcie *pcie, bool assert);
> > > @@ -1386,7 +1386,7 @@ static int imx_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > return 0;
> > > }
> > >
> > > -static const struct pci_epc_features imx8m_pcie_epc_features = {
> > > +static struct pci_epc_features imx8m_pcie_epc_features = {
> > > .msi_capable = true,
> > > .bar[BAR_1] = { .type = BAR_RESERVED, },
> > > .bar[BAR_3] = { .type = BAR_RESERVED, },
> > > @@ -1395,7 +1395,7 @@ static const struct pci_epc_features imx8m_pcie_epc_features = {
> > > .align = SZ_64K,
> > > };
> > >
> > > -static const struct pci_epc_features imx8q_pcie_epc_features = {
> > > +static struct pci_epc_features imx8q_pcie_epc_features = {
> >
> > Is it more simple if
> > #define DWC_EPC_DEFAULT .dynamtic_map = true
> >
> > Add
> > DWC_EPC_DEFAULT, into every epc_features.
>
> One corner case is that pci-layerscape-ep.c builds the pci_epc_features
It is our old platform. I checked code, it should be wrong. features should
report EPC hardware capibility.
> dynamically in ls_pcie_ep_init(), rather than providing a static definition
> with an initializer. I think setting (ie. overriding) the bit centrally in
> dw_pcie_ep_get_features() keeps things simpler.
In case one of chip have bugs, which need remove .dynamtic_map.
DWC_EPC_DEFAULT will be simple.
Frank
>
> Thanks,
> Koichiro
>
> >
> >
> > Frank
> >
> > > .msi_capable = true,
> > > .bar[BAR_1] = { .type = BAR_RESERVED, },
> > > .bar[BAR_3] = { .type = BAR_RESERVED, },
> > > @@ -1415,13 +1415,13 @@ static const struct pci_epc_features imx8q_pcie_epc_features = {
> > > * BAR4 | Enable | 32-bit | 1 MB | Programmable Size
> > > * BAR5 | Enable | 32-bit | 64 KB | Programmable Size
> > > */
> > > -static const struct pci_epc_features imx95_pcie_epc_features = {
> > > +static struct pci_epc_features imx95_pcie_epc_features = {
> > > .msi_capable = true,
> > > .bar[BAR_1] = { .type = BAR_FIXED, .fixed_size = SZ_64K, },
> > > .align = SZ_4K,
> > > };
> > >
> > > -static const struct pci_epc_features*
> > > +static struct pci_epc_features*
> > > imx_pcie_ep_get_features(struct dw_pcie_ep *ep)
> > > {
> > > struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> > > diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c
> > > index f86d9111f863..4292007a9b3a 100644
> > > --- a/drivers/pci/controller/dwc/pci-keystone.c
> > > +++ b/drivers/pci/controller/dwc/pci-keystone.c
> > > @@ -929,7 +929,7 @@ static int ks_pcie_am654_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > return 0;
> > > }
> > >
> > > -static const struct pci_epc_features ks_pcie_am654_epc_features = {
> > > +static struct pci_epc_features ks_pcie_am654_epc_features = {
> > > .msi_capable = true,
> > > .msix_capable = true,
> > > .bar[BAR_0] = { .type = BAR_RESERVED, },
> > > @@ -941,7 +941,7 @@ static const struct pci_epc_features ks_pcie_am654_epc_features = {
> > > .align = SZ_64K,
> > > };
> > >
> > > -static const struct pci_epc_features*
> > > +static struct pci_epc_features*
> > > ks_pcie_am654_get_features(struct dw_pcie_ep *ep)
> > > {
> > > return &ks_pcie_am654_epc_features;
> > > diff --git a/drivers/pci/controller/dwc/pci-layerscape-ep.c b/drivers/pci/controller/dwc/pci-layerscape-ep.c
> > > index a4a800699f89..8d48413050ef 100644
> > > --- a/drivers/pci/controller/dwc/pci-layerscape-ep.c
> > > +++ b/drivers/pci/controller/dwc/pci-layerscape-ep.c
> > > @@ -138,7 +138,7 @@ static int ls_pcie_ep_interrupt_init(struct ls_pcie_ep *pcie,
> > > return 0;
> > > }
> > >
> > > -static const struct pci_epc_features*
> > > +static struct pci_epc_features*
> > > ls_pcie_ep_get_features(struct dw_pcie_ep *ep)
> > > {
> > > struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> > > diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c
> > > index f4a136ee2daf..84111d8257f2 100644
> > > --- a/drivers/pci/controller/dwc/pcie-artpec6.c
> > > +++ b/drivers/pci/controller/dwc/pcie-artpec6.c
> > > @@ -369,11 +369,11 @@ static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > return 0;
> > > }
> > >
> > > -static const struct pci_epc_features artpec6_pcie_epc_features = {
> > > +static struct pci_epc_features artpec6_pcie_epc_features = {
> > > .msi_capable = true,
> > > };
> > >
> > > -static const struct pci_epc_features *
> > > +static struct pci_epc_features *
> > > artpec6_pcie_get_features(struct dw_pcie_ep *ep)
> > > {
> > > return &artpec6_pcie_epc_features;
> > > diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c
> > > index 12f41886c65d..60ada0eb838e 100644
> > > --- a/drivers/pci/controller/dwc/pcie-designware-plat.c
> > > +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c
> > > @@ -60,12 +60,12 @@ static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > return 0;
> > > }
> > >
> > > -static const struct pci_epc_features dw_plat_pcie_epc_features = {
> > > +static struct pci_epc_features dw_plat_pcie_epc_features = {
> > > .msi_capable = true,
> > > .msix_capable = true,
> > > };
> > >
> > > -static const struct pci_epc_features*
> > > +static struct pci_epc_features*
> > > dw_plat_pcie_get_features(struct dw_pcie_ep *ep)
> > > {
> > > return &dw_plat_pcie_epc_features;
> > > diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
> > > index f87c67a7a482..4dda9a38d46b 100644
> > > --- a/drivers/pci/controller/dwc/pcie-designware.h
> > > +++ b/drivers/pci/controller/dwc/pcie-designware.h
> > > @@ -449,7 +449,7 @@ struct dw_pcie_ep_ops {
> > > void (*init)(struct dw_pcie_ep *ep);
> > > int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no,
> > > unsigned int type, u16 interrupt_num);
> > > - const struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep);
> > > + struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep);
> > > /*
> > > * Provide a method to implement the different func config space
> > > * access for different platform, if different func have different
> > > diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
> > > index 352f513ebf03..1f3c91368dc3 100644
> > > --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c
> > > +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
> > > @@ -100,7 +100,7 @@ struct rockchip_pcie {
> > >
> > > struct rockchip_pcie_of_data {
> > > enum dw_pcie_device_mode mode;
> > > - const struct pci_epc_features *epc_features;
> > > + struct pci_epc_features *epc_features;
> > > };
> > >
> > > static int rockchip_pcie_readl_apb(struct rockchip_pcie *rockchip, u32 reg)
> > > @@ -383,7 +383,7 @@ static int rockchip_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > return 0;
> > > }
> > >
> > > -static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = {
> > > +static struct pci_epc_features rockchip_pcie_epc_features_rk3568 = {
> > > .linkup_notifier = true,
> > > .msi_capable = true,
> > > .msix_capable = true,
> > > @@ -403,7 +403,7 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = {
> > > * default.) If the host could write to BAR4, the iATU settings (for all other
> > > * BARs) would be overwritten, resulting in (all other BARs) no longer working.
> > > */
> > > -static const struct pci_epc_features rockchip_pcie_epc_features_rk3588 = {
> > > +static struct pci_epc_features rockchip_pcie_epc_features_rk3588 = {
> > > .linkup_notifier = true,
> > > .msi_capable = true,
> > > .msix_capable = true,
> > > @@ -416,7 +416,7 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3588 = {
> > > .bar[BAR_5] = { .type = BAR_RESIZABLE, },
> > > };
> > >
> > > -static const struct pci_epc_features *
> > > +static struct pci_epc_features *
> > > rockchip_pcie_get_features(struct dw_pcie_ep *ep)
> > > {
> > > struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> > > diff --git a/drivers/pci/controller/dwc/pcie-keembay.c b/drivers/pci/controller/dwc/pcie-keembay.c
> > > index 60e74ac782af..e6de5289329f 100644
> > > --- a/drivers/pci/controller/dwc/pcie-keembay.c
> > > +++ b/drivers/pci/controller/dwc/pcie-keembay.c
> > > @@ -308,7 +308,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 = {
> > > +static struct pci_epc_features keembay_pcie_epc_features = {
> > > .msi_capable = true,
> > > .msix_capable = true,
> > > .bar[BAR_0] = { .only_64bit = true, },
> > > @@ -320,7 +320,7 @@ static const struct pci_epc_features keembay_pcie_epc_features = {
> > > .align = SZ_16K,
> > > };
> > >
> > > -static const struct pci_epc_features *
> > > +static struct pci_epc_features *
> > > keembay_pcie_get_features(struct dw_pcie_ep *ep)
> > > {
> > > return &keembay_pcie_epc_features;
> > > diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c
> > > index f1bc0ac81a92..6ad033301909 100644
> > > --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c
> > > +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c
> > > @@ -819,7 +819,7 @@ static void qcom_pcie_ep_init_debugfs(struct qcom_pcie_ep *pcie_ep)
> > > qcom_pcie_ep_link_transition_count);
> > > }
> > >
> > > -static const struct pci_epc_features qcom_pcie_epc_features = {
> > > +static struct pci_epc_features qcom_pcie_epc_features = {
> > > .linkup_notifier = true,
> > > .msi_capable = true,
> > > .align = SZ_4K,
> > > @@ -829,7 +829,7 @@ static const struct pci_epc_features qcom_pcie_epc_features = {
> > > .bar[BAR_3] = { .type = BAR_RESERVED, },
> > > };
> > >
> > > -static const struct pci_epc_features *
> > > +static struct pci_epc_features *
> > > qcom_pcie_epc_get_features(struct dw_pcie_ep *pci_ep)
> > > {
> > > return &qcom_pcie_epc_features;
> > > diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> > > index 80778917d2dd..ff0c4af90eff 100644
> > > --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> > > +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> > > @@ -419,7 +419,7 @@ static int rcar_gen4_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > return 0;
> > > }
> > >
> > > -static const struct pci_epc_features rcar_gen4_pcie_epc_features = {
> > > +static struct pci_epc_features rcar_gen4_pcie_epc_features = {
> > > .msi_capable = true,
> > > .bar[BAR_1] = { .type = BAR_RESERVED, },
> > > .bar[BAR_3] = { .type = BAR_RESERVED, },
> > > @@ -428,7 +428,7 @@ static const struct pci_epc_features rcar_gen4_pcie_epc_features = {
> > > .align = SZ_1M,
> > > };
> > >
> > > -static const struct pci_epc_features*
> > > +static struct pci_epc_features*
> > > rcar_gen4_pcie_ep_get_features(struct dw_pcie_ep *ep)
> > > {
> > > return &rcar_gen4_pcie_epc_features;
> > > diff --git a/drivers/pci/controller/dwc/pcie-stm32-ep.c b/drivers/pci/controller/dwc/pcie-stm32-ep.c
> > > index 2cecf32d2b0f..8a892def54f5 100644
> > > --- a/drivers/pci/controller/dwc/pcie-stm32-ep.c
> > > +++ b/drivers/pci/controller/dwc/pcie-stm32-ep.c
> > > @@ -69,12 +69,12 @@ static int stm32_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > }
> > > }
> > >
> > > -static const struct pci_epc_features stm32_pcie_epc_features = {
> > > +static struct pci_epc_features stm32_pcie_epc_features = {
> > > .msi_capable = true,
> > > .align = SZ_64K,
> > > };
> > >
> > > -static const struct pci_epc_features*
> > > +static struct pci_epc_features*
> > > stm32_pcie_get_features(struct dw_pcie_ep *ep)
> > > {
> > > return &stm32_pcie_epc_features;
> > > diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
> > > index 0ddeef70726d..06f45a17e52c 100644
> > > --- a/drivers/pci/controller/dwc/pcie-tegra194.c
> > > +++ b/drivers/pci/controller/dwc/pcie-tegra194.c
> > > @@ -1987,7 +1987,7 @@ static int tegra_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > return 0;
> > > }
> > >
> > > -static const struct pci_epc_features tegra_pcie_epc_features = {
> > > +static struct pci_epc_features tegra_pcie_epc_features = {
> > > .linkup_notifier = true,
> > > .msi_capable = true,
> > > .bar[BAR_0] = { .type = BAR_FIXED, .fixed_size = SZ_1M,
> > > @@ -2000,7 +2000,7 @@ static const struct pci_epc_features tegra_pcie_epc_features = {
> > > .align = SZ_64K,
> > > };
> > >
> > > -static const struct pci_epc_features*
> > > +static struct pci_epc_features*
> > > tegra_pcie_ep_get_features(struct dw_pcie_ep *ep)
> > > {
> > > return &tegra_pcie_epc_features;
> > > diff --git a/drivers/pci/controller/dwc/pcie-uniphier-ep.c b/drivers/pci/controller/dwc/pcie-uniphier-ep.c
> > > index d6e73811216e..ddb5ff70340c 100644
> > > --- a/drivers/pci/controller/dwc/pcie-uniphier-ep.c
> > > +++ b/drivers/pci/controller/dwc/pcie-uniphier-ep.c
> > > @@ -82,7 +82,7 @@ struct uniphier_pcie_ep_soc_data {
> > > bool has_gio;
> > > void (*init)(struct uniphier_pcie_ep_priv *priv);
> > > int (*wait)(struct uniphier_pcie_ep_priv *priv);
> > > - const struct pci_epc_features features;
> > > + struct pci_epc_features *features;
> > > };
> > >
> > > #define to_uniphier_pcie(x) dev_get_drvdata((x)->dev)
> > > @@ -273,13 +273,13 @@ static int uniphier_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > return 0;
> > > }
> > >
> > > -static const struct pci_epc_features*
> > > +static struct pci_epc_features*
> > > uniphier_pcie_get_features(struct dw_pcie_ep *ep)
> > > {
> > > struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> > > struct uniphier_pcie_ep_priv *priv = to_uniphier_pcie(pci);
> > >
> > > - return &priv->data->features;
> > > + return priv->data->features;
> > > }
> > >
> > > static const struct dw_pcie_ep_ops uniphier_pcie_ep_ops = {
> > > @@ -415,40 +415,44 @@ static int uniphier_pcie_ep_probe(struct platform_device *pdev)
> > > return 0;
> > > }
> > >
> > > +static struct pci_epc_features uniphier_pro5_features = {
> > > + .linkup_notifier = false,
> > > + .msi_capable = true,
> > > + .msix_capable = false,
> > > + .align = 1 << 16,
> > > + .bar[BAR_0] = { .only_64bit = true, },
> > > + .bar[BAR_1] = { .type = BAR_RESERVED, },
> > > + .bar[BAR_2] = { .only_64bit = true, },
> > > + .bar[BAR_3] = { .type = BAR_RESERVED, },
> > > + .bar[BAR_4] = { .type = BAR_RESERVED, },
> > > + .bar[BAR_5] = { .type = BAR_RESERVED, },
> > > +};
> > > +
> > > +static struct pci_epc_features uniphier_nx1_features = {
> > > + .linkup_notifier = false,
> > > + .msi_capable = true,
> > > + .msix_capable = false,
> > > + .align = 1 << 12,
> > > + .bar[BAR_0] = { .only_64bit = true, },
> > > + .bar[BAR_1] = { .type = BAR_RESERVED, },
> > > + .bar[BAR_2] = { .only_64bit = true, },
> > > + .bar[BAR_3] = { .type = BAR_RESERVED, },
> > > + .bar[BAR_4] = { .only_64bit = true, },
> > > + .bar[BAR_5] = { .type = BAR_RESERVED, },
> > > +};
> > > +
> > > static const struct uniphier_pcie_ep_soc_data uniphier_pro5_data = {
> > > .has_gio = true,
> > > .init = uniphier_pcie_pro5_init_ep,
> > > .wait = NULL,
> > > - .features = {
> > > - .linkup_notifier = false,
> > > - .msi_capable = true,
> > > - .msix_capable = false,
> > > - .align = 1 << 16,
> > > - .bar[BAR_0] = { .only_64bit = true, },
> > > - .bar[BAR_1] = { .type = BAR_RESERVED, },
> > > - .bar[BAR_2] = { .only_64bit = true, },
> > > - .bar[BAR_3] = { .type = BAR_RESERVED, },
> > > - .bar[BAR_4] = { .type = BAR_RESERVED, },
> > > - .bar[BAR_5] = { .type = BAR_RESERVED, },
> > > - },
> > > + .features = &uniphier_pro5_features,
> > > };
> > >
> > > static const struct uniphier_pcie_ep_soc_data uniphier_nx1_data = {
> > > .has_gio = false,
> > > .init = uniphier_pcie_nx1_init_ep,
> > > .wait = uniphier_pcie_nx1_wait_ep,
> > > - .features = {
> > > - .linkup_notifier = false,
> > > - .msi_capable = true,
> > > - .msix_capable = false,
> > > - .align = 1 << 12,
> > > - .bar[BAR_0] = { .only_64bit = true, },
> > > - .bar[BAR_1] = { .type = BAR_RESERVED, },
> > > - .bar[BAR_2] = { .only_64bit = true, },
> > > - .bar[BAR_3] = { .type = BAR_RESERVED, },
> > > - .bar[BAR_4] = { .only_64bit = true, },
> > > - .bar[BAR_5] = { .type = BAR_RESERVED, },
> > > - },
> > > + .features = &uniphier_nx1_features,
> > > };
> > >
> > > static const struct of_device_id uniphier_pcie_ep_match[] = {
> > > --
> > > 2.51.0
> > >
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v7 3/6] PCI: dwc: Allow glue drivers to return mutable EPC features
2026-01-14 15:20 ` Frank Li
@ 2026-01-14 17:17 ` Koichiro Den
2026-01-14 19:44 ` Frank Li
0 siblings, 1 reply; 23+ messages in thread
From: Koichiro Den @ 2026-01-14 17:17 UTC (permalink / raw)
To: Frank Li
Cc: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas, cassel,
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,
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 Wed, Jan 14, 2026 at 10:20:08AM -0500, Frank Li wrote:
> On Wed, Jan 14, 2026 at 12:29:00PM +0900, Koichiro Den wrote:
> > On Tue, Jan 13, 2026 at 03:38:45PM -0500, Frank Li wrote:
> > > On Wed, Jan 14, 2026 at 01:27:16AM +0900, Koichiro Den wrote:
> > > > The DesignWare EP midlayer needs to advertise additional capabilities at
> > > > the DWC layer (e.g. subrange_mapping) without duplicating the same bit
> > > > in every DWC-based glue driver and without copying feature structures.
> > > >
> > > > Change dw_pcie_ep_ops.get_features() to return a mutable
> > > > struct pci_epc_features * and update all DWC-based glue drivers
> > > > accordingly. The DWC midlayer can then adjust/augment the returned
> > > > features while still exposing a const struct pci_epc_features * to the
> > > > PCI EPC core.
> > > >
> > > > No functional change on its own.
> > > >
> > > > Suggested-by: Niklas Cassel <cassel@kernel.org>
> > > > Reviewed-by: Niklas Cassel <cassel@kernel.org>
> > > > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> > > > ---
> > > > drivers/pci/controller/dwc/pci-dra7xx.c | 4 +-
> > > > drivers/pci/controller/dwc/pci-imx6.c | 10 ++--
> > > > drivers/pci/controller/dwc/pci-keystone.c | 4 +-
> > > > .../pci/controller/dwc/pci-layerscape-ep.c | 2 +-
> > > > drivers/pci/controller/dwc/pcie-artpec6.c | 4 +-
> > > > .../pci/controller/dwc/pcie-designware-plat.c | 4 +-
> > > > drivers/pci/controller/dwc/pcie-designware.h | 2 +-
> > > > drivers/pci/controller/dwc/pcie-dw-rockchip.c | 8 +--
> > > > drivers/pci/controller/dwc/pcie-keembay.c | 4 +-
> > > > drivers/pci/controller/dwc/pcie-qcom-ep.c | 4 +-
> > > > drivers/pci/controller/dwc/pcie-rcar-gen4.c | 4 +-
> > > > drivers/pci/controller/dwc/pcie-stm32-ep.c | 4 +-
> > > > drivers/pci/controller/dwc/pcie-tegra194.c | 4 +-
> > > > drivers/pci/controller/dwc/pcie-uniphier-ep.c | 58 ++++++++++---------
> > > > 14 files changed, 60 insertions(+), 56 deletions(-)
> > > >
> > > > diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c
> > > > index 01cfd9aeb0b8..e67f8b7b56cb 100644
> > > > --- a/drivers/pci/controller/dwc/pci-dra7xx.c
> > > > +++ b/drivers/pci/controller/dwc/pci-dra7xx.c
> > > > @@ -423,12 +423,12 @@ static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > > return 0;
> > > > }
> > > >
> > > > -static const struct pci_epc_features dra7xx_pcie_epc_features = {
> > > > +static struct pci_epc_features dra7xx_pcie_epc_features = {
> > > > .linkup_notifier = true,
> > > > .msi_capable = true,
> > > > };
> > > >
> > > > -static const struct pci_epc_features*
> > > > +static struct pci_epc_features*
> > > > dra7xx_pcie_get_features(struct dw_pcie_ep *ep)
> > > > {
> > > > return &dra7xx_pcie_epc_features;
> > > > diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
> > > > index 4668fc9648bf..fe1de30b3df6 100644
> > > > --- a/drivers/pci/controller/dwc/pci-imx6.c
> > > > +++ b/drivers/pci/controller/dwc/pci-imx6.c
> > > > @@ -131,7 +131,7 @@ struct imx_pcie_drvdata {
> > > > const u32 ltssm_mask;
> > > > const u32 mode_off[IMX_PCIE_MAX_INSTANCES];
> > > > const u32 mode_mask[IMX_PCIE_MAX_INSTANCES];
> > > > - const struct pci_epc_features *epc_features;
> > > > + struct pci_epc_features *epc_features;
> > > > int (*init_phy)(struct imx_pcie *pcie);
> > > > int (*enable_ref_clk)(struct imx_pcie *pcie, bool enable);
> > > > int (*core_reset)(struct imx_pcie *pcie, bool assert);
> > > > @@ -1386,7 +1386,7 @@ static int imx_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > > return 0;
> > > > }
> > > >
> > > > -static const struct pci_epc_features imx8m_pcie_epc_features = {
> > > > +static struct pci_epc_features imx8m_pcie_epc_features = {
> > > > .msi_capable = true,
> > > > .bar[BAR_1] = { .type = BAR_RESERVED, },
> > > > .bar[BAR_3] = { .type = BAR_RESERVED, },
> > > > @@ -1395,7 +1395,7 @@ static const struct pci_epc_features imx8m_pcie_epc_features = {
> > > > .align = SZ_64K,
> > > > };
> > > >
> > > > -static const struct pci_epc_features imx8q_pcie_epc_features = {
> > > > +static struct pci_epc_features imx8q_pcie_epc_features = {
> > >
> > > Is it more simple if
> > > #define DWC_EPC_DEFAULT .dynamtic_map = true
> > >
> > > Add
> > > DWC_EPC_DEFAULT, into every epc_features.
> >
> > One corner case is that pci-layerscape-ep.c builds the pci_epc_features
>
> It is our old platform. I checked code, it should be wrong. features should
> report EPC hardware capibility.
I'm not really sure whether commit cc255eb0bfbe ("PCI: layerscape: Modify
the way of getting capability with different PEX") was wrong.
Do you have a patch in mind?
>
> > dynamically in ls_pcie_ep_init(), rather than providing a static definition
> > with an initializer. I think setting (ie. overriding) the bit centrally in
> > dw_pcie_ep_get_features() keeps things simpler.
>
> In case one of chip have bugs, which need remove .dynamtic_map.
This is a good point.
Thanks,
Koichiro
> DWC_EPC_DEFAULT will be simple.
>
> Frank
> >
> > Thanks,
> > Koichiro
> >
> > >
> > >
> > > Frank
> > >
> > > > .msi_capable = true,
> > > > .bar[BAR_1] = { .type = BAR_RESERVED, },
> > > > .bar[BAR_3] = { .type = BAR_RESERVED, },
> > > > @@ -1415,13 +1415,13 @@ static const struct pci_epc_features imx8q_pcie_epc_features = {
> > > > * BAR4 | Enable | 32-bit | 1 MB | Programmable Size
> > > > * BAR5 | Enable | 32-bit | 64 KB | Programmable Size
> > > > */
> > > > -static const struct pci_epc_features imx95_pcie_epc_features = {
> > > > +static struct pci_epc_features imx95_pcie_epc_features = {
> > > > .msi_capable = true,
> > > > .bar[BAR_1] = { .type = BAR_FIXED, .fixed_size = SZ_64K, },
> > > > .align = SZ_4K,
> > > > };
> > > >
> > > > -static const struct pci_epc_features*
> > > > +static struct pci_epc_features*
> > > > imx_pcie_ep_get_features(struct dw_pcie_ep *ep)
> > > > {
> > > > struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> > > > diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c
> > > > index f86d9111f863..4292007a9b3a 100644
> > > > --- a/drivers/pci/controller/dwc/pci-keystone.c
> > > > +++ b/drivers/pci/controller/dwc/pci-keystone.c
> > > > @@ -929,7 +929,7 @@ static int ks_pcie_am654_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > > return 0;
> > > > }
> > > >
> > > > -static const struct pci_epc_features ks_pcie_am654_epc_features = {
> > > > +static struct pci_epc_features ks_pcie_am654_epc_features = {
> > > > .msi_capable = true,
> > > > .msix_capable = true,
> > > > .bar[BAR_0] = { .type = BAR_RESERVED, },
> > > > @@ -941,7 +941,7 @@ static const struct pci_epc_features ks_pcie_am654_epc_features = {
> > > > .align = SZ_64K,
> > > > };
> > > >
> > > > -static const struct pci_epc_features*
> > > > +static struct pci_epc_features*
> > > > ks_pcie_am654_get_features(struct dw_pcie_ep *ep)
> > > > {
> > > > return &ks_pcie_am654_epc_features;
> > > > diff --git a/drivers/pci/controller/dwc/pci-layerscape-ep.c b/drivers/pci/controller/dwc/pci-layerscape-ep.c
> > > > index a4a800699f89..8d48413050ef 100644
> > > > --- a/drivers/pci/controller/dwc/pci-layerscape-ep.c
> > > > +++ b/drivers/pci/controller/dwc/pci-layerscape-ep.c
> > > > @@ -138,7 +138,7 @@ static int ls_pcie_ep_interrupt_init(struct ls_pcie_ep *pcie,
> > > > return 0;
> > > > }
> > > >
> > > > -static const struct pci_epc_features*
> > > > +static struct pci_epc_features*
> > > > ls_pcie_ep_get_features(struct dw_pcie_ep *ep)
> > > > {
> > > > struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> > > > diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c
> > > > index f4a136ee2daf..84111d8257f2 100644
> > > > --- a/drivers/pci/controller/dwc/pcie-artpec6.c
> > > > +++ b/drivers/pci/controller/dwc/pcie-artpec6.c
> > > > @@ -369,11 +369,11 @@ static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > > return 0;
> > > > }
> > > >
> > > > -static const struct pci_epc_features artpec6_pcie_epc_features = {
> > > > +static struct pci_epc_features artpec6_pcie_epc_features = {
> > > > .msi_capable = true,
> > > > };
> > > >
> > > > -static const struct pci_epc_features *
> > > > +static struct pci_epc_features *
> > > > artpec6_pcie_get_features(struct dw_pcie_ep *ep)
> > > > {
> > > > return &artpec6_pcie_epc_features;
> > > > diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c
> > > > index 12f41886c65d..60ada0eb838e 100644
> > > > --- a/drivers/pci/controller/dwc/pcie-designware-plat.c
> > > > +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c
> > > > @@ -60,12 +60,12 @@ static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > > return 0;
> > > > }
> > > >
> > > > -static const struct pci_epc_features dw_plat_pcie_epc_features = {
> > > > +static struct pci_epc_features dw_plat_pcie_epc_features = {
> > > > .msi_capable = true,
> > > > .msix_capable = true,
> > > > };
> > > >
> > > > -static const struct pci_epc_features*
> > > > +static struct pci_epc_features*
> > > > dw_plat_pcie_get_features(struct dw_pcie_ep *ep)
> > > > {
> > > > return &dw_plat_pcie_epc_features;
> > > > diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
> > > > index f87c67a7a482..4dda9a38d46b 100644
> > > > --- a/drivers/pci/controller/dwc/pcie-designware.h
> > > > +++ b/drivers/pci/controller/dwc/pcie-designware.h
> > > > @@ -449,7 +449,7 @@ struct dw_pcie_ep_ops {
> > > > void (*init)(struct dw_pcie_ep *ep);
> > > > int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no,
> > > > unsigned int type, u16 interrupt_num);
> > > > - const struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep);
> > > > + struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep);
> > > > /*
> > > > * Provide a method to implement the different func config space
> > > > * access for different platform, if different func have different
> > > > diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
> > > > index 352f513ebf03..1f3c91368dc3 100644
> > > > --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c
> > > > +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
> > > > @@ -100,7 +100,7 @@ struct rockchip_pcie {
> > > >
> > > > struct rockchip_pcie_of_data {
> > > > enum dw_pcie_device_mode mode;
> > > > - const struct pci_epc_features *epc_features;
> > > > + struct pci_epc_features *epc_features;
> > > > };
> > > >
> > > > static int rockchip_pcie_readl_apb(struct rockchip_pcie *rockchip, u32 reg)
> > > > @@ -383,7 +383,7 @@ static int rockchip_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > > return 0;
> > > > }
> > > >
> > > > -static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = {
> > > > +static struct pci_epc_features rockchip_pcie_epc_features_rk3568 = {
> > > > .linkup_notifier = true,
> > > > .msi_capable = true,
> > > > .msix_capable = true,
> > > > @@ -403,7 +403,7 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = {
> > > > * default.) If the host could write to BAR4, the iATU settings (for all other
> > > > * BARs) would be overwritten, resulting in (all other BARs) no longer working.
> > > > */
> > > > -static const struct pci_epc_features rockchip_pcie_epc_features_rk3588 = {
> > > > +static struct pci_epc_features rockchip_pcie_epc_features_rk3588 = {
> > > > .linkup_notifier = true,
> > > > .msi_capable = true,
> > > > .msix_capable = true,
> > > > @@ -416,7 +416,7 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3588 = {
> > > > .bar[BAR_5] = { .type = BAR_RESIZABLE, },
> > > > };
> > > >
> > > > -static const struct pci_epc_features *
> > > > +static struct pci_epc_features *
> > > > rockchip_pcie_get_features(struct dw_pcie_ep *ep)
> > > > {
> > > > struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> > > > diff --git a/drivers/pci/controller/dwc/pcie-keembay.c b/drivers/pci/controller/dwc/pcie-keembay.c
> > > > index 60e74ac782af..e6de5289329f 100644
> > > > --- a/drivers/pci/controller/dwc/pcie-keembay.c
> > > > +++ b/drivers/pci/controller/dwc/pcie-keembay.c
> > > > @@ -308,7 +308,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 = {
> > > > +static struct pci_epc_features keembay_pcie_epc_features = {
> > > > .msi_capable = true,
> > > > .msix_capable = true,
> > > > .bar[BAR_0] = { .only_64bit = true, },
> > > > @@ -320,7 +320,7 @@ static const struct pci_epc_features keembay_pcie_epc_features = {
> > > > .align = SZ_16K,
> > > > };
> > > >
> > > > -static const struct pci_epc_features *
> > > > +static struct pci_epc_features *
> > > > keembay_pcie_get_features(struct dw_pcie_ep *ep)
> > > > {
> > > > return &keembay_pcie_epc_features;
> > > > diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c
> > > > index f1bc0ac81a92..6ad033301909 100644
> > > > --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c
> > > > +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c
> > > > @@ -819,7 +819,7 @@ static void qcom_pcie_ep_init_debugfs(struct qcom_pcie_ep *pcie_ep)
> > > > qcom_pcie_ep_link_transition_count);
> > > > }
> > > >
> > > > -static const struct pci_epc_features qcom_pcie_epc_features = {
> > > > +static struct pci_epc_features qcom_pcie_epc_features = {
> > > > .linkup_notifier = true,
> > > > .msi_capable = true,
> > > > .align = SZ_4K,
> > > > @@ -829,7 +829,7 @@ static const struct pci_epc_features qcom_pcie_epc_features = {
> > > > .bar[BAR_3] = { .type = BAR_RESERVED, },
> > > > };
> > > >
> > > > -static const struct pci_epc_features *
> > > > +static struct pci_epc_features *
> > > > qcom_pcie_epc_get_features(struct dw_pcie_ep *pci_ep)
> > > > {
> > > > return &qcom_pcie_epc_features;
> > > > diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> > > > index 80778917d2dd..ff0c4af90eff 100644
> > > > --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> > > > +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> > > > @@ -419,7 +419,7 @@ static int rcar_gen4_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > > return 0;
> > > > }
> > > >
> > > > -static const struct pci_epc_features rcar_gen4_pcie_epc_features = {
> > > > +static struct pci_epc_features rcar_gen4_pcie_epc_features = {
> > > > .msi_capable = true,
> > > > .bar[BAR_1] = { .type = BAR_RESERVED, },
> > > > .bar[BAR_3] = { .type = BAR_RESERVED, },
> > > > @@ -428,7 +428,7 @@ static const struct pci_epc_features rcar_gen4_pcie_epc_features = {
> > > > .align = SZ_1M,
> > > > };
> > > >
> > > > -static const struct pci_epc_features*
> > > > +static struct pci_epc_features*
> > > > rcar_gen4_pcie_ep_get_features(struct dw_pcie_ep *ep)
> > > > {
> > > > return &rcar_gen4_pcie_epc_features;
> > > > diff --git a/drivers/pci/controller/dwc/pcie-stm32-ep.c b/drivers/pci/controller/dwc/pcie-stm32-ep.c
> > > > index 2cecf32d2b0f..8a892def54f5 100644
> > > > --- a/drivers/pci/controller/dwc/pcie-stm32-ep.c
> > > > +++ b/drivers/pci/controller/dwc/pcie-stm32-ep.c
> > > > @@ -69,12 +69,12 @@ static int stm32_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > > }
> > > > }
> > > >
> > > > -static const struct pci_epc_features stm32_pcie_epc_features = {
> > > > +static struct pci_epc_features stm32_pcie_epc_features = {
> > > > .msi_capable = true,
> > > > .align = SZ_64K,
> > > > };
> > > >
> > > > -static const struct pci_epc_features*
> > > > +static struct pci_epc_features*
> > > > stm32_pcie_get_features(struct dw_pcie_ep *ep)
> > > > {
> > > > return &stm32_pcie_epc_features;
> > > > diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
> > > > index 0ddeef70726d..06f45a17e52c 100644
> > > > --- a/drivers/pci/controller/dwc/pcie-tegra194.c
> > > > +++ b/drivers/pci/controller/dwc/pcie-tegra194.c
> > > > @@ -1987,7 +1987,7 @@ static int tegra_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > > return 0;
> > > > }
> > > >
> > > > -static const struct pci_epc_features tegra_pcie_epc_features = {
> > > > +static struct pci_epc_features tegra_pcie_epc_features = {
> > > > .linkup_notifier = true,
> > > > .msi_capable = true,
> > > > .bar[BAR_0] = { .type = BAR_FIXED, .fixed_size = SZ_1M,
> > > > @@ -2000,7 +2000,7 @@ static const struct pci_epc_features tegra_pcie_epc_features = {
> > > > .align = SZ_64K,
> > > > };
> > > >
> > > > -static const struct pci_epc_features*
> > > > +static struct pci_epc_features*
> > > > tegra_pcie_ep_get_features(struct dw_pcie_ep *ep)
> > > > {
> > > > return &tegra_pcie_epc_features;
> > > > diff --git a/drivers/pci/controller/dwc/pcie-uniphier-ep.c b/drivers/pci/controller/dwc/pcie-uniphier-ep.c
> > > > index d6e73811216e..ddb5ff70340c 100644
> > > > --- a/drivers/pci/controller/dwc/pcie-uniphier-ep.c
> > > > +++ b/drivers/pci/controller/dwc/pcie-uniphier-ep.c
> > > > @@ -82,7 +82,7 @@ struct uniphier_pcie_ep_soc_data {
> > > > bool has_gio;
> > > > void (*init)(struct uniphier_pcie_ep_priv *priv);
> > > > int (*wait)(struct uniphier_pcie_ep_priv *priv);
> > > > - const struct pci_epc_features features;
> > > > + struct pci_epc_features *features;
> > > > };
> > > >
> > > > #define to_uniphier_pcie(x) dev_get_drvdata((x)->dev)
> > > > @@ -273,13 +273,13 @@ static int uniphier_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > > return 0;
> > > > }
> > > >
> > > > -static const struct pci_epc_features*
> > > > +static struct pci_epc_features*
> > > > uniphier_pcie_get_features(struct dw_pcie_ep *ep)
> > > > {
> > > > struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> > > > struct uniphier_pcie_ep_priv *priv = to_uniphier_pcie(pci);
> > > >
> > > > - return &priv->data->features;
> > > > + return priv->data->features;
> > > > }
> > > >
> > > > static const struct dw_pcie_ep_ops uniphier_pcie_ep_ops = {
> > > > @@ -415,40 +415,44 @@ static int uniphier_pcie_ep_probe(struct platform_device *pdev)
> > > > return 0;
> > > > }
> > > >
> > > > +static struct pci_epc_features uniphier_pro5_features = {
> > > > + .linkup_notifier = false,
> > > > + .msi_capable = true,
> > > > + .msix_capable = false,
> > > > + .align = 1 << 16,
> > > > + .bar[BAR_0] = { .only_64bit = true, },
> > > > + .bar[BAR_1] = { .type = BAR_RESERVED, },
> > > > + .bar[BAR_2] = { .only_64bit = true, },
> > > > + .bar[BAR_3] = { .type = BAR_RESERVED, },
> > > > + .bar[BAR_4] = { .type = BAR_RESERVED, },
> > > > + .bar[BAR_5] = { .type = BAR_RESERVED, },
> > > > +};
> > > > +
> > > > +static struct pci_epc_features uniphier_nx1_features = {
> > > > + .linkup_notifier = false,
> > > > + .msi_capable = true,
> > > > + .msix_capable = false,
> > > > + .align = 1 << 12,
> > > > + .bar[BAR_0] = { .only_64bit = true, },
> > > > + .bar[BAR_1] = { .type = BAR_RESERVED, },
> > > > + .bar[BAR_2] = { .only_64bit = true, },
> > > > + .bar[BAR_3] = { .type = BAR_RESERVED, },
> > > > + .bar[BAR_4] = { .only_64bit = true, },
> > > > + .bar[BAR_5] = { .type = BAR_RESERVED, },
> > > > +};
> > > > +
> > > > static const struct uniphier_pcie_ep_soc_data uniphier_pro5_data = {
> > > > .has_gio = true,
> > > > .init = uniphier_pcie_pro5_init_ep,
> > > > .wait = NULL,
> > > > - .features = {
> > > > - .linkup_notifier = false,
> > > > - .msi_capable = true,
> > > > - .msix_capable = false,
> > > > - .align = 1 << 16,
> > > > - .bar[BAR_0] = { .only_64bit = true, },
> > > > - .bar[BAR_1] = { .type = BAR_RESERVED, },
> > > > - .bar[BAR_2] = { .only_64bit = true, },
> > > > - .bar[BAR_3] = { .type = BAR_RESERVED, },
> > > > - .bar[BAR_4] = { .type = BAR_RESERVED, },
> > > > - .bar[BAR_5] = { .type = BAR_RESERVED, },
> > > > - },
> > > > + .features = &uniphier_pro5_features,
> > > > };
> > > >
> > > > static const struct uniphier_pcie_ep_soc_data uniphier_nx1_data = {
> > > > .has_gio = false,
> > > > .init = uniphier_pcie_nx1_init_ep,
> > > > .wait = uniphier_pcie_nx1_wait_ep,
> > > > - .features = {
> > > > - .linkup_notifier = false,
> > > > - .msi_capable = true,
> > > > - .msix_capable = false,
> > > > - .align = 1 << 12,
> > > > - .bar[BAR_0] = { .only_64bit = true, },
> > > > - .bar[BAR_1] = { .type = BAR_RESERVED, },
> > > > - .bar[BAR_2] = { .only_64bit = true, },
> > > > - .bar[BAR_3] = { .type = BAR_RESERVED, },
> > > > - .bar[BAR_4] = { .only_64bit = true, },
> > > > - .bar[BAR_5] = { .type = BAR_RESERVED, },
> > > > - },
> > > > + .features = &uniphier_nx1_features,
> > > > };
> > > >
> > > > static const struct of_device_id uniphier_pcie_ep_match[] = {
> > > > --
> > > > 2.51.0
> > > >
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v7 5/6] PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU
2026-01-14 10:39 ` Niklas Cassel
@ 2026-01-14 17:29 ` Koichiro Den
0 siblings, 0 replies; 23+ messages in thread
From: Koichiro Den @ 2026-01-14 17: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 Wed, Jan 14, 2026 at 11:39:03AM +0100, Niklas Cassel wrote:
> On Wed, Jan 14, 2026 at 12:54:37PM +0900, Koichiro Den wrote:
> > I realized that I missed one case in v7.
> >
> > I think dw_pcie_ep_clear_ib_maps() should also be called from
> > dw_pcie_ep_ib_atu_bar() to tear down any existing inbound mappings for the
> > same BAR before re-programming it in BAR Match Mode.
> >
> > This matters when updating inbound mappings for a BAR without resetting the
> > BAR in between. There are four possible transition patterns, and pattern #4
> > below was overlooked:
> >
> > 1. BAR Match Mode -> BAR Match Mode
> > As the current implementation does, the mapping is simply updated
> > (with the same atu index)
> >
> > 2. BAR Match Mode -> Address Match Mode
> > This patch series already ensures the old BAR Match mapping is
> > torn down before reprogramming.
> >
> > 3. Address Match Mode -> Address Match Mode
> > Likewise, existing Address Match mappings are cleared first.
> >
> > 4. Address Match Mode -> BAR Match Mode
> > This case was not handled. The change below adds the missing
> > teardown so that stale Address Match mappings do not remain active.
> >
> > --- a/drivers/pci/controller/dwc/pcie-designware-ep.c
> > +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
> > @@ -148,9 +148,12 @@ static int dw_pcie_ep_ib_atu_bar(struct dw_pcie_ep *ep, u8 func_no, int type,
> > u32 free_win;
> > struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> >
> > - if (!ep->bar_to_atu[bar])
> > + if (!ep->bar_to_atu[bar]) {
> > + /* Tear down existing mappings before (re)programming. */
> > + dw_pcie_ep_clear_ib_maps(ep, bar);
> > +
> > free_win = find_first_zero_bit(ep->ib_window_map,
> > pci->num_ib_windows);
> > - else
> > + } else
> > free_win = ep->bar_to_atu[bar] - 1;
>
> If one of the branches has braces, both branches should have braces:
> https://www.kernel.org/doc/html/latest/process/coding-style.html#placing-braces-and-spaces
>
>
> >
> > Unless there are objections, I'll include this fix in v8.
>
> Isn't it easier/cleaner if we call dw_pcie_ep_clear_ib_maps() in
> dw_pcie_ep_set_bar(), rather than calling it in both dw_pcie_ep_ib_atu_addr()
> and dw_pcie_ep_ib_atu_bar() ?
>
> dw_pcie_ep_set_bar() knows the condition if we are dynamically reprogramming
> a BAR or not, and all the four cases are when dynamically reprogramming a BAR.
>
> I.e. instead of adding additional code to dw_pcie_ep_ib_atu_bar(), we do
> something like:
>
> diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
> index b2ea2c2c986f..63ae5471fe13 100644
> --- a/drivers/pci/controller/dwc/pcie-designware-ep.c
> +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
> @@ -318,9 +318,6 @@ static int dw_pcie_ep_ib_atu_addr(struct dw_pcie_ep *ep, u8 func_no, int type,
> 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;
> @@ -571,6 +568,9 @@ 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;
>
> + 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.
>
For pattern #2 and #3 (ie. either mode -> Address Match Mode), the v7 code
withholds the dw_pcie_ep_clear_ib_maps() call unless the submap validation
passes. The above patch differs slightly in that sense, but I agree it
looks much simpler. I don't think the difference matters much, since
pci_epc_set_bar() with an invalid submap should already indicate that
something has gone wrong (most likely a bug in the API call site). So I
think I'll go with your suggestion.
Thanks!
Koichiro
>
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v7 3/6] PCI: dwc: Allow glue drivers to return mutable EPC features
2026-01-14 17:17 ` Koichiro Den
@ 2026-01-14 19:44 ` Frank Li
2026-01-14 20:13 ` Niklas Cassel
0 siblings, 1 reply; 23+ messages in thread
From: Frank Li @ 2026-01-14 19:44 UTC (permalink / raw)
To: Koichiro Den
Cc: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas, cassel,
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,
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 15, 2026 at 02:17:07AM +0900, Koichiro Den wrote:
> On Wed, Jan 14, 2026 at 10:20:08AM -0500, Frank Li wrote:
> > On Wed, Jan 14, 2026 at 12:29:00PM +0900, Koichiro Den wrote:
> > > On Tue, Jan 13, 2026 at 03:38:45PM -0500, Frank Li wrote:
> > > > On Wed, Jan 14, 2026 at 01:27:16AM +0900, Koichiro Den wrote:
> > > > > The DesignWare EP midlayer needs to advertise additional capabilities at
> > > > > the DWC layer (e.g. subrange_mapping) without duplicating the same bit
> > > > > in every DWC-based glue driver and without copying feature structures.
> > > > >
> > > > > Change dw_pcie_ep_ops.get_features() to return a mutable
> > > > > struct pci_epc_features * and update all DWC-based glue drivers
> > > > > accordingly. The DWC midlayer can then adjust/augment the returned
> > > > > features while still exposing a const struct pci_epc_features * to the
> > > > > PCI EPC core.
> > > > >
> > > > > No functional change on its own.
> > > > >
> > > > > Suggested-by: Niklas Cassel <cassel@kernel.org>
> > > > > Reviewed-by: Niklas Cassel <cassel@kernel.org>
> > > > > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> > > > > ---
> > > > > drivers/pci/controller/dwc/pci-dra7xx.c | 4 +-
> > > > > drivers/pci/controller/dwc/pci-imx6.c | 10 ++--
> > > > > drivers/pci/controller/dwc/pci-keystone.c | 4 +-
> > > > > .../pci/controller/dwc/pci-layerscape-ep.c | 2 +-
> > > > > drivers/pci/controller/dwc/pcie-artpec6.c | 4 +-
> > > > > .../pci/controller/dwc/pcie-designware-plat.c | 4 +-
> > > > > drivers/pci/controller/dwc/pcie-designware.h | 2 +-
> > > > > drivers/pci/controller/dwc/pcie-dw-rockchip.c | 8 +--
> > > > > drivers/pci/controller/dwc/pcie-keembay.c | 4 +-
> > > > > drivers/pci/controller/dwc/pcie-qcom-ep.c | 4 +-
> > > > > drivers/pci/controller/dwc/pcie-rcar-gen4.c | 4 +-
> > > > > drivers/pci/controller/dwc/pcie-stm32-ep.c | 4 +-
> > > > > drivers/pci/controller/dwc/pcie-tegra194.c | 4 +-
> > > > > drivers/pci/controller/dwc/pcie-uniphier-ep.c | 58 ++++++++++---------
> > > > > 14 files changed, 60 insertions(+), 56 deletions(-)
> > > > >
> > > > > diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c
> > > > > index 01cfd9aeb0b8..e67f8b7b56cb 100644
> > > > > --- a/drivers/pci/controller/dwc/pci-dra7xx.c
> > > > > +++ b/drivers/pci/controller/dwc/pci-dra7xx.c
> > > > > @@ -423,12 +423,12 @@ static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > > > return 0;
> > > > > }
> > > > >
> > > > > -static const struct pci_epc_features dra7xx_pcie_epc_features = {
> > > > > +static struct pci_epc_features dra7xx_pcie_epc_features = {
> > > > > .linkup_notifier = true,
> > > > > .msi_capable = true,
> > > > > };
> > > > >
> > > > > -static const struct pci_epc_features*
> > > > > +static struct pci_epc_features*
> > > > > dra7xx_pcie_get_features(struct dw_pcie_ep *ep)
> > > > > {
> > > > > return &dra7xx_pcie_epc_features;
> > > > > diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
> > > > > index 4668fc9648bf..fe1de30b3df6 100644
> > > > > --- a/drivers/pci/controller/dwc/pci-imx6.c
> > > > > +++ b/drivers/pci/controller/dwc/pci-imx6.c
> > > > > @@ -131,7 +131,7 @@ struct imx_pcie_drvdata {
> > > > > const u32 ltssm_mask;
> > > > > const u32 mode_off[IMX_PCIE_MAX_INSTANCES];
> > > > > const u32 mode_mask[IMX_PCIE_MAX_INSTANCES];
> > > > > - const struct pci_epc_features *epc_features;
> > > > > + struct pci_epc_features *epc_features;
> > > > > int (*init_phy)(struct imx_pcie *pcie);
> > > > > int (*enable_ref_clk)(struct imx_pcie *pcie, bool enable);
> > > > > int (*core_reset)(struct imx_pcie *pcie, bool assert);
> > > > > @@ -1386,7 +1386,7 @@ static int imx_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > > > return 0;
> > > > > }
> > > > >
> > > > > -static const struct pci_epc_features imx8m_pcie_epc_features = {
> > > > > +static struct pci_epc_features imx8m_pcie_epc_features = {
> > > > > .msi_capable = true,
> > > > > .bar[BAR_1] = { .type = BAR_RESERVED, },
> > > > > .bar[BAR_3] = { .type = BAR_RESERVED, },
> > > > > @@ -1395,7 +1395,7 @@ static const struct pci_epc_features imx8m_pcie_epc_features = {
> > > > > .align = SZ_64K,
> > > > > };
> > > > >
> > > > > -static const struct pci_epc_features imx8q_pcie_epc_features = {
> > > > > +static struct pci_epc_features imx8q_pcie_epc_features = {
> > > >
> > > > Is it more simple if
> > > > #define DWC_EPC_DEFAULT .dynamtic_map = true
> > > >
> > > > Add
> > > > DWC_EPC_DEFAULT, into every epc_features.
> > >
> > > One corner case is that pci-layerscape-ep.c builds the pci_epc_features
> >
> > It is our old platform. I checked code, it should be wrong. features should
> > report EPC hardware capibility.
>
> I'm not really sure whether commit cc255eb0bfbe ("PCI: layerscape: Modify
> the way of getting capability with different PEX") was wrong.
> Do you have a patch in mind?
I think it's wrong. And it doesn't impact your patch. Just leave it as it.
we can add submap support later if need.
Frank
>
> >
> > > dynamically in ls_pcie_ep_init(), rather than providing a static definition
> > > with an initializer. I think setting (ie. overriding) the bit centrally in
> > > dw_pcie_ep_get_features() keeps things simpler.
> >
> > In case one of chip have bugs, which need remove .dynamtic_map.
>
> This is a good point.
>
> Thanks,
> Koichiro
>
> > DWC_EPC_DEFAULT will be simple.
> >
> > Frank
> > >
> > > Thanks,
> > > Koichiro
> > >
> > > >
> > > >
> > > > Frank
> > > >
> > > > > .msi_capable = true,
> > > > > .bar[BAR_1] = { .type = BAR_RESERVED, },
> > > > > .bar[BAR_3] = { .type = BAR_RESERVED, },
> > > > > @@ -1415,13 +1415,13 @@ static const struct pci_epc_features imx8q_pcie_epc_features = {
> > > > > * BAR4 | Enable | 32-bit | 1 MB | Programmable Size
> > > > > * BAR5 | Enable | 32-bit | 64 KB | Programmable Size
> > > > > */
> > > > > -static const struct pci_epc_features imx95_pcie_epc_features = {
> > > > > +static struct pci_epc_features imx95_pcie_epc_features = {
> > > > > .msi_capable = true,
> > > > > .bar[BAR_1] = { .type = BAR_FIXED, .fixed_size = SZ_64K, },
> > > > > .align = SZ_4K,
> > > > > };
> > > > >
> > > > > -static const struct pci_epc_features*
> > > > > +static struct pci_epc_features*
> > > > > imx_pcie_ep_get_features(struct dw_pcie_ep *ep)
> > > > > {
> > > > > struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> > > > > diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c
> > > > > index f86d9111f863..4292007a9b3a 100644
> > > > > --- a/drivers/pci/controller/dwc/pci-keystone.c
> > > > > +++ b/drivers/pci/controller/dwc/pci-keystone.c
> > > > > @@ -929,7 +929,7 @@ static int ks_pcie_am654_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > > > return 0;
> > > > > }
> > > > >
> > > > > -static const struct pci_epc_features ks_pcie_am654_epc_features = {
> > > > > +static struct pci_epc_features ks_pcie_am654_epc_features = {
> > > > > .msi_capable = true,
> > > > > .msix_capable = true,
> > > > > .bar[BAR_0] = { .type = BAR_RESERVED, },
> > > > > @@ -941,7 +941,7 @@ static const struct pci_epc_features ks_pcie_am654_epc_features = {
> > > > > .align = SZ_64K,
> > > > > };
> > > > >
> > > > > -static const struct pci_epc_features*
> > > > > +static struct pci_epc_features*
> > > > > ks_pcie_am654_get_features(struct dw_pcie_ep *ep)
> > > > > {
> > > > > return &ks_pcie_am654_epc_features;
> > > > > diff --git a/drivers/pci/controller/dwc/pci-layerscape-ep.c b/drivers/pci/controller/dwc/pci-layerscape-ep.c
> > > > > index a4a800699f89..8d48413050ef 100644
> > > > > --- a/drivers/pci/controller/dwc/pci-layerscape-ep.c
> > > > > +++ b/drivers/pci/controller/dwc/pci-layerscape-ep.c
> > > > > @@ -138,7 +138,7 @@ static int ls_pcie_ep_interrupt_init(struct ls_pcie_ep *pcie,
> > > > > return 0;
> > > > > }
> > > > >
> > > > > -static const struct pci_epc_features*
> > > > > +static struct pci_epc_features*
> > > > > ls_pcie_ep_get_features(struct dw_pcie_ep *ep)
> > > > > {
> > > > > struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> > > > > diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c
> > > > > index f4a136ee2daf..84111d8257f2 100644
> > > > > --- a/drivers/pci/controller/dwc/pcie-artpec6.c
> > > > > +++ b/drivers/pci/controller/dwc/pcie-artpec6.c
> > > > > @@ -369,11 +369,11 @@ static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > > > return 0;
> > > > > }
> > > > >
> > > > > -static const struct pci_epc_features artpec6_pcie_epc_features = {
> > > > > +static struct pci_epc_features artpec6_pcie_epc_features = {
> > > > > .msi_capable = true,
> > > > > };
> > > > >
> > > > > -static const struct pci_epc_features *
> > > > > +static struct pci_epc_features *
> > > > > artpec6_pcie_get_features(struct dw_pcie_ep *ep)
> > > > > {
> > > > > return &artpec6_pcie_epc_features;
> > > > > diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c
> > > > > index 12f41886c65d..60ada0eb838e 100644
> > > > > --- a/drivers/pci/controller/dwc/pcie-designware-plat.c
> > > > > +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c
> > > > > @@ -60,12 +60,12 @@ static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > > > return 0;
> > > > > }
> > > > >
> > > > > -static const struct pci_epc_features dw_plat_pcie_epc_features = {
> > > > > +static struct pci_epc_features dw_plat_pcie_epc_features = {
> > > > > .msi_capable = true,
> > > > > .msix_capable = true,
> > > > > };
> > > > >
> > > > > -static const struct pci_epc_features*
> > > > > +static struct pci_epc_features*
> > > > > dw_plat_pcie_get_features(struct dw_pcie_ep *ep)
> > > > > {
> > > > > return &dw_plat_pcie_epc_features;
> > > > > diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
> > > > > index f87c67a7a482..4dda9a38d46b 100644
> > > > > --- a/drivers/pci/controller/dwc/pcie-designware.h
> > > > > +++ b/drivers/pci/controller/dwc/pcie-designware.h
> > > > > @@ -449,7 +449,7 @@ struct dw_pcie_ep_ops {
> > > > > void (*init)(struct dw_pcie_ep *ep);
> > > > > int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no,
> > > > > unsigned int type, u16 interrupt_num);
> > > > > - const struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep);
> > > > > + struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep);
> > > > > /*
> > > > > * Provide a method to implement the different func config space
> > > > > * access for different platform, if different func have different
> > > > > diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
> > > > > index 352f513ebf03..1f3c91368dc3 100644
> > > > > --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c
> > > > > +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
> > > > > @@ -100,7 +100,7 @@ struct rockchip_pcie {
> > > > >
> > > > > struct rockchip_pcie_of_data {
> > > > > enum dw_pcie_device_mode mode;
> > > > > - const struct pci_epc_features *epc_features;
> > > > > + struct pci_epc_features *epc_features;
> > > > > };
> > > > >
> > > > > static int rockchip_pcie_readl_apb(struct rockchip_pcie *rockchip, u32 reg)
> > > > > @@ -383,7 +383,7 @@ static int rockchip_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > > > return 0;
> > > > > }
> > > > >
> > > > > -static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = {
> > > > > +static struct pci_epc_features rockchip_pcie_epc_features_rk3568 = {
> > > > > .linkup_notifier = true,
> > > > > .msi_capable = true,
> > > > > .msix_capable = true,
> > > > > @@ -403,7 +403,7 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = {
> > > > > * default.) If the host could write to BAR4, the iATU settings (for all other
> > > > > * BARs) would be overwritten, resulting in (all other BARs) no longer working.
> > > > > */
> > > > > -static const struct pci_epc_features rockchip_pcie_epc_features_rk3588 = {
> > > > > +static struct pci_epc_features rockchip_pcie_epc_features_rk3588 = {
> > > > > .linkup_notifier = true,
> > > > > .msi_capable = true,
> > > > > .msix_capable = true,
> > > > > @@ -416,7 +416,7 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3588 = {
> > > > > .bar[BAR_5] = { .type = BAR_RESIZABLE, },
> > > > > };
> > > > >
> > > > > -static const struct pci_epc_features *
> > > > > +static struct pci_epc_features *
> > > > > rockchip_pcie_get_features(struct dw_pcie_ep *ep)
> > > > > {
> > > > > struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> > > > > diff --git a/drivers/pci/controller/dwc/pcie-keembay.c b/drivers/pci/controller/dwc/pcie-keembay.c
> > > > > index 60e74ac782af..e6de5289329f 100644
> > > > > --- a/drivers/pci/controller/dwc/pcie-keembay.c
> > > > > +++ b/drivers/pci/controller/dwc/pcie-keembay.c
> > > > > @@ -308,7 +308,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 = {
> > > > > +static struct pci_epc_features keembay_pcie_epc_features = {
> > > > > .msi_capable = true,
> > > > > .msix_capable = true,
> > > > > .bar[BAR_0] = { .only_64bit = true, },
> > > > > @@ -320,7 +320,7 @@ static const struct pci_epc_features keembay_pcie_epc_features = {
> > > > > .align = SZ_16K,
> > > > > };
> > > > >
> > > > > -static const struct pci_epc_features *
> > > > > +static struct pci_epc_features *
> > > > > keembay_pcie_get_features(struct dw_pcie_ep *ep)
> > > > > {
> > > > > return &keembay_pcie_epc_features;
> > > > > diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c
> > > > > index f1bc0ac81a92..6ad033301909 100644
> > > > > --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c
> > > > > +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c
> > > > > @@ -819,7 +819,7 @@ static void qcom_pcie_ep_init_debugfs(struct qcom_pcie_ep *pcie_ep)
> > > > > qcom_pcie_ep_link_transition_count);
> > > > > }
> > > > >
> > > > > -static const struct pci_epc_features qcom_pcie_epc_features = {
> > > > > +static struct pci_epc_features qcom_pcie_epc_features = {
> > > > > .linkup_notifier = true,
> > > > > .msi_capable = true,
> > > > > .align = SZ_4K,
> > > > > @@ -829,7 +829,7 @@ static const struct pci_epc_features qcom_pcie_epc_features = {
> > > > > .bar[BAR_3] = { .type = BAR_RESERVED, },
> > > > > };
> > > > >
> > > > > -static const struct pci_epc_features *
> > > > > +static struct pci_epc_features *
> > > > > qcom_pcie_epc_get_features(struct dw_pcie_ep *pci_ep)
> > > > > {
> > > > > return &qcom_pcie_epc_features;
> > > > > diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> > > > > index 80778917d2dd..ff0c4af90eff 100644
> > > > > --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> > > > > +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
> > > > > @@ -419,7 +419,7 @@ static int rcar_gen4_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > > > return 0;
> > > > > }
> > > > >
> > > > > -static const struct pci_epc_features rcar_gen4_pcie_epc_features = {
> > > > > +static struct pci_epc_features rcar_gen4_pcie_epc_features = {
> > > > > .msi_capable = true,
> > > > > .bar[BAR_1] = { .type = BAR_RESERVED, },
> > > > > .bar[BAR_3] = { .type = BAR_RESERVED, },
> > > > > @@ -428,7 +428,7 @@ static const struct pci_epc_features rcar_gen4_pcie_epc_features = {
> > > > > .align = SZ_1M,
> > > > > };
> > > > >
> > > > > -static const struct pci_epc_features*
> > > > > +static struct pci_epc_features*
> > > > > rcar_gen4_pcie_ep_get_features(struct dw_pcie_ep *ep)
> > > > > {
> > > > > return &rcar_gen4_pcie_epc_features;
> > > > > diff --git a/drivers/pci/controller/dwc/pcie-stm32-ep.c b/drivers/pci/controller/dwc/pcie-stm32-ep.c
> > > > > index 2cecf32d2b0f..8a892def54f5 100644
> > > > > --- a/drivers/pci/controller/dwc/pcie-stm32-ep.c
> > > > > +++ b/drivers/pci/controller/dwc/pcie-stm32-ep.c
> > > > > @@ -69,12 +69,12 @@ static int stm32_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > > > }
> > > > > }
> > > > >
> > > > > -static const struct pci_epc_features stm32_pcie_epc_features = {
> > > > > +static struct pci_epc_features stm32_pcie_epc_features = {
> > > > > .msi_capable = true,
> > > > > .align = SZ_64K,
> > > > > };
> > > > >
> > > > > -static const struct pci_epc_features*
> > > > > +static struct pci_epc_features*
> > > > > stm32_pcie_get_features(struct dw_pcie_ep *ep)
> > > > > {
> > > > > return &stm32_pcie_epc_features;
> > > > > diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
> > > > > index 0ddeef70726d..06f45a17e52c 100644
> > > > > --- a/drivers/pci/controller/dwc/pcie-tegra194.c
> > > > > +++ b/drivers/pci/controller/dwc/pcie-tegra194.c
> > > > > @@ -1987,7 +1987,7 @@ static int tegra_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > > > return 0;
> > > > > }
> > > > >
> > > > > -static const struct pci_epc_features tegra_pcie_epc_features = {
> > > > > +static struct pci_epc_features tegra_pcie_epc_features = {
> > > > > .linkup_notifier = true,
> > > > > .msi_capable = true,
> > > > > .bar[BAR_0] = { .type = BAR_FIXED, .fixed_size = SZ_1M,
> > > > > @@ -2000,7 +2000,7 @@ static const struct pci_epc_features tegra_pcie_epc_features = {
> > > > > .align = SZ_64K,
> > > > > };
> > > > >
> > > > > -static const struct pci_epc_features*
> > > > > +static struct pci_epc_features*
> > > > > tegra_pcie_ep_get_features(struct dw_pcie_ep *ep)
> > > > > {
> > > > > return &tegra_pcie_epc_features;
> > > > > diff --git a/drivers/pci/controller/dwc/pcie-uniphier-ep.c b/drivers/pci/controller/dwc/pcie-uniphier-ep.c
> > > > > index d6e73811216e..ddb5ff70340c 100644
> > > > > --- a/drivers/pci/controller/dwc/pcie-uniphier-ep.c
> > > > > +++ b/drivers/pci/controller/dwc/pcie-uniphier-ep.c
> > > > > @@ -82,7 +82,7 @@ struct uniphier_pcie_ep_soc_data {
> > > > > bool has_gio;
> > > > > void (*init)(struct uniphier_pcie_ep_priv *priv);
> > > > > int (*wait)(struct uniphier_pcie_ep_priv *priv);
> > > > > - const struct pci_epc_features features;
> > > > > + struct pci_epc_features *features;
> > > > > };
> > > > >
> > > > > #define to_uniphier_pcie(x) dev_get_drvdata((x)->dev)
> > > > > @@ -273,13 +273,13 @@ static int uniphier_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > > > return 0;
> > > > > }
> > > > >
> > > > > -static const struct pci_epc_features*
> > > > > +static struct pci_epc_features*
> > > > > uniphier_pcie_get_features(struct dw_pcie_ep *ep)
> > > > > {
> > > > > struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> > > > > struct uniphier_pcie_ep_priv *priv = to_uniphier_pcie(pci);
> > > > >
> > > > > - return &priv->data->features;
> > > > > + return priv->data->features;
> > > > > }
> > > > >
> > > > > static const struct dw_pcie_ep_ops uniphier_pcie_ep_ops = {
> > > > > @@ -415,40 +415,44 @@ static int uniphier_pcie_ep_probe(struct platform_device *pdev)
> > > > > return 0;
> > > > > }
> > > > >
> > > > > +static struct pci_epc_features uniphier_pro5_features = {
> > > > > + .linkup_notifier = false,
> > > > > + .msi_capable = true,
> > > > > + .msix_capable = false,
> > > > > + .align = 1 << 16,
> > > > > + .bar[BAR_0] = { .only_64bit = true, },
> > > > > + .bar[BAR_1] = { .type = BAR_RESERVED, },
> > > > > + .bar[BAR_2] = { .only_64bit = true, },
> > > > > + .bar[BAR_3] = { .type = BAR_RESERVED, },
> > > > > + .bar[BAR_4] = { .type = BAR_RESERVED, },
> > > > > + .bar[BAR_5] = { .type = BAR_RESERVED, },
> > > > > +};
> > > > > +
> > > > > +static struct pci_epc_features uniphier_nx1_features = {
> > > > > + .linkup_notifier = false,
> > > > > + .msi_capable = true,
> > > > > + .msix_capable = false,
> > > > > + .align = 1 << 12,
> > > > > + .bar[BAR_0] = { .only_64bit = true, },
> > > > > + .bar[BAR_1] = { .type = BAR_RESERVED, },
> > > > > + .bar[BAR_2] = { .only_64bit = true, },
> > > > > + .bar[BAR_3] = { .type = BAR_RESERVED, },
> > > > > + .bar[BAR_4] = { .only_64bit = true, },
> > > > > + .bar[BAR_5] = { .type = BAR_RESERVED, },
> > > > > +};
> > > > > +
> > > > > static const struct uniphier_pcie_ep_soc_data uniphier_pro5_data = {
> > > > > .has_gio = true,
> > > > > .init = uniphier_pcie_pro5_init_ep,
> > > > > .wait = NULL,
> > > > > - .features = {
> > > > > - .linkup_notifier = false,
> > > > > - .msi_capable = true,
> > > > > - .msix_capable = false,
> > > > > - .align = 1 << 16,
> > > > > - .bar[BAR_0] = { .only_64bit = true, },
> > > > > - .bar[BAR_1] = { .type = BAR_RESERVED, },
> > > > > - .bar[BAR_2] = { .only_64bit = true, },
> > > > > - .bar[BAR_3] = { .type = BAR_RESERVED, },
> > > > > - .bar[BAR_4] = { .type = BAR_RESERVED, },
> > > > > - .bar[BAR_5] = { .type = BAR_RESERVED, },
> > > > > - },
> > > > > + .features = &uniphier_pro5_features,
> > > > > };
> > > > >
> > > > > static const struct uniphier_pcie_ep_soc_data uniphier_nx1_data = {
> > > > > .has_gio = false,
> > > > > .init = uniphier_pcie_nx1_init_ep,
> > > > > .wait = uniphier_pcie_nx1_wait_ep,
> > > > > - .features = {
> > > > > - .linkup_notifier = false,
> > > > > - .msi_capable = true,
> > > > > - .msix_capable = false,
> > > > > - .align = 1 << 12,
> > > > > - .bar[BAR_0] = { .only_64bit = true, },
> > > > > - .bar[BAR_1] = { .type = BAR_RESERVED, },
> > > > > - .bar[BAR_2] = { .only_64bit = true, },
> > > > > - .bar[BAR_3] = { .type = BAR_RESERVED, },
> > > > > - .bar[BAR_4] = { .only_64bit = true, },
> > > > > - .bar[BAR_5] = { .type = BAR_RESERVED, },
> > > > > - },
> > > > > + .features = &uniphier_nx1_features,
> > > > > };
> > > > >
> > > > > static const struct of_device_id uniphier_pcie_ep_match[] = {
> > > > > --
> > > > > 2.51.0
> > > > >
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v7 3/6] PCI: dwc: Allow glue drivers to return mutable EPC features
2026-01-14 19:44 ` Frank Li
@ 2026-01-14 20:13 ` Niklas Cassel
0 siblings, 0 replies; 23+ messages in thread
From: Niklas Cassel @ 2026-01-14 20:13 UTC (permalink / raw)
To: Frank Li
Cc: Koichiro Den, 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,
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 Wed, Jan 14, 2026 at 02:44:12PM -0500, Frank Li wrote:
> > > > > Add
> > > > > DWC_EPC_DEFAULT, into every epc_features.
> > > >
> > > > One corner case is that pci-layerscape-ep.c builds the pci_epc_features
> > >
> > > It is our old platform. I checked code, it should be wrong. features should
> > > report EPC hardware capibility.
> >
> > I'm not really sure whether commit cc255eb0bfbe ("PCI: layerscape: Modify
> > the way of getting capability with different PEX") was wrong.
> > Do you have a patch in mind?
>
> I think it's wrong. And it doesn't impact your patch. Just leave it as it.
> we can add submap support later if need.
Adding a macro:
#define DWC_EPC_COMMON_FEATURES .dynamic_inbound_mapping = true
And including it in all the DWC glue drivers' epc_features sounds
fine to me.
Frank does have a point that if some glue driver ever has some random
errata or bad integration, it will be simpler for a single glue driver
to override the defaults.
The commit that adds subrange mapping support would then just
change the macro to something like:
#define DWC_EPC_COMMON_FEATURES .dynamic_inbound_mapping = true, \
.subrange_mapping = true
Kind regards,
Niklas
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v7 5/6] PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU
2026-01-13 20:53 ` Frank Li
@ 2026-01-15 8:48 ` Koichiro Den
0 siblings, 0 replies; 23+ messages in thread
From: Koichiro Den @ 2026-01-15 8:48 UTC (permalink / raw)
To: Frank Li
Cc: jingoohan1, mani, lpieralisi, kwilczynski, robh, bhelgaas, cassel,
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,
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 Tue, Jan 13, 2026 at 03:53:52PM -0500, Frank Li wrote:
> On Wed, Jan 14, 2026 at 01:27:18AM +0900, Koichiro Den wrote:
> > 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.
> >
> > Advertise this capability by setting subrange_mapping in the EPC
> > features returned from dw_pcie_ep_get_features().
> >
> > Reviewed-by: Niklas Cassel <cassel@kernel.org>
> > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> > ---
> > .../pci/controller/dwc/pcie-designware-ep.c | 230 +++++++++++++++++-
> > drivers/pci/controller/dwc/pcie-designware.h | 2 +
> > 2 files changed, 222 insertions(+), 10 deletions(-)
> >
> > diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
> > index 0e5a8d200b00..b2ea2c2c986f 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;
> > }
> >
> ...
> > +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;
>
> submap[i].offset is unnecessary, you can use expected += size as off.
> code logic will be simple.
Will fix this. as per my earlier response:
https://lore.kernel.org/all/ngvqrju3bi6sugynhksxsci6rmgqevzpoijjflp2373c6uxlum@vyepxqghbzvn/
>
> Frank
> > +
> > + 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;
> > + }
>
> Simple alloc an array struct dw_pcie_ib_map[num_submap] should be simpler
> than link list and alloc some small news.
I'll do so in v8. Thank you for the review!
Koichiro
>
> Frank
> > +
> > + 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,8 +609,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->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)
> > return ret;
> >
> > @@ -638,6 +843,9 @@ dw_pcie_ep_get_features(struct pci_epc *epc, u8 func_no, u8 vfunc_no)
> > /* All DWC-based glue drivers support dynamic inbound mapping */
> > features->dynamic_inbound_mapping = true;
> >
> > + /* All DWC-based glue drivers support inbound subrange mapping */
> > + features->subrange_mapping = true;
> > +
> > return features;
> > }
> >
> > @@ -1128,6 +1336,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 4dda9a38d46b..969b1f32dddf 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 [flat|nested] 23+ messages in thread
end of thread, other threads:[~2026-01-15 8:48 UTC | newest]
Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-13 16:27 [PATCH v7 0/6] PCI: endpoint: BAR subrange mapping support Koichiro Den
2026-01-13 16:27 ` [PATCH v7 1/6] PCI: endpoint: Add dynamic_inbound_mapping EPC feature Koichiro Den
2026-01-13 20:19 ` Frank Li
2026-01-13 16:27 ` [PATCH v7 2/6] PCI: endpoint: Add BAR subrange mapping support Koichiro Den
2026-01-13 20:26 ` Frank Li
2026-01-14 3:22 ` Koichiro Den
2026-01-13 16:27 ` [PATCH v7 3/6] PCI: dwc: Allow glue drivers to return mutable EPC features Koichiro Den
2026-01-13 20:38 ` Frank Li
2026-01-14 3:29 ` Koichiro Den
2026-01-14 15:20 ` Frank Li
2026-01-14 17:17 ` Koichiro Den
2026-01-14 19:44 ` Frank Li
2026-01-14 20:13 ` Niklas Cassel
2026-01-13 16:27 ` [PATCH v7 4/6] PCI: dwc: Advertise dynamic inbound mapping support Koichiro Den
2026-01-13 18:44 ` Niklas Cassel
2026-01-13 16:27 ` [PATCH v7 5/6] PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU Koichiro Den
2026-01-13 20:53 ` Frank Li
2026-01-15 8:48 ` Koichiro Den
2026-01-14 3:54 ` Koichiro Den
2026-01-14 10:39 ` Niklas Cassel
2026-01-14 17:29 ` Koichiro Den
2026-01-13 16:27 ` [PATCH v7 6/6] Documentation: PCI: endpoint: Clarify pci_epc_set_bar() usage Koichiro Den
2026-01-13 20:56 ` Frank Li
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox