* [PATCH v2 1/3] PCI: dwc: Reuse Endpoint resources after fundamental reset
2026-10-21 6:19 [PATCH v2 0/3] PCI: dwc: Avoid full EP cleanup on PERST# Manikanta Maddireddy
@ 2026-10-21 6:19 ` Manikanta Maddireddy
2026-10-21 6:19 ` [PATCH v2 2/3] PCI: tegra194: Avoid full Endpoint reinit on PERST# Manikanta Maddireddy
2026-10-21 6:19 ` [PATCH v2 3/3] PCI: tegra194: Notify Endpoint functions on PERST# assert Manikanta Maddireddy
2 siblings, 0 replies; 4+ messages in thread
From: Manikanta Maddireddy @ 2026-10-21 6:19 UTC (permalink / raw)
To: jingoohan1, mani, lpieralisi, kwilczynski, bhelgaas, linux-pci
Cc: robh, thierry.reding, jonathanh, vidyas, cassel, linux-kernel,
linux-tegra, mmaddireddy
A fundamental reset clears DWC Endpoint registers but does not end the
lifetime of the bound driver. Each full cleanup/init cycle removes and
recreates debugfs entries and, when eDMA is present, unregisters and
reprobes the DMA controller. eDMA detection also allocates managed DMA
linked-list memory that is retained until device detach. The
inbound/outbound allocation bitmaps, outbound-address array and
per-function descriptors already have allocation guards and can be
reused.
Track successful resource initialization in dw_pcie_ep_init_registers().
On later calls, restore the platform-specific registers, BAR defaults,
PTM and non-sticky registers without repeating resource setup. Keep the
register restoration helper private and use the existing public API for
both initial setup and reset recovery. Clear the initialization flag on
full cleanup so a later call performs resource setup again.
Invalidate the outbound window bitmap, address array and MSI iATU cache
after hardware has reset the iATU. This resets software bookkeeping; it
does not disable or unmap live hardware windows. Document that reset
recovery requires the hardware reset and quiesced EPF activity.
Signed-off-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
---
Changes V1 -> V2:
- Fold init/reinit selection into dw_pcie_ep_init_registers() instead of
adding a separate init-or-reinit API.
- Keep the register restoration helper private and remove its public
declarations, export and disabled-config stub.
- Rename the outbound helper to dw_pcie_ep_reset_outbound_state() and
explain that it resets software bookkeeping after hardware reset.
- Document reset-recovery prerequisites and clarify resource lifetimes.
- Update the title and commit message to describe resource reuse.
.../pci/controller/dwc/pcie-designware-ep.c | 149 +++++++++++++-----
drivers/pci/controller/dwc/pcie-designware.h | 1 +
2 files changed, 108 insertions(+), 42 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 0262d85ed390..3c26c03b42f0 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -1155,6 +1155,32 @@ 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;
+}
+
+/*
+ * Reset only the software bookkeeping for outbound windows after hardware
+ * has reset the iATU. This does not disable or unmap live hardware windows.
+ */
+static void dw_pcie_ep_reset_outbound_state(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 +1195,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,13 +1334,84 @@ 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
+ *
+ * Restore the EP registers lost after a fundamental reset without repeating
+ * software resource setup. Hardware must already have reset the iATU; this
+ * helper invalidates software iATU state but does not disable live windows.
+ */
+static 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_reset_outbound_state(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;
+}
+
/**
* dw_pcie_ep_init_registers - Initialize DWC EP specific registers
* @ep: DWC EP device
*
* Initialize the registers (CSRs) specific to DWC EP. This API should be called
* only when the endpoint receives an active refclk (either from host or
- * generated locally).
+ * generated locally). The caller must quiesce EPF activity and ensure that
+ * hardware has reset the outbound iATU before calling this API after a
+ * fundamental reset. Cached outbound mappings are invalidated without
+ * disabling live hardware windows.
+ *
+ * The first successful call sets up the DWC EP software resources. Later
+ * calls restore reset-lost registers while retaining those resources until
+ * dw_pcie_ep_cleanup() or dw_pcie_ep_deinit().
*/
int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep)
{
@@ -1321,7 +1419,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;
@@ -1336,6 +1433,9 @@ int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep)
return -EIO;
}
+ if (ep->resources_initialized)
+ return dw_pcie_ep_reinit_registers(ep);
+
dw_pcie_version_detect(pci);
dw_pcie_iatu_detect(pci);
@@ -1386,47 +1486,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 e7cdf1c69744..2d3563c3c4bf 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -513,6 +513,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;
--
2.34.1
^ permalink raw reply [flat|nested] 4+ messages in thread* [PATCH v2 2/3] PCI: tegra194: Avoid full Endpoint reinit on PERST#
2026-10-21 6:19 [PATCH v2 0/3] PCI: dwc: Avoid full EP cleanup on PERST# Manikanta Maddireddy
2026-10-21 6:19 ` [PATCH v2 1/3] PCI: dwc: Reuse Endpoint resources after fundamental reset Manikanta Maddireddy
@ 2026-10-21 6:19 ` Manikanta Maddireddy
2026-10-21 6:19 ` [PATCH v2 3/3] PCI: tegra194: Notify Endpoint functions on PERST# assert Manikanta Maddireddy
2 siblings, 0 replies; 4+ messages in thread
From: Manikanta Maddireddy @ 2026-10-21 6:19 UTC (permalink / raw)
To: jingoohan1, mani, lpieralisi, kwilczynski, bhelgaas, linux-pci
Cc: robh, thierry.reding, jonathanh, vidyas, cassel, linux-kernel,
linux-tegra, mmaddireddy
Tegra194 and Tegra234 lose controller register state across PERST#, but
the driver remains bound. The common DWC initialization path now
restores those registers while preserving its software resources. Tegra
therefore no longer needs to call dw_pcie_ep_cleanup() on each PERST#
cycle.
Remove the per-reset dw_pcie_ep_cleanup() call. The existing
dw_pcie_ep_init_registers() entry point now restores reset-lost
registers after the first initialization while retaining software
resources until final driver teardown. The allocation guards already
reuse the bitmaps, address array and per-function descriptors; this
change also avoids repeating the full debugfs/eDMA teardown and setup.
Keep the EPF deinit notification in the PERST# deassert path after
REFCLK is available and core reset has been deasserted, because EPF
deinit callbacks can clear BARs and need register access. The final
dw_pcie_ep_deinit() during driver removal still performs full cleanup.
Signed-off-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
---
Changes V1 -> V2:
- Keep the existing dw_pcie_ep_init_registers() call now that it handles
both initial setup and reset recovery; only remove per-reset cleanup.
- Clarify the retained resource lifetimes and shorten the opening
paragraph to focus on the Tegra change.
drivers/pci/controller/dwc/pcie-tegra194.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index 0f49ea6df8bc..47a83a69533d 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, LINK_WIDTH_SPEED_CTRL);
val &= ~SPEED_CHANGE;
--
2.34.1
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v2 3/3] PCI: tegra194: Notify Endpoint functions on PERST# assert
2026-10-21 6:19 [PATCH v2 0/3] PCI: dwc: Avoid full EP cleanup on PERST# Manikanta Maddireddy
2026-10-21 6:19 ` [PATCH v2 1/3] PCI: dwc: Reuse Endpoint resources after fundamental reset Manikanta Maddireddy
2026-10-21 6:19 ` [PATCH v2 2/3] PCI: tegra194: Avoid full Endpoint reinit on PERST# Manikanta Maddireddy
@ 2026-10-21 6:19 ` Manikanta Maddireddy
2 siblings, 0 replies; 4+ messages in thread
From: Manikanta Maddireddy @ 2026-10-21 6:19 UTC (permalink / raw)
To: jingoohan1, mani, lpieralisi, kwilczynski, bhelgaas, linux-pci
Cc: robh, thierry.reding, jonathanh, vidyas, cassel, linux-kernel,
linux-tegra, mmaddireddy
Tegra powers down the Endpoint controller when the host asserts PERST#.
Notify EPF drivers of link down before disabling the controller so their
link_down callbacks can quiesce outstanding work. For example,
pci_epf_test_link_down() synchronously cancels its command worker.
Without this notification, the worker can remain active after the
controller is powered down and while EPC resources are later removed.
Call pci_epc_linkdown() directly instead of dw_pcie_ep_linkdown(), which
also reinitializes non-sticky DWC registers through DBI when the host
may already have withdrawn REFCLK. The direct notification avoids that
common DWC register reinitialization. It does not guarantee that EPF
callbacks are register-free: they can access MMIO and must tolerate the
hardware state at PERST# assertion, including unavailable DBI access.
Signed-off-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
---
Changes V1 -> V2:
- Clarify that the direct notification avoids common DWC DBI register
reinitialization, but EPF callbacks may still access hardware.
- Add a comment explaining why dw_pcie_ep_linkdown() is not used.
- Drop the unrelated pci_epc_deinit_notify() pointer cleanup.
- Remove unrelated teardown and deferred-deinit discussion from the
commit message.
drivers/pci/controller/dwc/pcie-tegra194.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index 47a83a69533d..1eaefbbdd67a 100644
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
@@ -1745,6 +1745,9 @@ 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);
+ /* Avoid the DBI register reinitialization in dw_pcie_ep_linkdown(). */
+ pci_epc_linkdown(pcie->pci.ep.epc);
+
/*
* Deassert LTSSM state to stop the state toggling between
* Polling and Detect.
--
2.34.1
^ permalink raw reply [flat|nested] 4+ messages in thread