Commit 4bd70bc9 authored by Bjorn Helgaas's avatar Bjorn Helgaas

Merge branch 'pci/host-imx6' into next

* pci/host-imx6:
  PCI: imx6: Fix bugs in PCIe startup code
  PCI: imx6: Start link in Gen1 before negotiating for Gen2 mode
  PCI: imx6: Factor out link up wait loop
  PCI: imx6: Factor out PHY reset
  PCI: imx6: Report "link up" only after link training completes
  PCI: imx6: Make reset-gpio optional
parents 608235a3 bc9ef770
...@@ -19,6 +19,8 @@ Required properties: ...@@ -19,6 +19,8 @@ Required properties:
to define the mapping of the PCIe interface to interrupt to define the mapping of the PCIe interface to interrupt
numbers. numbers.
- num-lanes: number of lanes to use - num-lanes: number of lanes to use
Optional properties:
- reset-gpio: gpio pin number of power good signal - reset-gpio: gpio pin number of power good signal
Optional properties for fsl,imx6q-pcie Optional properties for fsl,imx6q-pcie
......
...@@ -44,10 +44,18 @@ struct imx6_pcie { ...@@ -44,10 +44,18 @@ struct imx6_pcie {
void __iomem *mem_base; void __iomem *mem_base;
}; };
/* PCIe Root Complex registers (memory-mapped) */
#define PCIE_RC_LCR 0x7c
#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
/* PCIe Port Logic registers (memory-mapped) */ /* PCIe Port Logic registers (memory-mapped) */
#define PL_OFFSET 0x700 #define PL_OFFSET 0x700
#define PCIE_PHY_DEBUG_R0 (PL_OFFSET + 0x28) #define PCIE_PHY_DEBUG_R0 (PL_OFFSET + 0x28)
#define PCIE_PHY_DEBUG_R1 (PL_OFFSET + 0x2c) #define PCIE_PHY_DEBUG_R1 (PL_OFFSET + 0x2c)
#define PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING (1 << 29)
#define PCIE_PHY_DEBUG_R1_XMLH_LINK_UP (1 << 4)
#define PCIE_PHY_CTRL (PL_OFFSET + 0x114) #define PCIE_PHY_CTRL (PL_OFFSET + 0x114)
#define PCIE_PHY_CTRL_DATA_LOC 0 #define PCIE_PHY_CTRL_DATA_LOC 0
...@@ -59,6 +67,9 @@ struct imx6_pcie { ...@@ -59,6 +67,9 @@ struct imx6_pcie {
#define PCIE_PHY_STAT (PL_OFFSET + 0x110) #define PCIE_PHY_STAT (PL_OFFSET + 0x110)
#define PCIE_PHY_STAT_ACK_LOC 16 #define PCIE_PHY_STAT_ACK_LOC 16
#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17)
/* PHY registers (not memory-mapped) */ /* PHY registers (not memory-mapped) */
#define PCIE_PHY_RX_ASIC_OUT 0x100D #define PCIE_PHY_RX_ASIC_OUT 0x100D
...@@ -209,15 +220,9 @@ static int imx6_pcie_assert_core_reset(struct pcie_port *pp) ...@@ -209,15 +220,9 @@ static int imx6_pcie_assert_core_reset(struct pcie_port *pp)
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
IMX6Q_GPR1_PCIE_TEST_PD, 1 << 18); IMX6Q_GPR1_PCIE_TEST_PD, 1 << 18);
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
IMX6Q_GPR12_PCIE_CTL_2, 1 << 10);
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
IMX6Q_GPR1_PCIE_REF_CLK_EN, 0 << 16); IMX6Q_GPR1_PCIE_REF_CLK_EN, 0 << 16);
gpio_set_value(imx6_pcie->reset_gpio, 0);
msleep(100);
gpio_set_value(imx6_pcie->reset_gpio, 1);
return 0; return 0;
} }
...@@ -261,6 +266,12 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp) ...@@ -261,6 +266,12 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
/* allow the clocks to stabilize */ /* allow the clocks to stabilize */
usleep_range(200, 500); usleep_range(200, 500);
/* Some boards don't have PCIe reset GPIO. */
if (gpio_is_valid(imx6_pcie->reset_gpio)) {
gpio_set_value(imx6_pcie->reset_gpio, 0);
msleep(100);
gpio_set_value(imx6_pcie->reset_gpio, 1);
}
return 0; return 0;
err_pcie_axi: err_pcie_axi:
...@@ -299,11 +310,90 @@ static void imx6_pcie_init_phy(struct pcie_port *pp) ...@@ -299,11 +310,90 @@ static void imx6_pcie_init_phy(struct pcie_port *pp)
IMX6Q_GPR8_TX_SWING_LOW, 127 << 25); IMX6Q_GPR8_TX_SWING_LOW, 127 << 25);
} }
static void imx6_pcie_host_init(struct pcie_port *pp) static int imx6_pcie_wait_for_link(struct pcie_port *pp)
{
int count = 200;
while (!dw_pcie_link_up(pp)) {
usleep_range(100, 1000);
if (--count)
continue;
dev_err(pp->dev, "phy link never came up\n");
dev_dbg(pp->dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
readl(pp->dbi_base + PCIE_PHY_DEBUG_R0),
readl(pp->dbi_base + PCIE_PHY_DEBUG_R1));
return -EINVAL;
}
return 0;
}
static int imx6_pcie_start_link(struct pcie_port *pp)
{ {
int count = 0;
struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp); struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
uint32_t tmp;
int ret, count;
/*
* Force Gen1 operation when starting the link. In case the link is
* started in Gen2 mode, there is a possibility the devices on the
* bus will not be detected at all. This happens with PCIe switches.
*/
tmp = readl(pp->dbi_base + PCIE_RC_LCR);
tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;
tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1;
writel(tmp, pp->dbi_base + PCIE_RC_LCR);
/* Start LTSSM. */
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
IMX6Q_GPR12_PCIE_CTL_2, 1 << 10);
ret = imx6_pcie_wait_for_link(pp);
if (ret)
return ret;
/* Allow Gen2 mode after the link is up. */
tmp = readl(pp->dbi_base + PCIE_RC_LCR);
tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;
tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2;
writel(tmp, pp->dbi_base + PCIE_RC_LCR);
/*
* Start Directed Speed Change so the best possible speed both link
* partners support can be negotiated.
*/
tmp = readl(pp->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
tmp |= PORT_LOGIC_SPEED_CHANGE;
writel(tmp, pp->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
count = 200;
while (count--) {
tmp = readl(pp->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
/* Test if the speed change finished. */
if (!(tmp & PORT_LOGIC_SPEED_CHANGE))
break;
usleep_range(100, 1000);
}
/* Make sure link training is finished as well! */
if (count)
ret = imx6_pcie_wait_for_link(pp);
else
ret = -EINVAL;
if (ret) {
dev_err(pp->dev, "Failed to bring link up!\n");
} else {
tmp = readl(pp->dbi_base + 0x80);
dev_dbg(pp->dev, "Link up, Gen=%i\n", (tmp >> 16) & 0xf);
}
return ret;
}
static void imx6_pcie_host_init(struct pcie_port *pp)
{
imx6_pcie_assert_core_reset(pp); imx6_pcie_assert_core_reset(pp);
imx6_pcie_init_phy(pp); imx6_pcie_init_phy(pp);
...@@ -312,33 +402,41 @@ static void imx6_pcie_host_init(struct pcie_port *pp) ...@@ -312,33 +402,41 @@ static void imx6_pcie_host_init(struct pcie_port *pp)
dw_pcie_setup_rc(pp); dw_pcie_setup_rc(pp);
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, imx6_pcie_start_link(pp);
IMX6Q_GPR12_PCIE_CTL_2, 1 << 10); }
while (!dw_pcie_link_up(pp)) { static void imx6_pcie_reset_phy(struct pcie_port *pp)
usleep_range(100, 1000); {
count++; uint32_t temp;
if (count >= 200) {
dev_err(pp->dev, "phy link never came up\n"); pcie_phy_read(pp->dbi_base, PHY_RX_OVRD_IN_LO, &temp);
dev_dbg(pp->dev, temp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN |
"DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n", PHY_RX_OVRD_IN_LO_RX_PLL_EN);
readl(pp->dbi_base + PCIE_PHY_DEBUG_R0), pcie_phy_write(pp->dbi_base, PHY_RX_OVRD_IN_LO, temp);
readl(pp->dbi_base + PCIE_PHY_DEBUG_R1));
break; usleep_range(2000, 3000);
}
}
return; pcie_phy_read(pp->dbi_base, PHY_RX_OVRD_IN_LO, &temp);
temp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN |
PHY_RX_OVRD_IN_LO_RX_PLL_EN);
pcie_phy_write(pp->dbi_base, PHY_RX_OVRD_IN_LO, temp);
} }
static int imx6_pcie_link_up(struct pcie_port *pp) static int imx6_pcie_link_up(struct pcie_port *pp)
{ {
u32 rc, ltssm, rx_valid, temp; u32 rc, ltssm, rx_valid;
/* link is debug bit 36, debug register 1 starts at bit 32 */ /*
rc = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1) & (0x1 << (36 - 32)); * Test if the PHY reports that the link is up and also that
if (rc) * the link training finished. It might happen that the PHY
return -EAGAIN; * reports the link is already up, but the link training bit
* is still set, so make sure to check the training is done
* as well here.
*/
rc = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1);
if ((rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_UP) &&
!(rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING))
return 1;
/* /*
* From L0, initiate MAC entry to gen2 if EP/RC supports gen2. * From L0, initiate MAC entry to gen2 if EP/RC supports gen2.
...@@ -358,21 +456,7 @@ static int imx6_pcie_link_up(struct pcie_port *pp) ...@@ -358,21 +456,7 @@ static int imx6_pcie_link_up(struct pcie_port *pp)
dev_err(pp->dev, "transition to gen2 is stuck, reset PHY!\n"); dev_err(pp->dev, "transition to gen2 is stuck, reset PHY!\n");
pcie_phy_read(pp->dbi_base, imx6_pcie_reset_phy(pp);
PHY_RX_OVRD_IN_LO, &temp);
temp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN
| PHY_RX_OVRD_IN_LO_RX_PLL_EN);
pcie_phy_write(pp->dbi_base,
PHY_RX_OVRD_IN_LO, temp);
usleep_range(2000, 3000);
pcie_phy_read(pp->dbi_base,
PHY_RX_OVRD_IN_LO, &temp);
temp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN
| PHY_RX_OVRD_IN_LO_RX_PLL_EN);
pcie_phy_write(pp->dbi_base,
PHY_RX_OVRD_IN_LO, temp);
return 0; return 0;
} }
...@@ -432,17 +516,13 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) ...@@ -432,17 +516,13 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
/* Fetch GPIOs */ /* Fetch GPIOs */
imx6_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); imx6_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
if (!gpio_is_valid(imx6_pcie->reset_gpio)) { if (gpio_is_valid(imx6_pcie->reset_gpio)) {
dev_err(&pdev->dev, "no reset-gpio defined\n"); ret = devm_gpio_request_one(&pdev->dev, imx6_pcie->reset_gpio,
ret = -ENODEV; GPIOF_OUT_INIT_LOW, "PCIe reset");
} if (ret) {
ret = devm_gpio_request_one(&pdev->dev, dev_err(&pdev->dev, "unable to get reset gpio\n");
imx6_pcie->reset_gpio, return ret;
GPIOF_OUT_INIT_LOW, }
"PCIe reset");
if (ret) {
dev_err(&pdev->dev, "unable to get reset gpio\n");
return ret;
} }
imx6_pcie->power_on_gpio = of_get_named_gpio(np, "power-on-gpio", 0); imx6_pcie->power_on_gpio = of_get_named_gpio(np, "power-on-gpio", 0);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment