* [PATCH 00/11] PCI: tegra194: Fix EP and Root Port corner cases
@ 2026-09-23 7:22 Manikanta Maddireddy
2026-09-23 7:22 ` [PATCH 01/11] PCI: tegra194: Propagate REFCLK select GPIO errors Manikanta Maddireddy
` (10 more replies)
0 siblings, 11 replies; 14+ messages in thread
From: Manikanta Maddireddy @ 2026-09-23 7:22 UTC (permalink / raw)
To: Lorenzo Pieralisi, Krzysztof Wilczynski, Manivannan Sadhasivam,
Bjorn Helgaas, linux-pci
Cc: Rob Herring, Thierry Reding, Jonathan Hunter,
Kishon Vijay Abraham I, Frank Li, Vidya Sagar, Niklas Cassel,
Koichiro Den, Marco Crivellari, Andy Shevchenko, linux-tegra,
linux-kernel, Manikanta Maddireddy
This series fixes a set of independent Tegra194/Tegra234 PCIe
controller issues found while testing Root Port and Endpoint flows.
The Root Port fixes tighten probe/remove and suspend/resume error handling:
propagate optional GPIO failures, check reset deassertion, avoid skipping
cleanup when no link came up, and balance the optional core monitor clock
on failures.
The Endpoint fixes cover PERST# IRQ suspend handling, PHY/PLL error
unwinds, capability checks before programming optional PCIe extended
capabilities, hiding Tegra234 EP L1.2 advertisement for the documented
hardware limitation, and MSI/MSI-X handling. The MSI-X patches also keep
pci_epf_test from relocating controller-owned MSI-X table/PBA storage when
the EPC describes a fixed MSI-X BAR layout.
The patches are ordered from low-level error handling toward endpoint
capability/MSI-X fixes so each change remains reviewable and
self-contained.
Manikanta Maddireddy (11):
PCI: tegra194: Propagate REFCLK select GPIO errors
PCI: tegra194: Check core reset deassertion
PCI: tegra194: Fix Endpoint PERST# IRQ suspend race
PCI: tegra194: Do not skip no-link Root Port remove cleanup
PCI: tegra194: Check for 16 GT/s capability before programming
PCI: tegra194: Check for L1SS capability before programming
PCI: tegra194: Always disable Tegra234 Endpoint L1.2
PCI: tegra194: Guard Endpoint PLL-off error path
PCI: tegra194: Balance core monitor clock on failures
PCI: tegra194: Fix Endpoint MSI/MSI-X numbering
PCI: endpoint: test: Do not relocate fixed MSI-X tables
drivers/pci/controller/dwc/pcie-tegra194.c | 243 ++++++++++++------
drivers/pci/endpoint/functions/pci-epf-test.c | 27 +-
2 files changed, 186 insertions(+), 84 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 01/11] PCI: tegra194: Propagate REFCLK select GPIO errors
2026-09-23 7:22 [PATCH 00/11] PCI: tegra194: Fix EP and Root Port corner cases Manikanta Maddireddy
@ 2026-09-23 7:22 ` Manikanta Maddireddy
2026-09-23 7:22 ` [PATCH 02/11] PCI: tegra194: Check core reset deassertion Manikanta Maddireddy
` (9 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Manikanta Maddireddy @ 2026-09-23 7:22 UTC (permalink / raw)
To: Lorenzo Pieralisi, Krzysztof Wilczynski, Manivannan Sadhasivam,
Bjorn Helgaas, linux-pci
Cc: Rob Herring, Thierry Reding, Jonathan Hunter,
Kishon Vijay Abraham I, Frank Li, Vidya Sagar, Niklas Cassel,
Koichiro Den, Marco Crivellari, Andy Shevchenko, linux-tegra,
linux-kernel, Manikanta Maddireddy
devm_gpiod_get_optional() returns NULL only when the optional GPIO is
absent. Other error pointers, including -EPROBE_DEFER, describe real
failures and should not be converted to NULL.
Return dev_err_probe() errors for nvidia,refclk-select so probe does not
continue without a GPIO that exists but is not ready or could not be
requested.
Fixes: c57247f940e8 ("PCI: tegra: Add support for PCIe endpoint mode in Tegra194")
Signed-off-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
---
drivers/pci/controller/dwc/pcie-tegra194.c | 15 +++------------
1 file changed, 3 insertions(+), 12 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index 2462da8664e0..f2b75bf49a12 100644
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
@@ -1206,18 +1206,9 @@ static int tegra_pcie_dw_parse_dt(struct tegra_pcie_dw *pcie)
pcie->pex_refclk_sel_gpiod = devm_gpiod_get_optional(pcie->dev,
"nvidia,refclk-select",
GPIOD_OUT_HIGH);
- if (IS_ERR(pcie->pex_refclk_sel_gpiod)) {
- int err = PTR_ERR(pcie->pex_refclk_sel_gpiod);
- const char *level = KERN_ERR;
-
- if (err == -EPROBE_DEFER)
- level = KERN_DEBUG;
-
- dev_printk(level, pcie->dev,
- dev_fmt("Failed to get REFCLK select GPIOs: %d\n"),
- err);
- pcie->pex_refclk_sel_gpiod = NULL;
- }
+ if (IS_ERR(pcie->pex_refclk_sel_gpiod))
+ return dev_err_probe(pcie->dev, PTR_ERR(pcie->pex_refclk_sel_gpiod),
+ "Failed to get REFCLK select GPIO\n");
return 0;
}
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 02/11] PCI: tegra194: Check core reset deassertion
2026-09-23 7:22 [PATCH 00/11] PCI: tegra194: Fix EP and Root Port corner cases Manikanta Maddireddy
2026-09-23 7:22 ` [PATCH 01/11] PCI: tegra194: Propagate REFCLK select GPIO errors Manikanta Maddireddy
@ 2026-09-23 7:22 ` Manikanta Maddireddy
2026-09-23 7:22 ` [PATCH 03/11] PCI: tegra194: Fix Endpoint PERST# IRQ suspend race Manikanta Maddireddy
` (8 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Manikanta Maddireddy @ 2026-09-23 7:22 UTC (permalink / raw)
To: Lorenzo Pieralisi, Krzysztof Wilczynski, Manivannan Sadhasivam,
Bjorn Helgaas, linux-pci
Cc: Rob Herring, Thierry Reding, Jonathan Hunter,
Kishon Vijay Abraham I, Frank Li, Vidya Sagar, Niklas Cassel,
Koichiro Den, Marco Crivellari, Andy Shevchenko, linux-tegra,
linux-kernel, Manikanta Maddireddy
Several paths access DBI registers after deasserting the controller core
reset. If reset_control_deassert() fails, those DBI accesses can trigger a
CBB timeout, or setup can return success with the core still in reset.
Check core reset deassertion in Root Port setup, the DLF retry path and the
Endpoint PERST# deassert path. Unwind before DBI access when deassertion
fails.
Fixes: 56e15a238d92 ("PCI: tegra: Add Tegra194 PCIe support")
Signed-off-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
---
drivers/pci/controller/dwc/pcie-tegra194.c | 23 +++++++++++++++++++---
1 file changed, 20 insertions(+), 3 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index f2b75bf49a12..810695d8e5c8 100644
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
@@ -954,6 +954,7 @@ static int tegra_pcie_dw_start_link(struct dw_pcie *pci)
struct tegra_pcie_dw *pcie = to_tegra_pcie(pci);
struct dw_pcie_rp *pp = &pci->pp;
u32 val, offset, tmp;
+ int ret;
bool retry = true;
if (pcie->of_data->mode == DW_PCIE_EP_TYPE) {
@@ -1009,7 +1010,12 @@ static int tegra_pcie_dw_start_link(struct dw_pcie *pci)
appl_writel(pcie, val, APPL_CTRL);
reset_control_assert(pcie->core_rst);
- reset_control_deassert(pcie->core_rst);
+ ret = reset_control_deassert(pcie->core_rst);
+ if (ret) {
+ dev_err(pci->dev, "Failed to deassert core reset: %d\n",
+ ret);
+ return ret;
+ }
offset = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_DLF);
val = dw_pcie_readl_dbi(pci, offset + PCI_DLF_CAP);
@@ -1455,10 +1461,16 @@ static int tegra_pcie_config_controller(struct tegra_pcie_dw *pcie,
pcie->atu_dma_res->start & APPL_CFG_IATU_DMA_BASE_ADDR_MASK,
APPL_CFG_IATU_DMA_BASE_ADDR);
- reset_control_deassert(pcie->core_rst);
+ ret = reset_control_deassert(pcie->core_rst);
+ if (ret) {
+ dev_err(pcie->dev, "Failed to deassert core reset: %d\n", ret);
+ goto fail_core_rst;
+ }
return ret;
+fail_core_rst:
+ tegra_pcie_disable_phy(pcie);
fail_phy:
reset_control_assert(pcie->core_apb_rst);
fail_core_apb_rst:
@@ -1853,7 +1865,11 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie)
LTR_NOSNOOP_MSG_REQ;
appl_writel(pcie, val, APPL_LTR_MSG_1);
- reset_control_deassert(pcie->core_rst);
+ ret = reset_control_deassert(pcie->core_rst);
+ if (ret) {
+ dev_err(dev, "Failed to deassert core reset: %d\n", ret);
+ goto fail_core_rst;
+ }
/* Perform cleanup that requires refclk and core reset deasserted */
pci_epc_deinit_notify(pcie->pci.ep.epc);
@@ -1927,6 +1943,7 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie)
fail_init_complete:
reset_control_assert(pcie->core_rst);
+fail_core_rst:
tegra_pcie_disable_phy(pcie);
fail_phy:
reset_control_assert(pcie->core_apb_rst);
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 03/11] PCI: tegra194: Fix Endpoint PERST# IRQ suspend race
2026-09-23 7:22 [PATCH 00/11] PCI: tegra194: Fix EP and Root Port corner cases Manikanta Maddireddy
2026-09-23 7:22 ` [PATCH 01/11] PCI: tegra194: Propagate REFCLK select GPIO errors Manikanta Maddireddy
2026-09-23 7:22 ` [PATCH 02/11] PCI: tegra194: Check core reset deassertion Manikanta Maddireddy
@ 2026-09-23 7:22 ` Manikanta Maddireddy
2026-09-23 7:22 ` [PATCH 04/11] PCI: tegra194: Do not skip no-link Root Port remove cleanup Manikanta Maddireddy
` (7 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Manikanta Maddireddy @ 2026-09-23 7:22 UTC (permalink / raw)
To: Lorenzo Pieralisi, Krzysztof Wilczynski, Manivannan Sadhasivam,
Bjorn Helgaas, linux-pci
Cc: Rob Herring, Thierry Reding, Jonathan Hunter,
Kishon Vijay Abraham I, Frank Li, Vidya Sagar, Niklas Cassel,
Koichiro Den, Marco Crivellari, Andy Shevchenko, linux-tegra,
linux-kernel, Manikanta Maddireddy
Endpoint mode disables the PERST# IRQ during system suspend when the
link is down. The old code checked ep_state before disabling the IRQ,
so the host could deassert PERST# in that window and the IRQ thread
could move the endpoint to EP_STATE_ENABLED after the suspend check had
already passed.
Disable the PERST# IRQ before checking ep_state and re-enable it
immediately when suspend is rejected. Pair the successful suspend
disable with the normal resume callback, because PM rollback after
another device aborts suspend invokes .resume, not .resume_early.
Wrap the PM ops pointer with pm_sleep_ptr() so the callbacks are used
only when system sleep is enabled.
Fixes: c76f8eae7d46 ("PCI: tegra194: Allow system suspend when the Endpoint link is not up")
Link: https://lore.kernel.org/r/20260324190755.1094879-10-mmaddireddy@nvidia.com
Signed-off-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
---
drivers/pci/controller/dwc/pcie-tegra194.c | 33 ++++++++++++++--------
1 file changed, 21 insertions(+), 12 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index 810695d8e5c8..b91073a6305a 100644
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
@@ -2374,19 +2374,29 @@ static int tegra_pcie_dw_suspend(struct device *dev)
{
struct tegra_pcie_dw *pcie = dev_get_drvdata(dev);
- if (pcie->of_data->mode == DW_PCIE_EP_TYPE) {
- if (pcie->ep_state == EP_STATE_ENABLED) {
- dev_err(dev, "Tegra PCIe is in EP mode, suspend not allowed\n");
- return -EPERM;
- }
-
- disable_irq(pcie->pex_rst_irq);
+ if (pcie->of_data->mode != DW_PCIE_EP_TYPE)
return 0;
+
+ disable_irq(pcie->pex_rst_irq);
+ if (pcie->ep_state == EP_STATE_ENABLED) {
+ enable_irq(pcie->pex_rst_irq);
+ dev_err(dev, "Tegra PCIe is in EP mode, suspend not allowed\n");
+ return -EPERM;
}
return 0;
}
+static int tegra_pcie_dw_resume(struct device *dev)
+{
+ struct tegra_pcie_dw *pcie = dev_get_drvdata(dev);
+
+ if (pcie->of_data->mode == DW_PCIE_EP_TYPE)
+ enable_irq(pcie->pex_rst_irq);
+
+ return 0;
+}
+
static int tegra_pcie_dw_suspend_late(struct device *dev)
{
struct tegra_pcie_dw *pcie = dev_get_drvdata(dev);
@@ -2462,10 +2472,8 @@ static int tegra_pcie_dw_resume_early(struct device *dev)
struct tegra_pcie_dw *pcie = dev_get_drvdata(dev);
u32 val;
- if (pcie->of_data->mode == DW_PCIE_EP_TYPE) {
- enable_irq(pcie->pex_rst_irq);
+ if (pcie->of_data->mode == DW_PCIE_EP_TYPE)
return 0;
- }
if (!pcie->link_state)
return 0;
@@ -2569,10 +2577,11 @@ static const struct of_device_id tegra_pcie_dw_of_match[] = {
static const struct dev_pm_ops tegra_pcie_dw_pm_ops = {
.suspend = tegra_pcie_dw_suspend,
+ .resume = tegra_pcie_dw_resume,
.suspend_late = tegra_pcie_dw_suspend_late,
+ .resume_early = tegra_pcie_dw_resume_early,
.suspend_noirq = tegra_pcie_dw_suspend_noirq,
.resume_noirq = tegra_pcie_dw_resume_noirq,
- .resume_early = tegra_pcie_dw_resume_early,
};
static struct platform_driver tegra_pcie_dw_driver = {
@@ -2581,7 +2590,7 @@ static struct platform_driver tegra_pcie_dw_driver = {
.shutdown = tegra_pcie_dw_shutdown,
.driver = {
.name = "tegra194-pcie",
- .pm = &tegra_pcie_dw_pm_ops,
+ .pm = pm_sleep_ptr(&tegra_pcie_dw_pm_ops),
.of_match_table = tegra_pcie_dw_of_match,
},
};
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 04/11] PCI: tegra194: Do not skip no-link Root Port remove cleanup
2026-09-23 7:22 [PATCH 00/11] PCI: tegra194: Fix EP and Root Port corner cases Manikanta Maddireddy
` (2 preceding siblings ...)
2026-09-23 7:22 ` [PATCH 03/11] PCI: tegra194: Fix Endpoint PERST# IRQ suspend race Manikanta Maddireddy
@ 2026-09-23 7:22 ` Manikanta Maddireddy
2026-09-23 7:22 ` [PATCH 05/11] PCI: tegra194: Check for 16 GT/s capability before programming Manikanta Maddireddy
` (6 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Manikanta Maddireddy @ 2026-09-23 7:22 UTC (permalink / raw)
To: Lorenzo Pieralisi, Krzysztof Wilczynski, Manivannan Sadhasivam,
Bjorn Helgaas, linux-pci
Cc: Rob Herring, Thierry Reding, Jonathan Hunter,
Kishon Vijay Abraham I, Frank Li, Vidya Sagar, Niklas Cassel,
Koichiro Den, Marco Crivellari, Andy Shevchenko, linux-tegra,
linux-kernel, Manikanta Maddireddy
tegra_pcie_config_rp() deinitializes the controller and disables runtime PM
when link training fails, then probe treats -ENOMEDIUM as success so the
driver can remain bound without a link.
remove() returned immediately when the Root Port link was down. That skips
common cleanup such as tegra_bpmp_put() and deasserting the refclk-select
GPIO. Keep link-dependent controller teardown conditional, but always run
common remove cleanup.
Fixes: 56e15a238d92 ("PCI: tegra: Add Tegra194 PCIe support")
Signed-off-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
---
drivers/pci/controller/dwc/pcie-tegra194.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index b91073a6305a..aa47052a37cf 100644
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
@@ -2352,19 +2352,19 @@ static void tegra_pcie_dw_remove(struct platform_device *pdev)
struct dw_pcie_ep *ep = &pcie->pci.ep;
if (pcie->of_data->mode == DW_PCIE_RC_TYPE) {
- if (!pcie->link_state)
- return;
-
- debugfs_remove_recursive(pcie->debugfs);
- tegra_pcie_deinit_controller(pcie);
- pm_runtime_put_sync(pcie->dev);
+ if (pcie->link_state) {
+ debugfs_remove_recursive(pcie->debugfs);
+ tegra_pcie_deinit_controller(pcie);
+ pm_runtime_put_sync(pcie->dev);
+ pm_runtime_disable(pcie->dev);
+ }
} else {
disable_irq(pcie->pex_rst_irq);
pex_ep_event_pex_rst_assert(pcie);
dw_pcie_ep_deinit(ep);
+ pm_runtime_disable(pcie->dev);
}
- pm_runtime_disable(pcie->dev);
tegra_bpmp_put(pcie->bpmp);
if (pcie->pex_refclk_sel_gpiod)
gpiod_set_value(pcie->pex_refclk_sel_gpiod, 0);
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 05/11] PCI: tegra194: Check for 16 GT/s capability before programming
2026-09-23 7:22 [PATCH 00/11] PCI: tegra194: Fix EP and Root Port corner cases Manikanta Maddireddy
` (3 preceding siblings ...)
2026-09-23 7:22 ` [PATCH 04/11] PCI: tegra194: Do not skip no-link Root Port remove cleanup Manikanta Maddireddy
@ 2026-09-23 7:22 ` Manikanta Maddireddy
2026-09-23 7:22 ` [PATCH 06/11] PCI: tegra194: Check for L1SS " Manikanta Maddireddy
` (5 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Manikanta Maddireddy @ 2026-09-23 7:22 UTC (permalink / raw)
To: Lorenzo Pieralisi, Krzysztof Wilczynski, Manivannan Sadhasivam,
Bjorn Helgaas, linux-pci
Cc: Rob Herring, Thierry Reding, Jonathan Hunter,
Kishon Vijay Abraham I, Frank Li, Vidya Sagar, Niklas Cassel,
Koichiro Den, Marco Crivellari, Andy Shevchenko, linux-tegra,
linux-kernel, Manikanta Maddireddy
config_gen3_gen4_eq_presets() added PCI_PL_16GT_LE_CTRL to the result of
dw_pcie_find_ext_capability() without checking whether the 16 GT/s
capability exists. If the capability is absent, the code writes lane
presets at an unrelated configuration offset.
Look up the capability once before the lane loop and skip the 16 GT/s lane
equivalent control programming when it is absent.
Fixes: 56e15a238d92 ("PCI: tegra: Add Tegra194 PCIe support")
Signed-off-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
---
drivers/pci/controller/dwc/pcie-tegra194.c | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index aa47052a37cf..358156c63da0 100644
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
@@ -840,7 +840,11 @@ static void tegra_pcie_enable_interrupts(struct dw_pcie_rp *pp)
static void config_gen3_gen4_eq_presets(struct tegra_pcie_dw *pcie)
{
struct dw_pcie *pci = &pcie->pci;
- u32 val, offset, i;
+ u32 pl16g, val, i;
+
+ pl16g = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_PL_16GT);
+ if (pl16g)
+ pl16g += PCI_PL_16GT_LE_CTRL;
/* Program init preset */
for (i = 0; i < pcie->num_lanes; i++) {
@@ -851,15 +855,15 @@ static void config_gen3_gen4_eq_presets(struct tegra_pcie_dw *pcie)
GEN3_GEN4_EQ_PRESET_INIT);
dw_pcie_writew_dbi(pci, CAP_SPCIE_CAP_OFF + (i * 2), val);
- offset = dw_pcie_find_ext_capability(pci,
- PCI_EXT_CAP_ID_PL_16GT) +
- PCI_PL_16GT_LE_CTRL;
- val = dw_pcie_readb_dbi(pci, offset + i);
+ if (!pl16g)
+ continue;
+
+ val = dw_pcie_readb_dbi(pci, pl16g + i);
FIELD_MODIFY(PCI_PL_16GT_LE_CTRL_DSP_TX_PRESET_MASK, &val,
GEN3_GEN4_EQ_PRESET_INIT);
FIELD_MODIFY(PCI_PL_16GT_LE_CTRL_USP_TX_PRESET_MASK, &val,
GEN3_GEN4_EQ_PRESET_INIT);
- dw_pcie_writeb_dbi(pci, offset + i, val);
+ dw_pcie_writeb_dbi(pci, pl16g + i, val);
}
val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 06/11] PCI: tegra194: Check for L1SS capability before programming
2026-09-23 7:22 [PATCH 00/11] PCI: tegra194: Fix EP and Root Port corner cases Manikanta Maddireddy
` (4 preceding siblings ...)
2026-09-23 7:22 ` [PATCH 05/11] PCI: tegra194: Check for 16 GT/s capability before programming Manikanta Maddireddy
@ 2026-09-23 7:22 ` Manikanta Maddireddy
2026-09-23 7:22 ` [PATCH 07/11] PCI: tegra194: Always disable Tegra234 Endpoint L1.2 Manikanta Maddireddy
` (4 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Manikanta Maddireddy @ 2026-09-23 7:22 UTC (permalink / raw)
To: Lorenzo Pieralisi, Krzysztof Wilczynski, Manivannan Sadhasivam,
Bjorn Helgaas, linux-pci
Cc: Rob Herring, Thierry Reding, Jonathan Hunter,
Kishon Vijay Abraham I, Frank Li, Vidya Sagar, Niklas Cassel,
Koichiro Den, Marco Crivellari, Andy Shevchenko, linux-tegra,
linux-kernel, Manikanta Maddireddy
init_host_aspm() programs the L1 PM Substates capability without checking
whether the controller advertises it. If the capability is absent, the
code writes offsets from zero instead of L1SS registers.
Check for the L1SS capability before programming L1SS-specific fields.
Keep RAS-DES discovery, ASPM counter enablement and AFR latency
programming outside that guard because they do not depend on L1SS.
Only create the ASPM counter debugfs file when the RAS-DES capability is
present.
Fixes: 56e15a238d92 ("PCI: tegra: Add Tegra194 PCIe support")
Signed-off-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
---
drivers/pci/controller/dwc/pcie-tegra194.c | 72 ++++++++++++----------
1 file changed, 41 insertions(+), 31 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index 358156c63da0..b35d9456e9de 100644
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
@@ -673,37 +673,46 @@ static void init_host_aspm(struct tegra_pcie_dw *pcie)
pcie->ras_des_cap = dw_pcie_find_ext_capability(&pcie->pci,
PCI_EXT_CAP_ID_VNDR);
-
- /* Enable ASPM counters */
- val = FIELD_PREP(EVENT_COUNTER_ENABLE_MASK, EVENT_COUNTER_ENABLE_ALL);
- val |= FIELD_PREP(EVENT_COUNTER_GROUP_SEL_MASK, EVENT_COUNTER_GROUP_5);
- dw_pcie_writel_dbi(pci, pcie->ras_des_cap +
- PCIE_RAS_DES_EVENT_COUNTER_CONTROL, val);
-
- /* Program T_cmrt and T_pwr_on values */
- val = dw_pcie_readl_dbi(pci, l1ss + PCI_L1SS_CAP);
- FIELD_MODIFY(PCI_L1SS_CAP_CM_RESTORE_TIME, &val, pcie->aspm_cmrt);
- FIELD_MODIFY(PCI_L1SS_CAP_P_PWR_ON_VALUE, &val, pcie->aspm_pwr_on_t);
- dw_pcie_writel_dbi(pci, l1ss + PCI_L1SS_CAP, val);
-
- if (pcie->supports_clkreq)
- pci->l1ss_support = true;
-
- /*
- * Disable L1.2 capability advertisement for Tegra234 Endpoint mode.
- * Tegra234 has a hardware bug where during L1.2 exit, the UPHY PLL is
- * powered up immediately without waiting for REFCLK to stabilize. This
- * causes the PLL to fail to lock to the correct frequency, resulting in
- * PCIe link loss. Since there is no hardware fix available, we prevent
- * the Endpoint from advertising L1.2 support by clearing the L1.2 bits
- * in the L1 PM Substates Capabilities register. This ensures the host
- * will not attempt to enter L1.2 state with this Endpoint.
- */
- if (pcie->of_data->disable_l1_2 &&
- pcie->of_data->mode == DW_PCIE_EP_TYPE) {
+ if (pcie->ras_des_cap) {
+ /* Enable ASPM counters */
+ val = FIELD_PREP(EVENT_COUNTER_ENABLE_MASK,
+ EVENT_COUNTER_ENABLE_ALL);
+ val |= FIELD_PREP(EVENT_COUNTER_GROUP_SEL_MASK,
+ EVENT_COUNTER_GROUP_5);
+ dw_pcie_writel_dbi(pci, pcie->ras_des_cap +
+ PCIE_RAS_DES_EVENT_COUNTER_CONTROL, val);
+ }
+
+ if (l1ss) {
+ /* Program T_cmrt and T_pwr_on values */
val = dw_pcie_readl_dbi(pci, l1ss + PCI_L1SS_CAP);
- val &= ~(PCI_L1SS_CAP_PCIPM_L1_2 | PCI_L1SS_CAP_ASPM_L1_2);
+ FIELD_MODIFY(PCI_L1SS_CAP_CM_RESTORE_TIME, &val,
+ pcie->aspm_cmrt);
+ FIELD_MODIFY(PCI_L1SS_CAP_P_PWR_ON_VALUE, &val,
+ pcie->aspm_pwr_on_t);
dw_pcie_writel_dbi(pci, l1ss + PCI_L1SS_CAP, val);
+
+ if (pcie->supports_clkreq)
+ pci->l1ss_support = true;
+
+ /*
+ * Disable L1.2 capability advertisement for Tegra234 Endpoint
+ * mode. Tegra234 has a hardware bug where during L1.2 exit,
+ * the UPHY PLL is powered up immediately without waiting for
+ * REFCLK to stabilize. This causes the PLL to fail to lock to
+ * the correct frequency, resulting in PCIe link loss. Since
+ * there is no hardware fix available, prevent the Endpoint
+ * from advertising L1.2 support by clearing the L1.2 bits in
+ * the L1 PM Substates Capabilities register. This ensures the
+ * host will not attempt to enter L1.2 with this Endpoint.
+ */
+ if (pcie->of_data->disable_l1_2 &&
+ pcie->of_data->mode == DW_PCIE_EP_TYPE) {
+ val = dw_pcie_readl_dbi(pci, l1ss + PCI_L1SS_CAP);
+ val &= ~(PCI_L1SS_CAP_PCIPM_L1_2 |
+ PCI_L1SS_CAP_ASPM_L1_2);
+ dw_pcie_writel_dbi(pci, l1ss + PCI_L1SS_CAP, val);
+ }
}
/* Program L0s and L1 entrance latencies */
@@ -727,8 +736,9 @@ static void init_debugfs(struct tegra_pcie_dw *pcie)
pcie->debugfs = debugfs_create_dir(name, NULL);
- debugfs_create_devm_seqfile(dev, "aspm_state_cnt", pcie->debugfs,
- aspm_state_cnt);
+ if (pcie->ras_des_cap)
+ debugfs_create_devm_seqfile(dev, "aspm_state_cnt", pcie->debugfs,
+ aspm_state_cnt);
}
#else
static inline void init_host_aspm(struct tegra_pcie_dw *pcie) { return; }
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 07/11] PCI: tegra194: Always disable Tegra234 Endpoint L1.2
2026-09-23 7:22 [PATCH 00/11] PCI: tegra194: Fix EP and Root Port corner cases Manikanta Maddireddy
` (5 preceding siblings ...)
2026-09-23 7:22 ` [PATCH 06/11] PCI: tegra194: Check for L1SS " Manikanta Maddireddy
@ 2026-09-23 7:22 ` Manikanta Maddireddy
2026-09-23 7:22 ` [PATCH 08/11] PCI: tegra194: Guard Endpoint PLL-off error path Manikanta Maddireddy
` (3 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Manikanta Maddireddy @ 2026-09-23 7:22 UTC (permalink / raw)
To: Lorenzo Pieralisi, Krzysztof Wilczynski, Manivannan Sadhasivam,
Bjorn Helgaas, linux-pci
Cc: Rob Herring, Thierry Reding, Jonathan Hunter,
Kishon Vijay Abraham I, Frank Li, Vidya Sagar, Niklas Cassel,
Koichiro Den, Marco Crivellari, Andy Shevchenko, linux-tegra,
linux-kernel, Manikanta Maddireddy
Tegra234 Endpoint mode must not advertise L1.2 because exiting L1.2 can
bring the link down. That hardware workaround is required regardless of
whether the endpoint kernel is built with CONFIG_PCIEASPM.
Move the L1.2 capability masking out of init_host_aspm(), which is compiled
out when CONFIG_PCIEASPM is disabled, and apply it from the common
initialization paths.
Fixes: f59df1d9e6bd ("PCI: tegra194: Disable L1.2 capability of Tegra234 EP")
Signed-off-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
---
drivers/pci/controller/dwc/pcie-tegra194.c | 47 +++++++++++++---------
1 file changed, 28 insertions(+), 19 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index b35d9456e9de..f8e39459db41 100644
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
@@ -611,6 +611,33 @@ static struct pci_ops tegra_pci_ops = {
.write = tegra_pcie_dw_wr_own_conf,
};
+/*
+ * Disable L1.2 capability advertisement for Tegra234 Endpoint mode.
+ * Tegra234 has a hardware bug where during L1.2 exit, the UPHY PLL is
+ * powered up immediately without waiting for REFCLK to stabilize. This causes
+ * the PLL to fail to lock to the correct frequency, resulting in PCIe link
+ * loss. Since there is no hardware fix available, prevent the Endpoint from
+ * advertising L1.2 support so the host will not attempt to enter L1.2 with
+ * this Endpoint.
+ */
+static void tegra_pcie_disable_ep_l1_2(struct tegra_pcie_dw *pcie)
+{
+ struct dw_pcie *pci = &pcie->pci;
+ u32 l1ss, val;
+
+ if (!pcie->of_data->disable_l1_2 ||
+ pcie->of_data->mode != DW_PCIE_EP_TYPE)
+ return;
+
+ l1ss = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_L1SS);
+ if (!l1ss)
+ return;
+
+ val = dw_pcie_readl_dbi(pci, l1ss + PCI_L1SS_CAP);
+ val &= ~(PCI_L1SS_CAP_PCIPM_L1_2 | PCI_L1SS_CAP_ASPM_L1_2);
+ dw_pcie_writel_dbi(pci, l1ss + PCI_L1SS_CAP, val);
+}
+
#if defined(CONFIG_PCIEASPM)
static inline u32 event_counter_prog(struct tegra_pcie_dw *pcie, u32 event)
{
@@ -694,25 +721,6 @@ static void init_host_aspm(struct tegra_pcie_dw *pcie)
if (pcie->supports_clkreq)
pci->l1ss_support = true;
-
- /*
- * Disable L1.2 capability advertisement for Tegra234 Endpoint
- * mode. Tegra234 has a hardware bug where during L1.2 exit,
- * the UPHY PLL is powered up immediately without waiting for
- * REFCLK to stabilize. This causes the PLL to fail to lock to
- * the correct frequency, resulting in PCIe link loss. Since
- * there is no hardware fix available, prevent the Endpoint
- * from advertising L1.2 support by clearing the L1.2 bits in
- * the L1 PM Substates Capabilities register. This ensures the
- * host will not attempt to enter L1.2 with this Endpoint.
- */
- if (pcie->of_data->disable_l1_2 &&
- pcie->of_data->mode == DW_PCIE_EP_TYPE) {
- val = dw_pcie_readl_dbi(pci, l1ss + PCI_L1SS_CAP);
- val &= ~(PCI_L1SS_CAP_PCIPM_L1_2 |
- PCI_L1SS_CAP_ASPM_L1_2);
- dw_pcie_writel_dbi(pci, l1ss + PCI_L1SS_CAP, val);
- }
}
/* Program L0s and L1 entrance latencies */
@@ -1903,6 +1911,7 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie)
config_gen3_gen4_eq_presets(pcie);
init_host_aspm(pcie);
+ tegra_pcie_disable_ep_l1_2(pcie);
if (!pcie->of_data->has_l1ss_exit_fix) {
val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 08/11] PCI: tegra194: Guard Endpoint PLL-off error path
2026-09-23 7:22 [PATCH 00/11] PCI: tegra194: Fix EP and Root Port corner cases Manikanta Maddireddy
` (6 preceding siblings ...)
2026-09-23 7:22 ` [PATCH 07/11] PCI: tegra194: Always disable Tegra234 Endpoint L1.2 Manikanta Maddireddy
@ 2026-09-23 7:22 ` Manikanta Maddireddy
2026-09-23 7:22 ` [PATCH 09/11] PCI: tegra194: Balance core monitor clock on failures Manikanta Maddireddy
` (2 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Manikanta Maddireddy @ 2026-09-23 7:22 UTC (permalink / raw)
To: Lorenzo Pieralisi, Krzysztof Wilczynski, Manivannan Sadhasivam,
Bjorn Helgaas, linux-pci
Cc: Rob Herring, Thierry Reding, Jonathan Hunter,
Kishon Vijay Abraham I, Frank Li, Vidya Sagar, Niklas Cassel,
Koichiro Den, Marco Crivellari, Andy Shevchenko, linux-tegra,
linux-kernel, Manikanta Maddireddy
The Endpoint PERST# deassert path initializes the external-refclk PLL only
when enable_ext_refclk is set, but the error unwind always sent the PLL-off
request.
Guard the PLL-off request with enable_ext_refclk so Tegra234 Endpoint
configurations that did not enable the PLL do not send a spurious BPMP
PLL-off command.
Fixes: a54e19073718 ("PCI: tegra194: Add Tegra234 PCIe support")
Signed-off-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
---
drivers/pci/controller/dwc/pcie-tegra194.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index f8e39459db41..637779993c40 100644
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
@@ -1973,7 +1973,8 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie)
fail_core_apb_rst:
clk_disable_unprepare(pcie->core_clk);
fail_core_clk_enable:
- tegra_pcie_bpmp_set_pll_state(pcie, false);
+ if (pcie->enable_ext_refclk)
+ tegra_pcie_bpmp_set_pll_state(pcie, false);
fail_pll_init:
tegra_pcie_bpmp_set_ctrl_state(pcie, false);
fail_set_ctrl_state:
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 09/11] PCI: tegra194: Balance core monitor clock on failures
2026-09-23 7:22 [PATCH 00/11] PCI: tegra194: Fix EP and Root Port corner cases Manikanta Maddireddy
` (7 preceding siblings ...)
2026-09-23 7:22 ` [PATCH 08/11] PCI: tegra194: Guard Endpoint PLL-off error path Manikanta Maddireddy
@ 2026-09-23 7:22 ` Manikanta Maddireddy
2026-09-23 7:22 ` [PATCH 10/11] PCI: tegra194: Fix Endpoint MSI/MSI-X numbering Manikanta Maddireddy
2026-09-23 7:22 ` [PATCH 11/11] PCI: endpoint: test: Do not relocate fixed MSI-X tables Manikanta Maddireddy
10 siblings, 0 replies; 14+ messages in thread
From: Manikanta Maddireddy @ 2026-09-23 7:22 UTC (permalink / raw)
To: Lorenzo Pieralisi, Krzysztof Wilczynski, Manivannan Sadhasivam,
Bjorn Helgaas, linux-pci
Cc: Rob Herring, Thierry Reding, Jonathan Hunter,
Kishon Vijay Abraham I, Frank Li, Vidya Sagar, Niklas Cassel,
Koichiro Den, Marco Crivellari, Andy Shevchenko, linux-tegra,
linux-kernel, Manikanta Maddireddy
tegra_pcie_dw_host_init() enables core_clk_m, but failure paths that leave
after the host init callback can unconfigure the controller without
disabling that clock. The enable error is also only logged, so later
cleanup can try to disable a clock that was never enabled.
Track the monitor clock state, return enable failures, and use a common
helper on retry, remove, suspend, shutdown and host-init failure paths so
the clock is disabled exactly when it was enabled.
Fixes: a86ca8698c88 ("PCI: tegra194: Add core monitor clock support")
Signed-off-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
---
drivers/pci/controller/dwc/pcie-tegra194.c | 33 ++++++++++++++++++----
1 file changed, 27 insertions(+), 6 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index 637779993c40..9b337dbada70 100644
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
@@ -246,6 +246,7 @@ struct tegra_pcie_dw {
void __iomem *appl_base;
struct clk *core_clk;
struct clk *core_clk_m;
+ bool core_clk_m_enabled;
struct reset_control *core_apb_rst;
struct reset_control *core_rst;
struct dw_pcie pci;
@@ -855,6 +856,15 @@ static void tegra_pcie_enable_interrupts(struct dw_pcie_rp *pp)
tegra_pcie_enable_msi_interrupts(pp);
}
+static void tegra_pcie_disable_core_clk_m(struct tegra_pcie_dw *pcie)
+{
+ if (!pcie->core_clk_m_enabled)
+ return;
+
+ clk_disable_unprepare(pcie->core_clk_m);
+ pcie->core_clk_m_enabled = false;
+}
+
static void config_gen3_gen4_eq_presets(struct tegra_pcie_dw *pcie)
{
struct dw_pcie *pci = &pcie->pci;
@@ -914,6 +924,7 @@ static int tegra_pcie_dw_host_init(struct dw_pcie_rp *pp)
struct tegra_pcie_dw *pcie = to_tegra_pcie(pci);
u32 val;
u16 val_16;
+ int ret;
pp->bridge->ops = &tegra_pci_ops;
@@ -965,8 +976,12 @@ static int tegra_pcie_dw_host_init(struct dw_pcie_rp *pp)
}
clk_set_rate(pcie->core_clk, GEN4_CORE_CLK_FREQ);
- if (clk_prepare_enable(pcie->core_clk_m))
- dev_err(pci->dev, "Failed to enable core monitor clock\n");
+ ret = clk_prepare_enable(pcie->core_clk_m);
+ if (ret) {
+ dev_err(pci->dev, "Failed to enable core monitor clock: %d\n", ret);
+ return ret;
+ }
+ pcie->core_clk_m_enabled = true;
return 0;
}
@@ -1049,8 +1064,11 @@ static int tegra_pcie_dw_start_link(struct dw_pcie *pci)
* dw_pcie_host_init(). Disable the clock since below
* tegra_pcie_dw_host_init() will enable it again.
*/
- clk_disable_unprepare(pcie->core_clk_m);
- tegra_pcie_dw_host_init(pp);
+ tegra_pcie_disable_core_clk_m(pcie);
+ ret = tegra_pcie_dw_host_init(pp);
+ if (ret)
+ return ret;
+
dw_pcie_setup_rc(pp);
retry = false;
@@ -1565,6 +1583,7 @@ static int tegra_pcie_init_controller(struct tegra_pcie_dw *pcie)
return 0;
fail_host_init:
+ tegra_pcie_disable_core_clk_m(pcie);
tegra_pcie_unconfig_controller(pcie);
return ret;
}
@@ -1653,7 +1672,7 @@ static void tegra_pcie_dw_pme_turnoff(struct tegra_pcie_dw *pcie)
static void tegra_pcie_deinit_controller(struct tegra_pcie_dw *pcie)
{
- clk_disable_unprepare(pcie->core_clk_m);
+ tegra_pcie_disable_core_clk_m(pcie);
dw_pcie_host_deinit(&pcie->pci.pp);
tegra_pcie_dw_pme_turnoff(pcie);
tegra_pcie_unconfig_controller(pcie);
@@ -2450,7 +2469,7 @@ static int tegra_pcie_dw_suspend_noirq(struct device *dev)
if (!pcie->link_state)
return 0;
- clk_disable_unprepare(pcie->core_clk_m);
+ tegra_pcie_disable_core_clk_m(pcie);
tegra_pcie_dw_pme_turnoff(pcie);
tegra_pcie_unconfig_controller(pcie);
@@ -2487,6 +2506,7 @@ static int tegra_pcie_dw_resume_noirq(struct device *dev)
return 0;
fail_host_init:
+ tegra_pcie_disable_core_clk_m(pcie);
tegra_pcie_unconfig_controller(pcie);
return ret;
}
@@ -2528,6 +2548,7 @@ static void tegra_pcie_dw_shutdown(struct platform_device *pdev)
if (IS_ENABLED(CONFIG_PCI_MSI))
disable_irq(pcie->pci.pp.msi_irq[0]);
+ tegra_pcie_disable_core_clk_m(pcie);
tegra_pcie_dw_pme_turnoff(pcie);
tegra_pcie_unconfig_controller(pcie);
pm_runtime_put_sync(pcie->dev);
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 10/11] PCI: tegra194: Fix Endpoint MSI/MSI-X numbering
2026-09-23 7:22 [PATCH 00/11] PCI: tegra194: Fix EP and Root Port corner cases Manikanta Maddireddy
` (8 preceding siblings ...)
2026-09-23 7:22 ` [PATCH 09/11] PCI: tegra194: Balance core monitor clock on failures Manikanta Maddireddy
@ 2026-09-23 7:22 ` Manikanta Maddireddy
2026-09-23 13:32 ` Niklas Cassel
2026-09-23 7:22 ` [PATCH 11/11] PCI: endpoint: test: Do not relocate fixed MSI-X tables Manikanta Maddireddy
10 siblings, 1 reply; 14+ messages in thread
From: Manikanta Maddireddy @ 2026-09-23 7:22 UTC (permalink / raw)
To: Lorenzo Pieralisi, Krzysztof Wilczynski, Manivannan Sadhasivam,
Bjorn Helgaas, linux-pci
Cc: Rob Herring, Thierry Reding, Jonathan Hunter,
Kishon Vijay Abraham I, Frank Li, Vidya Sagar, Niklas Cassel,
Koichiro Den, Marco Crivellari, Andy Shevchenko, linux-tegra,
linux-kernel, Manikanta Maddireddy
pci_epc_raise_irq() passes MSI and MSI-X interrupt numbers in the
1-N range. The Tegra MSI path rejected values above 32 but accepted
zero, which makes BIT(irq - 1) shift by a negative amount.
The Tegra MSI-X path also wrote that 1-based number to the DWC MSI-X
doorbell. The doorbell expects a zero-based vector index, matching the
common DesignWare doorbell helper which programs interrupt_num - 1.
Reject zero for both MSI and MSI-X, read the MSI-X table size directly
from the DWC capability, and write irq - 1 to the MSI-X doorbell.
Also advertise MSI-X support in the Tegra Endpoint features so endpoint
functions can expose and exercise the Tegra MSI-X path.
Fixes: c57247f940e8 ("PCI: tegra: Add support for PCIe endpoint mode in Tegra194")
Signed-off-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
---
drivers/pci/controller/dwc/pcie-tegra194.c | 25 ++++++++++++++++++----
1 file changed, 21 insertions(+), 4 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index 9b337dbada70..513299541f18 100644
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
@@ -2026,7 +2026,7 @@ static int tegra_pcie_ep_raise_intx_irq(struct tegra_pcie_dw *pcie, u16 irq)
static int tegra_pcie_ep_raise_msi_irq(struct tegra_pcie_dw *pcie, u16 irq)
{
- if (unlikely(irq > 32))
+ if (unlikely(!irq || irq > 32))
return -EINVAL;
appl_writel(pcie, BIT(irq - 1), APPL_MSI_CTRL_1);
@@ -2034,11 +2034,26 @@ static int tegra_pcie_ep_raise_msi_irq(struct tegra_pcie_dw *pcie, u16 irq)
return 0;
}
-static int tegra_pcie_ep_raise_msix_irq(struct tegra_pcie_dw *pcie, u16 irq)
+static int tegra_pcie_ep_raise_msix_irq(struct tegra_pcie_dw *pcie,
+ u8 func_no, u16 irq)
{
struct dw_pcie_ep *ep = &pcie->pci.ep;
+ struct dw_pcie_ep_func *ep_func;
+ u32 reg;
+ u16 msix;
+ u16 val;
+
+ ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
+ if (!ep_func || !ep_func->msix_cap || !irq)
+ return -EINVAL;
+
+ reg = ep_func->msix_cap + PCI_MSIX_FLAGS;
+ val = dw_pcie_ep_readw_dbi(ep, func_no, reg);
+ msix = FIELD_GET(PCI_MSIX_FLAGS_QSIZE, val) + 1;
+ if (!(val & PCI_MSIX_FLAGS_ENABLE) || irq > msix)
+ return -EINVAL;
- writel(irq, ep->msi_mem);
+ writel(irq - 1, ep->msi_mem);
return 0;
}
@@ -2057,7 +2072,8 @@ static int tegra_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
return tegra_pcie_ep_raise_msi_irq(pcie, interrupt_num);
case PCI_IRQ_MSIX:
- return tegra_pcie_ep_raise_msix_irq(pcie, interrupt_num);
+ return tegra_pcie_ep_raise_msix_irq(pcie, func_no,
+ interrupt_num);
default:
dev_err(pci->dev, "Unknown IRQ type\n");
@@ -2096,6 +2112,7 @@ static const struct pci_epc_features tegra_pcie_epc_features = {
DWC_EPC_COMMON_FEATURES,
.linkup_notifier = true,
.msi_capable = true,
+ .msix_capable = true,
.bar[BAR_0] = { .only_64bit = true, },
.bar[BAR_2] = {
.type = BAR_RESERVED,
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 11/11] PCI: endpoint: test: Do not relocate fixed MSI-X tables
2026-09-23 7:22 [PATCH 00/11] PCI: tegra194: Fix EP and Root Port corner cases Manikanta Maddireddy
` (9 preceding siblings ...)
2026-09-23 7:22 ` [PATCH 10/11] PCI: tegra194: Fix Endpoint MSI/MSI-X numbering Manikanta Maddireddy
@ 2026-09-23 7:22 ` Manikanta Maddireddy
2026-09-23 14:32 ` Niklas Cassel
10 siblings, 1 reply; 14+ messages in thread
From: Manikanta Maddireddy @ 2026-09-23 7:22 UTC (permalink / raw)
To: Lorenzo Pieralisi, Krzysztof Wilczynski, Manivannan Sadhasivam,
Bjorn Helgaas, linux-pci
Cc: Rob Herring, Thierry Reding, Jonathan Hunter,
Kishon Vijay Abraham I, Frank Li, Vidya Sagar, Niklas Cassel,
Koichiro Den, Marco Crivellari, Andy Shevchenko, linux-tegra,
linux-kernel, Manikanta Maddireddy
Some endpoint controllers expose MSI-X table/PBA storage at fixed,
hardware-owned BARs. Such BARs are described to endpoint functions as
BAR_RESERVED with PCI_EPC_BAR_RSVD_MSIX_TBL_RAM, so the function driver
must not reprogram the MSI-X Table/PBA capability to point into ordinary
function BAR memory.
pci_epf_test always allocates MSI-X storage after its test registers and
calls pci_epc_set_msix(). On controllers with fixed MSI-X storage, this
overwrites the controller-defined MSI-X layout. For example, the generic
DesignWare path derives the PBA offset from the number of vectors, which
can make the host program the wrong MSI-X storage and prevent MSI-X
delivery.
Detect fixed MSI-X table BARs and leave the controller-owned MSI-X
capability layout unchanged. Keep allocating and programming MSI-X
storage in the test BAR only for controllers that do not advertise fixed
MSI-X table storage.
Signed-off-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
---
drivers/pci/endpoint/functions/pci-epf-test.c | 27 +++++++++++++++++--
1 file changed, 25 insertions(+), 2 deletions(-)
diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c
index d4905aa8e4c0..82d4714e998c 100644
--- a/drivers/pci/endpoint/functions/pci-epf-test.c
+++ b/drivers/pci/endpoint/functions/pci-epf-test.c
@@ -83,6 +83,7 @@ struct pci_epf_test {
struct config_group group;
enum pci_barno test_reg_bar;
size_t msix_table_offset;
+ bool msix_table_fixed;
struct delayed_work cmd_handler;
struct dma_chan *dma_chan_tx;
struct dma_chan *dma_chan_rx;
@@ -98,6 +99,26 @@ struct pci_epf_test {
size_t bar_size[PCI_STD_NUM_BARS];
};
+static bool pci_epf_test_msix_table_fixed(const struct pci_epc_features *features)
+{
+ enum pci_barno bar;
+ int i;
+
+ for (bar = BAR_0; bar < PCI_STD_NUM_BARS; bar++) {
+ const struct pci_epc_bar_desc *desc = &features->bar[bar];
+
+ if (desc->type != BAR_RESERVED)
+ continue;
+
+ for (i = 0; i < desc->nr_rsvd_regions; i++) {
+ if (desc->rsvd_regions[i].type == PCI_EPC_BAR_RSVD_MSIX_TBL_RAM)
+ return true;
+ }
+ }
+
+ return false;
+}
+
struct pci_epf_test_reg {
__le32 magic;
__le32 command;
@@ -1214,7 +1235,7 @@ static int pci_epf_test_epc_init(struct pci_epf *epf)
}
}
- if (epc_features->msix_capable) {
+ if (epc_features->msix_capable && !epf_test->msix_table_fixed) {
ret = pci_epc_set_msix(epc, epf->func_no, epf->vfunc_no,
epf->msix_interrupts,
epf_test->test_reg_bar,
@@ -1282,7 +1303,9 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf)
test_reg_bar_size = ALIGN(sizeof(struct pci_epf_test_reg), 128);
- if (epc_features->msix_capable) {
+ epf_test->msix_table_fixed = pci_epf_test_msix_table_fixed(epc_features);
+
+ if (epc_features->msix_capable && !epf_test->msix_table_fixed) {
msix_table_size = PCI_MSIX_ENTRY_SIZE * epf->msix_interrupts;
epf_test->msix_table_offset = test_reg_bar_size;
/* Align to QWORD or 8 Bytes */
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 10/11] PCI: tegra194: Fix Endpoint MSI/MSI-X numbering
2026-09-23 7:22 ` [PATCH 10/11] PCI: tegra194: Fix Endpoint MSI/MSI-X numbering Manikanta Maddireddy
@ 2026-09-23 13:32 ` Niklas Cassel
0 siblings, 0 replies; 14+ messages in thread
From: Niklas Cassel @ 2026-09-23 13:32 UTC (permalink / raw)
To: Manikanta Maddireddy
Cc: Lorenzo Pieralisi, Krzysztof Wilczynski, Manivannan Sadhasivam,
Bjorn Helgaas, linux-pci, Rob Herring, Thierry Reding,
Jonathan Hunter, Kishon Vijay Abraham I, Frank Li, Vidya Sagar,
Koichiro Den, Marco Crivellari, Andy Shevchenko, linux-tegra,
linux-kernel
On Wed, Sep 23, 2026 at 12:52:36PM +0530, Manikanta Maddireddy wrote:
> pci_epc_raise_irq() passes MSI and MSI-X interrupt numbers in the
> 1-N range. The Tegra MSI path rejected values above 32 but accepted
> zero, which makes BIT(irq - 1) shift by a negative amount.
>
> The Tegra MSI-X path also wrote that 1-based number to the DWC MSI-X
> doorbell. The doorbell expects a zero-based vector index, matching the
> common DesignWare doorbell helper which programs interrupt_num - 1.
>
> Reject zero for both MSI and MSI-X, read the MSI-X table size directly
> from the DWC capability, and write irq - 1 to the MSI-X doorbell.
>
> Also advertise MSI-X support in the Tegra Endpoint features so endpoint
> functions can expose and exercise the Tegra MSI-X path.
>
> Fixes: c57247f940e8 ("PCI: tegra: Add support for PCIe endpoint mode in Tegra194")
> Signed-off-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
> ---
> drivers/pci/controller/dwc/pcie-tegra194.c | 25 ++++++++++++++++++----
> 1 file changed, 21 insertions(+), 4 deletions(-)
Subject: PCI: tegra194: Fix Endpoint MSI/MSI-X numbering
Makes it look like the MSI numbering was wrong. It wasn't.
Don't get me wrong, I think this change makes sense:
> @@ -2026,7 +2026,7 @@ static int tegra_pcie_ep_raise_intx_irq(struct tegra_pcie_dw *pcie, u16 irq)
>
> static int tegra_pcie_ep_raise_msi_irq(struct tegra_pcie_dw *pcie, u16 irq)
> {
> - if (unlikely(irq > 32))
> + if (unlikely(!irq || irq > 32))
> return -EINVAL;
>
> appl_writel(pcie, BIT(irq - 1), APPL_MSI_CTRL_1);
But that change should be in a separate commit IMO, as does not not actually
fix any existing problem. All EPF drivers follow the pci_epc_raise_irq() kdoc,
and currently call pci_epc_raise_irq() with a value in (1-N).
That way the commit message for fixing tegra_pcie_ep_raise_msix_irq() is
also clearer, as you would not be talking about two separate paths in the
same commit.
Kind regards,
Niklas
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 11/11] PCI: endpoint: test: Do not relocate fixed MSI-X tables
2026-09-23 7:22 ` [PATCH 11/11] PCI: endpoint: test: Do not relocate fixed MSI-X tables Manikanta Maddireddy
@ 2026-09-23 14:32 ` Niklas Cassel
0 siblings, 0 replies; 14+ messages in thread
From: Niklas Cassel @ 2026-09-23 14:32 UTC (permalink / raw)
To: Manikanta Maddireddy, Koichiro Den
Cc: Lorenzo Pieralisi, Krzysztof Wilczynski, Manivannan Sadhasivam,
Bjorn Helgaas, linux-pci, Rob Herring, Thierry Reding,
Jonathan Hunter, Kishon Vijay Abraham I, Frank Li, Vidya Sagar,
Marco Crivellari, Andy Shevchenko, linux-tegra, linux-kernel
On Wed, Sep 23, 2026 at 12:52:37PM +0530, Manikanta Maddireddy wrote:
> Some endpoint controllers expose MSI-X table/PBA storage at fixed,
> hardware-owned BARs. Such BARs are described to endpoint functions as
> BAR_RESERVED with PCI_EPC_BAR_RSVD_MSIX_TBL_RAM, so the function driver
> must not reprogram the MSI-X Table/PBA capability to point into ordinary
> function BAR memory.
>
> pci_epf_test always allocates MSI-X storage after its test registers and
> calls pci_epc_set_msix(). On controllers with fixed MSI-X storage, this
> overwrites the controller-defined MSI-X layout. For example, the generic
> DesignWare path derives the PBA offset from the number of vectors, which
> can make the host program the wrong MSI-X storage and prevent MSI-X
> delivery.
>
> Detect fixed MSI-X table BARs and leave the controller-owned MSI-X
> capability layout unchanged. Keep allocating and programming MSI-X
> storage in the test BAR only for controllers that do not advertise fixed
> MSI-X table storage.
>
> Signed-off-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
> ---
> drivers/pci/endpoint/functions/pci-epf-test.c | 27 +++++++++++++++++--
> 1 file changed, 25 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c
> index d4905aa8e4c0..82d4714e998c 100644
> --- a/drivers/pci/endpoint/functions/pci-epf-test.c
> +++ b/drivers/pci/endpoint/functions/pci-epf-test.c
> @@ -83,6 +83,7 @@ struct pci_epf_test {
> struct config_group group;
> enum pci_barno test_reg_bar;
> size_t msix_table_offset;
> + bool msix_table_fixed;
> struct delayed_work cmd_handler;
> struct dma_chan *dma_chan_tx;
> struct dma_chan *dma_chan_rx;
> @@ -98,6 +99,26 @@ struct pci_epf_test {
> size_t bar_size[PCI_STD_NUM_BARS];
> };
>
> +static bool pci_epf_test_msix_table_fixed(const struct pci_epc_features *features)
> +{
> + enum pci_barno bar;
> + int i;
> +
> + for (bar = BAR_0; bar < PCI_STD_NUM_BARS; bar++) {
> + const struct pci_epc_bar_desc *desc = &features->bar[bar];
> +
> + if (desc->type != BAR_RESERVED)
> + continue;
> +
> + for (i = 0; i < desc->nr_rsvd_regions; i++) {
> + if (desc->rsvd_regions[i].type == PCI_EPC_BAR_RSVD_MSIX_TBL_RAM)
> + return true;
> + }
> + }
> +
> + return false;
> +}
> +
> struct pci_epf_test_reg {
> __le32 magic;
> __le32 command;
> @@ -1214,7 +1235,7 @@ static int pci_epf_test_epc_init(struct pci_epf *epf)
> }
> }
>
> - if (epc_features->msix_capable) {
> + if (epc_features->msix_capable && !epf_test->msix_table_fixed) {
> ret = pci_epc_set_msix(epc, epf->func_no, epf->vfunc_no,
> epf->msix_interrupts,
> epf_test->test_reg_bar,
> @@ -1282,7 +1303,9 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf)
>
> test_reg_bar_size = ALIGN(sizeof(struct pci_epf_test_reg), 128);
>
> - if (epc_features->msix_capable) {
> + epf_test->msix_table_fixed = pci_epf_test_msix_table_fixed(epc_features);
> +
> + if (epc_features->msix_capable && !epf_test->msix_table_fixed) {
> msix_table_size = PCI_MSIX_ENTRY_SIZE * epf->msix_interrupts;
> epf_test->msix_table_offset = test_reg_bar_size;
> /* Align to QWORD or 8 Bytes */
Here you are only updating pci-epf-test. What about the other EPF drivers?
Also, if you run then pci_endpoint kselftest, with the default 2048 MSI-X
interrupts. Are really all 2048 MSI-X successful?
It seems that often the "hardware owned" MSI-X table is much smaller than
that. See e.g. Koichiro's series which tries to use "hardware owned" MSI-X
table, but if the "hardware owned" MSI-X table is too small to hold all
2048 MSI-X IRQs, then "hardware owned" MSI-X table would not be used:
https://lore.kernel.org/linux-pci/20260830151948.3547577-2-den@valinux.co.jp/
+ if (layout->table_size < table_size || layout->pba_size < pba_size)
+ return -ENOSPC;
Apparently NTB EPF needs to read the MSI-X table from RAM:
https://lore.kernel.org/linux-pci/pj5vpwwd5tr5tuvhdq3bcpefnq27r6ljvwoysguz2o4tglcqx5@l7zmb6wjnhla/
Which is why he also decided to let an EPF driver choose if it should
use "hardware owned" MSI-X table or not.
Kind regards,
Niklas
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2026-09-23 14:32 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-23 7:22 [PATCH 00/11] PCI: tegra194: Fix EP and Root Port corner cases Manikanta Maddireddy
2026-09-23 7:22 ` [PATCH 01/11] PCI: tegra194: Propagate REFCLK select GPIO errors Manikanta Maddireddy
2026-09-23 7:22 ` [PATCH 02/11] PCI: tegra194: Check core reset deassertion Manikanta Maddireddy
2026-09-23 7:22 ` [PATCH 03/11] PCI: tegra194: Fix Endpoint PERST# IRQ suspend race Manikanta Maddireddy
2026-09-23 7:22 ` [PATCH 04/11] PCI: tegra194: Do not skip no-link Root Port remove cleanup Manikanta Maddireddy
2026-09-23 7:22 ` [PATCH 05/11] PCI: tegra194: Check for 16 GT/s capability before programming Manikanta Maddireddy
2026-09-23 7:22 ` [PATCH 06/11] PCI: tegra194: Check for L1SS " Manikanta Maddireddy
2026-09-23 7:22 ` [PATCH 07/11] PCI: tegra194: Always disable Tegra234 Endpoint L1.2 Manikanta Maddireddy
2026-09-23 7:22 ` [PATCH 08/11] PCI: tegra194: Guard Endpoint PLL-off error path Manikanta Maddireddy
2026-09-23 7:22 ` [PATCH 09/11] PCI: tegra194: Balance core monitor clock on failures Manikanta Maddireddy
2026-09-23 7:22 ` [PATCH 10/11] PCI: tegra194: Fix Endpoint MSI/MSI-X numbering Manikanta Maddireddy
2026-09-23 13:32 ` Niklas Cassel
2026-09-23 7:22 ` [PATCH 11/11] PCI: endpoint: test: Do not relocate fixed MSI-X tables Manikanta Maddireddy
2026-09-23 14:32 ` Niklas Cassel
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®