PCI: rockchip: rk: Add PCIe udma transfer support

Change-Id: I68b60192a90962e03fe52b907a17234e8567e4b4
Signed-off-by: Simon Xue <xxm@rock-chips.com>
This commit is contained in:
Simon Xue
2021-04-07 16:36:16 +08:00
committed by Tao Huang
parent 1a0834789f
commit 15d983e5b6
2 changed files with 313 additions and 39 deletions

View File

@@ -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))

View File

@@ -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)