From 015ec0a52858e4b1b3c1ecdef4a02cf258fa26d7 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 25 Apr 2016 22:37:01 -0700 Subject: PCI: imx6: Add proper i.MX6+ reset sequence I.MX6+ version of the silicon exposed PCIe core's reset signal as a bit in one of the control registers. As a result using old, pre-i.MX6+, reset sequence on i.MX6+ leads to Barebox hanging during startup. Using exposed reset bit instead solves the problem. This commit is based on portions of commit c34068d48273e24d392d9a49a38be807954420ed in http://git.freescale.com/git/cgit.cgi/imx/linux-2.6-imx.git Signed-off-by: Andrey Smirnov Acked-by: Lucas Stach Signed-off-by: Sascha Hauer --- drivers/pci/pci-imx6.c | 94 +++++++++++++++++++++++++++++------------- include/mfd/imx6q-iomuxc-gpr.h | 1 + 2 files changed, 66 insertions(+), 29 deletions(-) diff --git a/drivers/pci/pci-imx6.c b/drivers/pci/pci-imx6.c index 241df3ff8d..13a8b2262e 100644 --- a/drivers/pci/pci-imx6.c +++ b/drivers/pci/pci-imx6.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,11 @@ #define to_imx6_pcie(x) container_of(x, struct imx6_pcie, pp) +enum imx6_pcie_variants { + IMX6Q, + IMX6QP, +}; + struct imx6_pcie { int reset_gpio; struct clk *pcie_bus; @@ -43,6 +49,7 @@ struct imx6_pcie { struct clk *pcie; struct pcie_port pp; void __iomem *iomuxc_gpr; + enum imx6_pcie_variants variant; void __iomem *mem_base; }; @@ -219,39 +226,48 @@ static int imx6_pcie_assert_core_reset(struct pcie_port *pp) struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp); u32 val, gpr1, gpr12; - /* - * If the bootloader already enabled the link we need some special - * handling to get the core back into a state where it is safe to - * touch it for configuration. As there is no dedicated reset signal - * wired up for MX6QDL, we need to manually force LTSSM into "detect" - * state before completely disabling LTSSM, which is a prerequisite - * for core configuration. - * - * If both LTSSM_ENABLE and REF_SSP_ENABLE are active we have a strong - * indication that the bootloader activated the link. - */ - gpr1 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); - gpr12 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR12); + switch (imx6_pcie->variant) { + case IMX6QP: + gpr1 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); + gpr1 |= IMX6Q_GPR1_PCIE_SW_RST; + writel(gpr1, imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); + break; + case IMX6Q: + /* + * If the bootloader already enabled the link we need some special + * handling to get the core back into a state where it is safe to + * touch it for configuration. As there is no dedicated reset signal + * wired up for MX6QDL, we need to manually force LTSSM into "detect" + * state before completely disabling LTSSM, which is a prerequisite + * for core configuration. + * + * If both LTSSM_ENABLE and REF_SSP_ENABLE are active we have a strong + * indication that the bootloader activated the link. + */ + gpr1 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); + gpr12 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR12); - if ((gpr1 & IMX6Q_GPR1_PCIE_REF_CLK_EN) && - (gpr12 & IMX6Q_GPR12_PCIE_CTL_2)) { - val = readl(pp->dbi_base + PCIE_PL_PFLR); - val &= ~PCIE_PL_PFLR_LINK_STATE_MASK; - val |= PCIE_PL_PFLR_FORCE_LINK; + if ((gpr1 & IMX6Q_GPR1_PCIE_REF_CLK_EN) && + (gpr12 & IMX6Q_GPR12_PCIE_CTL_2)) { + val = readl(pp->dbi_base + PCIE_PL_PFLR); + val &= ~PCIE_PL_PFLR_LINK_STATE_MASK; + val |= PCIE_PL_PFLR_FORCE_LINK; - data_abort_mask(); - writel(val, pp->dbi_base + PCIE_PL_PFLR); - data_abort_unmask(); + data_abort_mask(); + writel(val, pp->dbi_base + PCIE_PL_PFLR); + data_abort_unmask(); - gpr12 &= ~IMX6Q_GPR12_PCIE_CTL_2; - writel(gpr12, imx6_pcie->iomuxc_gpr + IOMUXC_GPR12); - } + gpr12 &= ~IMX6Q_GPR12_PCIE_CTL_2; + writel(gpr12, imx6_pcie->iomuxc_gpr + IOMUXC_GPR12); + } - gpr1 |= IMX6Q_GPR1_PCIE_TEST_PD; - writel(gpr1, imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); + gpr1 |= IMX6Q_GPR1_PCIE_TEST_PD; + writel(gpr1, imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); - gpr1 &= ~IMX6Q_GPR1_PCIE_REF_CLK_EN; - writel(gpr1, imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); + gpr1 &= ~IMX6Q_GPR1_PCIE_REF_CLK_EN; + writel(gpr1, imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); + break; + } return 0; } @@ -298,6 +314,22 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp) mdelay(100); gpio_set_value(imx6_pcie->reset_gpio, 1); } + + /* + * Release the PCIe PHY reset here + */ + switch (imx6_pcie->variant) { + case IMX6QP: + gpr1 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); + gpr1 &= ~IMX6Q_GPR1_PCIE_SW_RST; + writel(gpr1, imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); + + udelay(200); + break; + case IMX6Q: /* Nothing to do */ + break; + } + return 0; err_pcie: @@ -568,6 +600,9 @@ static int __init imx6_pcie_probe(struct device_d *dev) pp = &imx6_pcie->pp; pp->dev = dev; + imx6_pcie->variant = + (enum imx6_pcie_variants)of_device_get_match_data(dev); + iores = dev_request_mem_resource(dev, 0); if (IS_ERR(iores)) return PTR_ERR(iores); @@ -623,7 +658,8 @@ static void imx6_pcie_remove(struct device_d *dev) } static struct of_device_id imx6_pcie_of_match[] = { - { .compatible = "fsl,imx6q-pcie", }, + { .compatible = "fsl,imx6q-pcie", .data = (void *)IMX6Q, }, + { .compatible = "fsl,imx6qp-pcie", .data = (void *)IMX6QP, }, {}, }; diff --git a/include/mfd/imx6q-iomuxc-gpr.h b/include/mfd/imx6q-iomuxc-gpr.h index a7ef1c8915..b2c9da6579 100644 --- a/include/mfd/imx6q-iomuxc-gpr.h +++ b/include/mfd/imx6q-iomuxc-gpr.h @@ -95,6 +95,7 @@ #define IMX6Q_GPR0_DMAREQ_MUX_SEL0_IOMUX BIT(0) #define IMX6Q_GPR1_PCIE_REQ_MASK (0x3 << 30) +#define IMX6Q_GPR1_PCIE_SW_RST BIT(29) #define IMX6Q_GPR1_PCIE_EXIT_L1 BIT(28) #define IMX6Q_GPR1_PCIE_RDY_L23 BIT(27) #define IMX6Q_GPR1_PCIE_ENTER_L1 BIT(26) -- cgit v1.2.3