diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c index 9705059523a6..42df7296d4d4 100644 --- a/drivers/pci/controller/pcie-rockchip-host.c +++ b/drivers/pci/controller/pcie-rockchip-host.c @@ -38,6 +38,37 @@ #include "../pci.h" #include "pcie-rockchip.h" +#include "rockchip-pcie-dma.h" + +static void rk_pcie_start_dma_rk3399(struct dma_trx_obj *obj) +{ + struct rockchip_pcie *rockchip = dev_get_drvdata(obj->dev); + struct dma_table *tbl = obj->cur; + int chn = tbl->chn; + + rockchip_pcie_write(rockchip, (u32)(tbl->phys_descs & 0xffffffff), + PCIE_APB_CORE_UDMA_BASE + 0x14 * chn + 0x04); + rockchip_pcie_write(rockchip, (u32)(tbl->phys_descs >> 32), + PCIE_APB_CORE_UDMA_BASE + 0x14 * chn + 0x08); + rockchip_pcie_write(rockchip, BIT(0) | (tbl->dir << 1), + PCIE_APB_CORE_UDMA_BASE + 0x14 * chn + 0x00); +} + +static void rk_pcie_config_dma_rk3399(struct dma_table *table) +{ + u32 *desc = table->descs; + + *(desc + 0) = (u32)(table->local & 0xffffffff); + *(desc + 1) = (u32)(table->local >> 32); + *(desc + 2) = (u32)(table->bus & 0xffffffff); + *(desc + 3) = (u32)(table->bus >> 32); + *(desc + 4) = 0; + *(desc + 5) = 0; + *(desc + 6) = table->buf_size; + *(desc + 7) = 0; + *(desc + 8) = 0; + *(desc + 6) |= 1 << 24; +} static void rockchip_pcie_enable_bw_int(struct rockchip_pcie *rockchip) { @@ -159,6 +190,9 @@ static int rockchip_pcie_rd_other_conf(struct rockchip_pcie *rockchip, { u32 busdev; + if (rockchip->in_remove) + return PCIBIOS_SUCCESSFUL; + busdev = PCIE_ECAM_ADDR(bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), where); @@ -193,6 +227,9 @@ static int rockchip_pcie_wr_other_conf(struct rockchip_pcie *rockchip, { u32 busdev; + if (rockchip->in_remove) + return PCIBIOS_SUCCESSFUL; + busdev = PCIE_ECAM_ADDR(bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), where); if (!IS_ALIGNED(busdev, size)) @@ -299,6 +336,7 @@ static int rockchip_pcie_host_init_port(struct rockchip_pcie *rockchip) struct device *dev = rockchip->dev; int err, i = MAX_LANE_NUM; u32 status; + int timeouts = 500; gpiod_set_value_cansleep(rockchip->ep_gpio, 0); @@ -330,15 +368,26 @@ static int rockchip_pcie_host_init_port(struct rockchip_pcie *rockchip) gpiod_set_value_cansleep(rockchip->ep_gpio, 1); + if (rockchip->wait_ep) + timeouts = 10000; + /* 500ms timeout value should be enough for Gen1/2 training */ err = readl_poll_timeout(rockchip->apb_base + PCIE_CLIENT_BASIC_STATUS1, status, PCIE_LINK_UP(status), 20, - 500 * USEC_PER_MSEC); + timeouts * USEC_PER_MSEC); if (err) { dev_err(dev, "PCIe link training gen1 timeout!\n"); goto err_power_off_phy; } + err = readl_poll_timeout(rockchip->apb_base + PCIE_CLIENT_DEBUG_OUT_0, + status, PCIE_LINK_IS_L0(status), 20, + timeouts * USEC_PER_MSEC); + if (err) { + dev_err(dev, "LTSSM is not L0!\n"); + return -ETIMEDOUT; + } + if (rockchip->link_gen == 2) { /* * Enable retrain for gen2. This should be configured only after @@ -370,6 +419,11 @@ static int rockchip_pcie_host_init_port(struct rockchip_pcie *rockchip) } } + /* disable ltssm */ + if (rockchip->dma_trx_enabled) + rockchip_pcie_write(rockchip, PCIE_CLIENT_LINK_TRAIN_DISABLE, + PCIE_CLIENT_CONFIG); + rockchip_pcie_write(rockchip, ROCKCHIP_VENDOR_ID, PCIE_CORE_CONFIG_VENDOR); rockchip_pcie_write(rockchip, @@ -403,6 +457,33 @@ err_power_off_phy: return err; } +static inline void +rockchip_pcie_handle_dma_interrupt(struct rockchip_pcie *rockchip) +{ + u32 dma_status; + struct dma_trx_obj *obj = rockchip->dma_obj; + + dma_status = rockchip_pcie_read(rockchip, + PCIE_APB_CORE_UDMA_BASE + PCIE_UDMA_INT_REG); + + /* Core: clear dma interrupt */ + rockchip_pcie_write(rockchip, dma_status, + PCIE_APB_CORE_UDMA_BASE + PCIE_UDMA_INT_REG); + + WARN_ONCE(!(dma_status & 0x3), "dma_status 0x%x\n", dma_status); + + if (dma_status & (1 << 0)) { + obj->irq_num++; + obj->dma_free = true; + } + + if (list_empty(&obj->tbl_list)) { + if (obj->dma_free && + obj->loop_count >= obj->loop_count_threshold) + complete(&obj->done); + } +} + static irqreturn_t rockchip_pcie_subsys_irq_handler(int irq, void *arg) { struct rockchip_pcie *rockchip = arg; @@ -411,9 +492,10 @@ static irqreturn_t rockchip_pcie_subsys_irq_handler(int irq, void *arg) u32 sub_reg; reg = rockchip_pcie_read(rockchip, PCIE_CLIENT_INT_STATUS); + sub_reg = rockchip_pcie_read(rockchip, PCIE_CORE_INT_STATUS); + dev_dbg(dev, "reg = 0x%x, sub_reg = 0x%x\n", reg, sub_reg); if (reg & PCIE_CLIENT_INT_LOCAL) { dev_dbg(dev, "local interrupt received\n"); - sub_reg = rockchip_pcie_read(rockchip, PCIE_CORE_INT_STATUS); if (sub_reg & PCIE_CORE_INT_PRFPE) dev_dbg(dev, "parity error detected while reading from the PNP receive FIFO RAM\n"); @@ -463,6 +545,12 @@ static irqreturn_t rockchip_pcie_subsys_irq_handler(int irq, void *arg) rockchip_pcie_clr_bw_int(rockchip); } + if (reg & PCIE_CLIENT_INT_UDMA) { + rockchip_pcie_handle_dma_interrupt(rockchip); + rockchip_pcie_write(rockchip, sub_reg, PCIE_CLIENT_INT_STATUS); + rockchip_pcie_write(rockchip, reg, PCIE_CLIENT_INT_STATUS); + } + rockchip_pcie_write(rockchip, reg & PCIE_CLIENT_INT_LOCAL, PCIE_CLIENT_INT_STATUS); @@ -677,6 +765,8 @@ static void rockchip_pcie_enable_interrupts(struct rockchip_pcie *rockchip) PCIE_CORE_INT_MASK); rockchip_pcie_enable_bw_int(rockchip); + rockchip_pcie_write(rockchip, PCIE_UDMA_INT_ENABLE_MASK, + PCIE_APB_CORE_UDMA_BASE + PCIE_UDMA_INT_ENABLE_REG); } static int rockchip_pcie_intx_map(struct irq_domain *domain, unsigned int irq, @@ -815,6 +905,12 @@ static int rockchip_pcie_cfg_atu(struct rockchip_pcie *rockchip) } } + /* Workaround for PCIe DMA transfer */ + if (rockchip->dma_trx_enabled) { + rockchip_pcie_prog_ob_atu(rockchip, 1, AXI_WRAPPER_MEM_WRITE, + 32 - 1, rockchip->mem_reserve_start, 0x0); + } + err = rockchip_pcie_prog_ib_atu(rockchip, 2, 32 - 1, 0x0, 0); if (err) { dev_err(dev, "program RC mem inbound ATU failed\n"); @@ -850,6 +946,9 @@ static int rockchip_pcie_cfg_atu(struct rockchip_pcie *rockchip) 20 - 1, 0, 0); rockchip->msg_bus_addr += ((reg_no + offset) << 20); + rockchip->msg_region = devm_ioremap(dev, rockchip->msg_bus_addr, SZ_1M); + if (!rockchip->msg_region) + err = -ENOMEM; return err; } @@ -858,6 +957,10 @@ static int rockchip_pcie_wait_l2(struct rockchip_pcie *rockchip) u32 value; int err; + /* Don't enter L2 state when no ep connected */ + if (rockchip->dma_trx_enabled == 1) + return 0; + /* send PME_TURN_OFF message */ writel(0x0, rockchip->msg_region + PCIE_RC_SEND_PME_OFF); @@ -873,7 +976,7 @@ static int rockchip_pcie_wait_l2(struct rockchip_pcie *rockchip) return 0; } -static int __maybe_unused rockchip_pcie_suspend_noirq(struct device *dev) +static int rockchip_pcie_suspend_for_user(struct device *dev) { struct rockchip_pcie *rockchip = dev_get_drvdata(dev); int ret; @@ -889,8 +992,43 @@ static int __maybe_unused rockchip_pcie_suspend_noirq(struct device *dev) return ret; } + /* disable ltssm */ + rockchip_pcie_write(rockchip, PCIE_CLIENT_LINK_TRAIN_DISABLE, + PCIE_CLIENT_CONFIG); + rockchip_pcie_deinit_phys(rockchip); + return ret; +} + +static int rockchip_pcie_resume_for_user(struct device *dev) +{ + struct rockchip_pcie *rockchip = dev_get_drvdata(dev); + int err; + + err = rockchip_pcie_host_init_port(rockchip); + if (err) + return err; + + err = rockchip_pcie_cfg_atu(rockchip); + if (err) + return err; + + /* Need this to enter L1 again */ + rockchip_pcie_update_txcredit_mui(rockchip); + rockchip_pcie_enable_interrupts(rockchip); + + return 0; +} + +static int __maybe_unused rockchip_pcie_suspend_noirq(struct device *dev) +{ + struct rockchip_pcie *rockchip = dev_get_drvdata(dev); + int ret = 0; + + if (!rockchip->dma_trx_enabled) + ret = rockchip_pcie_suspend_for_user(dev); + rockchip_pcie_disable_clocks(rockchip); regulator_disable(rockchip->vpcie0v9); @@ -913,29 +1051,101 @@ static int __maybe_unused rockchip_pcie_resume_noirq(struct device *dev) if (err) goto err_disable_0v9; - err = rockchip_pcie_host_init_port(rockchip); + if (!rockchip->dma_trx_enabled) + err = rockchip_pcie_resume_for_user(dev); if (err) - goto err_pcie_resume; - - err = rockchip_pcie_cfg_atu(rockchip); - if (err) - goto err_err_deinit_port; - - /* Need this to enter L1 again */ - rockchip_pcie_update_txcredit_mui(rockchip); - rockchip_pcie_enable_interrupts(rockchip); + goto err_disable_clocks; return 0; -err_err_deinit_port: - rockchip_pcie_deinit_phys(rockchip); -err_pcie_resume: +err_disable_clocks: rockchip_pcie_disable_clocks(rockchip); err_disable_0v9: regulator_disable(rockchip->vpcie0v9); + return err; } +static int rockchip_pcie_really_probe(struct rockchip_pcie *rockchip) +{ + int err; + + err = rockchip_pcie_host_init_port(rockchip); + if (err) + return err; + + rockchip_pcie_enable_interrupts(rockchip); + + err = rockchip_pcie_cfg_atu(rockchip); + if (err) + return err; + + rockchip->bridge->sysdata = rockchip; + rockchip->bridge->ops = &rockchip_pcie_ops; + + return pci_host_probe(rockchip->bridge); +} + +static ssize_t pcie_deferred_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u32 val = 0; + int err; + struct rockchip_pcie *rockchip = dev_get_drvdata(dev); + + err = kstrtou32(buf, 10, &val); + if (err) + return err; + + if (val) { + rockchip->wait_ep = 1; + err = rockchip_pcie_really_probe(rockchip); + if (err) + return -EINVAL; + } + + return size; +} + +static ssize_t pcie_reset_ep_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u32 val = 0; + int err; + struct rockchip_pcie *rockchip = dev_get_drvdata(dev); + struct dma_trx_obj *obj = rockchip->dma_obj; + + dev_info(dev, "loop_cout = %d\n", obj->loop_count); + + err = kstrtou32(buf, 10, &val); + if (err) + return err; + + if (val == PCIE_USER_UNLINK) + rockchip_pcie_suspend_for_user(rockchip->dev); + else if (val == PCIE_USER_RELINK) + rockchip_pcie_resume_for_user(rockchip->dev); + else + return -EINVAL; + + return size; +} + +static DEVICE_ATTR_WO(pcie_deferred); +static DEVICE_ATTR_WO(pcie_reset_ep); + +static struct attribute *pcie_attrs[] = { + &dev_attr_pcie_deferred.attr, + &dev_attr_pcie_reset_ep.attr, + NULL +}; + +static const struct attribute_group pcie_attr_group = { + .attrs = pcie_attrs, +}; + static int rockchip_pcie_probe(struct platform_device *pdev) { struct rockchip_pcie *rockchip; @@ -952,6 +1162,8 @@ static int rockchip_pcie_probe(struct platform_device *pdev) rockchip = pci_host_bridge_priv(bridge); + rockchip->bridge = bridge; + platform_set_drvdata(pdev, rockchip); rockchip->dev = dev; rockchip->is_rc = true; @@ -970,39 +1182,47 @@ static int rockchip_pcie_probe(struct platform_device *pdev) goto err_set_vpcie; } - err = rockchip_pcie_host_init_port(rockchip); - if (err) - goto err_vpcie; - - rockchip_pcie_enable_interrupts(rockchip); - err = rockchip_pcie_init_irq_domain(rockchip); if (err < 0) - goto err_deinit_port; + goto err_vpcie; - err = rockchip_pcie_cfg_atu(rockchip); - if (err) - goto err_remove_irq_domain; - - rockchip->msg_region = devm_ioremap(dev, rockchip->msg_bus_addr, SZ_1M); - if (!rockchip->msg_region) { - err = -ENOMEM; - goto err_remove_irq_domain; + if (rockchip->deferred) { + err = sysfs_create_group(&pdev->dev.kobj, &pcie_attr_group); + if (err) { + dev_err(&pdev->dev, "SysFS group creation failed\n"); + goto err_remove_irq_domain; + } + } else { + err = rockchip_pcie_really_probe(rockchip); + if (err) { + dev_err(&pdev->dev, "deferred probe failed\n"); + goto err_deinit_port; + } } - bridge->sysdata = rockchip; - bridge->ops = &rockchip_pcie_ops; + if (rockchip->dma_trx_enabled == 0) + return 0; - err = pci_host_probe(bridge); - if (err < 0) - goto err_remove_irq_domain; + rockchip->dma_obj = rk_pcie_dma_obj_probe(dev); + if (IS_ERR(rockchip->dma_obj)) { + dev_err(dev, "failed to prepare dma object\n"); + err = -EINVAL; + goto err_deinit_port; + } + + if (rockchip->dma_obj) { + rockchip->dma_obj->start_dma_func = rk_pcie_start_dma_rk3399; + rockchip->dma_obj->config_dma_func = rk_pcie_config_dma_rk3399; + } return 0; -err_remove_irq_domain: - irq_domain_remove(rockchip->irq_domain); err_deinit_port: rockchip_pcie_deinit_phys(rockchip); + if (rockchip->deferred) + sysfs_remove_group(&pdev->dev.kobj, &pcie_attr_group); +err_remove_irq_domain: + irq_domain_remove(rockchip->irq_domain); err_vpcie: if (!IS_ERR(rockchip->vpcie12v)) regulator_disable(rockchip->vpcie12v); @@ -1019,16 +1239,41 @@ static int rockchip_pcie_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct rockchip_pcie *rockchip = dev_get_drvdata(dev); + u32 status1, status2; + u32 status; struct pci_host_bridge *bridge = pci_host_bridge_from_priv(rockchip); + status1 = rockchip_pcie_read(rockchip, PCIE_CLIENT_BASIC_STATUS1); + status2 = rockchip_pcie_read(rockchip, PCIE_CLIENT_DEBUG_OUT_0); + + if (!PCIE_LINK_UP(status1) || !PCIE_LINK_IS_L0(status2)) + rockchip->in_remove = 1; + pci_stop_root_bus(bridge->bus); pci_remove_root_bus(bridge->bus); irq_domain_remove(rockchip->irq_domain); + /* disable link state */ + status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LCS); + status |= BIT(4); + rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LCS); + + mdelay(1); + + status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LCS); + status &= ~BIT(4); + rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LCS); + rockchip_pcie_deinit_phys(rockchip); rockchip_pcie_disable_clocks(rockchip); + if (rockchip->dma_trx_enabled) + rk_pcie_dma_obj_remove(rockchip->dma_obj); + + if (rockchip->deferred) + sysfs_remove_group(&pdev->dev.kobj, &pcie_attr_group); + if (!IS_ERR(rockchip->vpcie12v)) regulator_disable(rockchip->vpcie12v); if (!IS_ERR(rockchip->vpcie3v3)) diff --git a/drivers/pci/controller/pcie-rockchip.h b/drivers/pci/controller/pcie-rockchip.h index c7d0178fc8c2..e34d332641f0 100644 --- a/drivers/pci/controller/pcie-rockchip.h +++ b/drivers/pci/controller/pcie-rockchip.h @@ -31,6 +31,7 @@ #define PCIE_CLIENT_CONF_ENABLE HIWORD_UPDATE_BIT(0x0001) #define PCIE_CLIENT_CONF_DISABLE HIWORD_UPDATE(0x0001, 0) #define PCIE_CLIENT_LINK_TRAIN_ENABLE HIWORD_UPDATE_BIT(0x0002) +#define PCIE_CLIENT_LINK_TRAIN_DISABLE HIWORD_UPDATE(0x0002, 0x0000) #define PCIE_CLIENT_ARI_ENABLE HIWORD_UPDATE_BIT(0x0008) #define PCIE_CLIENT_CONF_LANE_NUM(x) HIWORD_UPDATE(0x0030, ENCODE_LANES(x)) #define PCIE_CLIENT_MODE_RC HIWORD_UPDATE_BIT(0x0040) @@ -39,6 +40,7 @@ #define PCIE_CLIENT_GEN_SEL_2 HIWORD_UPDATE_BIT(0x0080) #define PCIE_CLIENT_DEBUG_OUT_0 (PCIE_CLIENT_BASE + 0x3c) #define PCIE_CLIENT_DEBUG_LTSSM_MASK GENMASK(5, 0) +#define PCIE_CLIENT_DEBUG_LTSSM_L0 0x10 #define PCIE_CLIENT_DEBUG_LTSSM_L1 0x18 #define PCIE_CLIENT_DEBUG_LTSSM_L2 0x19 #define PCIE_CLIENT_BASIC_STATUS1 (PCIE_CLIENT_BASE + 0x48) @@ -74,7 +76,20 @@ PCIE_CLIENT_INT_FATAL_ERR | PCIE_CLIENT_INT_DPA | \ PCIE_CLIENT_INT_HOT_RST | PCIE_CLIENT_INT_MSG | \ PCIE_CLIENT_INT_LEGACY_DONE | PCIE_CLIENT_INT_LEGACY | \ - PCIE_CLIENT_INT_PHY) + PCIE_CLIENT_INT_PHY | PCIE_CLIENT_INT_UDMA) + +#define PCIE_APB_CORE_UDMA_BASE (BIT(23) | BIT(22) | BIT(21)) +#define PCIE_CH0_DONE_ENABLE BIT(0) +#define PCIE_CH1_DONE_ENABLE BIT(1) +#define PCIE_CH0_ERR_ENABLE BIT(8) +#define PCIE_CH1_ERR_ENABLE BIT(9) + +#define PCIE_UDMA_INT_REG 0xa0 +#define PCIE_UDMA_INT_ENABLE_REG 0xa4 + +#define PCIE_UDMA_INT_ENABLE_MASK \ + (PCIE_CH0_DONE_ENABLE | PCIE_CH1_DONE_ENABLE | \ + PCIE_CH0_ERR_ENABLE | PCIE_CH1_ERR_ENABLE) #define PCIE_CORE_CTRL_MGMT_BASE 0x900000 #define PCIE_CORE_CTRL (PCIE_CORE_CTRL_MGMT_BASE + 0x000) @@ -185,6 +200,8 @@ #define PCIE_ECAM_ADDR(bus, dev, func, reg) \ (PCIE_ECAM_BUS(bus) | PCIE_ECAM_DEV(dev) | \ PCIE_ECAM_FUNC(func) | PCIE_ECAM_REG(reg)) +#define PCIE_LINK_IS_L0(x) \ + (((x) & PCIE_CLIENT_DEBUG_LTSSM_MASK) == PCIE_CLIENT_DEBUG_LTSSM_L0) #define PCIE_LINK_IS_L2(x) \ (((x) & PCIE_CLIENT_DEBUG_LTSSM_MASK) == PCIE_CLIENT_DEBUG_LTSSM_L2) #define PCIE_LINK_UP(x) \ @@ -275,6 +292,9 @@ (((c) << ((b) * 8 + 5)) & \ ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b)) +#define PCIE_USER_RELINK 0x1 +#define PCIE_USER_UNLINK 0x2 + struct rockchip_pcie { void __iomem *reg_base; /* DT axi-base */ void __iomem *apb_base; /* DT apb-base */ @@ -306,6 +326,15 @@ struct rockchip_pcie { phys_addr_t msg_bus_addr; bool is_rc; struct resource *mem_res; + phys_addr_t mem_reserve_start; + size_t mem_reserve_size; + int dma_trx_enabled; + int deferred; + int wait_ep; + struct dma_trx_obj *dma_obj; + struct list_head resources; + struct pci_host_bridge *bridge; + int in_remove; }; static u32 rockchip_pcie_read(struct rockchip_pcie *rockchip, u32 reg)