msm: ep_pcie: Disable hot reset and ignore linkdown

Intel host tends to retrain the link if device doesn't send PM_Enter_L23
in time, leading to linkdown and hot reset in device side. The hot reset
here even results to PERST# timeout and device crash to PBL, which is
not expected.

Since we are in the entry of shutdown the link when linkdown happen, it
makes sense to ignore the linkdown. To avoid device crash because of
PERST# timeout, disable hot reset in pm_turnoff irq and reenable it when
we do linktrain again.

Change-Id: I87961faaae2a14c9a5e7d24543b416914076dd2e
Signed-off-by: Qiang Yu <quic_qianyu@quicinc.com>
Signed-off-by: Sai Chaitanya Kaveti <quic_skaveti@quicinc.com>
This commit is contained in:
Qiang Yu 2024-03-12 20:27:22 -07:00 • committed by Pallavi Singh
commit 32de0804b1
3 changed files with 53 additions and 1 deletions

View file

@ -1,5 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.*/
/* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.*/
#ifndef __EP_PCIE_COM_H
#define __EP_PCIE_COM_H
@ -429,6 +430,7 @@ struct ep_pcie_dev_t {
struct clk *pipe_clk_mux;
struct clk *pipe_clk_ext_src;
struct clk *ref_clk_src;
u32 tcsr_hot_reset_en_offset;
};
extern struct ep_pcie_dev_t ep_pcie_dev;

View file

@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.*/
/* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.*/
/*
* MSM PCIe endpoint core driver.
@ -37,6 +38,7 @@
#define PCIE_MHI_STATUS(n) ((n) + 0x148)
#define TCSR_PERST_SEPARATION_ENABLE 0x270
#define TCSR_PCIE_RST_SEPARATION 0x3F8
#define TCSR_HOT_RESET_EN 0x3e8
#define PCIE_ISSUE_WAKE 1
#define PCIE_MHI_FWD_STATUS_MIN 5000
#define PCIE_MHI_FWD_STATUS_MAX 5100
@ -1815,6 +1817,13 @@ int ep_pcie_core_enable_endpoint(enum ep_pcie_options opt)
*/
writel_relaxed(0, dev->tcsr_perst_en +
TCSR_PERST_SEPARATION_ENABLE);
/*
* Re-enable hot reset before link up since we disable it
* in pm_turnoff irq.
*/
ep_pcie_write_reg_field(dev->tcsr_perst_en,
ep_pcie_dev.tcsr_hot_reset_en_offset, BIT(0), BIT(0));
}
/* check link status during initial bootup */
@ -2280,6 +2289,10 @@ static irqreturn_t ep_pcie_handle_linkdown_irq(int irq, void *data)
EP_PCIE_DBG(dev,
"PCIe V%d:Linkdown IRQ happened when the link is suspending\n",
dev->rev);
} else if (dev->link_status == EP_PCIE_LINK_IN_L23READY) {
EP_PCIE_DBG(dev,
"PCIe V%d:Linkdown IRQ happened when link goes to l23ready\n",
dev->rev);
} else {
dev->link_status = EP_PCIE_LINK_DISABLED;
EP_PCIE_ERR(dev, "PCIe V%d:PCIe link is down for %ld times\n",
@ -2320,13 +2333,29 @@ static irqreturn_t ep_pcie_handle_pm_turnoff_irq(int irq, void *data)
spin_lock_irqsave(&dev->isr_lock, irqsave_flags);
if (!dev->tcsr_not_supported)
/*
* Some hosts will try to recovery link if it doesn't receive PM_L23_Enter
* within 10ms after sending PME turn off, in which case if Hot reset is
* enabled, PERST# timeout circuit will start to work. If it measures that
* the link doesn't enter L0 within a predetermined time, device will crash
* with PERST_TIMEOUT_RESET_STATUS set to 1.
*
* Note that PERST# timeout circuit monitors the Detect to L0 transition and
* it gets activated in two scenario:
* 1) When PERST# gets de-asserted and perst-separation is enabled.
* 2) When hot-reset is enabled and link training is initiated.
*/
ep_pcie_write_reg_field(dev->tcsr_perst_en,
ep_pcie_dev.tcsr_hot_reset_en_offset, BIT(0), 0);
dev->pm_to_counter++;
EP_PCIE_DBG2(dev,
"PCIe V%d: No. %ld PM_TURNOFF is received\n",
dev->rev, dev->pm_to_counter);
EP_PCIE_DBG2(dev, "PCIe V%d: Put the link into L23\n", dev->rev);
ep_pcie_write_mask(dev->parf + PCIE20_PARF_PM_CTRL, 0, BIT(2));
dev->link_status = EP_PCIE_LINK_IN_L23READY;
spin_unlock_irqrestore(&dev->isr_lock, irqsave_flags);
return IRQ_HANDLED;
@ -2943,6 +2972,11 @@ enum ep_pcie_link_status ep_pcie_core_get_linkstatus(void)
return EP_PCIE_LINK_DISABLED;
}
if (dev->link_status == EP_PCIE_LINK_IN_L23READY) {
EP_PCIE_DBG(dev, "PCIe V%d: PCIe endpoint has sent PM_ENTER_L23\n", dev->rev);
return EP_PCIE_LINK_IN_L23READY;
}
bme = readl_relaxed(dev->dm_core +
PCIE20_COMMAND_STATUS) & BIT(2);
if (bme) {
@ -3499,6 +3533,21 @@ static int ep_pcie_probe(struct platform_device *pdev)
EP_PCIE_DBG(&ep_pcie_dev,
"PCIe V%d: tcsr pcie perst is %s supported\n",
ep_pcie_dev.rev, ep_pcie_dev.tcsr_not_supported ? "not" : "");
if (!ep_pcie_dev.tcsr_not_supported) {
ret = of_property_read_u32((&pdev->dev)->of_node, "qcom,tcsr-hot-reset-en-offset",
&ep_pcie_dev.tcsr_hot_reset_en_offset);
if (ret) {
EP_PCIE_DBG(&ep_pcie_dev,
"PCIe V%d: TCSR Hot Reset Enable Offset is not supplied from DT",
ep_pcie_dev.rev);
ep_pcie_dev.tcsr_hot_reset_en_offset = TCSR_HOT_RESET_EN;
}
EP_PCIE_DBG(&ep_pcie_dev,
"PCIe V%d: TCSR Hot Reset Enable Offset: 0x%x\n",
ep_pcie_dev.rev, ep_pcie_dev.tcsr_hot_reset_en_offset);
}
ep_pcie_dev.aoss_rst_clear = of_property_read_bool
((&pdev->dev)->of_node,

View file

@ -20,6 +20,7 @@ enum ep_pcie_link_status {
EP_PCIE_LINK_DISABLED,
EP_PCIE_LINK_UP,
EP_PCIE_LINK_ENABLED,
EP_PCIE_LINK_IN_L23READY,
};
enum ep_pcie_event {