* [PATCH 1/3] PCI: dwc: Add Endpoint reinit helper
2026-09-23 7:25 [PATCH 0/3] PCI: dwc: Avoid full EP cleanup on PERST# Manikanta Maddireddy
@ 2026-09-23 7:25 ` Manikanta Maddireddy
2026-09-23 7:25 ` [PATCH 2/3] PCI: tegra194: Avoid full Endpoint reinit on PERST# Manikanta Maddireddy
2026-09-23 7:25 ` [PATCH 3/3] PCI: tegra194: Notify Endpoint functions on PERST# assert Manikanta Maddireddy
2 siblings, 0 replies; 4+ messages in thread
From: Manikanta Maddireddy @ 2026-09-23 7:25 UTC (permalink / raw)
To: Jingoo Han, Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczynski, Bjorn Helgaas, linux-pci
Cc: Rob Herring, Thierry Reding, Jonathan Hunter, Vidya Sagar,
Niklas Cassel, linux-kernel, linux-tegra, Manikanta Maddireddy
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@nvidia.com>
---
.../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
^ permalink raw reply [flat|nested] 4+ messages in thread* [PATCH 2/3] PCI: tegra194: Avoid full Endpoint reinit on PERST#
2026-09-23 7:25 [PATCH 0/3] PCI: dwc: Avoid full EP cleanup on PERST# Manikanta Maddireddy
2026-09-23 7:25 ` [PATCH 1/3] PCI: dwc: Add Endpoint reinit helper Manikanta Maddireddy
@ 2026-09-23 7:25 ` Manikanta Maddireddy
2026-09-23 7:25 ` [PATCH 3/3] PCI: tegra194: Notify Endpoint functions on PERST# assert Manikanta Maddireddy
2 siblings, 0 replies; 4+ messages in thread
From: Manikanta Maddireddy @ 2026-09-23 7:25 UTC (permalink / raw)
To: Jingoo Han, Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczynski, Bjorn Helgaas, linux-pci
Cc: Rob Herring, Thierry Reding, Jonathan Hunter, Vidya Sagar,
Niklas Cassel, linux-kernel, linux-tegra, Manikanta Maddireddy
Tegra194 and Tegra234 Endpoint mode receive PERST# as a fundamental
reset. Controller registers are lost across that reset, but DWC
Endpoint resources live for the controller lifetime.
Use the DWC init-or-reinit helper for Tegra PERST# deassertions so only
reset-lost registers are restored after the first initialization. Keep
the EPF deinit notification in the deassert path where refclk and core
reset are available.
Signed-off-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
---
drivers/pci/controller/dwc/pcie-tegra194.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index 513299541f18..dd3f4c30ba52 100644
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
@@ -1914,7 +1914,6 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie)
/* Perform cleanup that requires refclk and core reset deasserted */
pci_epc_deinit_notify(pcie->pci.ep.epc);
- dw_pcie_ep_cleanup(&pcie->pci.ep);
val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
val &= ~PORT_LOGIC_SPEED_CHANGE;
@@ -1958,7 +1957,7 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie)
val = (upper_32_bits(ep->msi_mem_phys) & MSIX_ADDR_MATCH_HIGH_OFF_MASK);
dw_pcie_writel_dbi(pci, MSIX_ADDR_MATCH_HIGH_OFF, val);
- ret = dw_pcie_ep_init_registers(ep);
+ ret = dw_pcie_ep_init_or_reinit_registers(ep);
if (ret) {
dev_err(dev, "Failed to complete initialization: %d\n", ret);
goto fail_init_complete;
--
2.34.1
^ permalink raw reply [flat|nested] 4+ messages in thread* [PATCH 3/3] PCI: tegra194: Notify Endpoint functions on PERST# assert
2026-09-23 7:25 [PATCH 0/3] PCI: dwc: Avoid full EP cleanup on PERST# Manikanta Maddireddy
2026-09-23 7:25 ` [PATCH 1/3] PCI: dwc: Add Endpoint reinit helper Manikanta Maddireddy
2026-09-23 7:25 ` [PATCH 2/3] PCI: tegra194: Avoid full Endpoint reinit on PERST# Manikanta Maddireddy
@ 2026-09-23 7:25 ` Manikanta Maddireddy
2 siblings, 0 replies; 4+ messages in thread
From: Manikanta Maddireddy @ 2026-09-23 7:25 UTC (permalink / raw)
To: Jingoo Han, Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczynski, Bjorn Helgaas, linux-pci
Cc: Rob Herring, Thierry Reding, Jonathan Hunter, Vidya Sagar,
Niklas Cassel, linux-kernel, linux-tegra, Manikanta Maddireddy
Tegra Endpoint mode powers the controller down when the host asserts
PERST#. EPF drivers must be told about link down before that happens,
otherwise remove while PERST# is asserted can free EPC resources while
EPF work is still active.
Use pci_epc_linkdown() directly in the PERST# assert path. It is a
software-only EPF notification and avoids dw_pcie_ep_linkdown(), which
reinitializes non-sticky DWC registers and touches DBI while the host
REFCLK may be disappearing.
Keep pci_epc_deinit_notify() in the PERST# deassert path, after core
reset is deasserted, because EPF deinit callbacks can clear BARs and
therefore need register access. If remove runs while the controller is
already powered down, pex_ep_event_pex_rst_assert() returns because
ep_state is disabled; that is safe because the link-down notification
was sent at the earlier PERST# assert.
The final dw_pcie_ep_deinit() does not require Tegra register access
today: Tegra uses its own MSI-X address-match window instead of the
DWC MSI iATU, and the supported DTs do not register a DWC eDMA device.
Signed-off-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
---
drivers/pci/controller/dwc/pcie-tegra194.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index dd3f4c30ba52..3e9d46850a78 100644
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
@@ -1724,6 +1724,7 @@ static int tegra_pcie_config_rp(struct tegra_pcie_dw *pcie)
static void pex_ep_event_pex_rst_assert(struct tegra_pcie_dw *pcie)
{
+ struct dw_pcie_ep *ep = &pcie->pci.ep;
u32 val;
int ret;
@@ -1745,6 +1746,8 @@ static void pex_ep_event_pex_rst_assert(struct tegra_pcie_dw *pcie)
if (ret)
dev_info(pcie->dev, "LTSSM state: 0x%x detect timeout: %d\n", val, ret);
+ pci_epc_linkdown(ep->epc);
+
/*
* Deassert LTSSM state to stop the state toggling between
* Polling and Detect.
@@ -1913,7 +1916,7 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie)
}
/* Perform cleanup that requires refclk and core reset deasserted */
- pci_epc_deinit_notify(pcie->pci.ep.epc);
+ pci_epc_deinit_notify(ep->epc);
val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
val &= ~PORT_LOGIC_SPEED_CHANGE;
--
2.34.1
^ permalink raw reply [flat|nested] 4+ messages in thread