9
0
Fork 0

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 <andrew.smirnov@gmail.com>
Acked-by: Lucas Stach <l.stach@pengutronix.de>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
This commit is contained in:
Andrey Smirnov 2016-04-25 22:37:01 -07:00 committed by Sascha Hauer
parent bd55401e97
commit 015ec0a528
2 changed files with 67 additions and 30 deletions

View File

@ -20,6 +20,7 @@
#include <gpio.h>
#include <asm/mmu.h>
#include <of_gpio.h>
#include <of_device.h>
#include <linux/clk.h>
#include <linux/kernel.h>
#include <of_address.h>
@ -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,40 +226,49 @@ 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_REF_CLK_EN;
writel(gpr1, imx6_pcie->iomuxc_gpr + IOMUXC_GPR1);
break;
}
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);
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, },
{},
};

View File

@ -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)