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
This commit is contained in:
Bjorn Helgaas
2026-04-13 12:50:47 -05:00
5 changed files with 169 additions and 106 deletions

View File

@@ -55,12 +55,16 @@ properties:
- const: intr
clocks:
minItems: 1
items:
- description: module clock
- description: core clock
- description: monitor clock
clock-names:
minItems: 1
items:
- const: core
- const: core_m
resets:
items:

View File

@@ -58,12 +58,16 @@ properties:
- const: msi
clocks:
minItems: 1
items:
- description: module clock
- description: core clock
- description: monitor clock
clock-names:
minItems: 1
items:
- const: core
- const: core_m
resets:
items:

View File

@@ -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);

View File

@@ -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)

View File

@@ -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(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);
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->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->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,