From 59c53960c177d801cc709b2a78297a467ea4e999 Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Fri, 17 Nov 2017 14:46:32 +0800 Subject: [PATCH] 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 to the bar0 of ep. - RC: access the , and this address would be mapped to the 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 , for example ./memtool -32 =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 --- drivers/pci/host/pci-imx6-ep-driver.c | 62 +++++++--- drivers/pci/host/pci-imx6.c | 157 +++++++++++++++++++------- drivers/pci/host/pcie-designware.c | 9 +- drivers/pci/host/pcie-designware.h | 2 + 4 files changed, 168 insertions(+), 62 deletions(-) diff --git a/drivers/pci/host/pci-imx6-ep-driver.c b/drivers/pci/host/pci-imx6-ep-driver.c index 192ced7cbffc..dd300a99ab21 100644 --- a/drivers/pci/host/pci-imx6-ep-driver.c +++ b/drivers/pci/host/pci-imx6-ep-driver.c @@ -29,6 +29,7 @@ #include #include #include +#include #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/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index 182751256790..9f025a956fbf 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c @@ -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; diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 3c3eaa317c73..0765eafa58fc 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -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)); diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index 92ad1ccad458..101fc5fee2ed 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -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 */ -- 2.17.1