summaryrefslogtreecommitdiff
path: root/drivers/pci
diff options
context:
space:
mode:
authorRichard Zhu <hongxing.zhu@nxp.com>2017-11-17 14:46:32 +0800
committerJason Liu <jason.hui.liu@nxp.com>2019-02-12 10:29:18 +0800
commit67b1a931c7d30fa05ace942ea5492cc9623f54b5 (patch)
tree6c1cd830c87d70b76a26a7f72e357a7491fdec26 /drivers/pci
parent2304bca4a95f158a8822da96de2641cd35a4fe86 (diff)
MLK-16818-2 PCI: imx: enable the pcie ep rc for imx8
Enable the PCIE EP RC for iMX8 RC access memory of EP: - EP: write the <ddr_region_address> to the bar0 of ep. - RC: access the <mem_base_address>, and this address would be mapped to the <ddr_region_address> of ep. Note: ddr_region_address mem_base_addr bar0_addr imx8mq 0xb820_0000 0x2000_0000 0x33c0_0010 imx8qxp 0xb820_0000 0x6000_0000 0x5f00_0010 imx8qm 0xb820_0000 0x7000_0000 0x5f01_0010 MSI verification: - EP: write 0 to the <msi_addr>, for example ./memtool -32 <msi_addr>=0 - RC: check the msi is triggered or not. cat /proc/interrupts | grep msi Note: The msi_addr can be get by the following command after RC platform is boot up. For example root@imx8_all:~# dmesg | grep msi_addr [ 2.670247] pci_msi_addr = 0x7ff80000, cpu_base 0x80000000 msi_addr imx8mq 0x27f8_0000 imx8qxp 0x6ff8_0000 imx8qm 0x7ff8_0000 iMX8QM: BuildInfo: - SCFW daabd5d3, IMX-MKIMAGE 0ad6069a, ATF 93dd1cc - U-Boot 2017.03-imx_v2017.03+gc662e0a iMX8QXP: BuildInfo: - SCFW daabd5d3, IMX-MKIMAGE 0ad6069a, ATF 93dd1cc - U-Boot 2017.03-imx_v2017.03+gc662e0a Signed-off-by: Richard Zhu <hongxing.zhu@nxp.com>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/dwc/pci-imx6-ep-driver.c62
-rw-r--r--drivers/pci/dwc/pci-imx6.c159
2 files changed, 162 insertions, 59 deletions
diff --git a/drivers/pci/dwc/pci-imx6-ep-driver.c b/drivers/pci/dwc/pci-imx6-ep-driver.c
index 192ced7cbffc..dd300a99ab21 100644
--- a/drivers/pci/dwc/pci-imx6-ep-driver.c
+++ b/drivers/pci/dwc/pci-imx6-ep-driver.c
@@ -29,6 +29,7 @@
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#define DRV_DESCRIPTION "i.MX PCIE endpoint device driver"
#define DRV_VERSION "version 0.1"
@@ -49,8 +50,10 @@ struct imx_pcie_ep_priv {
static int imx_pcie_ep_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
- int ret = 0;
- unsigned int msi_addr = 0;
+ int ret = 0, index = 0, found = 0;
+ unsigned int msi_addr = 0, cpu_base;
+ struct resource cfg_res;
+ const char *name = NULL;
struct device_node *np;
struct device *dev = &pdev->dev;
struct imx_pcie_ep_priv *priv;
@@ -74,7 +77,7 @@ static int imx_pcie_ep_probe(struct pci_dev *pdev,
priv->hw_base = pci_iomap(pdev, 0, 0);
if (!priv->hw_base) {
ret = -ENODEV;
- goto out;
+ goto err_pci_disable;
}
pr_info("pci_resource_len = 0x%08llx\n",
@@ -84,23 +87,45 @@ static int imx_pcie_ep_probe(struct pci_dev *pdev,
ret = pci_enable_msi(priv->pci_dev);
if (ret < 0) {
dev_err(dev, "can't enable msi\n");
- return ret;
+ goto err_pci_unmap_mmio;
}
np = of_find_compatible_node(NULL, NULL, "snps,dw-pcie");
+ if (of_property_read_u32(np, "cpu-base-addr", &cpu_base))
+ cpu_base = 0;
+
+ while (!of_property_read_string_index(np, "reg-names", index, &name)) {
+ if (strcmp("config", name)) {
+ index++;
+ continue;
+ }
+
+ /* We have a match and @index is where it's at */
+ found = 1;
+ break;
+ }
+
+ if (!found) {
+ dev_err(dev, "can't find config reg space.\n");
+ ret = -EINVAL;
+ goto err_pci_disable_msi;
+ }
- /*
- * Force to use the hardcode MSI address to do the MSI demo
- */
- if (of_device_is_compatible(np, "fsl,imx7d-pcie"))
- msi_addr = 0x4FFC0000;
- else if (of_device_is_compatible(np, "fsl,imx6sx-pcie"))
- msi_addr = 0x08FF8000;
- else
- msi_addr = 0x01FF8000;
-
- pr_info("pci_msi_addr = 0x%08x\n", msi_addr);
+ ret = of_address_to_resource(np, index, &cfg_res);
+ if (ret) {
+ dev_err(dev, "can't get cfg_res.\n");
+ ret = -EINVAL;
+ goto err_pci_disable_msi;
+ } else {
+ msi_addr = cfg_res.start + resource_size(&cfg_res);
+ }
+
+ pr_info("pci_msi_addr = 0x%08x, cpu_base 0x%08x\n", msi_addr, cpu_base);
pci_bus_write_config_dword(pdev->bus, 0, 0x54, msi_addr);
+ if (cpu_base) {
+ msi_addr = msi_addr & 0xFFFFFFF;
+ msi_addr |= (cpu_base & 0xF0000000);
+ }
pci_bus_write_config_dword(pdev->bus->parent, 0, 0x820, msi_addr);
/* configure rc's msi cap */
@@ -112,7 +137,14 @@ static int imx_pcie_ep_probe(struct pci_dev *pdev,
return 0;
+err_pci_disable_msi:
+ pci_disable_msi(pdev);
+err_pci_unmap_mmio:
+ pci_iounmap(pdev, priv->hw_base);
+err_pci_disable:
+ pci_disable_device(pdev);
out:
+ kfree(priv);
return ret;
}
diff --git a/drivers/pci/dwc/pci-imx6.c b/drivers/pci/dwc/pci-imx6.c
index 76fa24f6f134..5b4d5f8656e0 100644
--- a/drivers/pci/dwc/pci-imx6.c
+++ b/drivers/pci/dwc/pci-imx6.c
@@ -66,6 +66,7 @@ struct imx6_pcie {
u32 ext_osc;
u32 ctrl_id;
u32 cpu_base;
+ u32 hard_wired;
int clkreq_gpio;
int dis_gpio;
int power_on_gpio;
@@ -105,7 +106,6 @@ struct imx6_pcie {
#define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1 0x1
#define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2 0x2
#define PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK 0xf
-
#define PCIE_RC_LCSR 0x80
/* PCIe Port Logic registers (memory-mapped) */
@@ -113,6 +113,11 @@ struct imx6_pcie {
#define PCIE_PL_PFLR (PL_OFFSET + 0x08)
#define PCIE_PL_PFLR_LINK_STATE_MASK (0x3f << 16)
#define PCIE_PL_PFLR_FORCE_LINK (1 << 15)
+#define PCIE_PORT_LINK_CONTROL 0x710
+#define PORT_LINK_MODE_MASK (0x3f << 16)
+#define PORT_LINK_MODE_1_LANES (0x1 << 16)
+#define PORT_LINK_MODE_2_LANES (0x3 << 16)
+
#define PCIE_PHY_DEBUG_R0 (PL_OFFSET + 0x28)
#define PCIE_PHY_DEBUG_R1 (PL_OFFSET + 0x2c)
#define PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING (1 << 29)
@@ -130,10 +135,15 @@ struct imx6_pcie {
#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17)
+#define PORT_LOGIC_LINK_WIDTH_MASK (0x1f << 8)
+#define PORT_LOGIC_LINK_WIDTH_1_LANES (0x1 << 8)
+#define PORT_LOGIC_LINK_WIDTH_2_LANES (0x2 << 8)
#define PCIE_MISC_CTRL (PL_OFFSET + 0x1BC)
#define PCIE_MISC_DBI_RO_WR_EN BIT(0)
+#define PCIE_ATU_VIEWPORT 0x900
+
/* PHY registers (not memory-mapped) */
#define PCIE_PHY_RX_ASIC_OUT 0x100D
#define PCIE_PHY_RX_ASIC_OUT_VALID (1 << 0)
@@ -1417,13 +1427,15 @@ static int imx6_pcie_host_init(struct pcie_port *pp)
if (ret < 0)
return ret;
- dw_pcie_setup_rc(pp);
- ret = imx6_pcie_establish_link(imx6_pcie);
- if (ret < 0)
- return ret;
+ if (!IS_ENABLED(CONFIG_EP_MODE_IN_EP_RC_SYS)) {
+ dw_pcie_setup_rc(pp);
+ ret = imx6_pcie_establish_link(imx6_pcie);
+ if (ret < 0)
+ return ret;
- if (IS_ENABLED(CONFIG_PCI_MSI))
- dw_pcie_msi_init(pp);
+ if (IS_ENABLED(CONFIG_PCI_MSI))
+ dw_pcie_msi_init(pp);
+ }
return 0;
}
@@ -1508,26 +1520,29 @@ static void imx_pcie_regions_setup(struct device *dev)
struct dw_pcie *pci = imx6_pcie->pci;
struct pcie_port *pp = &pci->pp;
- if (imx6_pcie->variant == IMX7D && ddr_test_region == 0)
- ddr_test_region = 0xb0000000;
- else if (imx6_pcie->variant == IMX6SX && ddr_test_region == 0)
+ switch (imx6_pcie->variant) {
+ case IMX8QM:
+ case IMX8QXP:
+ case IMX8MQ:
+ /*
+ * RPMSG reserved 4Mbytes, but only used up to 2Mbytes.
+ * The left 2Mbytes can be used here.
+ */
+ ddr_test_region = 0xb8200000;
+ break;
+ case IMX6SX:
+ case IMX7D:
ddr_test_region = 0xb0000000;
- else if (ddr_test_region == 0)
+ break;
+
+ case IMX6Q:
+ case IMX6QP:
ddr_test_region = 0x40000000;
+ break;
+ }
- /*
- * region2 outbound used to access rc/ep mem
- * in imx6 pcie ep/rc validation system
- */
- writel(2, pci->dbi_base + 0x900);
- writel((u32)pp->mem_base, pci->dbi_base + 0x90c);
- writel(0, pci->dbi_base + 0x910);
- writel((u32)pp->mem_base + test_region_size, pci->dbi_base + 0x914);
-
- writel(ddr_test_region, pci->dbi_base + 0x918);
- writel(0, pci->dbi_base + 0x91c);
- writel(0, pci->dbi_base + 0x904);
- writel(1 << 31, pci->dbi_base + 0x908);
+ dw_pcie_prog_outbound_atu(pci, 2, 0, pp->mem_base,
+ ddr_test_region, test_region_size);
}
static ssize_t imx_pcie_memw_info(struct device *dev,
@@ -1641,6 +1656,51 @@ static struct attribute_group imx_pcie_attrgroup = {
static void imx6_pcie_setup_ep(struct dw_pcie *pci)
{
+ int ret;
+ u32 val;
+ u32 lanes;
+ struct device_node *np = pci->dev->of_node;
+
+ ret = of_property_read_u32(np, "num-lanes", &lanes);
+ if (ret)
+ lanes = 0;
+
+ /* set the number of lanes */
+ val = dw_pcie_readl_dbi(pci, PCIE_PORT_LINK_CONTROL);
+ val &= ~PORT_LINK_MODE_MASK;
+ switch (lanes) {
+ case 1:
+ val |= PORT_LINK_MODE_1_LANES;
+ break;
+ case 2:
+ val |= PORT_LINK_MODE_2_LANES;
+ break;
+ default:
+ dev_err(pci->dev, "num-lanes %u: invalid value\n", lanes);
+ return;
+ }
+ dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val);
+
+ /* set link width speed control register */
+ val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
+ val &= ~PORT_LOGIC_LINK_WIDTH_MASK;
+ switch (lanes) {
+ case 1:
+ val |= PORT_LOGIC_LINK_WIDTH_1_LANES;
+ break;
+ case 2:
+ val |= PORT_LOGIC_LINK_WIDTH_2_LANES;
+ break;
+ }
+ dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
+
+ /* get iATU unroll support */
+ val = dw_pcie_readl_dbi(pci, PCIE_ATU_VIEWPORT);
+ if (val == 0xffffffff)
+ pci->iatu_unroll_enabled = 1;
+ dev_info(pci->dev, "iATU unroll: %s\n",
+ pci->iatu_unroll_enabled ? "enabled" : "disabled");
+
/* CMD reg:I/O space, MEM space, and Bus Master Enable */
writel(readl(pci->dbi_base + PCI_COMMAND)
| PCI_COMMAND_IO
@@ -1653,7 +1713,7 @@ static void imx6_pcie_setup_ep(struct dw_pcie *pci)
* bar0 and bar1 of ep
*/
writel(0xdeadbeaf, pci->dbi_base + PCI_VENDOR_ID);
- writel(readl(pci->dbi_base + PCI_CLASS_REVISION)
+ writel((readl(pci->dbi_base + PCI_CLASS_REVISION) & 0xFFFF)
| (PCI_CLASS_MEMORY_RAM << 16),
pci->dbi_base + PCI_CLASS_REVISION);
writel(0xdeadbeaf, pci->dbi_base
@@ -1922,6 +1982,8 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
if (of_property_read_u32(node, "cpu-base-addr", &imx6_pcie->cpu_base))
imx6_pcie->cpu_base = 0;
+ if (of_property_read_u32(node, "hard-wired", &imx6_pcie->hard_wired))
+ imx6_pcie->hard_wired = 0;
np = of_find_compatible_node(NULL, NULL, "fsl,imx-pcie-phy");
if (np != NULL) {
@@ -2168,8 +2230,9 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, imx6_pcie);
- if (IS_ENABLED(CONFIG_EP_MODE_IN_EP_RC_SYS)) {
- int i;
+ if (IS_ENABLED(CONFIG_EP_MODE_IN_EP_RC_SYS)
+ && (imx6_pcie->hard_wired == 0)) {
+ int i = 0;
void *test_reg1, *test_reg2;
void __iomem *pcie_arb_base_addr;
struct timeval tv1s, tv1e, tv2s, tv2e;
@@ -2178,6 +2241,7 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
struct pcie_port *pp = &pci->pp;
LIST_HEAD(res);
struct resource_entry *win, *tmp;
+ unsigned long timeout = jiffies + msecs_to_jiffies(300000);
/* add attributes for device */
ret = sysfs_create_group(&pdev->dev.kobj, &imx_pcie_attrgroup);
@@ -2210,17 +2274,16 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
pp->mem_base = pp->mem->start;
pp->ops = &imx6_pcie_host_ops;
+ dev_info(dev, " try to initialize pcie ep.\n");
+ ret = imx6_pcie_host_init(pp);
+ if (ret) {
+ dev_info(dev, " fail to initialize pcie ep.\n");
+ return ret;
+ }
- /* enable disp_mix power domain */
- if ((imx6_pcie->variant == IMX7D)
- || (imx6_pcie->variant == IMX8MQ)
- || (imx6_pcie->variant == IMX8QM)
- || (imx6_pcie->variant == IMX8QXP))
- pm_runtime_get_sync(dev);
-
- imx6_pcie_assert_core_reset(imx6_pcie);
- imx6_pcie_init_phy(imx6_pcie);
- imx6_pcie_deassert_core_reset(imx6_pcie);
+ imx6_pcie_setup_ep(pci);
+ platform_set_drvdata(pdev, imx6_pcie);
+ imx_pcie_regions_setup(&pdev->dev);
/*
* iMX6SX PCIe has the stand-alone power domain.
@@ -2240,12 +2303,12 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
/* link is indicated by the bit4 of DB_R1 register */
do {
usleep_range(10, 20);
+ if (time_after(jiffies, timeout)) {
+ dev_info(&pdev->dev, "PCIe EP: link down.\n");
+ return 0;
+ }
} while ((readl(pci->dbi_base + PCIE_PHY_DEBUG_R1) & 0x10) == 0);
- imx6_pcie_setup_ep(pci);
-
- imx_pcie_regions_setup(&pdev->dev);
-
/* self io test */
test_reg1 = devm_kzalloc(&pdev->dev,
test_region_size, GFP_KERNEL);
@@ -2331,10 +2394,18 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
return -EINVAL;
ret = imx6_add_pcie_port(imx6_pcie, pdev);
- if (ret < 0)
+ if (ret < 0) {
+ if (IS_ENABLED(CONFIG_PCI_IMX6_COMPLIANCE_TEST)) {
+ /* The PCIE clocks wouldn't be turned off */
+ dev_info(dev, "To do the compliance tests.\n");
+ ret = 0;
+ } else {
+ dev_err(dev, "unable to add pcie port.\n");
+ }
return ret;
-
- if (IS_ENABLED(CONFIG_RC_MODE_IN_EP_RC_SYS))
+ }
+ if (IS_ENABLED(CONFIG_RC_MODE_IN_EP_RC_SYS)
+ && (imx6_pcie->hard_wired == 0))
imx_pcie_regions_setup(&pdev->dev);
}
return 0;