Merge tag 'pci-v5.1-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci
[sfrench/cifs-2.6.git] / drivers / pci / controller / dwc / pci-imx6.c
index 80f843030e363e872cfb228879361a3d5c500575..3d627f94a16664d57a7eee76fc69dfdecadda19e 100644 (file)
@@ -8,6 +8,7 @@
  * Author: Sean Cross <xobs@kosagi.com>
  */
 
+#include <linux/bitfield.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/gpio.h>
@@ -18,6 +19,7 @@
 #include <linux/module.h>
 #include <linux/of_gpio.h>
 #include <linux/of_device.h>
+#include <linux/of_address.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 
 #include "pcie-designware.h"
 
+#define IMX8MQ_GPR_PCIE_REF_USE_PAD            BIT(9)
+#define IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN    BIT(10)
+#define IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE       BIT(11)
+#define IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE    GENMASK(11, 8)
+#define IMX8MQ_PCIE2_BASE_ADDR                 0x33c00000
+
 #define to_imx6_pcie(x)        dev_get_drvdata((x)->dev)
 
 enum imx6_pcie_variants {
@@ -39,6 +47,15 @@ enum imx6_pcie_variants {
        IMX6SX,
        IMX6QP,
        IMX7D,
+       IMX8MQ,
+};
+
+#define IMX6_PCIE_FLAG_IMX6_PHY                        BIT(0)
+#define IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE       BIT(1)
+
+struct imx6_pcie_drvdata {
+       enum imx6_pcie_variants variant;
+       u32 flags;
 };
 
 struct imx6_pcie {
@@ -49,11 +66,12 @@ struct imx6_pcie {
        struct clk              *pcie_phy;
        struct clk              *pcie_inbound_axi;
        struct clk              *pcie;
+       struct clk              *pcie_aux;
        struct regmap           *iomuxc_gpr;
+       u32                     controller_id;
        struct reset_control    *pciephy_reset;
        struct reset_control    *apps_reset;
        struct reset_control    *turnoff_reset;
-       enum imx6_pcie_variants variant;
        u32                     tx_deemph_gen1;
        u32                     tx_deemph_gen2_3p5db;
        u32                     tx_deemph_gen2_6db;
@@ -61,11 +79,13 @@ struct imx6_pcie {
        u32                     tx_swing_low;
        int                     link_gen;
        struct regulator        *vpcie;
+       void __iomem            *phy_base;
 
        /* power domain for pcie */
        struct device           *pd_pcie;
        /* power domain for pcie phy */
        struct device           *pd_pcie_phy;
+       const struct imx6_pcie_drvdata *drvdata;
 };
 
 /* Parameters for the waiting for PCIe PHY PLL to lock on i.MX7 */
@@ -101,7 +121,6 @@ struct imx6_pcie {
 #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_ATEOVRD                       0x10
@@ -117,6 +136,23 @@ struct imx6_pcie {
 #define PCIE_PHY_RX_ASIC_OUT 0x100D
 #define PCIE_PHY_RX_ASIC_OUT_VALID     (1 << 0)
 
+/* iMX7 PCIe PHY registers */
+#define PCIE_PHY_CMN_REG4              0x14
+/* These are probably the bits that *aren't* DCC_FB_EN */
+#define PCIE_PHY_CMN_REG4_DCC_FB_EN    0x29
+
+#define PCIE_PHY_CMN_REG15             0x54
+#define PCIE_PHY_CMN_REG15_DLY_4       BIT(2)
+#define PCIE_PHY_CMN_REG15_PLL_PD      BIT(5)
+#define PCIE_PHY_CMN_REG15_OVRD_PLL_PD BIT(7)
+
+#define PCIE_PHY_CMN_REG24             0x90
+#define PCIE_PHY_CMN_REG24_RX_EQ       BIT(6)
+#define PCIE_PHY_CMN_REG24_RX_EQ_SEL   BIT(3)
+
+#define PCIE_PHY_CMN_REG26             0x98
+#define PCIE_PHY_CMN_REG26_ATT_MODE    0xBC
+
 #define PHY_RX_OVRD_IN_LO 0x1005
 #define PHY_RX_OVRD_IN_LO_RX_DATA_EN (1 << 5)
 #define PHY_RX_OVRD_IN_LO_RX_PLL_EN (1 << 3)
@@ -251,6 +287,9 @@ static void imx6_pcie_reset_phy(struct imx6_pcie *imx6_pcie)
 {
        u32 tmp;
 
+       if (!(imx6_pcie->drvdata->flags & IMX6_PCIE_FLAG_IMX6_PHY))
+               return;
+
        pcie_phy_read(imx6_pcie, PHY_RX_OVRD_IN_LO, &tmp);
        tmp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN |
                PHY_RX_OVRD_IN_LO_RX_PLL_EN);
@@ -264,6 +303,7 @@ static void imx6_pcie_reset_phy(struct imx6_pcie *imx6_pcie)
        pcie_phy_write(imx6_pcie, PHY_RX_OVRD_IN_LO, tmp);
 }
 
+#ifdef CONFIG_ARM
 /*  Added for PCI abort handling */
 static int imx6q_pcie_abort_handler(unsigned long addr,
                unsigned int fsr, struct pt_regs *regs)
@@ -297,6 +337,7 @@ static int imx6q_pcie_abort_handler(unsigned long addr,
 
        return 1;
 }
+#endif
 
 static int imx6_pcie_attach_pd(struct device *dev)
 {
@@ -342,8 +383,9 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)
 {
        struct device *dev = imx6_pcie->pci->dev;
 
-       switch (imx6_pcie->variant) {
+       switch (imx6_pcie->drvdata->variant) {
        case IMX7D:
+       case IMX8MQ:
                reset_control_assert(imx6_pcie->pciephy_reset);
                reset_control_assert(imx6_pcie->apps_reset);
                break;
@@ -378,13 +420,20 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)
        }
 }
 
+static unsigned int imx6_pcie_grp_offset(const struct imx6_pcie *imx6_pcie)
+{
+       WARN_ON(imx6_pcie->drvdata->variant != IMX8MQ);
+       return imx6_pcie->controller_id == 1 ? IOMUXC_GPR16 : IOMUXC_GPR14;
+}
+
 static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
 {
        struct dw_pcie *pci = imx6_pcie->pci;
        struct device *dev = pci->dev;
+       unsigned int offset;
        int ret = 0;
 
-       switch (imx6_pcie->variant) {
+       switch (imx6_pcie->drvdata->variant) {
        case IMX6SX:
                ret = clk_prepare_enable(imx6_pcie->pcie_inbound_axi);
                if (ret) {
@@ -412,6 +461,25 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
                break;
        case IMX7D:
                break;
+       case IMX8MQ:
+               ret = clk_prepare_enable(imx6_pcie->pcie_aux);
+               if (ret) {
+                       dev_err(dev, "unable to enable pcie_aux clock\n");
+                       break;
+               }
+
+               offset = imx6_pcie_grp_offset(imx6_pcie);
+               /*
+                * Set the over ride low and enabled
+                * make sure that REF_CLK is turned on.
+                */
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, offset,
+                                  IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE,
+                                  0);
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, offset,
+                                  IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN,
+                                  IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN);
+               break;
        }
 
        return ret;
@@ -487,9 +555,32 @@ static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)
                                        !imx6_pcie->gpio_active_high);
        }
 
-       switch (imx6_pcie->variant) {
+       switch (imx6_pcie->drvdata->variant) {
+       case IMX8MQ:
+               reset_control_deassert(imx6_pcie->pciephy_reset);
+               break;
        case IMX7D:
                reset_control_deassert(imx6_pcie->pciephy_reset);
+
+               /* Workaround for ERR010728, failure of PCI-e PLL VCO to
+                * oscillate, especially when cold.  This turns off "Duty-cycle
+                * Corrector" and other mysterious undocumented things.
+                */
+               if (likely(imx6_pcie->phy_base)) {
+                       /* De-assert DCC_FB_EN */
+                       writel(PCIE_PHY_CMN_REG4_DCC_FB_EN,
+                              imx6_pcie->phy_base + PCIE_PHY_CMN_REG4);
+                       /* Assert RX_EQS and RX_EQS_SEL */
+                       writel(PCIE_PHY_CMN_REG24_RX_EQ_SEL
+                               | PCIE_PHY_CMN_REG24_RX_EQ,
+                              imx6_pcie->phy_base + PCIE_PHY_CMN_REG24);
+                       /* Assert ATT_MODE */
+                       writel(PCIE_PHY_CMN_REG26_ATT_MODE,
+                              imx6_pcie->phy_base + PCIE_PHY_CMN_REG26);
+               } else {
+                       dev_warn(dev, "Unable to apply ERR010728 workaround. DT missing fsl,imx7d-pcie-phy phandle ?\n");
+               }
+
                imx7d_pcie_wait_for_phy_pll_lock(imx6_pcie);
                break;
        case IMX6SX:
@@ -523,9 +614,37 @@ err_pcie_phy:
        }
 }
 
+static void imx6_pcie_configure_type(struct imx6_pcie *imx6_pcie)
+{
+       unsigned int mask, val;
+
+       if (imx6_pcie->drvdata->variant == IMX8MQ &&
+           imx6_pcie->controller_id == 1) {
+               mask   = IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE;
+               val    = FIELD_PREP(IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE,
+                                   PCI_EXP_TYPE_ROOT_PORT);
+       } else {
+               mask = IMX6Q_GPR12_DEVICE_TYPE;
+               val  = FIELD_PREP(IMX6Q_GPR12_DEVICE_TYPE,
+                                 PCI_EXP_TYPE_ROOT_PORT);
+       }
+
+       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, mask, val);
+}
+
 static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
 {
-       switch (imx6_pcie->variant) {
+       switch (imx6_pcie->drvdata->variant) {
+       case IMX8MQ:
+               /*
+                * TODO: Currently this code assumes external
+                * oscillator is being used
+                */
+               regmap_update_bits(imx6_pcie->iomuxc_gpr,
+                                  imx6_pcie_grp_offset(imx6_pcie),
+                                  IMX8MQ_GPR_PCIE_REF_USE_PAD,
+                                  IMX8MQ_GPR_PCIE_REF_USE_PAD);
+               break;
        case IMX7D:
                regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
                                   IMX7D_GPR12_PCIE_PHY_REFCLK_SEL, 0);
@@ -561,8 +680,7 @@ static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
                break;
        }
 
-       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
-                       IMX6Q_GPR12_DEVICE_TYPE, PCI_EXP_TYPE_ROOT_PORT << 12);
+       imx6_pcie_configure_type(imx6_pcie);
 }
 
 static int imx6_setup_phy_mpll(struct imx6_pcie *imx6_pcie)
@@ -571,6 +689,9 @@ static int imx6_setup_phy_mpll(struct imx6_pcie *imx6_pcie)
        int mult, div;
        u32 val;
 
+       if (!(imx6_pcie->drvdata->flags & IMX6_PCIE_FLAG_IMX6_PHY))
+               return 0;
+
        switch (phy_rate) {
        case 125000000:
                /*
@@ -647,7 +768,7 @@ static void imx6_pcie_ltssm_enable(struct device *dev)
 {
        struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev);
 
-       switch (imx6_pcie->variant) {
+       switch (imx6_pcie->drvdata->variant) {
        case IMX6Q:
        case IMX6SX:
        case IMX6QP:
@@ -656,6 +777,7 @@ static void imx6_pcie_ltssm_enable(struct device *dev)
                                   IMX6Q_GPR12_PCIE_CTL_2);
                break;
        case IMX7D:
+       case IMX8MQ:
                reset_control_deassert(imx6_pcie->apps_reset);
                break;
        }
@@ -700,7 +822,8 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
                tmp |= PORT_LOGIC_SPEED_CHANGE;
                dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp);
 
-               if (imx6_pcie->variant != IMX7D) {
+               if (imx6_pcie->drvdata->flags &
+                   IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE) {
                        /*
                         * On i.MX7, DIRECT_SPEED_CHANGE behaves differently
                         * from i.MX6 family when no link speed transition
@@ -797,7 +920,7 @@ static void imx6_pcie_ltssm_disable(struct device *dev)
 {
        struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev);
 
-       switch (imx6_pcie->variant) {
+       switch (imx6_pcie->drvdata->variant) {
        case IMX6SX:
        case IMX6QP:
                regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
@@ -823,7 +946,7 @@ static void imx6_pcie_pm_turnoff(struct imx6_pcie *imx6_pcie)
        }
 
        /* Others poke directly at IOMUXC registers */
-       switch (imx6_pcie->variant) {
+       switch (imx6_pcie->drvdata->variant) {
        case IMX6SX:
                regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
                                IMX6SX_GPR12_PCIE_PM_TURN_OFF,
@@ -853,7 +976,7 @@ static void imx6_pcie_clk_disable(struct imx6_pcie *imx6_pcie)
        clk_disable_unprepare(imx6_pcie->pcie_phy);
        clk_disable_unprepare(imx6_pcie->pcie_bus);
 
-       switch (imx6_pcie->variant) {
+       switch (imx6_pcie->drvdata->variant) {
        case IMX6SX:
                clk_disable_unprepare(imx6_pcie->pcie_inbound_axi);
                break;
@@ -862,6 +985,9 @@ static void imx6_pcie_clk_disable(struct imx6_pcie *imx6_pcie)
                                   IMX7D_GPR12_PCIE_PHY_REFCLK_SEL,
                                   IMX7D_GPR12_PCIE_PHY_REFCLK_SEL);
                break;
+       case IMX8MQ:
+               clk_disable_unprepare(imx6_pcie->pcie_aux);
+               break;
        default:
                break;
        }
@@ -869,8 +995,8 @@ static void imx6_pcie_clk_disable(struct imx6_pcie *imx6_pcie)
 
 static inline bool imx6_pcie_supports_suspend(struct imx6_pcie *imx6_pcie)
 {
-       return (imx6_pcie->variant == IMX7D ||
-               imx6_pcie->variant == IMX6SX);
+       return (imx6_pcie->drvdata->variant == IMX7D ||
+               imx6_pcie->drvdata->variant == IMX6SX);
 }
 
 static int imx6_pcie_suspend_noirq(struct device *dev)
@@ -919,6 +1045,7 @@ static int imx6_pcie_probe(struct platform_device *pdev)
        struct device *dev = &pdev->dev;
        struct dw_pcie *pci;
        struct imx6_pcie *imx6_pcie;
+       struct device_node *np;
        struct resource *dbi_base;
        struct device_node *node = dev->of_node;
        int ret;
@@ -936,8 +1063,24 @@ static int imx6_pcie_probe(struct platform_device *pdev)
        pci->ops = &dw_pcie_ops;
 
        imx6_pcie->pci = pci;
-       imx6_pcie->variant =
-               (enum imx6_pcie_variants)of_device_get_match_data(dev);
+       imx6_pcie->drvdata = of_device_get_match_data(dev);
+
+       /* Find the PHY if one is defined, only imx7d uses it */
+       np = of_parse_phandle(node, "fsl,imx7d-pcie-phy", 0);
+       if (np) {
+               struct resource res;
+
+               ret = of_address_to_resource(np, 0, &res);
+               if (ret) {
+                       dev_err(dev, "Unable to map PCIe PHY\n");
+                       return ret;
+               }
+               imx6_pcie->phy_base = devm_ioremap_resource(dev, &res);
+               if (IS_ERR(imx6_pcie->phy_base)) {
+                       dev_err(dev, "Unable to map PCIe PHY\n");
+                       return PTR_ERR(imx6_pcie->phy_base);
+               }
+       }
 
        dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        pci->dbi_base = devm_ioremap_resource(dev, dbi_base);
@@ -981,7 +1124,7 @@ static int imx6_pcie_probe(struct platform_device *pdev)
                return PTR_ERR(imx6_pcie->pcie);
        }
 
-       switch (imx6_pcie->variant) {
+       switch (imx6_pcie->drvdata->variant) {
        case IMX6SX:
                imx6_pcie->pcie_inbound_axi = devm_clk_get(dev,
                                                           "pcie_inbound_axi");
@@ -990,7 +1133,17 @@ static int imx6_pcie_probe(struct platform_device *pdev)
                        return PTR_ERR(imx6_pcie->pcie_inbound_axi);
                }
                break;
+       case IMX8MQ:
+               imx6_pcie->pcie_aux = devm_clk_get(dev, "pcie_aux");
+               if (IS_ERR(imx6_pcie->pcie_aux)) {
+                       dev_err(dev, "pcie_aux clock source missing or invalid\n");
+                       return PTR_ERR(imx6_pcie->pcie_aux);
+               }
+               /* fall through */
        case IMX7D:
+               if (dbi_base->start == IMX8MQ_PCIE2_BASE_ADDR)
+                       imx6_pcie->controller_id = 1;
+
                imx6_pcie->pciephy_reset = devm_reset_control_get_exclusive(dev,
                                                                            "pciephy");
                if (IS_ERR(imx6_pcie->pciephy_reset)) {
@@ -1087,11 +1240,36 @@ static void imx6_pcie_shutdown(struct platform_device *pdev)
        imx6_pcie_assert_core_reset(imx6_pcie);
 }
 
+static const struct imx6_pcie_drvdata drvdata[] = {
+       [IMX6Q] = {
+               .variant = IMX6Q,
+               .flags = IMX6_PCIE_FLAG_IMX6_PHY |
+                        IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE,
+       },
+       [IMX6SX] = {
+               .variant = IMX6SX,
+               .flags = IMX6_PCIE_FLAG_IMX6_PHY |
+                        IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE,
+       },
+       [IMX6QP] = {
+               .variant = IMX6QP,
+               .flags = IMX6_PCIE_FLAG_IMX6_PHY |
+                        IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE,
+       },
+       [IMX7D] = {
+               .variant = IMX7D,
+       },
+       [IMX8MQ] = {
+               .variant = IMX8MQ,
+       },
+};
+
 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,  },
+       { .compatible = "fsl,imx6q-pcie",  .data = &drvdata[IMX6Q],  },
+       { .compatible = "fsl,imx6sx-pcie", .data = &drvdata[IMX6SX], },
+       { .compatible = "fsl,imx6qp-pcie", .data = &drvdata[IMX6QP], },
+       { .compatible = "fsl,imx7d-pcie",  .data = &drvdata[IMX7D],  },
+       { .compatible = "fsl,imx8mq-pcie", .data = &drvdata[IMX8MQ], } ,
        {},
 };
 
@@ -1108,6 +1286,7 @@ static struct platform_driver imx6_pcie_driver = {
 
 static int __init imx6_pcie_init(void)
 {
+#ifdef CONFIG_ARM
        /*
         * Since probe() can be deferred we need to make sure that
         * hook_fault_code is not called after __init memory is freed
@@ -1117,6 +1296,7 @@ static int __init imx6_pcie_init(void)
         */
        hook_fault_code(8, imx6q_pcie_abort_handler, SIGBUS, 0,
                        "external abort on non-linefetch");
+#endif
 
        return platform_driver_register(&imx6_pcie_driver);
 }