PCI: rockchip: dw_ep: Delaying the link training after hot reset

Delaying the link training after hot reset, so that it's possible
to read/write some register status through the DBI.

The controller support delaying the Link Training by setting
app_dly2_en/done register.

Change-Id: Ic6d1b28ca42e3d355610db33f4e5086cf26d705c
Signed-off-by: Jon Lin <jon.lin@rock-chips.com>
This commit is contained in:
Jon Lin
2023-06-16 17:13:31 +08:00
committed by Tao Huang
parent cabfbcfe75
commit c39fac2b31

View File

@ -10,6 +10,7 @@
#include <linux/clk.h>
#include <linux/gpio/consumer.h>
#include <linux/iopoll.h>
#include <linux/miscdevice.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
@ -81,6 +82,10 @@
#define PCIE_CLIENT_HOT_RESET_CTRL 0x180
#define PCIE_CLIENT_LTSSM_STATUS 0x300
#define PCIE_CLIENT_INTR_MASK 0x24
#define PCIE_LTSSM_APP_DLY1_EN BIT(0)
#define PCIE_LTSSM_APP_DLY2_EN BIT(1)
#define PCIE_LTSSM_APP_DLY1_DONE BIT(2)
#define PCIE_LTSSM_APP_DLY2_DONE BIT(3)
#define PCIE_LTSSM_ENABLE_ENHANCE BIT(4)
#define PCIE_CLIENT_MSI_GEN_CON 0x38
@ -106,6 +111,7 @@
#define PCIE_EP_OBJ_INFO_DRV_VERSION 0x00000001
#define PCIE_BAR_MAX_NUM 6
#define PCIE_HOTRESET_TMOUT_US 10000
struct rockchip_pcie {
struct dw_pcie pci;
@ -130,6 +136,8 @@ struct rockchip_pcie {
phys_addr_t dbi_base_physical;
struct pcie_ep_obj_info *obj_info;
enum pcie_ep_mmap_resource cur_mmap_res;
struct workqueue_struct *hot_rst_wq;
struct work_struct hot_rst_work;
};
struct rockchip_pcie_misc_dev {
@ -590,7 +598,8 @@ static void rockchip_pcie_fast_link_setup(struct rockchip_pcie *rockchip)
/* LTSSM EN ctrl mode */
val = rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_HOT_RESET_CTRL);
val |= PCIE_LTSSM_ENABLE_ENHANCE | (PCIE_LTSSM_ENABLE_ENHANCE << 16);
val |= (PCIE_LTSSM_ENABLE_ENHANCE | PCIE_LTSSM_APP_DLY2_EN) |
((PCIE_LTSSM_ENABLE_ENHANCE | PCIE_LTSSM_APP_DLY2_EN) << 16);
rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_HOT_RESET_CTRL);
}
@ -646,7 +655,7 @@ static irqreturn_t rockchip_pcie_sys_irq_handler(int irq, void *arg)
u32 chn;
union int_status wr_status, rd_status;
union int_clear clears;
u32 reg, val, mask;
u32 reg, mask;
bool sigio = false;
/* ELBI helper, only check the valid bits, and discard the rest interrupts */
@ -717,14 +726,8 @@ out:
}
reg = rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_INTR_STATUS_MISC);
if (reg & BIT(2)) {
/* Setup command register */
val = dw_pcie_readl_dbi(pci, PCI_COMMAND);
val &= 0xffff0000;
val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
dw_pcie_writel_dbi(pci, PCI_COMMAND, val);
}
if (reg & BIT(2))
queue_work(rockchip->hot_rst_wq, &rockchip->hot_rst_work);
rockchip_pcie_writel_apb(rockchip, reg, PCIE_CLIENT_INTR_STATUS_MISC);
@ -874,6 +877,23 @@ static void rockchip_pcie_config_dma_dwc(struct dma_table *table)
table->start.chnl = table->chn;
}
static void rockchip_pcie_hot_rst_work(struct work_struct *work)
{
struct rockchip_pcie *rockchip = container_of(work, struct rockchip_pcie, hot_rst_work);
u32 status;
int ret;
if (rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_HOT_RESET_CTRL) & PCIE_LTSSM_APP_DLY2_EN) {
ret = readl_poll_timeout(rockchip->apb_base + PCIE_CLIENT_LTSSM_STATUS,
status, ((status & 0x3F) == 0), 100, PCIE_HOTRESET_TMOUT_US);
if (ret)
dev_err(rockchip->pci.dev, "wait for detect quiet failed!\n");
rockchip_pcie_writel_apb(rockchip, (PCIE_LTSSM_APP_DLY2_DONE) | ((PCIE_LTSSM_APP_DLY2_DONE) << 16),
PCIE_CLIENT_HOT_RESET_CTRL);
}
}
static int rockchip_pcie_get_dma_status(struct dma_trx_obj *obj, u8 chn, enum dma_dir dir)
{
struct rockchip_pcie *rockchip = dev_get_drvdata(obj->dev);
@ -1125,6 +1145,7 @@ static int rockchip_pcie_ep_probe(struct platform_device *pdev)
struct rockchip_pcie *rockchip;
int ret;
int retry, i;
u32 reg;
rockchip = devm_kzalloc(dev, sizeof(*rockchip), GFP_KERNEL);
if (!rockchip)
@ -1186,6 +1207,26 @@ static int rockchip_pcie_ep_probe(struct platform_device *pdev)
rockchip_pcie_start_link(&rockchip->pci);
rockchip_pcie_devmode_update(rockchip, RKEP_MODE_KERNEL, RKEP_SMODE_LNKRDY);
rockchip->hot_rst_wq = create_singlethread_workqueue("rkep_hot_rst_wq");
if (!rockchip->hot_rst_wq) {
dev_err(dev, "failed to create hot_rst workqueue\n");
ret = -ENOMEM;
goto deinit_phy;
}
INIT_WORK(&rockchip->hot_rst_work, rockchip_pcie_hot_rst_work);
reg = rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_INTR_STATUS_MISC);
if ((reg & BIT(2)) &&
(rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_HOT_RESET_CTRL) & PCIE_LTSSM_APP_DLY2_EN)) {
rockchip_pcie_writel_apb(rockchip, PCIE_LTSSM_APP_DLY2_DONE | (PCIE_LTSSM_APP_DLY2_DONE << 16),
PCIE_CLIENT_HOT_RESET_CTRL);
dev_info(dev, "hot reset ever\n");
}
rockchip_pcie_writel_apb(rockchip, reg, PCIE_CLIENT_INTR_STATUS_MISC);
/* Enable client reset or link down interrupt */
rockchip_pcie_writel_apb(rockchip, 0x40000, PCIE_CLIENT_INTR_MASK);
for (retry = 0; retry < 10000; retry++) {
if (dw_pcie_link_up(&rockchip->pci)) {
/*