]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/pci/dwc/pci-imx6.c
Merge tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
[karo-tx-linux.git] / drivers / pci / dwc / pci-imx6.c
index 801e46cd266d79463e9b1a96fb418769c3414548..a98cba55c7f02e670436787c011a85a8772f5109 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/kernel.h>
 #include <linux/mfd/syscon.h>
 #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
+#include <linux/mfd/syscon/imx7-iomuxc-gpr.h>
 #include <linux/module.h>
 #include <linux/of_gpio.h>
 #include <linux/of_device.h>
@@ -27,6 +28,7 @@
 #include <linux/signal.h>
 #include <linux/types.h>
 #include <linux/interrupt.h>
+#include <linux/reset.h>
 
 #include "pcie-designware.h"
 
@@ -36,6 +38,7 @@ enum imx6_pcie_variants {
        IMX6Q,
        IMX6SX,
        IMX6QP,
+       IMX7D,
 };
 
 struct imx6_pcie {
@@ -47,6 +50,8 @@ struct imx6_pcie {
        struct clk              *pcie_inbound_axi;
        struct clk              *pcie;
        struct regmap           *iomuxc_gpr;
+       struct reset_control    *pciephy_reset;
+       struct reset_control    *apps_reset;
        enum imx6_pcie_variants variant;
        u32                     tx_deemph_gen1;
        u32                     tx_deemph_gen2_3p5db;
@@ -56,6 +61,11 @@ struct imx6_pcie {
        int                     link_gen;
 };
 
+/* Parameters for the waiting for PCIe PHY PLL to lock on i.MX7 */
+#define PHY_PLL_LOCK_WAIT_MAX_RETRIES  2000
+#define PHY_PLL_LOCK_WAIT_USLEEP_MIN   50
+#define PHY_PLL_LOCK_WAIT_USLEEP_MAX   200
+
 /* PCIe Root Complex registers (memory-mapped) */
 #define PCIE_RC_LCR                            0x7c
 #define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1       0x1
@@ -248,6 +258,10 @@ static int imx6q_pcie_abort_handler(unsigned long addr,
 static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)
 {
        switch (imx6_pcie->variant) {
+       case IMX7D:
+               reset_control_assert(imx6_pcie->pciephy_reset);
+               reset_control_assert(imx6_pcie->apps_reset);
+               break;
        case IMX6SX:
                regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
                                   IMX6SX_GPR12_PCIE_TEST_POWERDOWN,
@@ -303,11 +317,32 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
                regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
                                   IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16);
                break;
+       case IMX7D:
+               break;
        }
 
        return ret;
 }
 
+static void imx7d_pcie_wait_for_phy_pll_lock(struct imx6_pcie *imx6_pcie)
+{
+       u32 val;
+       unsigned int retries;
+       struct device *dev = imx6_pcie->pci->dev;
+
+       for (retries = 0; retries < PHY_PLL_LOCK_WAIT_MAX_RETRIES; retries++) {
+               regmap_read(imx6_pcie->iomuxc_gpr, IOMUXC_GPR22, &val);
+
+               if (val & IMX7D_GPR22_PCIE_PHY_PLL_LOCKED)
+                       return;
+
+               usleep_range(PHY_PLL_LOCK_WAIT_USLEEP_MIN,
+                            PHY_PLL_LOCK_WAIT_USLEEP_MAX);
+       }
+
+       dev_err(dev, "PCIe PLL lock timeout\n");
+}
+
 static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)
 {
        struct dw_pcie *pci = imx6_pcie->pci;
@@ -351,6 +386,10 @@ static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)
        }
 
        switch (imx6_pcie->variant) {
+       case IMX7D:
+               reset_control_deassert(imx6_pcie->pciephy_reset);
+               imx7d_pcie_wait_for_phy_pll_lock(imx6_pcie);
+               break;
        case IMX6SX:
                regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5,
                                   IMX6SX_GPR5_PCIE_BTNRST_RESET, 0);
@@ -377,35 +416,44 @@ err_pcie_bus:
 
 static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
 {
-       if (imx6_pcie->variant == IMX6SX)
+       switch (imx6_pcie->variant) {
+       case IMX7D:
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+                                  IMX7D_GPR12_PCIE_PHY_REFCLK_SEL, 0);
+               break;
+       case IMX6SX:
                regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
                                   IMX6SX_GPR12_PCIE_RX_EQ_MASK,
                                   IMX6SX_GPR12_PCIE_RX_EQ_2);
+               /* FALLTHROUGH */
+       default:
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+                                  IMX6Q_GPR12_PCIE_CTL_2, 0 << 10);
 
-       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
-                       IMX6Q_GPR12_PCIE_CTL_2, 0 << 10);
+               /* configure constant input signal to the pcie ctrl and phy */
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+                                  IMX6Q_GPR12_LOS_LEVEL, 9 << 4);
+
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
+                                  IMX6Q_GPR8_TX_DEEMPH_GEN1,
+                                  imx6_pcie->tx_deemph_gen1 << 0);
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
+                                  IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB,
+                                  imx6_pcie->tx_deemph_gen2_3p5db << 6);
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
+                                  IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB,
+                                  imx6_pcie->tx_deemph_gen2_6db << 12);
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
+                                  IMX6Q_GPR8_TX_SWING_FULL,
+                                  imx6_pcie->tx_swing_full << 18);
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
+                                  IMX6Q_GPR8_TX_SWING_LOW,
+                                  imx6_pcie->tx_swing_low << 25);
+               break;
+       }
 
-       /* configure constant input signal to the pcie ctrl and phy */
        regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
                        IMX6Q_GPR12_DEVICE_TYPE, PCI_EXP_TYPE_ROOT_PORT << 12);
-       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
-                       IMX6Q_GPR12_LOS_LEVEL, 9 << 4);
-
-       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
-                          IMX6Q_GPR8_TX_DEEMPH_GEN1,
-                          imx6_pcie->tx_deemph_gen1 << 0);
-       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
-                          IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB,
-                          imx6_pcie->tx_deemph_gen2_3p5db << 6);
-       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
-                          IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB,
-                          imx6_pcie->tx_deemph_gen2_6db << 12);
-       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
-                          IMX6Q_GPR8_TX_SWING_FULL,
-                          imx6_pcie->tx_swing_full << 18);
-       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
-                          IMX6Q_GPR8_TX_SWING_LOW,
-                          imx6_pcie->tx_swing_low << 25);
 }
 
 static int imx6_pcie_wait_for_link(struct imx6_pcie *imx6_pcie)
@@ -469,8 +517,11 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
        dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp);
 
        /* Start LTSSM. */
-       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
-                       IMX6Q_GPR12_PCIE_CTL_2, 1 << 10);
+       if (imx6_pcie->variant == IMX7D)
+               reset_control_deassert(imx6_pcie->apps_reset);
+       else
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+                                  IMX6Q_GPR12_PCIE_CTL_2, 1 << 10);
 
        ret = imx6_pcie_wait_for_link(imx6_pcie);
        if (ret)
@@ -482,29 +533,40 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
                tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;
                tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2;
                dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp);
-       } else {
-               dev_info(dev, "Link: Gen2 disabled\n");
-       }
-
-       /*
-        * Start Directed Speed Change so the best possible speed both link
-        * partners support can be negotiated.
-        */
-       tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
-       tmp |= PORT_LOGIC_SPEED_CHANGE;
-       dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp);
 
-       ret = imx6_pcie_wait_for_speed_change(imx6_pcie);
-       if (ret) {
-               dev_err(dev, "Failed to bring link up!\n");
-               goto err_reset_phy;
-       }
+               /*
+                * Start Directed Speed Change so the best possible
+                * speed both link partners support can be negotiated.
+                */
+               tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
+               tmp |= PORT_LOGIC_SPEED_CHANGE;
+               dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp);
+
+               if (imx6_pcie->variant != IMX7D) {
+                       /*
+                        * On i.MX7, DIRECT_SPEED_CHANGE behaves differently
+                        * from i.MX6 family when no link speed transition
+                        * occurs and we go Gen1 -> yep, Gen1. The difference
+                        * is that, in such case, it will not be cleared by HW
+                        * which will cause the following code to report false
+                        * failure.
+                        */
+
+                       ret = imx6_pcie_wait_for_speed_change(imx6_pcie);
+                       if (ret) {
+                               dev_err(dev, "Failed to bring link up!\n");
+                               goto err_reset_phy;
+                       }
+               }
 
-       /* Make sure link training is finished as well! */
-       ret = imx6_pcie_wait_for_link(imx6_pcie);
-       if (ret) {
-               dev_err(dev, "Failed to bring link up!\n");
-               goto err_reset_phy;
+               /* Make sure link training is finished as well! */
+               ret = imx6_pcie_wait_for_link(imx6_pcie);
+               if (ret) {
+                       dev_err(dev, "Failed to bring link up!\n");
+                       goto err_reset_phy;
+               }
+       } else {
+               dev_info(dev, "Link: Gen2 disabled\n");
        }
 
        tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCSR);
@@ -544,8 +606,8 @@ static struct dw_pcie_host_ops imx6_pcie_host_ops = {
        .host_init = imx6_pcie_host_init,
 };
 
-static int __init imx6_add_pcie_port(struct imx6_pcie *imx6_pcie,
-                                    struct platform_device *pdev)
+static int imx6_add_pcie_port(struct imx6_pcie *imx6_pcie,
+                             struct platform_device *pdev)
 {
        struct dw_pcie *pci = imx6_pcie->pci;
        struct pcie_port *pp = &pci->pp;
@@ -585,7 +647,7 @@ static const struct dw_pcie_ops dw_pcie_ops = {
        .link_up = imx6_pcie_link_up,
 };
 
-static int __init imx6_pcie_probe(struct platform_device *pdev)
+static int imx6_pcie_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct dw_pcie *pci;
@@ -609,10 +671,6 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
        imx6_pcie->variant =
                (enum imx6_pcie_variants)of_device_get_match_data(dev);
 
-       /* Added for PCI abort handling */
-       hook_fault_code(16 + 6, imx6q_pcie_abort_handler, SIGBUS, 0,
-               "imprecise external abort");
-
        dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        pci->dbi_base = devm_ioremap_resource(dev, dbi_base);
        if (IS_ERR(pci->dbi_base))
@@ -632,6 +690,8 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
                        dev_err(dev, "unable to get reset gpio\n");
                        return ret;
                }
+       } else if (imx6_pcie->reset_gpio == -EPROBE_DEFER) {
+               return imx6_pcie->reset_gpio;
        }
 
        /* Fetch clocks */
@@ -653,13 +713,31 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
                return PTR_ERR(imx6_pcie->pcie);
        }
 
-       if (imx6_pcie->variant == IMX6SX) {
+       switch (imx6_pcie->variant) {
+       case IMX6SX:
                imx6_pcie->pcie_inbound_axi = devm_clk_get(dev,
                                                           "pcie_inbound_axi");
                if (IS_ERR(imx6_pcie->pcie_inbound_axi)) {
                        dev_err(dev, "pcie_inbound_axi clock missing or invalid\n");
                        return PTR_ERR(imx6_pcie->pcie_inbound_axi);
                }
+               break;
+       case IMX7D:
+               imx6_pcie->pciephy_reset = devm_reset_control_get(dev,
+                                                                 "pciephy");
+               if (IS_ERR(imx6_pcie->pciephy_reset)) {
+                       dev_err(dev, "Failed to get PCIEPHY reset control\n");
+                       return PTR_ERR(imx6_pcie->pciephy_reset);
+               }
+
+               imx6_pcie->apps_reset = devm_reset_control_get(dev, "apps");
+               if (IS_ERR(imx6_pcie->apps_reset)) {
+                       dev_err(dev, "Failed to get PCIE APPS reset control\n");
+                       return PTR_ERR(imx6_pcie->apps_reset);
+               }
+               break;
+       default:
+               break;
        }
 
        /* Grab GPR config register range */
@@ -718,6 +796,7 @@ static const struct of_device_id imx6_pcie_of_match[] = {
        { .compatible = "fsl,imx6q-pcie",  .data = (void *)IMX6Q,  },
        { .compatible = "fsl,imx6sx-pcie", .data = (void *)IMX6SX, },
        { .compatible = "fsl,imx6qp-pcie", .data = (void *)IMX6QP, },
+       { .compatible = "fsl,imx7d-pcie",  .data = (void *)IMX7D,  },
        {},
 };
 
@@ -725,12 +804,24 @@ static struct platform_driver imx6_pcie_driver = {
        .driver = {
                .name   = "imx6q-pcie",
                .of_match_table = imx6_pcie_of_match,
+               .suppress_bind_attrs = true,
        },
+       .probe    = imx6_pcie_probe,
        .shutdown = imx6_pcie_shutdown,
 };
 
 static int __init imx6_pcie_init(void)
 {
-       return platform_driver_probe(&imx6_pcie_driver, imx6_pcie_probe);
+       /*
+        * Since probe() can be deferred we need to make sure that
+        * hook_fault_code is not called after __init memory is freed
+        * by kernel and since imx6q_pcie_abort_handler() is a no-op,
+        * we can install the handler here without risking it
+        * accessing some uninitialized driver state.
+        */
+       hook_fault_code(16 + 6, imx6q_pcie_abort_handler, SIGBUS, 0,
+                       "imprecise external abort");
+
+       return platform_driver_register(&imx6_pcie_driver);
 }
 device_initcall(imx6_pcie_init);