From c39fac2b3189076399b1a83498b625c7c6bca6aa Mon Sep 17 00:00:00 2001 From: Jon Lin Date: Fri, 16 Jun 2023 17:13:31 +0800 Subject: [PATCH] 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 --- .../pci/controller/dwc/pcie-dw-ep-rockchip.c | 61 ++++++++++++++++--- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c index 14611f9354e1..09a5dc98de77 100644 --- a/drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -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)) { /*