Merge remote-tracking branches 'spi/fix/lock', 'spi/fix/maintainers', 'spi/fix/put...
[deliverable/linux.git] / drivers / pci / host / pci-imx6.c
index 2f817fa4c661e72274e873c38f460cefe04faa79..b741a36a67f3c0ba77acfaa3606ea2031a7742f7 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
 #include <linux/module.h>
 #include <linux/of_gpio.h>
+#include <linux/of_device.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 
 #define to_imx6_pcie(x)        container_of(x, struct imx6_pcie, pp)
 
+enum imx6_pcie_variants {
+       IMX6Q,
+       IMX6SX,
+       IMX6QP,
+};
+
 struct imx6_pcie {
        int                     reset_gpio;
+       bool                    gpio_active_high;
        struct clk              *pcie_bus;
        struct clk              *pcie_phy;
+       struct clk              *pcie_inbound_axi;
        struct clk              *pcie;
        struct pcie_port        pp;
        struct regmap           *iomuxc_gpr;
+       enum imx6_pcie_variants variant;
        void __iomem            *mem_base;
        u32                     tx_deemph_gen1;
        u32                     tx_deemph_gen2_3p5db;
        u32                     tx_deemph_gen2_6db;
        u32                     tx_swing_full;
        u32                     tx_swing_low;
+       int                     link_gen;
 };
 
 /* PCIe Root Complex registers (memory-mapped) */
@@ -236,37 +247,93 @@ 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.
-        */
-       regmap_read(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, &gpr1);
-       regmap_read(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, &gpr12);
+       switch (imx6_pcie->variant) {
+       case IMX6SX:
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+                                  IMX6SX_GPR12_PCIE_TEST_POWERDOWN,
+                                  IMX6SX_GPR12_PCIE_TEST_POWERDOWN);
+               /* Force PCIe PHY reset */
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5,
+                                  IMX6SX_GPR5_PCIE_BTNRST_RESET,
+                                  IMX6SX_GPR5_PCIE_BTNRST_RESET);
+               break;
+       case IMX6QP:
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
+                                  IMX6Q_GPR1_PCIE_SW_RST,
+                                  IMX6Q_GPR1_PCIE_SW_RST);
+               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.
+                */
+               regmap_read(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, &gpr1);
+               regmap_read(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, &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;
+                       writel(val, pp->dbi_base + PCIE_PL_PFLR);
+
+                       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+                                          IMX6Q_GPR12_PCIE_CTL_2, 0 << 10);
+               }
+
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
+                                  IMX6Q_GPR1_PCIE_TEST_PD, 1 << 18);
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
+                                  IMX6Q_GPR1_PCIE_REF_CLK_EN, 0 << 16);
+               break;
+       }
+
+       return 0;
+}
+
+static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
+{
+       struct pcie_port *pp = &imx6_pcie->pp;
+       int ret = 0;
 
-       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;
-               writel(val, pp->dbi_base + PCIE_PL_PFLR);
+       switch (imx6_pcie->variant) {
+       case IMX6SX:
+               ret = clk_prepare_enable(imx6_pcie->pcie_inbound_axi);
+               if (ret) {
+                       dev_err(pp->dev, "unable to enable pcie_axi clock\n");
+                       break;
+               }
 
                regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
-                               IMX6Q_GPR12_PCIE_CTL_2, 0 << 10);
+                                  IMX6SX_GPR12_PCIE_TEST_POWERDOWN, 0);
+               break;
+       case IMX6QP:            /* FALLTHROUGH */
+       case IMX6Q:
+               /* power up core phy and enable ref clock */
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
+                                  IMX6Q_GPR1_PCIE_TEST_PD, 0 << 18);
+               /*
+                * the async reset input need ref clock to sync internally,
+                * when the ref clock comes after reset, internal synced
+                * reset time is too short, cannot meet the requirement.
+                * add one ~10us delay here.
+                */
+               udelay(10);
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
+                                  IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16);
+               break;
        }
 
-       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
-                       IMX6Q_GPR1_PCIE_TEST_PD, 1 << 18);
-       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
-                       IMX6Q_GPR1_PCIE_REF_CLK_EN, 0 << 16);
-
-       return 0;
+       return ret;
 }
 
 static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
@@ -292,43 +359,60 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
                goto err_pcie;
        }
 
-       /* power up core phy and enable ref clock */
-       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
-                       IMX6Q_GPR1_PCIE_TEST_PD, 0 << 18);
-       /*
-        * the async reset input need ref clock to sync internally,
-        * when the ref clock comes after reset, internal synced
-        * reset time is too short, cannot meet the requirement.
-        * add one ~10us delay here.
-        */
-       udelay(10);
-       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
-                       IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16);
+       ret = imx6_pcie_enable_ref_clk(imx6_pcie);
+       if (ret) {
+               dev_err(pp->dev, "unable to enable pcie ref clock\n");
+               goto err_ref_clk;
+       }
 
        /* 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_cansleep(imx6_pcie->reset_gpio, 0);
+               gpio_set_value_cansleep(imx6_pcie->reset_gpio,
+                                       imx6_pcie->gpio_active_high);
                msleep(100);
-               gpio_set_value_cansleep(imx6_pcie->reset_gpio, 1);
+               gpio_set_value_cansleep(imx6_pcie->reset_gpio,
+                                       !imx6_pcie->gpio_active_high);
        }
+
+       switch (imx6_pcie->variant) {
+       case IMX6SX:
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5,
+                                  IMX6SX_GPR5_PCIE_BTNRST_RESET, 0);
+               break;
+       case IMX6QP:
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
+                                  IMX6Q_GPR1_PCIE_SW_RST, 0);
+
+               usleep_range(200, 500);
+               break;
+       case IMX6Q:             /* Nothing to do */
+               break;
+       }
+
        return 0;
 
+err_ref_clk:
+       clk_disable_unprepare(imx6_pcie->pcie);
 err_pcie:
        clk_disable_unprepare(imx6_pcie->pcie_bus);
 err_pcie_bus:
        clk_disable_unprepare(imx6_pcie->pcie_phy);
 err_pcie_phy:
        return ret;
-
 }
 
 static void imx6_pcie_init_phy(struct pcie_port *pp)
 {
        struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
 
+       if (imx6_pcie->variant == IMX6SX)
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+                                  IMX6SX_GPR12_PCIE_RX_EQ_MASK,
+                                  IMX6SX_GPR12_PCIE_RX_EQ_2);
+
        regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
                        IMX6Q_GPR12_PCIE_CTL_2, 0 << 10);
 
@@ -417,11 +501,15 @@ static int imx6_pcie_establish_link(struct pcie_port *pp)
                goto err_reset_phy;
        }
 
-       /* 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);
+       if (imx6_pcie->link_gen == 2) {
+               /* 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);
+       } else {
+               dev_info(pp->dev, "Link: Gen2 disabled\n");
+       }
 
        /*
         * Start Directed Speed Change so the best possible speed both link
@@ -445,8 +533,7 @@ static int imx6_pcie_establish_link(struct pcie_port *pp)
        }
 
        tmp = readl(pp->dbi_base + PCIE_RC_LCSR);
-       dev_dbg(pp->dev, "Link up, Gen=%i\n", (tmp >> 16) & 0xf);
-
+       dev_info(pp->dev, "Link up, Gen%i\n", (tmp >> 16) & 0xf);
        return 0;
 
 err_reset_phy:
@@ -535,6 +622,9 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
        pp = &imx6_pcie->pp;
        pp->dev = &pdev->dev;
 
+       imx6_pcie->variant =
+               (enum imx6_pcie_variants)of_device_get_match_data(&pdev->dev);
+
        /* Added for PCI abort handling */
        hook_fault_code(16 + 6, imx6q_pcie_abort_handler, SIGBUS, 0,
                "imprecise external abort");
@@ -546,9 +636,14 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
 
        /* Fetch GPIOs */
        imx6_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
+       imx6_pcie->gpio_active_high = of_property_read_bool(np,
+                                               "reset-gpio-active-high");
        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");
+                               imx6_pcie->gpio_active_high ?
+                                       GPIOF_OUT_INIT_HIGH :
+                                       GPIOF_OUT_INIT_LOW,
+                               "PCIe reset");
                if (ret) {
                        dev_err(&pdev->dev, "unable to get reset gpio\n");
                        return ret;
@@ -577,6 +672,16 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
                return PTR_ERR(imx6_pcie->pcie);
        }
 
+       if (imx6_pcie->variant == IMX6SX) {
+               imx6_pcie->pcie_inbound_axi = devm_clk_get(&pdev->dev,
+                                                          "pcie_inbound_axi");
+               if (IS_ERR(imx6_pcie->pcie_inbound_axi)) {
+                       dev_err(&pdev->dev,
+                               "pcie_incbound_axi clock missing or invalid\n");
+                       return PTR_ERR(imx6_pcie->pcie_inbound_axi);
+               }
+       }
+
        /* Grab GPR config register range */
        imx6_pcie->iomuxc_gpr =
                 syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
@@ -606,6 +711,12 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
                                 &imx6_pcie->tx_swing_low))
                imx6_pcie->tx_swing_low = 127;
 
+       /* Limit link speed */
+       ret = of_property_read_u32(pp->dev->of_node, "fsl,max-link-speed",
+                                  &imx6_pcie->link_gen);
+       if (ret)
+               imx6_pcie->link_gen = 1;
+
        ret = imx6_add_pcie_port(pp, pdev);
        if (ret < 0)
                return ret;
@@ -623,7 +734,9 @@ static void imx6_pcie_shutdown(struct platform_device *pdev)
 }
 
 static const struct of_device_id imx6_pcie_of_match[] = {
-       { .compatible = "fsl,imx6q-pcie", },
+       { .compatible = "fsl,imx6q-pcie",  .data = (void *)IMX6Q,  },
+       { .compatible = "fsl,imx6sx-pcie", .data = (void *)IMX6SX, },
+       { .compatible = "fsl,imx6qp-pcie", .data = (void *)IMX6QP, },
        {},
 };
 MODULE_DEVICE_TABLE(of, imx6_pcie_of_match);
This page took 0.040637 seconds and 5 git commands to generate.