[PATCH 1/3] PCI: dwc: Add Endpoint reinit helper

From: Manikanta Maddireddy

Date: Wed Sep 23 2026 - 03:34:21 EST


Endpoint controllers can receive a fundamental reset while software
resources such as eDMA, debugfs and function state must live for the
controller lifetime.

Track whether DWC Endpoint resources are initialized and add helpers
that perform full initialization once, then reinitialize only reset-lost
registers on later fundamental reset cycles.

A fundamental reset also clears outbound iATU programming. Clear the
cached outbound window map, outbound address array and MSI iATU cache
before reinitializing registers so stale software state cannot hide a
window that was reset by hardware.

Signed-off-by: Manikanta Maddireddy <mmaddireddy@xxxxxxxxxx>
---
.../pci/controller/dwc/pcie-designware-ep.c | 152 +++++++++++++-----
drivers/pci/controller/dwc/pcie-designware.h | 13 ++
2 files changed, 124 insertions(+), 41 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index de8ee3db4360..7ba40220b821 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -1155,6 +1155,28 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
}
EXPORT_SYMBOL_GPL(dw_pcie_ep_raise_msix_irq);

+static void dw_pcie_ep_clear_msi_iatu(struct dw_pcie_ep *ep)
+{
+ ep->msi_iatu_mapped = false;
+ ep->msi_iatu_mapped_offset = 0;
+ ep->msi_msg_addr = 0;
+ ep->msi_map_size = 0;
+}
+
+static void dw_pcie_ep_clear_outbound_atus(struct dw_pcie_ep *ep)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+
+ if (ep->ob_window_map)
+ bitmap_zero(ep->ob_window_map, pci->num_ob_windows);
+
+ if (ep->outbound_addr)
+ memset(ep->outbound_addr, 0,
+ pci->num_ob_windows * sizeof(*ep->outbound_addr));
+
+ dw_pcie_ep_clear_msi_iatu(ep);
+}
+
/**
* dw_pcie_ep_cleanup - Cleanup DWC EP resources after fundamental reset
* @ep: DWC EP device
@@ -1169,11 +1191,12 @@ void dw_pcie_ep_cleanup(struct dw_pcie_ep *ep)

if (ep->msi_iatu_mapped) {
dw_pcie_ep_unmap_addr(ep->epc, 0, 0, ep->msi_mem_phys);
- ep->msi_iatu_mapped = false;
+ dw_pcie_ep_clear_msi_iatu(ep);
}

dwc_pcie_debugfs_deinit(pci);
dw_pcie_edma_remove(pci);
+ ep->resources_initialized = false;
}
EXPORT_SYMBOL_GPL(dw_pcie_ep_cleanup);

@@ -1307,6 +1330,89 @@ static void dw_pcie_ep_disable_bars(struct dw_pcie_ep *ep)
}
}

+static void dw_pcie_ep_init_ptm(struct dw_pcie_ep *ep)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ u32 ptm_cap_base, reg;
+
+ /*
+ * PCIe r6.0, section 7.9.15 states that for endpoints that support
+ * PTM, this capability structure is required in exactly one
+ * function, which controls the PTM behavior of all PTM capable
+ * functions. This indicates the PTM capability structure
+ * represents controller-level registers rather than per-function
+ * registers.
+ *
+ * Therefore, PTM capability registers are configured using the
+ * standard DBI accessors, instead of func_no indexed per-function
+ * accessors.
+ */
+ ptm_cap_base = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_PTM);
+
+ /*
+ * PTM responder capability can be disabled only after disabling
+ * PTM root capability.
+ */
+ if (ptm_cap_base) {
+ dw_pcie_dbi_ro_wr_en(pci);
+ reg = dw_pcie_readl_dbi(pci, ptm_cap_base + PCI_PTM_CAP);
+ reg &= ~PCI_PTM_CAP_ROOT;
+ dw_pcie_writel_dbi(pci, ptm_cap_base + PCI_PTM_CAP, reg);
+
+ reg = dw_pcie_readl_dbi(pci, ptm_cap_base + PCI_PTM_CAP);
+ reg &= ~(PCI_PTM_CAP_RES | PCI_PTM_GRANULARITY_MASK);
+ dw_pcie_writel_dbi(pci, ptm_cap_base + PCI_PTM_CAP, reg);
+ dw_pcie_dbi_ro_wr_dis(pci);
+ }
+}
+
+/**
+ * dw_pcie_ep_reinit_registers - Reinitialize DWC EP reset-lost registers
+ * @ep: DWC EP device
+ *
+ * Reinitialize the EP registers lost after a fundamental reset without
+ * reallocating EP resources that live until dw_pcie_ep_deinit(). The
+ * caller must only use this after hardware has reset the iATU; this
+ * helper invalidates software iATU state but does not disable live
+ * windows.
+ */
+int dw_pcie_ep_reinit_registers(struct dw_pcie_ep *ep)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ int ret;
+
+ dw_pcie_ep_clear_outbound_atus(ep);
+
+ if (ep->ops->init) {
+ ret = ep->ops->init(ep);
+ if (ret)
+ return ret;
+ }
+
+ dw_pcie_ep_disable_bars(ep);
+ dw_pcie_ep_init_ptm(ep);
+ dw_pcie_ep_init_non_sticky_registers(pci);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dw_pcie_ep_reinit_registers);
+
+/**
+ * dw_pcie_ep_init_or_reinit_registers - Initialize or reinitialize DWC EP
+ * @ep: DWC EP device
+ *
+ * Initialize DWC EP resources on first use. If resources are already
+ * initialized, reinitialize only the reset-lost registers.
+ */
+int dw_pcie_ep_init_or_reinit_registers(struct dw_pcie_ep *ep)
+{
+ if (ep->resources_initialized)
+ return dw_pcie_ep_reinit_registers(ep);
+
+ return dw_pcie_ep_init_registers(ep);
+}
+EXPORT_SYMBOL_GPL(dw_pcie_ep_init_or_reinit_registers);
+
/**
* dw_pcie_ep_init_registers - Initialize DWC EP specific registers
* @ep: DWC EP device
@@ -1321,7 +1427,6 @@ int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep)
struct dw_pcie_ep_func *ep_func;
struct device *dev = pci->dev;
struct pci_epc *epc = ep->epc;
- u32 ptm_cap_base, reg;
u8 hdr_type;
u8 func_no;
void *addr;
@@ -1386,47 +1491,12 @@ int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep)
list_add_tail(&ep_func->list, &ep->func_list);
}

- if (ep->ops->init) {
- ret = ep->ops->init(ep);
- if (ret)
- goto err_remove_edma;
- }
-
- dw_pcie_ep_disable_bars(ep);
-
- /*
- * PCIe r6.0, section 7.9.15 states that for endpoints that support
- * PTM, this capability structure is required in exactly one
- * function, which controls the PTM behavior of all PTM capable
- * functions. This indicates the PTM capability structure
- * represents controller-level registers rather than per-function
- * registers.
- *
- * Therefore, PTM capability registers are configured using the
- * standard DBI accessors, instead of func_no indexed per-function
- * accessors.
- */
- ptm_cap_base = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_PTM);
-
- /*
- * PTM responder capability can be disabled only after disabling
- * PTM root capability.
- */
- if (ptm_cap_base) {
- dw_pcie_dbi_ro_wr_en(pci);
- reg = dw_pcie_readl_dbi(pci, ptm_cap_base + PCI_PTM_CAP);
- reg &= ~PCI_PTM_CAP_ROOT;
- dw_pcie_writel_dbi(pci, ptm_cap_base + PCI_PTM_CAP, reg);
-
- reg = dw_pcie_readl_dbi(pci, ptm_cap_base + PCI_PTM_CAP);
- reg &= ~(PCI_PTM_CAP_RES | PCI_PTM_GRANULARITY_MASK);
- dw_pcie_writel_dbi(pci, ptm_cap_base + PCI_PTM_CAP, reg);
- dw_pcie_dbi_ro_wr_dis(pci);
- }
-
- dw_pcie_ep_init_non_sticky_registers(pci);
+ ret = dw_pcie_ep_reinit_registers(ep);
+ if (ret)
+ goto err_remove_edma;

dwc_pcie_debugfs_init(pci, DW_PCIE_EP_TYPE);
+ ep->resources_initialized = true;

return 0;

diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index 4d55339750f0..ae442de396ff 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -511,6 +511,7 @@ struct dw_pcie_ep {
unsigned long *ob_window_map;
void __iomem *msi_mem;
phys_addr_t msi_mem_phys;
+ bool resources_initialized;

/* MSI outbound iATU state */
bool msi_iatu_mapped;
@@ -922,6 +923,8 @@ void dw_pcie_ep_linkup(struct dw_pcie_ep *ep);
void dw_pcie_ep_linkdown(struct dw_pcie_ep *ep);
int dw_pcie_ep_init(struct dw_pcie_ep *ep);
int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep);
+int dw_pcie_ep_reinit_registers(struct dw_pcie_ep *ep);
+int dw_pcie_ep_init_or_reinit_registers(struct dw_pcie_ep *ep);
void dw_pcie_ep_deinit(struct dw_pcie_ep *ep);
void dw_pcie_ep_cleanup(struct dw_pcie_ep *ep);
int dw_pcie_ep_raise_intx_irq(struct dw_pcie_ep *ep, u8 func_no);
@@ -953,6 +956,16 @@ static inline int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep)
return 0;
}

+static inline int dw_pcie_ep_reinit_registers(struct dw_pcie_ep *ep)
+{
+ return 0;
+}
+
+static inline int dw_pcie_ep_init_or_reinit_registers(struct dw_pcie_ep *ep)
+{
+ return 0;
+}
+
static inline void dw_pcie_ep_deinit(struct dw_pcie_ep *ep)
{
}
--
2.34.1