summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorBjorn Helgaas <bhelgaas@google.com>2026-04-13 12:50:47 -0500
committerBjorn Helgaas <bhelgaas@google.com>2026-04-13 12:50:47 -0500
commitb43cdb32ee1370eacdcb9b2a48e6c8a3978c7179 (patch)
treeb2472c5a107f8cf2abe562b67a05e8ff5e0849ed /drivers
parentbc55afb8fae10fc81a9c83c80b5d83ba74f5051a (diff)
parenta86ca8698c88461dd5770b638a2e2459f58d370c (diff)
downloadlwn-b43cdb32ee1370eacdcb9b2a48e6c8a3978c7179.tar.gz
lwn-b43cdb32ee1370eacdcb9b2a48e6c8a3978c7179.zip
Merge branch 'pci/controller/dwc-tegra194'
- Poll less aggressively and non-atomically for PME_TO_Ack during transition to L2 (Vidya Sagar) - Increase LTSSM poll time on surprise link down (Manikanta Maddireddy) - Disable LTSSM after transition to Detect on surprise link down to stop toggling between Polling and Detect (Manikanta Maddireddy) - Don't force the device into the D0 state before L2 when suspending or shutting down the controller (Vidya Sagar) - Disable PERST# IRQ only in Endpoint mode because it's not registered in Root Port mode (Manikanta Maddireddy) - Handle 'nvidia,refclk-select' as optional (Vidya Sagar) - Disable direct speed change in Endpoint mode so link speed change is controlled by the host (Vidya Sagar) - Set LTR values before link up to avoid bogus LTR messages with 0 latency (Vidya Sagar) - Allow system suspend when the Endpoint link is down (Vidya Sagar) - During remove, free resources allocated during Endpoint .probe() (Vidya Sagar) - Use DWC IP core version, not Tegra custom values, to avoid DWC core version check warnings (Manikanta Maddireddy) - Apply ECRC workaround to devices based on DesignWare 5.00a as well as 4.90a (Manikanta Maddireddy) - Disable PM Substate L1.2 in Endpoint mode to work around Tegra234 erratum (Vidya Sagar) - Delay post-PERST# cleanup until core is powered on to avoid CBB timeout (Manikanta Maddireddy) - Assert CLKREQ# so switches that forward it to their downstream side can bring up those links successfully (Vidya Sagar) - Calibrate pipe to UPHY for Endpoint mode to reset stale PLL state from any previous bad link state (Vidya Sagar) - Remove IRQF_ONESHOT flag from Endpoint interrupt registration so DMA driver and Endpoint controller driver can share the interrupt line (Vidya Sagar) - Enable DMA interrupt to support DMA in both Root Port and Endpoint modes (Vidya Sagar) - Enable hardware link retraining after link goes down in Endpoint mode (Vidya Sagar) - Add DT binding and driver support for core clock monitoring (Vidya Sagar) * pci/controller/dwc-tegra194: PCI: tegra194: Add core monitor clock support dt-bindings: PCI: tegra194: Add monitor clock support PCI: tegra194: Enable hardware hot reset mode in Endpoint mode PCI: tegra194: Enable DMA interrupt PCI: tegra194: Remove IRQF_ONESHOT flag during Endpoint interrupt registration PCI: tegra194: Calibrate pipe to UPHY for Endpoint mode PCI: tegra194: Assert CLKREQ# explicitly by default PCI: tegra194: Fix CBB timeout caused by DBI access before core power-on PCI: tegra194: Disable L1.2 capability of Tegra234 EP PCI: dwc: Apply ECRC workaround to DesignWare 5.00a as well PCI: tegra194: Use DWC IP core version PCI: tegra194: Free up Endpoint resources during remove() PCI: tegra194: Allow system suspend when the Endpoint link is not up PCI: tegra194: Set LTR message request before PCIe link up in Endpoint mode PCI: tegra194: Disable direct speed change for Endpoint mode PCI: tegra194: Use devm_gpiod_get_optional() to parse "nvidia,refclk-select" PCI: tegra194: Disable PERST# IRQ only in Endpoint mode PCI: tegra194: Don't force the device into the D0 state before L2 PCI: tegra194: Disable LTSSM after transition to Detect on surprise link down PCI: tegra194: Increase LTSSM poll time on surprise link down PCI: tegra194: Fix polling delay for L2 state
Diffstat (limited to 'drivers')
-rw-r--r--drivers/pci/controller/dwc/pcie-designware.c16
-rw-r--r--drivers/pci/controller/dwc/pcie-designware.h2
-rw-r--r--drivers/pci/controller/dwc/pcie-tegra194.c243
3 files changed, 158 insertions, 103 deletions
diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index 06792ba92aa7..c11cf61b8319 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -487,13 +487,13 @@ static inline void dw_pcie_writel_atu_ob(struct dw_pcie *pci, u32 index, u32 reg
static inline u32 dw_pcie_enable_ecrc(u32 val)
{
/*
- * DesignWare core version 4.90A has a design issue where the 'TD'
- * bit in the Control register-1 of the ATU outbound region acts
- * like an override for the ECRC setting, i.e., the presence of TLP
- * Digest (ECRC) in the outgoing TLPs is solely determined by this
- * bit. This is contrary to the PCIe spec which says that the
- * enablement of the ECRC is solely determined by the AER
- * registers.
+ * DWC versions 0x3530302a and 0x3536322a have a design issue where
+ * the 'TD' bit in the Control register-1 of the ATU outbound
+ * region acts like an override for the ECRC setting, i.e., the
+ * presence of TLP Digest (ECRC) in the outgoing TLPs is solely
+ * determined by this bit. This is contrary to the PCIe spec which
+ * says that the enablement of the ECRC is solely determined by the
+ * AER registers.
*
* Because of this, even when the ECRC is enabled through AER
* registers, the transactions going through ATU won't have TLP
@@ -563,7 +563,7 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci,
if (upper_32_bits(limit_addr) > upper_32_bits(parent_bus_addr) &&
dw_pcie_ver_is_ge(pci, 460A))
val |= PCIE_ATU_INCREASE_REGION_SIZE;
- if (dw_pcie_ver_is(pci, 490A))
+ if (dw_pcie_ver_is(pci, 490A) || dw_pcie_ver_is(pci, 500A))
val = dw_pcie_enable_ecrc(val);
dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL1, val);
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index b12c5334552c..3e69ef60165b 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -34,8 +34,10 @@
#define DW_PCIE_VER_470A 0x3437302a
#define DW_PCIE_VER_480A 0x3438302a
#define DW_PCIE_VER_490A 0x3439302a
+#define DW_PCIE_VER_500A 0x3530302a
#define DW_PCIE_VER_520A 0x3532302a
#define DW_PCIE_VER_540A 0x3534302a
+#define DW_PCIE_VER_562A 0x3536322a
#define __dw_pcie_ver_cmp(_pci, _ver, _op) \
((_pci)->version _op DW_PCIE_VER_ ## _ver)
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index ab1a259fc763..9dcfa194050e 100644
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
@@ -35,8 +35,8 @@
#include <soc/tegra/bpmp-abi.h>
#include "../../pci.h"
-#define TEGRA194_DWC_IP_VER 0x490A
-#define TEGRA234_DWC_IP_VER 0x562A
+#define TEGRA194_DWC_IP_VER DW_PCIE_VER_500A
+#define TEGRA234_DWC_IP_VER DW_PCIE_VER_562A
#define APPL_PINMUX 0x0
#define APPL_PINMUX_PEX_RST BIT(0)
@@ -44,6 +44,7 @@
#define APPL_PINMUX_CLKREQ_OVERRIDE BIT(3)
#define APPL_PINMUX_CLK_OUTPUT_IN_OVERRIDE_EN BIT(4)
#define APPL_PINMUX_CLK_OUTPUT_IN_OVERRIDE BIT(5)
+#define APPL_PINMUX_CLKREQ_DEFAULT_VALUE BIT(13)
#define APPL_CTRL 0x4
#define APPL_CTRL_SYS_PRE_DET_STATE BIT(6)
@@ -90,6 +91,7 @@
#define APPL_INTR_EN_L1_8_0 0x44
#define APPL_INTR_EN_L1_8_BW_MGT_INT_EN BIT(2)
#define APPL_INTR_EN_L1_8_AUTO_BW_INT_EN BIT(3)
+#define APPL_INTR_EN_L1_8_EDMA_INT_EN BIT(6)
#define APPL_INTR_EN_L1_8_INTX_EN BIT(11)
#define APPL_INTR_EN_L1_8_AER_INT_EN BIT(15)
@@ -137,7 +139,11 @@
#define APPL_DEBUG_PM_LINKST_IN_L0 0x11
#define APPL_DEBUG_LTSSM_STATE_MASK GENMASK(8, 3)
#define APPL_DEBUG_LTSSM_STATE_SHIFT 3
-#define LTSSM_STATE_PRE_DETECT 5
+#define LTSSM_STATE_DETECT_QUIET 0x00
+#define LTSSM_STATE_DETECT_ACT 0x08
+#define LTSSM_STATE_PRE_DETECT_QUIET 0x28
+#define LTSSM_STATE_DETECT_WAIT 0x30
+#define LTSSM_STATE_L2_IDLE 0xa8
#define APPL_RADM_STATUS 0xE4
#define APPL_PM_XMT_TURNOFF_STATE BIT(0)
@@ -198,9 +204,8 @@
#define CAP_SPCIE_CAP_OFF_USP_TX_PRESET0_MASK GENMASK(11, 8)
#define CAP_SPCIE_CAP_OFF_USP_TX_PRESET0_SHIFT 8
-#define PME_ACK_TIMEOUT 10000
-
-#define LTSSM_TIMEOUT 50000 /* 50ms */
+#define LTSSM_DELAY_US 10000 /* 10 ms */
+#define LTSSM_TIMEOUT_US 120000 /* 120 ms */
#define GEN3_GEN4_EQ_PRESET_INIT 5
@@ -231,6 +236,7 @@ struct tegra_pcie_dw_of_data {
bool has_sbr_reset_fix;
bool has_l1ss_exit_fix;
bool has_ltr_req_fix;
+ bool disable_l1_2;
u32 cdm_chk_int_en_bit;
u32 gen4_preset_vec;
u8 n_fts[2];
@@ -243,6 +249,7 @@ struct tegra_pcie_dw {
struct resource *atu_dma_res;
void __iomem *appl_base;
struct clk *core_clk;
+ struct clk *core_clk_m;
struct reset_control *core_apb_rst;
struct reset_control *core_rst;
struct dw_pcie pci;
@@ -482,15 +489,6 @@ static irqreturn_t tegra_pcie_ep_irq_thread(int irq, void *arg)
if (val & PCI_COMMAND_MASTER) {
ktime_t timeout;
- /* 110us for both snoop and no-snoop */
- val = FIELD_PREP(PCI_LTR_VALUE_MASK, 110) |
- FIELD_PREP(PCI_LTR_SCALE_MASK, 2) |
- LTR_MSG_REQ |
- FIELD_PREP(PCI_LTR_NOSNOOP_VALUE, 110) |
- FIELD_PREP(PCI_LTR_NOSNOOP_SCALE, 2) |
- LTR_NOSNOOP_MSG_REQ;
- appl_writel(pcie, val, APPL_LTR_MSG_1);
-
/* Send LTR upstream */
val = appl_readl(pcie, APPL_LTR_MSG_2);
val |= APPL_LTR_MSG_2_LTR_MSG_REQ_STATE;
@@ -548,6 +546,17 @@ static irqreturn_t tegra_pcie_ep_hard_irq(int irq, void *arg)
spurious = 0;
}
+ if (status_l0 & APPL_INTR_STATUS_L0_INT_INT) {
+ status_l1 = appl_readl(pcie, APPL_INTR_STATUS_L1_8_0);
+
+ /*
+ * Interrupt is handled by DMA driver; don't treat it as
+ * spurious
+ */
+ if (status_l1 & APPL_INTR_STATUS_L1_8_0_EDMA_INT_MASK)
+ spurious = 0;
+ }
+
if (spurious) {
dev_warn(pcie->dev, "Random interrupt (STATUS = 0x%08X)\n",
status_l0);
@@ -685,6 +694,23 @@ 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, 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) {
+ 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 */
val = dw_pcie_readl_dbi(pci, PCIE_PORT_AFR);
val &= ~PORT_AFR_L0S_ENTRANCE_LAT_MASK;
@@ -767,6 +793,7 @@ static void tegra_pcie_enable_intx_interrupts(struct dw_pcie_rp *pp)
val |= APPL_INTR_EN_L1_8_INTX_EN;
val |= APPL_INTR_EN_L1_8_AUTO_BW_INT_EN;
val |= APPL_INTR_EN_L1_8_BW_MGT_INT_EN;
+ val |= APPL_INTR_EN_L1_8_EDMA_INT_EN;
if (IS_ENABLED(CONFIG_PCIEAER))
val |= APPL_INTR_EN_L1_8_AER_INT_EN;
appl_writel(pcie, val, APPL_INTR_EN_L1_8_0);
@@ -924,6 +951,8 @@ 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");
return 0;
}
@@ -996,6 +1025,12 @@ retry_link:
val &= ~PCI_DLF_EXCHANGE_ENABLE;
dw_pcie_writel_dbi(pci, offset + PCI_DLF_CAP, val);
+ /*
+ * core_clk_m is enabled as part of host_init callback in
+ * 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);
dw_pcie_setup_rc(pp);
@@ -1022,7 +1057,8 @@ static void tegra_pcie_dw_stop_link(struct dw_pcie *pci)
{
struct tegra_pcie_dw *pcie = to_tegra_pcie(pci);
- disable_irq(pcie->pex_rst_irq);
+ if (pcie->of_data->mode == DW_PCIE_EP_TYPE)
+ disable_irq(pcie->pex_rst_irq);
}
static const struct dw_pcie_ops tegra_dw_pcie_ops = {
@@ -1058,6 +1094,9 @@ static int tegra_pcie_enable_phy(struct tegra_pcie_dw *pcie)
ret = phy_power_on(pcie->phys[i]);
if (ret < 0)
goto phy_exit;
+
+ if (pcie->of_data->mode == DW_PCIE_EP_TYPE)
+ phy_calibrate(pcie->phys[i]);
}
return 0;
@@ -1163,9 +1202,9 @@ static int tegra_pcie_dw_parse_dt(struct tegra_pcie_dw *pcie)
return err;
}
- pcie->pex_refclk_sel_gpiod = devm_gpiod_get(pcie->dev,
- "nvidia,refclk-select",
- GPIOD_OUT_HIGH);
+ 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;
@@ -1255,44 +1294,6 @@ static int tegra_pcie_bpmp_set_pll_state(struct tegra_pcie_dw *pcie,
return 0;
}
-static void tegra_pcie_downstream_dev_to_D0(struct tegra_pcie_dw *pcie)
-{
- struct dw_pcie_rp *pp = &pcie->pci.pp;
- struct pci_bus *child, *root_port_bus = NULL;
- struct pci_dev *pdev;
-
- /*
- * link doesn't go into L2 state with some of the endpoints with Tegra
- * if they are not in D0 state. So, need to make sure that immediate
- * downstream devices are in D0 state before sending PME_TurnOff to put
- * link into L2 state.
- * This is as per PCI Express Base r4.0 v1.0 September 27-2017,
- * 5.2 Link State Power Management (Page #428).
- */
-
- list_for_each_entry(child, &pp->bridge->bus->children, node) {
- if (child->parent == pp->bridge->bus) {
- root_port_bus = child;
- break;
- }
- }
-
- if (!root_port_bus) {
- dev_err(pcie->dev, "Failed to find downstream bus of Root Port\n");
- return;
- }
-
- /* Bring downstream devices to D0 if they are not already in */
- list_for_each_entry(pdev, &root_port_bus->devices, bus_list) {
- if (PCI_SLOT(pdev->devfn) == 0) {
- if (pci_set_power_state(pdev, PCI_D0))
- dev_err(pcie->dev,
- "Failed to transition %s to D0 state\n",
- dev_name(&pdev->dev));
- }
- }
-}
-
static int tegra_pcie_get_slot_regulators(struct tegra_pcie_dw *pcie)
{
pcie->slot_ctl_3v3 = devm_regulator_get_optional(pcie->dev, "vpcie3v3");
@@ -1454,6 +1455,7 @@ static int tegra_pcie_config_controller(struct tegra_pcie_dw *pcie,
val = appl_readl(pcie, APPL_PINMUX);
val |= APPL_PINMUX_CLKREQ_OVERRIDE_EN;
val &= ~APPL_PINMUX_CLKREQ_OVERRIDE;
+ val &= ~APPL_PINMUX_CLKREQ_DEFAULT_VALUE;
appl_writel(pcie, val, APPL_PINMUX);
}
@@ -1553,9 +1555,10 @@ static int tegra_pcie_try_link_l2(struct tegra_pcie_dw *pcie)
val |= APPL_PM_XMT_TURNOFF_STATE;
appl_writel(pcie, val, APPL_RADM_STATUS);
- return readl_poll_timeout_atomic(pcie->appl_base + APPL_DEBUG, val,
- val & APPL_DEBUG_PM_LINKST_IN_L2_LAT,
- 1, PME_ACK_TIMEOUT);
+ return readl_poll_timeout(pcie->appl_base + APPL_DEBUG, val,
+ val & APPL_DEBUG_PM_LINKST_IN_L2_LAT,
+ PCIE_PME_TO_L2_TIMEOUT_US/10,
+ PCIE_PME_TO_L2_TIMEOUT_US);
}
static void tegra_pcie_dw_pme_turnoff(struct tegra_pcie_dw *pcie)
@@ -1590,23 +1593,22 @@ static void tegra_pcie_dw_pme_turnoff(struct tegra_pcie_dw *pcie)
data &= ~APPL_PINMUX_PEX_RST;
appl_writel(pcie, data, APPL_PINMUX);
+ err = readl_poll_timeout(pcie->appl_base + APPL_DEBUG, data,
+ ((data & APPL_DEBUG_LTSSM_STATE_MASK) == LTSSM_STATE_DETECT_QUIET) ||
+ ((data & APPL_DEBUG_LTSSM_STATE_MASK) == LTSSM_STATE_DETECT_ACT) ||
+ ((data & APPL_DEBUG_LTSSM_STATE_MASK) == LTSSM_STATE_PRE_DETECT_QUIET) ||
+ ((data & APPL_DEBUG_LTSSM_STATE_MASK) == LTSSM_STATE_DETECT_WAIT),
+ LTSSM_DELAY_US, LTSSM_TIMEOUT_US);
+ if (err)
+ dev_info(pcie->dev, "LTSSM state: 0x%x detect timeout: %d\n", data, err);
+
/*
- * Some cards do not go to detect state even after de-asserting
- * PERST#. So, de-assert LTSSM to bring link to detect state.
+ * Deassert LTSSM state to stop the state toggling between
+ * Polling and Detect.
*/
data = readl(pcie->appl_base + APPL_CTRL);
data &= ~APPL_CTRL_LTSSM_EN;
writel(data, pcie->appl_base + APPL_CTRL);
-
- err = readl_poll_timeout_atomic(pcie->appl_base + APPL_DEBUG,
- data,
- ((data &
- APPL_DEBUG_LTSSM_STATE_MASK) >>
- APPL_DEBUG_LTSSM_STATE_SHIFT) ==
- LTSSM_STATE_PRE_DETECT,
- 1, LTSSM_TIMEOUT);
- if (err)
- dev_info(pcie->dev, "Link didn't go to detect state\n");
}
/*
* DBI registers may not be accessible after this as PLL-E would be
@@ -1622,7 +1624,7 @@ static void tegra_pcie_dw_pme_turnoff(struct tegra_pcie_dw *pcie)
static void tegra_pcie_deinit_controller(struct tegra_pcie_dw *pcie)
{
- tegra_pcie_downstream_dev_to_D0(pcie);
+ clk_disable_unprepare(pcie->core_clk_m);
dw_pcie_host_deinit(&pcie->pci.pp);
tegra_pcie_dw_pme_turnoff(pcie);
tegra_pcie_unconfig_controller(pcie);
@@ -1680,19 +1682,24 @@ static void pex_ep_event_pex_rst_assert(struct tegra_pcie_dw *pcie)
if (pcie->ep_state == EP_STATE_DISABLED)
return;
- /* Disable LTSSM */
+ ret = readl_poll_timeout(pcie->appl_base + APPL_DEBUG, val,
+ ((val & APPL_DEBUG_LTSSM_STATE_MASK) == LTSSM_STATE_DETECT_QUIET) ||
+ ((val & APPL_DEBUG_LTSSM_STATE_MASK) == LTSSM_STATE_DETECT_ACT) ||
+ ((val & APPL_DEBUG_LTSSM_STATE_MASK) == LTSSM_STATE_PRE_DETECT_QUIET) ||
+ ((val & APPL_DEBUG_LTSSM_STATE_MASK) == LTSSM_STATE_DETECT_WAIT) ||
+ ((val & APPL_DEBUG_LTSSM_STATE_MASK) == LTSSM_STATE_L2_IDLE),
+ LTSSM_DELAY_US, LTSSM_TIMEOUT_US);
+ if (ret)
+ dev_info(pcie->dev, "LTSSM state: 0x%x detect timeout: %d\n", val, ret);
+
+ /*
+ * Deassert LTSSM state to stop the state toggling between
+ * Polling and Detect.
+ */
val = appl_readl(pcie, APPL_CTRL);
val &= ~APPL_CTRL_LTSSM_EN;
appl_writel(pcie, val, APPL_CTRL);
- ret = readl_poll_timeout(pcie->appl_base + APPL_DEBUG, val,
- ((val & APPL_DEBUG_LTSSM_STATE_MASK) >>
- APPL_DEBUG_LTSSM_STATE_SHIFT) ==
- LTSSM_STATE_PRE_DETECT,
- 1, LTSSM_TIMEOUT);
- if (ret)
- dev_err(pcie->dev, "Failed to go Detect state: %d\n", ret);
-
reset_control_assert(pcie->core_rst);
tegra_pcie_disable_phy(pcie);
@@ -1771,10 +1778,6 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie)
goto fail_phy;
}
- /* Perform cleanup that requires refclk */
- pci_epc_deinit_notify(pcie->pci.ep.epc);
- dw_pcie_ep_cleanup(&pcie->pci.ep);
-
/* Clear any stale interrupt statuses */
appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L0);
appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_0_0);
@@ -1803,6 +1806,8 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie)
val = appl_readl(pcie, APPL_CTRL);
val |= APPL_CTRL_SYS_PRE_DET_STATE;
val |= APPL_CTRL_HW_HOT_RST_EN;
+ val &= ~(APPL_CTRL_HW_HOT_RST_MODE_MASK << APPL_CTRL_HW_HOT_RST_MODE_SHIFT);
+ val |= (APPL_CTRL_HW_HOT_RST_MODE_IMDT_RST_LTSSM_EN << APPL_CTRL_HW_HOT_RST_MODE_SHIFT);
appl_writel(pcie, val, APPL_CTRL);
val = appl_readl(pcie, APPL_CFG_MISC);
@@ -1826,6 +1831,7 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie)
val |= APPL_INTR_EN_L0_0_SYS_INTR_EN;
val |= APPL_INTR_EN_L0_0_LINK_STATE_INT_EN;
val |= APPL_INTR_EN_L0_0_PCI_CMD_EN_INT_EN;
+ val |= APPL_INTR_EN_L0_0_INT_INT_EN;
appl_writel(pcie, val, APPL_INTR_EN_L0_0);
val = appl_readl(pcie, APPL_INTR_EN_L1_0_0);
@@ -1833,8 +1839,29 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie)
val |= APPL_INTR_EN_L1_0_0_RDLH_LINK_UP_INT_EN;
appl_writel(pcie, val, APPL_INTR_EN_L1_0_0);
+ val = appl_readl(pcie, APPL_INTR_EN_L1_8_0);
+ val |= APPL_INTR_EN_L1_8_EDMA_INT_EN;
+ appl_writel(pcie, val, APPL_INTR_EN_L1_8_0);
+
+ /* 110us for both snoop and no-snoop */
+ val = FIELD_PREP(PCI_LTR_VALUE_MASK, 110) |
+ FIELD_PREP(PCI_LTR_SCALE_MASK, 2) |
+ LTR_MSG_REQ |
+ FIELD_PREP(PCI_LTR_NOSNOOP_VALUE, 110) |
+ FIELD_PREP(PCI_LTR_NOSNOOP_SCALE, 2) |
+ LTR_NOSNOOP_MSG_REQ;
+ appl_writel(pcie, val, APPL_LTR_MSG_1);
+
reset_control_deassert(pcie->core_rst);
+ /* 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;
+ dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
+
if (pcie->update_fc_fixup) {
val = dw_pcie_readl_dbi(pci, CFG_TIMER_CTRL_MAX_FUNC_NUM_OFF);
val |= 0x1 << CFG_TIMER_CTRL_ACK_NAK_SHIFT;
@@ -2170,6 +2197,11 @@ static int tegra_pcie_dw_probe(struct platform_device *pdev)
return PTR_ERR(pcie->core_clk);
}
+ pcie->core_clk_m = devm_clk_get_optional(dev, "core_m");
+ if (IS_ERR(pcie->core_clk_m))
+ return dev_err_probe(dev, PTR_ERR(pcie->core_clk_m),
+ "Failed to get monitor clock\n");
+
pcie->appl_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"appl");
if (!pcie->appl_res) {
@@ -2269,7 +2301,7 @@ static int tegra_pcie_dw_probe(struct platform_device *pdev)
ret = devm_request_threaded_irq(dev, pp->irq,
tegra_pcie_ep_hard_irq,
tegra_pcie_ep_irq_thread,
- IRQF_SHARED | IRQF_ONESHOT,
+ IRQF_SHARED,
"tegra-pcie-ep-intr", pcie);
if (ret) {
dev_err(dev, "Failed to request IRQ %d: %d\n", pp->irq,
@@ -2298,6 +2330,7 @@ fail:
static void tegra_pcie_dw_remove(struct platform_device *pdev)
{
struct tegra_pcie_dw *pcie = platform_get_drvdata(pdev);
+ struct dw_pcie_ep *ep = &pcie->pci.ep;
if (pcie->of_data->mode == DW_PCIE_RC_TYPE) {
if (!pcie->link_state)
@@ -2309,6 +2342,7 @@ static void tegra_pcie_dw_remove(struct platform_device *pdev)
} else {
disable_irq(pcie->pex_rst_irq);
pex_ep_event_pex_rst_assert(pcie);
+ dw_pcie_ep_deinit(ep);
}
pm_runtime_disable(pcie->dev);
@@ -2317,16 +2351,28 @@ static void tegra_pcie_dw_remove(struct platform_device *pdev)
gpiod_set_value(pcie->pex_refclk_sel_gpiod, 0);
}
-static int tegra_pcie_dw_suspend_late(struct device *dev)
+static int tegra_pcie_dw_suspend(struct device *dev)
{
struct tegra_pcie_dw *pcie = dev_get_drvdata(dev);
- u32 val;
if (pcie->of_data->mode == DW_PCIE_EP_TYPE) {
- dev_err(dev, "Failed to Suspend as Tegra PCIe is in EP mode\n");
- return -EPERM;
+ 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);
+ return 0;
}
+ return 0;
+}
+
+static int tegra_pcie_dw_suspend_late(struct device *dev)
+{
+ struct tegra_pcie_dw *pcie = dev_get_drvdata(dev);
+ u32 val;
+
if (!pcie->link_state)
return 0;
@@ -2346,10 +2392,13 @@ static int tegra_pcie_dw_suspend_noirq(struct device *dev)
{
struct tegra_pcie_dw *pcie = dev_get_drvdata(dev);
+ if (pcie->of_data->mode == DW_PCIE_EP_TYPE)
+ return 0;
+
if (!pcie->link_state)
return 0;
- tegra_pcie_downstream_dev_to_D0(pcie);
+ clk_disable_unprepare(pcie->core_clk_m);
tegra_pcie_dw_pme_turnoff(pcie);
tegra_pcie_unconfig_controller(pcie);
@@ -2361,6 +2410,9 @@ static int tegra_pcie_dw_resume_noirq(struct device *dev)
struct tegra_pcie_dw *pcie = dev_get_drvdata(dev);
int ret;
+ if (pcie->of_data->mode == DW_PCIE_EP_TYPE)
+ return 0;
+
if (!pcie->link_state)
return 0;
@@ -2393,8 +2445,8 @@ static int tegra_pcie_dw_resume_early(struct device *dev)
u32 val;
if (pcie->of_data->mode == DW_PCIE_EP_TYPE) {
- dev_err(dev, "Suspend is not supported in EP mode");
- return -ENOTSUPP;
+ enable_irq(pcie->pex_rst_irq);
+ return 0;
}
if (!pcie->link_state)
@@ -2423,7 +2475,6 @@ static void tegra_pcie_dw_shutdown(struct platform_device *pdev)
return;
debugfs_remove_recursive(pcie->debugfs);
- tegra_pcie_downstream_dev_to_D0(pcie);
disable_irq(pcie->pci.pp.irq);
if (IS_ENABLED(CONFIG_PCI_MSI))
@@ -2473,6 +2524,7 @@ static const struct tegra_pcie_dw_of_data tegra234_pcie_dw_ep_of_data = {
.mode = DW_PCIE_EP_TYPE,
.has_l1ss_exit_fix = true,
.has_ltr_req_fix = true,
+ .disable_l1_2 = true,
.cdm_chk_int_en_bit = BIT(18),
/* Gen4 - 6, 8 and 9 presets enabled */
.gen4_preset_vec = 0x340,
@@ -2500,6 +2552,7 @@ 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,
.suspend_late = tegra_pcie_dw_suspend_late,
.suspend_noirq = tegra_pcie_dw_suspend_noirq,
.resume_noirq = tegra_pcie_dw_resume_noirq,