mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 19:30:30 +09:00
PCI: rockchip: dw: Adding a retry mechanism to the enumeration process
When there is a small probability of signal abnormality, try resetting and enumerating again. Change-Id: Id37950dc62a497dd1c4703c42de036ce50aaec68 Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com> Signed-off-by: Jon Lin <jon.lin@rock-chips.com>
This commit is contained in:
@@ -109,6 +109,7 @@
|
||||
#define RK_PCIE_HOTRESET_TMOUT_US 10000
|
||||
#define PORT_LOGIC_LTSSM_STATE_L2(ltssm) \
|
||||
((ltssm & PORT_LOGIC_LTSSM_STATE_MASK) == 0x15)
|
||||
#define RK_PCIE_ENUM_HW_RETRYIES 2
|
||||
|
||||
#define PORT_LOGIC_LTSSM_L2
|
||||
|
||||
@@ -165,6 +166,8 @@ struct rk_pcie_of_data {
|
||||
};
|
||||
|
||||
#define to_rk_pcie(x) dev_get_drvdata((x)->dev)
|
||||
static int rk_pcie_disable_power(struct rk_pcie *rk_pcie);
|
||||
static int rk_pcie_enable_power(struct rk_pcie *rk_pcie);
|
||||
|
||||
static int rk_pcie_read(void __iomem *addr, int size, u32 *val)
|
||||
{
|
||||
@@ -695,6 +698,8 @@ static int rk_pcie_establish_link(struct dw_pcie *pci)
|
||||
int retries, power;
|
||||
struct rk_pcie *rk_pcie = to_rk_pcie(pci);
|
||||
bool std_rc = rk_pcie->mode == DW_PCIE_RC_TYPE && !rk_pcie->dma_obj;
|
||||
int hw_retries = 0;
|
||||
u32 ltssm;
|
||||
|
||||
/*
|
||||
* For standard RC, even if the link has been setup by firmware,
|
||||
@@ -706,77 +711,92 @@ static int rk_pcie_establish_link(struct dw_pcie *pci)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Rest the device */
|
||||
gpiod_set_value_cansleep(rk_pcie->rst_gpio, 0);
|
||||
for (hw_retries = 0; hw_retries < RK_PCIE_ENUM_HW_RETRYIES; hw_retries++) {
|
||||
/* Rest the device */
|
||||
gpiod_set_value_cansleep(rk_pcie->rst_gpio, 0);
|
||||
|
||||
rk_pcie_disable_ltssm(rk_pcie);
|
||||
rk_pcie_link_status_clear(rk_pcie);
|
||||
rk_pcie_enable_debug(rk_pcie);
|
||||
rk_pcie_disable_ltssm(rk_pcie);
|
||||
rk_pcie_link_status_clear(rk_pcie);
|
||||
rk_pcie_enable_debug(rk_pcie);
|
||||
|
||||
/* Enable client reset or link down interrupt */
|
||||
rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_INTR_MASK, 0x40000);
|
||||
/* Enable client reset or link down interrupt */
|
||||
rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_INTR_MASK, 0x40000);
|
||||
|
||||
/* Enable LTSSM */
|
||||
rk_pcie_enable_ltssm(rk_pcie);
|
||||
/* Enable LTSSM */
|
||||
rk_pcie_enable_ltssm(rk_pcie);
|
||||
|
||||
/*
|
||||
* In resume routine, function devices' resume function must be late after
|
||||
* controllers'. Some devices, such as Wi-Fi, need special IO setting before
|
||||
* finishing training. So there must be timeout here. These kinds of devices
|
||||
* need rescan devices by its driver when used. So no need to waste time waiting
|
||||
* for training pass.
|
||||
*/
|
||||
if (rk_pcie->in_suspend && rk_pcie->skip_scan_in_resume) {
|
||||
rfkill_get_wifi_power_state(&power);
|
||||
if (!power) {
|
||||
gpiod_set_value_cansleep(rk_pcie->rst_gpio, 1);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* PCIe requires the refclk to be stable for 100µs prior to releasing
|
||||
* PERST and T_PVPERL (Power stable to PERST# inactive) should be a
|
||||
* minimum of 100ms. See table 2-4 in section 2.6.2 AC, the PCI Express
|
||||
* Card Electromechanical Specification 3.0. So 100ms in total is the min
|
||||
* requuirement here. We add a 200ms by default for sake of hoping everthings
|
||||
* work fine. If it doesn't, please add more in DT node by add rockchip,perst-inactive-ms.
|
||||
*/
|
||||
msleep(rk_pcie->perst_inactive_ms);
|
||||
gpiod_set_value_cansleep(rk_pcie->rst_gpio, 1);
|
||||
|
||||
/*
|
||||
* Add this 1ms delay because we observe link is always up stably after it and
|
||||
* could help us save 20ms for scanning devices.
|
||||
*/
|
||||
usleep_range(1000, 1100);
|
||||
|
||||
for (retries = 0; retries < 100; retries++) {
|
||||
if (dw_pcie_link_up(pci)) {
|
||||
/*
|
||||
* We may be here in case of L0 in Gen1. But if EP is capable
|
||||
* of Gen2 or Gen3, Gen switch may happen just in this time, but
|
||||
* we keep on accessing devices in unstable link status. Given
|
||||
* that LTSSM max timeout is 24ms per period, we can wait a bit
|
||||
* more for Gen switch.
|
||||
*/
|
||||
msleep(50);
|
||||
/* In case link drop after linkup, double check it */
|
||||
if (dw_pcie_link_up(pci)) {
|
||||
dev_info(pci->dev, "PCIe Link up, LTSSM is 0x%x\n",
|
||||
rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_LTSSM_STATUS));
|
||||
rk_pcie_debug_dump(rk_pcie);
|
||||
/*
|
||||
* In resume routine, function devices' resume function must be late after
|
||||
* controllers'. Some devices, such as Wi-Fi, need special IO setting before
|
||||
* finishing training. So there must be timeout here. These kinds of devices
|
||||
* need rescan devices by its driver when used. So no need to waste time waiting
|
||||
* for training pass.
|
||||
*/
|
||||
if (rk_pcie->in_suspend && rk_pcie->skip_scan_in_resume) {
|
||||
rfkill_get_wifi_power_state(&power);
|
||||
if (!power) {
|
||||
gpiod_set_value_cansleep(rk_pcie->rst_gpio, 1);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
dev_info_ratelimited(pci->dev, "PCIe Linking... LTSSM is 0x%x\n",
|
||||
rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_LTSSM_STATUS));
|
||||
rk_pcie_debug_dump(rk_pcie);
|
||||
msleep(20);
|
||||
}
|
||||
/*
|
||||
* PCIe requires the refclk to be stable for 100µs prior to releasing
|
||||
* PERST and T_PVPERL (Power stable to PERST# inactive) should be a
|
||||
* minimum of 100ms. See table 2-4 in section 2.6.2 AC, the PCI Express
|
||||
* Card Electromechanical Specification 3.0. So 100ms in total is the min
|
||||
* requuirement here. We add a 200ms by default for sake of hoping everthings
|
||||
* work fine. If it doesn't, please add more in DT node by add rockchip,perst-inactive-ms.
|
||||
*/
|
||||
msleep(rk_pcie->perst_inactive_ms);
|
||||
gpiod_set_value_cansleep(rk_pcie->rst_gpio, 1);
|
||||
|
||||
dev_err(pci->dev, "PCIe Link Fail\n");
|
||||
/*
|
||||
* Add this 1ms delay because we observe link is always up stably after it and
|
||||
* could help us save 20ms for scanning devices.
|
||||
*/
|
||||
usleep_range(1000, 1100);
|
||||
|
||||
for (retries = 0; retries < 100; retries++) {
|
||||
if (dw_pcie_link_up(pci)) {
|
||||
/*
|
||||
* We may be here in case of L0 in Gen1. But if EP is capable
|
||||
* of Gen2 or Gen3, Gen switch may happen just in this time, but
|
||||
* we keep on accessing devices in unstable link status. Given
|
||||
* that LTSSM max timeout is 24ms per period, we can wait a bit
|
||||
* more for Gen switch.
|
||||
*/
|
||||
msleep(50);
|
||||
/* In case link drop after linkup, double check it */
|
||||
if (dw_pcie_link_up(pci)) {
|
||||
dev_info(pci->dev, "PCIe Link up, LTSSM is 0x%x\n",
|
||||
rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_LTSSM_STATUS));
|
||||
rk_pcie_debug_dump(rk_pcie);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
dev_info_ratelimited(pci->dev, "PCIe Linking... LTSSM is 0x%x\n",
|
||||
rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_LTSSM_STATUS));
|
||||
rk_pcie_debug_dump(rk_pcie);
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
/*
|
||||
* In response to the situation where PCIe peripherals cannot be
|
||||
* enumerated due tosignal abnormalities, reset PERST# and reset
|
||||
* the peripheral power supply, then restart the enumeration.
|
||||
*/
|
||||
ltssm = rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_LTSSM_STATUS);
|
||||
dev_err(pci->dev, "PCIe Link Fail, LTSSM is 0x%x, hw_retries=%d\n", ltssm, hw_retries);
|
||||
if (ltssm >= 3 && !rk_pcie->is_signal_test) {
|
||||
rk_pcie_disable_power(rk_pcie);
|
||||
msleep(1000);
|
||||
rk_pcie_enable_power(rk_pcie);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return rk_pcie->is_signal_test == true ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user