Merge branch 'pci/host-imx6' into next
authorBjorn Helgaas <bhelgaas@google.com>
Fri, 20 Dec 2013 19:38:07 +0000 (12:38 -0700)
committerBjorn Helgaas <bhelgaas@google.com>
Fri, 20 Dec 2013 19:38:07 +0000 (12:38 -0700)
* 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

Documentation/devicetree/bindings/pci/designware-pcie.txt
drivers/pci/host/pci-imx6.c

index d5d26d443693ce70db814c9466bbc72d87d6b7fe..d6fae13ff0628bef56a69d671a9a684fa7ab2280 100644 (file)
@@ -19,6 +19,8 @@ Required properties:
        to define the mapping of the PCIe interface to interrupt
        numbers.
 - num-lanes: number of lanes to use
+
+Optional properties:
 - reset-gpio: gpio pin number of power good signal
 
 Optional properties for fsl,imx6q-pcie
index 9fc1cb66c64e98426aa077095a1dcea9b8685a37..e8663a8c3406a0cb73b5c4a2e1f7380e7fc9f898 100644 (file)
@@ -44,10 +44,18 @@ struct imx6_pcie {
        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) */
 #define PL_OFFSET 0x700
 #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)
+#define PCIE_PHY_DEBUG_R1_XMLH_LINK_UP         (1 << 4)
 
 #define PCIE_PHY_CTRL (PL_OFFSET + 0x114)
 #define PCIE_PHY_CTRL_DATA_LOC 0
@@ -59,6 +67,9 @@ struct imx6_pcie {
 #define PCIE_PHY_STAT (PL_OFFSET + 0x110)
 #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) */
 #define PCIE_PHY_RX_ASIC_OUT 0x100D
 
@@ -209,15 +220,9 @@ static int imx6_pcie_assert_core_reset(struct pcie_port *pp)
 
        regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
                        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,
                        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;
 }
 
@@ -261,6 +266,12 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
        /* allow the clocks to stabilize */
        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;
 
 err_pcie_axi:
@@ -299,11 +310,90 @@ static void imx6_pcie_init_phy(struct pcie_port *pp)
                        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);
+       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_init_phy(pp);
@@ -312,33 +402,41 @@ static void imx6_pcie_host_init(struct pcie_port *pp)
 
        dw_pcie_setup_rc(pp);
 
-       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
-                       IMX6Q_GPR12_PCIE_CTL_2, 1 << 10);
+       imx6_pcie_start_link(pp);
+}
 
-       while (!dw_pcie_link_up(pp)) {
-               usleep_range(100, 1000);
-               count++;
-               if (count >= 200) {
-                       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));
-                       break;
-               }
-       }
+static void imx6_pcie_reset_phy(struct pcie_port *pp)
+{
+       uint32_t temp;
+
+       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);
+
+       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)
 {
-       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));
-       if (rc)
-               return -EAGAIN;
+       /*
+        * Test if the PHY reports that the link is up and also that
+        * the link training finished.  It might happen that the PHY
+        * 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.
@@ -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");
 
-       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);
-
-       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);
+       imx6_pcie_reset_phy(pp);
 
        return 0;
 }
@@ -432,17 +516,13 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
 
        /* Fetch GPIOs */
        imx6_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
-       if (!gpio_is_valid(imx6_pcie->reset_gpio)) {
-               dev_err(&pdev->dev, "no reset-gpio defined\n");
-               ret = -ENODEV;
-       }
-       ret = devm_gpio_request_one(&pdev->dev,
-                               imx6_pcie->reset_gpio,
-                               GPIOF_OUT_INIT_LOW,
-                               "PCIe reset");
-       if (ret) {
-               dev_err(&pdev->dev, "unable to get reset gpio\n");
-               return ret;
+       if (gpio_is_valid(imx6_pcie->reset_gpio)) {
+               ret = devm_gpio_request_one(&pdev->dev, imx6_pcie->reset_gpio,
+                                           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);