From 68b673a039195f47d640b585c3b74710ed0f155b Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 20 Dec 2017 00:29:31 +0100 Subject: PCI: dwc: artpec6: Remove unused defines Commit b015b37e6693 ("PCI: artpec6: Stop enabling writes to DBI read-only registers") removed the only write using these defines, but it did not remove the defines. Remove the defines since they are now unused. Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi --- drivers/pci/dwc/pcie-artpec6.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/pci/dwc/pcie-artpec6.c') diff --git a/drivers/pci/dwc/pcie-artpec6.c b/drivers/pci/dwc/pcie-artpec6.c index 6653619db6a1..4b8ef266dc2f 100644 --- a/drivers/pci/dwc/pcie-artpec6.c +++ b/drivers/pci/dwc/pcie-artpec6.c @@ -37,9 +37,6 @@ struct artpec6_pcie { #define PCIE_PHY_DEBUG_R0 (PL_OFFSET + 0x28) #define PCIE_PHY_DEBUG_R1 (PL_OFFSET + 0x2c) -#define MISC_CONTROL_1_OFF (PL_OFFSET + 0x1bc) -#define DBI_RO_WR_EN 1 - /* ARTPEC-6 specific registers */ #define PCIECFG 0x18 #define PCIECFG_DBG_OEN (1 << 24) -- cgit From bc5d7dfa5b9ec3e8b75122e2e0f8e2e6780ff841 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 20 Dec 2017 00:29:32 +0100 Subject: PCI: dwc: artpec6: Use BIT and GENMASK macros Use BIT and GENMASK macros to improve readability. Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi --- drivers/pci/dwc/pcie-artpec6.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'drivers/pci/dwc/pcie-artpec6.c') diff --git a/drivers/pci/dwc/pcie-artpec6.c b/drivers/pci/dwc/pcie-artpec6.c index 4b8ef266dc2f..18075e0fab80 100644 --- a/drivers/pci/dwc/pcie-artpec6.c +++ b/drivers/pci/dwc/pcie-artpec6.c @@ -39,28 +39,28 @@ struct artpec6_pcie { /* ARTPEC-6 specific registers */ #define PCIECFG 0x18 -#define PCIECFG_DBG_OEN (1 << 24) -#define PCIECFG_CORE_RESET_REQ (1 << 21) -#define PCIECFG_LTSSM_ENABLE (1 << 20) -#define PCIECFG_CLKREQ_B (1 << 11) -#define PCIECFG_REFCLK_ENABLE (1 << 10) -#define PCIECFG_PLL_ENABLE (1 << 9) -#define PCIECFG_PCLK_ENABLE (1 << 8) -#define PCIECFG_RISRCREN (1 << 4) -#define PCIECFG_MODE_TX_DRV_EN (1 << 3) -#define PCIECFG_CISRREN (1 << 2) -#define PCIECFG_MACRO_ENABLE (1 << 0) +#define PCIECFG_DBG_OEN BIT(24) +#define PCIECFG_CORE_RESET_REQ BIT(21) +#define PCIECFG_LTSSM_ENABLE BIT(20) +#define PCIECFG_CLKREQ_B BIT(11) +#define PCIECFG_REFCLK_ENABLE BIT(10) +#define PCIECFG_PLL_ENABLE BIT(9) +#define PCIECFG_PCLK_ENABLE BIT(8) +#define PCIECFG_RISRCREN BIT(4) +#define PCIECFG_MODE_TX_DRV_EN BIT(3) +#define PCIECFG_CISRREN BIT(2) +#define PCIECFG_MACRO_ENABLE BIT(0) #define NOCCFG 0x40 -#define NOCCFG_ENABLE_CLK_PCIE (1 << 4) -#define NOCCFG_POWER_PCIE_IDLEACK (1 << 3) -#define NOCCFG_POWER_PCIE_IDLE (1 << 2) -#define NOCCFG_POWER_PCIE_IDLEREQ (1 << 1) +#define NOCCFG_ENABLE_CLK_PCIE BIT(4) +#define NOCCFG_POWER_PCIE_IDLEACK BIT(3) +#define NOCCFG_POWER_PCIE_IDLE BIT(2) +#define NOCCFG_POWER_PCIE_IDLEREQ BIT(1) #define PHY_STATUS 0x118 -#define PHY_COSPLLLOCK (1 << 0) +#define PHY_COSPLLLOCK BIT(0) -#define ARTPEC6_CPU_TO_BUS_ADDR 0x0fffffff +#define ARTPEC6_CPU_TO_BUS_ADDR GENMASK(27, 0) static u32 artpec6_pcie_readl(struct artpec6_pcie *artpec6_pcie, u32 offset) { -- cgit From 87c9a730fe3161984848afc78c53eeb98ba40b4f Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 20 Dec 2017 00:29:33 +0100 Subject: PCI: dwc: artpec6: Split artpec6_pcie_establish_link() into smaller functions Split artpec6_pcie_establish_link() into smaller functions to better match other drivers such as dra7xx and imx6. This is also done to prepare for endpoint mode support. Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi --- drivers/pci/dwc/pcie-artpec6.c | 55 ++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 26 deletions(-) (limited to 'drivers/pci/dwc/pcie-artpec6.c') diff --git a/drivers/pci/dwc/pcie-artpec6.c b/drivers/pci/dwc/pcie-artpec6.c index 18075e0fab80..b2783b475c2a 100644 --- a/drivers/pci/dwc/pcie-artpec6.c +++ b/drivers/pci/dwc/pcie-artpec6.c @@ -34,8 +34,6 @@ struct artpec6_pcie { /* PCIe Port Logic registers (memory-mapped) */ #define PL_OFFSET 0x700 -#define PCIE_PHY_DEBUG_R0 (PL_OFFSET + 0x28) -#define PCIE_PHY_DEBUG_R1 (PL_OFFSET + 0x2c) /* ARTPEC-6 specific registers */ #define PCIECFG 0x18 @@ -80,18 +78,23 @@ static u64 artpec6_pcie_cpu_addr_fixup(u64 pci_addr) return pci_addr & ARTPEC6_CPU_TO_BUS_ADDR; } -static int artpec6_pcie_establish_link(struct artpec6_pcie *artpec6_pcie) +static int artpec6_pcie_establish_link(struct dw_pcie *pci) { - struct dw_pcie *pci = artpec6_pcie->pci; - struct pcie_port *pp = &pci->pp; + struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci); u32 val; - unsigned int retries; - /* Hold DW core in reset */ val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); - val |= PCIECFG_CORE_RESET_REQ; + val |= PCIECFG_LTSSM_ENABLE; artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); + return 0; +} + +static void artpec6_pcie_init_phy(struct artpec6_pcie *artpec6_pcie) +{ + u32 val; + unsigned int retries; + val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); val |= PCIECFG_RISRCREN | /* Receiver term. 50 Ohm */ PCIECFG_MODE_TX_DRV_EN | @@ -131,30 +134,25 @@ static int artpec6_pcie_establish_link(struct artpec6_pcie *artpec6_pcie) val = readl(artpec6_pcie->phy_base + PHY_STATUS); retries--; } while (retries && !(val & PHY_COSPLLLOCK)); +} + +static void artpec6_pcie_assert_core_reset(struct artpec6_pcie *artpec6_pcie) +{ + u32 val; - /* Take DW core out of reset */ val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); - val &= ~PCIECFG_CORE_RESET_REQ; + val |= PCIECFG_CORE_RESET_REQ; artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); - usleep_range(100, 200); +} - /* setup root complex */ - dw_pcie_setup_rc(pp); +static void artpec6_pcie_deassert_core_reset(struct artpec6_pcie *artpec6_pcie) +{ + u32 val; - /* assert LTSSM enable */ val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); - val |= PCIECFG_LTSSM_ENABLE; + val &= ~PCIECFG_CORE_RESET_REQ; artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); - - /* check if the link is up or not */ - if (!dw_pcie_wait_for_link(pci)) - return 0; - - dev_dbg(pci->dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n", - dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R0), - dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1)); - - return -ETIMEDOUT; + usleep_range(100, 200); } static void artpec6_pcie_enable_interrupts(struct artpec6_pcie *artpec6_pcie) @@ -171,7 +169,12 @@ static int artpec6_pcie_host_init(struct pcie_port *pp) struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci); - artpec6_pcie_establish_link(artpec6_pcie); + artpec6_pcie_assert_core_reset(artpec6_pcie); + artpec6_pcie_init_phy(artpec6_pcie); + artpec6_pcie_deassert_core_reset(artpec6_pcie); + dw_pcie_setup_rc(pp); + artpec6_pcie_establish_link(pci); + dw_pcie_wait_for_link(pci); artpec6_pcie_enable_interrupts(artpec6_pcie); return 0; -- cgit From b5074ef6fe7d6ed4bb8cd8660907b3092b8ae325 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 20 Dec 2017 00:29:35 +0100 Subject: PCI: dwc: artpec6: Add support for endpoint mode The PCIe controller integrated in ARTPEC-6 SoCs is capable of operating in endpoint mode. Add endpoint mode support to the artpec6 driver. Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi --- drivers/pci/dwc/pcie-artpec6.c | 152 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 146 insertions(+), 6 deletions(-) (limited to 'drivers/pci/dwc/pcie-artpec6.c') diff --git a/drivers/pci/dwc/pcie-artpec6.c b/drivers/pci/dwc/pcie-artpec6.c index b2783b475c2a..e7de4e4649eb 100644 --- a/drivers/pci/dwc/pcie-artpec6.c +++ b/drivers/pci/dwc/pcie-artpec6.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -30,8 +31,15 @@ struct artpec6_pcie { struct dw_pcie *pci; struct regmap *regmap; /* DT axis,syscon-pcie */ void __iomem *phy_base; /* DT phy */ + enum dw_pcie_device_mode mode; }; +struct artpec_pcie_of_data { + enum dw_pcie_device_mode mode; +}; + +static const struct of_device_id artpec6_pcie_of_match[]; + /* PCIe Port Logic registers (memory-mapped) */ #define PL_OFFSET 0x700 @@ -40,6 +48,7 @@ struct artpec6_pcie { #define PCIECFG_DBG_OEN BIT(24) #define PCIECFG_CORE_RESET_REQ BIT(21) #define PCIECFG_LTSSM_ENABLE BIT(20) +#define PCIECFG_DEVICE_TYPE_MASK GENMASK(19, 16) #define PCIECFG_CLKREQ_B BIT(11) #define PCIECFG_REFCLK_ENABLE BIT(10) #define PCIECFG_PLL_ENABLE BIT(9) @@ -90,6 +99,22 @@ static int artpec6_pcie_establish_link(struct dw_pcie *pci) return 0; } +static void artpec6_pcie_stop_link(struct dw_pcie *pci) +{ + struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci); + u32 val; + + val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); + val &= ~PCIECFG_LTSSM_ENABLE; + artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); +} + +static const struct dw_pcie_ops dw_pcie_ops = { + .cpu_addr_fixup = artpec6_pcie_cpu_addr_fixup, + .start_link = artpec6_pcie_establish_link, + .stop_link = artpec6_pcie_stop_link, +}; + static void artpec6_pcie_init_phy(struct artpec6_pcie *artpec6_pcie) { u32 val; @@ -230,10 +255,76 @@ static int artpec6_add_pcie_port(struct artpec6_pcie *artpec6_pcie, return 0; } -static const struct dw_pcie_ops dw_pcie_ops = { - .cpu_addr_fixup = artpec6_pcie_cpu_addr_fixup, +static void artpec6_pcie_ep_init(struct dw_pcie_ep *ep) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci); + enum pci_barno bar; + + artpec6_pcie_assert_core_reset(artpec6_pcie); + artpec6_pcie_init_phy(artpec6_pcie); + artpec6_pcie_deassert_core_reset(artpec6_pcie); + + for (bar = BAR_0; bar <= BAR_5; bar++) + dw_pcie_ep_reset_bar(pci, bar); +} + +static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, + enum pci_epc_irq_type type, u8 interrupt_num) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + + switch (type) { + case PCI_EPC_IRQ_LEGACY: + dev_err(pci->dev, "EP cannot trigger legacy IRQs\n"); + return -EINVAL; + case PCI_EPC_IRQ_MSI: + return dw_pcie_ep_raise_msi_irq(ep, interrupt_num); + default: + dev_err(pci->dev, "UNKNOWN IRQ type\n"); + } + + return 0; +} + +static struct dw_pcie_ep_ops pcie_ep_ops = { + .ep_init = artpec6_pcie_ep_init, + .raise_irq = artpec6_pcie_raise_irq, }; +static int artpec6_add_pcie_ep(struct artpec6_pcie *artpec6_pcie, + struct platform_device *pdev) +{ + int ret; + struct dw_pcie_ep *ep; + struct resource *res; + struct device *dev = &pdev->dev; + struct dw_pcie *pci = artpec6_pcie->pci; + + ep = &pci->ep; + ep->ops = &pcie_ep_ops; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi2"); + pci->dbi_base2 = devm_ioremap(dev, res->start, resource_size(res)); + if (IS_ERR(pci->dbi_base2)) + return PTR_ERR(pci->dbi_base2); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space"); + if (!res) + return -EINVAL; + + ep->phys_base = res->start; + ep->addr_size = resource_size(res); + + ret = dw_pcie_ep_init(ep); + if (ret) { + dev_err(dev, "failed to initialize endpoint\n"); + return ret; + } + + return 0; +} + static int artpec6_pcie_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -242,6 +333,16 @@ static int artpec6_pcie_probe(struct platform_device *pdev) struct resource *dbi_base; struct resource *phy_base; int ret; + const struct of_device_id *match; + const struct artpec_pcie_of_data *data; + enum dw_pcie_device_mode mode; + + match = of_match_device(artpec6_pcie_of_match, dev); + if (!match) + return -EINVAL; + + data = (struct artpec_pcie_of_data *)match->data; + mode = (enum dw_pcie_device_mode)data->mode; artpec6_pcie = devm_kzalloc(dev, sizeof(*artpec6_pcie), GFP_KERNEL); if (!artpec6_pcie) @@ -255,6 +356,7 @@ static int artpec6_pcie_probe(struct platform_device *pdev) pci->ops = &dw_pcie_ops; artpec6_pcie->pci = pci; + artpec6_pcie->mode = mode; dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); pci->dbi_base = devm_ioremap_resource(dev, dbi_base); @@ -274,15 +376,53 @@ static int artpec6_pcie_probe(struct platform_device *pdev) platform_set_drvdata(pdev, artpec6_pcie); - ret = artpec6_add_pcie_port(artpec6_pcie, pdev); - if (ret < 0) - return ret; + switch (artpec6_pcie->mode) { + case DW_PCIE_RC_TYPE: + if (!IS_ENABLED(CONFIG_PCIE_ARTPEC6_HOST)) + return -ENODEV; + + ret = artpec6_add_pcie_port(artpec6_pcie, pdev); + if (ret < 0) + return ret; + break; + case DW_PCIE_EP_TYPE: { + u32 val; + + if (!IS_ENABLED(CONFIG_PCIE_ARTPEC6_EP)) + return -ENODEV; + + val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); + val &= ~PCIECFG_DEVICE_TYPE_MASK; + artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); + ret = artpec6_add_pcie_ep(artpec6_pcie, pdev); + if (ret < 0) + return ret; + break; + } + default: + dev_err(dev, "INVALID device type %d\n", artpec6_pcie->mode); + } return 0; } +static const struct artpec_pcie_of_data artpec6_pcie_rc_of_data = { + .mode = DW_PCIE_RC_TYPE, +}; + +static const struct artpec_pcie_of_data artpec6_pcie_ep_of_data = { + .mode = DW_PCIE_EP_TYPE, +}; + static const struct of_device_id artpec6_pcie_of_match[] = { - { .compatible = "axis,artpec6-pcie", }, + { + .compatible = "axis,artpec6-pcie", + .data = &artpec6_pcie_rc_of_data, + }, + { + .compatible = "axis,artpec6-pcie-ep", + .data = &artpec6_pcie_ep_of_data, + }, {}, }; -- cgit From b6900aeb1977ee1430578ce8b373e5c9fc6366d5 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 20 Dec 2017 00:29:36 +0100 Subject: PCI: dwc: Make cpu_addr_fixup take struct dw_pcie as argument The current cpu addr fixup mask for ARTPEC-6, GENMASK(27, 0), is wrong. The correct cpu addr fixup mask for ARTPEC-6 is GENMASK(28, 0). However, having a hardcoded cpu addr fixup mask in each driver is arguably wrong. A device tree property called something like "cpu-addr-fixup-mask" would have been a better solution. Introducing such a property is not needed though, since we already have pp->cfg0_base and ep->phys_base, which is derived from already existing device tree properties. It is also worth noting that for ARTPEC-7, hardcoding the cpu addr fixup mask is not possible, since it uses a High Address Bits Look Up Table, which means that it can, at runtime, map the PCIe window to an arbitrary address in the 32-bit address space. By using pp->cfg0_base and ep->phys_base, we avoid hardcoding a mask in each driver. This should work for ARTPEC-6, DRA7xx, and ARTPEC-7. I have not changed the code in DRA7xx though, since their existing code works, but if they want, they could use the same logic as artpec6_pcie_cpu_addr_fixup, and thus remove their hardcoded mask. The reason why the fixup mask is needed is explained in commit f4c55c5a3f7f ("PCI: designware: Program ATU with untranslated address"). Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- drivers/pci/dwc/pcie-artpec6.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'drivers/pci/dwc/pcie-artpec6.c') diff --git a/drivers/pci/dwc/pcie-artpec6.c b/drivers/pci/dwc/pcie-artpec6.c index e7de4e4649eb..318a2bd0d97e 100644 --- a/drivers/pci/dwc/pcie-artpec6.c +++ b/drivers/pci/dwc/pcie-artpec6.c @@ -67,8 +67,6 @@ static const struct of_device_id artpec6_pcie_of_match[]; #define PHY_STATUS 0x118 #define PHY_COSPLLLOCK BIT(0) -#define ARTPEC6_CPU_TO_BUS_ADDR GENMASK(27, 0) - static u32 artpec6_pcie_readl(struct artpec6_pcie *artpec6_pcie, u32 offset) { u32 val; @@ -82,9 +80,21 @@ static void artpec6_pcie_writel(struct artpec6_pcie *artpec6_pcie, u32 offset, u regmap_write(artpec6_pcie->regmap, offset, val); } -static u64 artpec6_pcie_cpu_addr_fixup(u64 pci_addr) +static u64 artpec6_pcie_cpu_addr_fixup(struct dw_pcie *pci, u64 pci_addr) { - return pci_addr & ARTPEC6_CPU_TO_BUS_ADDR; + struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci); + struct pcie_port *pp = &pci->pp; + struct dw_pcie_ep *ep = &pci->ep; + + switch (artpec6_pcie->mode) { + case DW_PCIE_RC_TYPE: + return pci_addr - pp->cfg0_base; + case DW_PCIE_EP_TYPE: + return pci_addr - ep->phys_base; + default: + dev_err(pci->dev, "UNKNOWN device type\n"); + } + return pci_addr; } static int artpec6_pcie_establish_link(struct dw_pcie *pci) -- cgit From 4fdd5b5b17f04fb6418a5f06adfbcf4369b8c375 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 20 Dec 2017 00:29:37 +0100 Subject: PCI: dwc: artpec6: Deassert the core before waiting for PHY Waiting for the PHY while the core was held in reset worked for artpec6, but for artpec7, in order to read the required registers, the core has to be out of reset. Refactor the code so we always wait for the PHY after the core has been deasserted, since this works for both artpec6 and artpec7. Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi --- drivers/pci/dwc/pcie-artpec6.c | 45 +++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 16 deletions(-) (limited to 'drivers/pci/dwc/pcie-artpec6.c') diff --git a/drivers/pci/dwc/pcie-artpec6.c b/drivers/pci/dwc/pcie-artpec6.c index 318a2bd0d97e..064c5a93ea80 100644 --- a/drivers/pci/dwc/pcie-artpec6.c +++ b/drivers/pci/dwc/pcie-artpec6.c @@ -125,11 +125,37 @@ static const struct dw_pcie_ops dw_pcie_ops = { .stop_link = artpec6_pcie_stop_link, }; -static void artpec6_pcie_init_phy(struct artpec6_pcie *artpec6_pcie) +static void artpec6_pcie_wait_for_phy(struct artpec6_pcie *artpec6_pcie) { + struct dw_pcie *pci = artpec6_pcie->pci; + struct device *dev = pci->dev; u32 val; unsigned int retries; + retries = 50; + do { + usleep_range(1000, 2000); + val = artpec6_pcie_readl(artpec6_pcie, NOCCFG); + retries--; + } while (retries && + (val & (NOCCFG_POWER_PCIE_IDLEACK | NOCCFG_POWER_PCIE_IDLE))); + if (!retries) + dev_err(dev, "PCIe clock manager did not leave idle state\n"); + + retries = 50; + do { + usleep_range(1000, 2000); + val = readl(artpec6_pcie->phy_base + PHY_STATUS); + retries--; + } while (retries && !(val & PHY_COSPLLLOCK)); + if (!retries) + dev_err(dev, "PHY PLL did not lock\n"); +} + +static void artpec6_pcie_init_phy(struct artpec6_pcie *artpec6_pcie) +{ + u32 val; + val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); val |= PCIECFG_RISRCREN | /* Receiver term. 50 Ohm */ PCIECFG_MODE_TX_DRV_EN | @@ -154,21 +180,6 @@ static void artpec6_pcie_init_phy(struct artpec6_pcie *artpec6_pcie) val = artpec6_pcie_readl(artpec6_pcie, NOCCFG); val &= ~NOCCFG_POWER_PCIE_IDLEREQ; artpec6_pcie_writel(artpec6_pcie, NOCCFG, val); - - retries = 50; - do { - usleep_range(1000, 2000); - val = artpec6_pcie_readl(artpec6_pcie, NOCCFG); - retries--; - } while (retries && - (val & (NOCCFG_POWER_PCIE_IDLEACK | NOCCFG_POWER_PCIE_IDLE))); - - retries = 50; - do { - usleep_range(1000, 2000); - val = readl(artpec6_pcie->phy_base + PHY_STATUS); - retries--; - } while (retries && !(val & PHY_COSPLLLOCK)); } static void artpec6_pcie_assert_core_reset(struct artpec6_pcie *artpec6_pcie) @@ -207,6 +218,7 @@ static int artpec6_pcie_host_init(struct pcie_port *pp) artpec6_pcie_assert_core_reset(artpec6_pcie); artpec6_pcie_init_phy(artpec6_pcie); artpec6_pcie_deassert_core_reset(artpec6_pcie); + artpec6_pcie_wait_for_phy(artpec6_pcie); dw_pcie_setup_rc(pp); artpec6_pcie_establish_link(pci); dw_pcie_wait_for_link(pci); @@ -274,6 +286,7 @@ static void artpec6_pcie_ep_init(struct dw_pcie_ep *ep) artpec6_pcie_assert_core_reset(artpec6_pcie); artpec6_pcie_init_phy(artpec6_pcie); artpec6_pcie_deassert_core_reset(artpec6_pcie); + artpec6_pcie_wait_for_phy(artpec6_pcie); for (bar = BAR_0; bar <= BAR_5; bar++) dw_pcie_ep_reset_bar(pci, bar); -- cgit From dc734ee02cb6f2603fd68d8b71d5e6c372357307 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 20 Dec 2017 00:29:39 +0100 Subject: PCI: dwc: artpec6: Add support for the ARTPEC-7 SoC Add support for the ARTPEC-7 SoC in the artpec6 driver. The ARTPEC-6 SoC and the ARTPEC-7 SoC are very similar. Unfortunately, some fields in the PCIECFG and PCIESTAT register have changed. Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi --- drivers/pci/dwc/pcie-artpec6.c | 187 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 183 insertions(+), 4 deletions(-) (limited to 'drivers/pci/dwc/pcie-artpec6.c') diff --git a/drivers/pci/dwc/pcie-artpec6.c b/drivers/pci/dwc/pcie-artpec6.c index 064c5a93ea80..312f21b6e013 100644 --- a/drivers/pci/dwc/pcie-artpec6.c +++ b/drivers/pci/dwc/pcie-artpec6.c @@ -27,14 +27,21 @@ #define to_artpec6_pcie(x) dev_get_drvdata((x)->dev) +enum artpec_pcie_variants { + ARTPEC6, + ARTPEC7, +}; + struct artpec6_pcie { struct dw_pcie *pci; struct regmap *regmap; /* DT axis,syscon-pcie */ void __iomem *phy_base; /* DT phy */ + enum artpec_pcie_variants variant; enum dw_pcie_device_mode mode; }; struct artpec_pcie_of_data { + enum artpec_pcie_variants variant; enum dw_pcie_device_mode mode; }; @@ -43,6 +50,13 @@ static const struct of_device_id artpec6_pcie_of_match[]; /* PCIe Port Logic registers (memory-mapped) */ #define PL_OFFSET 0x700 +#define ACK_F_ASPM_CTRL_OFF (PL_OFFSET + 0xc) +#define ACK_N_FTS_MASK GENMASK(15, 8) +#define ACK_N_FTS(x) (((x) << 8) & ACK_N_FTS_MASK) + +#define FAST_TRAINING_SEQ_MASK GENMASK(7, 0) +#define FAST_TRAINING_SEQ(x) (((x) << 0) & FAST_TRAINING_SEQ_MASK) + /* ARTPEC-6 specific registers */ #define PCIECFG 0x18 #define PCIECFG_DBG_OEN BIT(24) @@ -57,6 +71,13 @@ static const struct of_device_id artpec6_pcie_of_match[]; #define PCIECFG_MODE_TX_DRV_EN BIT(3) #define PCIECFG_CISRREN BIT(2) #define PCIECFG_MACRO_ENABLE BIT(0) +/* ARTPEC-7 specific fields */ +#define PCIECFG_REFCLKSEL BIT(23) +#define PCIECFG_NOC_RESET BIT(3) + +#define PCIESTAT 0x1c +/* ARTPEC-7 specific fields */ +#define PCIESTAT_EXTREFCLK BIT(3) #define NOCCFG 0x40 #define NOCCFG_ENABLE_CLK_PCIE BIT(4) @@ -67,6 +88,12 @@ static const struct of_device_id artpec6_pcie_of_match[]; #define PHY_STATUS 0x118 #define PHY_COSPLLLOCK BIT(0) +#define PHY_TX_ASIC_OUT 0x4040 +#define PHY_TX_ASIC_OUT_TX_ACK BIT(0) + +#define PHY_RX_ASIC_OUT 0x405c +#define PHY_RX_ASIC_OUT_ACK BIT(0) + static u32 artpec6_pcie_readl(struct artpec6_pcie *artpec6_pcie, u32 offset) { u32 val; @@ -125,7 +152,7 @@ static const struct dw_pcie_ops dw_pcie_ops = { .stop_link = artpec6_pcie_stop_link, }; -static void artpec6_pcie_wait_for_phy(struct artpec6_pcie *artpec6_pcie) +static void artpec6_pcie_wait_for_phy_a6(struct artpec6_pcie *artpec6_pcie) { struct dw_pcie *pci = artpec6_pcie->pci; struct device *dev = pci->dev; @@ -152,7 +179,49 @@ static void artpec6_pcie_wait_for_phy(struct artpec6_pcie *artpec6_pcie) dev_err(dev, "PHY PLL did not lock\n"); } -static void artpec6_pcie_init_phy(struct artpec6_pcie *artpec6_pcie) +static void artpec6_pcie_wait_for_phy_a7(struct artpec6_pcie *artpec6_pcie) +{ + struct dw_pcie *pci = artpec6_pcie->pci; + struct device *dev = pci->dev; + u32 val; + u16 phy_status_tx, phy_status_rx; + unsigned int retries; + + retries = 50; + do { + usleep_range(1000, 2000); + val = artpec6_pcie_readl(artpec6_pcie, NOCCFG); + retries--; + } while (retries && + (val & (NOCCFG_POWER_PCIE_IDLEACK | NOCCFG_POWER_PCIE_IDLE))); + if (!retries) + dev_err(dev, "PCIe clock manager did not leave idle state\n"); + + retries = 50; + do { + usleep_range(1000, 2000); + phy_status_tx = readw(artpec6_pcie->phy_base + PHY_TX_ASIC_OUT); + phy_status_rx = readw(artpec6_pcie->phy_base + PHY_RX_ASIC_OUT); + retries--; + } while (retries && ((phy_status_tx & PHY_TX_ASIC_OUT_TX_ACK) || + (phy_status_rx & PHY_RX_ASIC_OUT_ACK))); + if (!retries) + dev_err(dev, "PHY did not enter Pn state\n"); +} + +static void artpec6_pcie_wait_for_phy(struct artpec6_pcie *artpec6_pcie) +{ + switch (artpec6_pcie->variant) { + case ARTPEC6: + artpec6_pcie_wait_for_phy_a6(artpec6_pcie); + break; + case ARTPEC7: + artpec6_pcie_wait_for_phy_a7(artpec6_pcie); + break; + } +} + +static void artpec6_pcie_init_phy_a6(struct artpec6_pcie *artpec6_pcie) { u32 val; @@ -182,12 +251,90 @@ static void artpec6_pcie_init_phy(struct artpec6_pcie *artpec6_pcie) artpec6_pcie_writel(artpec6_pcie, NOCCFG, val); } +static void artpec6_pcie_init_phy_a7(struct artpec6_pcie *artpec6_pcie) +{ + struct dw_pcie *pci = artpec6_pcie->pci; + u32 val; + bool extrefclk; + + /* Check if external reference clock is connected */ + val = artpec6_pcie_readl(artpec6_pcie, PCIESTAT); + extrefclk = !!(val & PCIESTAT_EXTREFCLK); + dev_dbg(pci->dev, "Using reference clock: %s\n", + extrefclk ? "external" : "internal"); + + val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); + val |= PCIECFG_RISRCREN | /* Receiver term. 50 Ohm */ + PCIECFG_PCLK_ENABLE; + if (extrefclk) + val |= PCIECFG_REFCLKSEL; + else + val &= ~PCIECFG_REFCLKSEL; + artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); + usleep_range(10, 20); + + val = artpec6_pcie_readl(artpec6_pcie, NOCCFG); + val |= NOCCFG_ENABLE_CLK_PCIE; + artpec6_pcie_writel(artpec6_pcie, NOCCFG, val); + usleep_range(20, 30); + + val = artpec6_pcie_readl(artpec6_pcie, NOCCFG); + val &= ~NOCCFG_POWER_PCIE_IDLEREQ; + artpec6_pcie_writel(artpec6_pcie, NOCCFG, val); +} + +static void artpec6_pcie_init_phy(struct artpec6_pcie *artpec6_pcie) +{ + switch (artpec6_pcie->variant) { + case ARTPEC6: + artpec6_pcie_init_phy_a6(artpec6_pcie); + break; + case ARTPEC7: + artpec6_pcie_init_phy_a7(artpec6_pcie); + break; + } +} + +static void artpec6_pcie_set_nfts(struct artpec6_pcie *artpec6_pcie) +{ + struct dw_pcie *pci = artpec6_pcie->pci; + u32 val; + + if (artpec6_pcie->variant != ARTPEC7) + return; + + /* + * Increase the N_FTS (Number of Fast Training Sequences) + * to be transmitted when transitioning from L0s to L0. + */ + val = dw_pcie_readl_dbi(pci, ACK_F_ASPM_CTRL_OFF); + val &= ~ACK_N_FTS_MASK; + val |= ACK_N_FTS(180); + dw_pcie_writel_dbi(pci, ACK_F_ASPM_CTRL_OFF, val); + + /* + * Set the Number of Fast Training Sequences that the core + * advertises as its N_FTS during Gen2 or Gen3 link training. + */ + val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); + val &= ~FAST_TRAINING_SEQ_MASK; + val |= FAST_TRAINING_SEQ(180); + dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); +} + static void artpec6_pcie_assert_core_reset(struct artpec6_pcie *artpec6_pcie) { u32 val; val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); - val |= PCIECFG_CORE_RESET_REQ; + switch (artpec6_pcie->variant) { + case ARTPEC6: + val |= PCIECFG_CORE_RESET_REQ; + break; + case ARTPEC7: + val &= ~PCIECFG_NOC_RESET; + break; + } artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); } @@ -196,7 +343,14 @@ static void artpec6_pcie_deassert_core_reset(struct artpec6_pcie *artpec6_pcie) u32 val; val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); - val &= ~PCIECFG_CORE_RESET_REQ; + switch (artpec6_pcie->variant) { + case ARTPEC6: + val &= ~PCIECFG_CORE_RESET_REQ; + break; + case ARTPEC7: + val |= PCIECFG_NOC_RESET; + break; + } artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); usleep_range(100, 200); } @@ -219,6 +373,7 @@ static int artpec6_pcie_host_init(struct pcie_port *pp) artpec6_pcie_init_phy(artpec6_pcie); artpec6_pcie_deassert_core_reset(artpec6_pcie); artpec6_pcie_wait_for_phy(artpec6_pcie); + artpec6_pcie_set_nfts(artpec6_pcie); dw_pcie_setup_rc(pp); artpec6_pcie_establish_link(pci); dw_pcie_wait_for_link(pci); @@ -287,6 +442,7 @@ static void artpec6_pcie_ep_init(struct dw_pcie_ep *ep) artpec6_pcie_init_phy(artpec6_pcie); artpec6_pcie_deassert_core_reset(artpec6_pcie); artpec6_pcie_wait_for_phy(artpec6_pcie); + artpec6_pcie_set_nfts(artpec6_pcie); for (bar = BAR_0; bar <= BAR_5; bar++) dw_pcie_ep_reset_bar(pci, bar); @@ -358,6 +514,7 @@ static int artpec6_pcie_probe(struct platform_device *pdev) int ret; const struct of_device_id *match; const struct artpec_pcie_of_data *data; + enum artpec_pcie_variants variant; enum dw_pcie_device_mode mode; match = of_match_device(artpec6_pcie_of_match, dev); @@ -365,6 +522,7 @@ static int artpec6_pcie_probe(struct platform_device *pdev) return -EINVAL; data = (struct artpec_pcie_of_data *)match->data; + variant = (enum artpec_pcie_variants)data->variant; mode = (enum dw_pcie_device_mode)data->mode; artpec6_pcie = devm_kzalloc(dev, sizeof(*artpec6_pcie), GFP_KERNEL); @@ -379,6 +537,7 @@ static int artpec6_pcie_probe(struct platform_device *pdev) pci->ops = &dw_pcie_ops; artpec6_pcie->pci = pci; + artpec6_pcie->variant = variant; artpec6_pcie->mode = mode; dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); @@ -430,10 +589,22 @@ static int artpec6_pcie_probe(struct platform_device *pdev) } static const struct artpec_pcie_of_data artpec6_pcie_rc_of_data = { + .variant = ARTPEC6, .mode = DW_PCIE_RC_TYPE, }; static const struct artpec_pcie_of_data artpec6_pcie_ep_of_data = { + .variant = ARTPEC6, + .mode = DW_PCIE_EP_TYPE, +}; + +static const struct artpec_pcie_of_data artpec7_pcie_rc_of_data = { + .variant = ARTPEC7, + .mode = DW_PCIE_RC_TYPE, +}; + +static const struct artpec_pcie_of_data artpec7_pcie_ep_of_data = { + .variant = ARTPEC7, .mode = DW_PCIE_EP_TYPE, }; @@ -446,6 +617,14 @@ static const struct of_device_id artpec6_pcie_of_match[] = { .compatible = "axis,artpec6-pcie-ep", .data = &artpec6_pcie_ep_of_data, }, + { + .compatible = "axis,artpec7-pcie", + .data = &artpec7_pcie_rc_of_data, + }, + { + .compatible = "axis,artpec7-pcie-ep", + .data = &artpec7_pcie_ep_of_data, + }, {}, }; -- cgit From c68febfd68e0f01d90da15bd10e94b42544f0f68 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 3 Jan 2018 07:33:35 +0000 Subject: PCI: dwc: artpec6: Fix return value check in artpec6_add_pcie_ep() In case of error, the function devm_ioremap() returns NULL pointer not ERR_PTR(). The IS_ERR() test in the return value check should be replaced with NULL test. Signed-off-by: Wei Yongjun Signed-off-by: Lorenzo Pieralisi --- drivers/pci/dwc/pcie-artpec6.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci/dwc/pcie-artpec6.c') diff --git a/drivers/pci/dwc/pcie-artpec6.c b/drivers/pci/dwc/pcie-artpec6.c index 312f21b6e013..b1e98205110f 100644 --- a/drivers/pci/dwc/pcie-artpec6.c +++ b/drivers/pci/dwc/pcie-artpec6.c @@ -485,8 +485,8 @@ static int artpec6_add_pcie_ep(struct artpec6_pcie *artpec6_pcie, res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi2"); pci->dbi_base2 = devm_ioremap(dev, res->start, resource_size(res)); - if (IS_ERR(pci->dbi_base2)) - return PTR_ERR(pci->dbi_base2); + if (!pci->dbi_base2) + return -ENOMEM; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space"); if (!res) -- cgit From 8cfab3cf63cfe5a53e2e566b3b86b30c187edf3a Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 26 Jan 2018 12:50:27 -0600 Subject: PCI: Add SPDX GPL-2.0 to replace GPL v2 boilerplate Add SPDX GPL-2.0 to all PCI files that specified the GPL version 2 license. Remove the boilerplate GPL version 2 language, relying on the assertion in b24413180f56 ("License cleanup: add SPDX GPL-2.0 license identifier to files with no license") that the SPDX identifier may be used instead of the full boilerplate text. Signed-off-by: Bjorn Helgaas Reviewed-by: Greg Kroah-Hartman --- drivers/pci/dwc/pcie-artpec6.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/pci/dwc/pcie-artpec6.c') diff --git a/drivers/pci/dwc/pcie-artpec6.c b/drivers/pci/dwc/pcie-artpec6.c index 6653619db6a1..b89884919423 100644 --- a/drivers/pci/dwc/pcie-artpec6.c +++ b/drivers/pci/dwc/pcie-artpec6.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * PCIe host controller driver for Axis ARTPEC-6 SoC * * Author: Niklas Cassel * * Based on work done by Phil Edworthy - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include -- cgit