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