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:
@ -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)) {
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user