MLK-16818-2 PCI: imx: enable the pcie ep rc for imx8
authorRichard Zhu <hongxing.zhu@nxp.com>
Fri, 17 Nov 2017 06:46:32 +0000 (14:46 +0800)
committerNitin Garg <nitin.garg@nxp.com>
Mon, 19 Mar 2018 20:47:04 +0000 (15:47 -0500)
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>
drivers/pci/host/pci-imx6-ep-driver.c
drivers/pci/host/pci-imx6.c
drivers/pci/host/pcie-designware.c
drivers/pci/host/pcie-designware.h

index 192ced7..dd300a9 100644 (file)
@@ -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;
 }
 
index 1827512..9f025a9 100644 (file)
@@ -64,6 +64,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;
@@ -97,7 +98,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) */
@@ -105,6 +105,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)
@@ -122,10 +127,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)
@@ -1369,13 +1379,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;
 }
@@ -1455,26 +1467,29 @@ static void imx_pcie_regions_setup(struct device *dev)
        struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev);
        struct pcie_port *pp = &imx6_pcie->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, pp->dbi_base + 0x900);
-       writel((u32)pp->mem_base, pp->dbi_base + 0x90c);
-       writel(0, pp->dbi_base + 0x910);
-       writel((u32)pp->mem_base + test_region_size, pp->dbi_base + 0x914);
-
-       writel(ddr_test_region, pp->dbi_base + 0x918);
-       writel(0, pp->dbi_base + 0x91c);
-       writel(0, pp->dbi_base + 0x904);
-       writel(1 << 31, pp->dbi_base + 0x908);
+       dw_pcie_prog_outbound_atu(pp, 2, 0, pp->mem_base,
+                                 ddr_test_region, test_region_size);
 }
 
 static ssize_t imx_pcie_memw_info(struct device *dev,
@@ -1588,6 +1603,50 @@ static struct attribute_group imx_pcie_attrgroup = {
 
 static void imx6_pcie_setup_ep(struct pcie_port *pp)
 {
+       int ret;
+       u32 val;
+       struct device_node *np = pp->dev->of_node;
+
+       ret = of_property_read_u32(np, "num-lanes", &pp->lanes);
+       if (ret)
+               pp->lanes = 0;
+
+       /* set the number of lanes */
+       val = dw_pcie_readl_rc(pp, PCIE_PORT_LINK_CONTROL);
+       val &= ~PORT_LINK_MODE_MASK;
+       switch (pp->lanes) {
+       case 1:
+               val |= PORT_LINK_MODE_1_LANES;
+               break;
+       case 2:
+               val |= PORT_LINK_MODE_2_LANES;
+               break;
+       default:
+               dev_err(pp->dev, "num-lanes %u: invalid value\n", pp->lanes);
+               return;
+       }
+       dw_pcie_writel_rc(pp, PCIE_PORT_LINK_CONTROL, val);
+
+       /* set link width speed control register */
+       val = dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL);
+       val &= ~PORT_LOGIC_LINK_WIDTH_MASK;
+       switch (pp->lanes) {
+       case 1:
+               val |= PORT_LOGIC_LINK_WIDTH_1_LANES;
+               break;
+       case 2:
+               val |= PORT_LOGIC_LINK_WIDTH_2_LANES;
+               break;
+       }
+       dw_pcie_writel_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
+
+       /* get iATU unroll support */
+       val = dw_pcie_readl_rc(pp, PCIE_ATU_VIEWPORT);
+       if (val == 0xffffffff)
+               pp->iatu_unroll_enabled = 1;
+       dev_info(pp->dev, "iATU unroll: %s\n",
+               pp->iatu_unroll_enabled ? "enabled" : "disabled");
+
                /* CMD reg:I/O space, MEM space, and Bus Master Enable */
                writel(readl(pp->dbi_base + PCI_COMMAND)
                                | PCI_COMMAND_IO
@@ -1600,7 +1659,7 @@ static void imx6_pcie_setup_ep(struct pcie_port *pp)
                 * bar0 and bar1 of ep
                 */
                writel(0xdeadbeaf, pp->dbi_base + PCI_VENDOR_ID);
-               writel(readl(pp->dbi_base + PCI_CLASS_REVISION)
+               writel((readl(pp->dbi_base + PCI_CLASS_REVISION) & 0xFFFF)
                                | (PCI_CLASS_MEMORY_RAM << 16),
                                pp->dbi_base + PCI_CLASS_REVISION);
                writel(0xdeadbeaf, pp->dbi_base
@@ -1865,6 +1924,8 @@ static int 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) {
@@ -2104,8 +2165,9 @@ static int 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;
@@ -2113,6 +2175,7 @@ static int imx6_pcie_probe(struct platform_device *pdev)
                struct device_node *np = pp->dev->of_node;
                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);
@@ -2145,17 +2208,16 @@ static int 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(pp->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(pp);
+               platform_set_drvdata(pdev, imx6_pcie);
+               imx_pcie_regions_setup(&pdev->dev);
 
                /*
                 * iMX6SX PCIe has the stand-alone power domain.
@@ -2175,11 +2237,12 @@ static int 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(pp->dbi_base + PCIE_PHY_DEBUG_R1) & 0x10) == 0);
 
-               imx6_pcie_setup_ep(pp);
-               imx_pcie_regions_setup(&pdev->dev);
-
                /* self io test */
                test_reg1 = devm_kzalloc(&pdev->dev,
                                test_region_size, GFP_KERNEL);
@@ -2265,10 +2328,18 @@ static int 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;
index 3c3eaa3..0765eaf 100644 (file)
@@ -190,7 +190,7 @@ static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
        return dw_pcie_cfg_write(pp->dbi_base + where, size, val);
 }
 
-static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index,
+void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index,
                int type, u64 cpu_addr, u64 pci_addr, u32 size)
 {
        u32 retries, val;
@@ -244,8 +244,7 @@ static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index,
                if (val == PCIE_ATU_ENABLE)
                        return;
 
-               if (!IS_ENABLED(CONFIG_PCI_IMX6))
-                       usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX);
+               mdelay(LINK_WAIT_IATU_MAX/1000);
        }
        dev_err(pp->dev, "iATU is not being enabled\n");
 }
@@ -288,8 +287,10 @@ irqreturn_t dw_handle_msi_irq(struct pcie_port *pp)
 
 void dw_pcie_msi_init(struct pcie_port *pp)
 {
-       dma_alloc_coherent(pp->dev, 64, &pp->msi_target, GFP_KERNEL);
+       dma_addr_t msi_addr;
 
+       dma_alloc_coherent(pp->dev, 64, &msi_addr, GFP_KERNEL);
+       pp->msi_target = (u64)msi_addr;
        /* program the msi_data */
        dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4,
                            (u32)(pp->msi_target & 0xffffffff));
index 92ad1cc..101fc5f 100644 (file)
@@ -86,5 +86,7 @@ void dw_pcie_msi_cfg_restore(struct pcie_port *pp);
 int dw_pcie_link_up(struct pcie_port *pp);
 void dw_pcie_setup_rc(struct pcie_port *pp);
 int dw_pcie_host_init(struct pcie_port *pp);
+void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index,
+               int type, u64 cpu_addr, u64 pci_addr, u32 size);
 
 #endif /* _PCIE_DESIGNWARE_H */