MLK-11484-4 PCI: imx: enable imx7d pcie support
authorRichard Zhu <Richard.Zhu@freescale.com>
Fri, 21 Aug 2015 05:30:27 +0000 (13:30 +0800)
committerNitin Garg <nitin.garg@nxp.com>
Mon, 19 Mar 2018 19:48:34 +0000 (14:48 -0500)
- enable pcie functions on imx7d platforms
- grst/brst should be asserted/de-asserted during resume,
since the pcie power would be cut off automatically by HW
during system suspend/resume

Signed-off-by: Richard Zhu <Richard.Zhu@freescale.com>
drivers/pci/host/Kconfig
drivers/pci/host/pci-imx6.c

index d7e7c0a..9f087b7 100644 (file)
@@ -62,7 +62,7 @@ config PCI_EXYNOS
 
 config PCI_IMX6
        bool "Freescale i.MX6 PCIe controller"
-       depends on SOC_IMX6Q
+       depends on SOC_IMX6Q || SOC_IMX7D
        depends on PCI_MSI_IRQ_DOMAIN
        select PCIEPORTBUS
        select PCIE_DW
index c8cefb0..2e93007 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,8 @@
 #include <linux/signal.h>
 #include <linux/types.h>
 #include <linux/interrupt.h>
+#include <linux/busfreq-imx.h>
+#include <linux/regulator/consumer.h>
 
 #include "pcie-designware.h"
 
@@ -36,10 +39,13 @@ enum imx6_pcie_variants {
        IMX6Q,
        IMX6SX,
        IMX6QP,
+       IMX7D,
 };
 
 struct imx6_pcie {
        struct pcie_port        pp;     /* pp.dbi_base is DT 0th resource */
+       int                     dis_gpio;
+       int                     power_on_gpio;
        int                     reset_gpio;
        bool                    gpio_active_high;
        struct clk              *pcie_bus;
@@ -54,6 +60,8 @@ struct imx6_pcie {
        u32                     tx_swing_full;
        u32                     tx_swing_low;
        int                     link_gen;
+       struct regmap           *reg_src;
+       struct regulator        *pcie_phy_regulator;
 };
 
 /* PCIe Root Complex registers (memory-mapped) */
@@ -298,6 +306,12 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)
                regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
                                   IMX6Q_GPR1_PCIE_REF_CLK_EN, 0 << 16);
                break;
+       case IMX7D:
+               /* G_RST */
+               regmap_update_bits(imx6_pcie->reg_src, 0x2c, BIT(1), BIT(1));
+               /* BTNRST */
+               regmap_update_bits(imx6_pcie->reg_src, 0x2c, BIT(2), BIT(2));
+               break;
        }
 }
 
@@ -333,17 +347,38 @@ 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 pci_imx_phy_pll_locked(struct imx6_pcie *imx6_pcie)
+{
+       u32 val;
+       int count = 20000;
+
+       while (count--) {
+               regmap_read(imx6_pcie->iomuxc_gpr, IOMUXC_GPR22, &val);
+               if (val & BIT(31))
+                       break;
+               udelay(10);
+               if (count == 0)
+                       pr_info("pcie phy pll can't be locked.\n");
+       }
+}
+
 static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)
 {
        struct pcie_port *pp = &imx6_pcie->pp;
        struct device *dev = pp->dev;
        int ret;
 
+       if (gpio_is_valid(imx6_pcie->power_on_gpio))
+               gpio_set_value_cansleep(imx6_pcie->power_on_gpio, 1);
+
+       request_bus_freq(BUS_FREQ_HIGH);
        ret = clk_prepare_enable(imx6_pcie->pcie_phy);
        if (ret) {
                dev_err(dev, "unable to enable pcie_phy clock\n");
@@ -369,7 +404,7 @@ static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)
        }
 
        /* allow the clocks to stabilize */
-       usleep_range(200, 500);
+       udelay(200);
 
        /* Some boards don't have PCIe reset GPIO. */
        if (gpio_is_valid(imx6_pcie->reset_gpio)) {
@@ -393,6 +428,16 @@ static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)
                break;
        case IMX6Q:             /* Nothing to do */
                break;
+       case IMX7D:
+               /* wait for more than 10us to release phy g_rst and btnrst */
+               udelay(10);
+               regmap_update_bits(imx6_pcie->reg_src, 0x2c, BIT(6), 0);
+               regmap_update_bits(imx6_pcie->reg_src, 0x2c, BIT(1), 0);
+               regmap_update_bits(imx6_pcie->reg_src, 0x2c, BIT(2), 0);
+
+               /* wait for phy pll lock firstly. */
+               pci_imx_phy_pll_locked(imx6_pcie);
+               break;
        }
 
        return;
@@ -407,35 +452,55 @@ err_pcie_bus:
 
 static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
 {
+       int ret;
+
        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);
+       if (imx6_pcie->variant == IMX7D) {
+               /* Enable PCIe PHY 1P0D */
+               regulator_set_voltage(imx6_pcie->pcie_phy_regulator,
+                               1000000, 1000000);
+               ret = regulator_enable(imx6_pcie->pcie_phy_regulator);
+               if (ret)
+                       dev_err(imx6_pcie->pp.dev,
+                               "failed to enable pcie regulator\n");
 
-       /* configure constant input signal to the pcie ctrl and phy */
+               /* pcie phy ref clock select; 1? internal pll : external osc */
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+                               BIT(5), 0);
+       } else {
+               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);
+       }
+
+       /* configure the device type */
        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)
@@ -498,8 +563,11 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
        dw_pcie_writel_rc(pp, 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)
+               regmap_update_bits(imx6_pcie->reg_src, 0x2c, BIT(6), BIT(6));
+       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) {
@@ -611,6 +679,119 @@ static int __init imx6_add_pcie_port(struct imx6_pcie *imx6_pcie,
        return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+/* PM_TURN_OFF */
+static void pci_imx_pm_turn_off(struct imx6_pcie *imx6_pcie)
+{
+       /* PM_TURN_OFF */
+       if (imx6_pcie->variant == IMX7D) {
+               regmap_update_bits(imx6_pcie->reg_src, 0x2c,
+                               BIT(11), BIT(11));
+               regmap_update_bits(imx6_pcie->reg_src, 0x2c,
+                               BIT(11), 0);
+       } else {
+               pr_info("Info: don't support pm_turn_off yet.\n");
+               return;
+       }
+
+       udelay(1000);
+       if (gpio_is_valid(imx6_pcie->reset_gpio))
+               gpio_set_value_cansleep(imx6_pcie->reset_gpio, 0);
+}
+
+static int pci_imx_suspend_noirq(struct device *dev)
+{
+       struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev);
+       struct pcie_port *pp = &imx6_pcie->pp;
+
+       if (IS_ENABLED(CONFIG_PCI_MSI))
+               dw_pcie_msi_cfg_store(pp);
+
+       pci_imx_pm_turn_off(imx6_pcie);
+
+       if (imx6_pcie->variant == IMX7D) {
+               /* Disable clks */
+               clk_disable_unprepare(imx6_pcie->pcie);
+               clk_disable_unprepare(imx6_pcie->pcie_phy);
+               clk_disable_unprepare(imx6_pcie->pcie_bus);
+               /* turn off external osc input */
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+                               BIT(5), BIT(5));
+               release_bus_freq(BUS_FREQ_HIGH);
+
+               /* Power down PCIe PHY. */
+               regulator_disable(imx6_pcie->pcie_phy_regulator);
+               if (gpio_is_valid(imx6_pcie->power_on_gpio))
+                       gpio_set_value_cansleep(imx6_pcie->power_on_gpio, 0);
+       } else {
+               /*
+                * L2 can exit by 'reset' or Inband beacon (from remote EP)
+                * toggling phy_powerdown has same effect as 'inband beacon'
+                * So, toggle bit18 of GPR1, used as a workaround of errata
+                * "PCIe PCIe does not support L2 Power Down"
+                */
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
+                               IMX6Q_GPR1_PCIE_TEST_PD,
+                               IMX6Q_GPR1_PCIE_TEST_PD);
+       }
+
+       return 0;
+}
+
+static int pci_imx_resume_noirq(struct device *dev)
+{
+       int ret = 0;
+       struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev);
+       struct pcie_port *pp = &imx6_pcie->pp;
+
+       if (imx6_pcie->variant == IMX7D) {
+               regmap_update_bits(imx6_pcie->reg_src, 0x2c, BIT(6), 0);
+
+               imx6_pcie_assert_core_reset(imx6_pcie);
+
+               imx6_pcie_init_phy(imx6_pcie);
+
+               imx6_pcie_deassert_core_reset(imx6_pcie);
+
+               /*
+                * controller maybe turn off, re-configure again
+                */
+               dw_pcie_setup_rc(pp);
+
+               if (IS_ENABLED(CONFIG_PCI_MSI))
+                       dw_pcie_msi_cfg_restore(pp);
+
+               /* wait for phy pll lock firstly. */
+               pci_imx_phy_pll_locked(imx6_pcie);
+               regmap_update_bits(imx6_pcie->reg_src, 0x2c, BIT(6), BIT(6));
+
+               ret = imx6_pcie_wait_for_link(imx6_pcie);
+               if (ret < 0)
+                       pr_info("pcie link is down after resume.\n");
+       } else {
+               /*
+                * L2 can exit by 'reset' or Inband beacon (from remote EP)
+                * toggling phy_powerdown has same effect as 'inband beacon'
+                * So, toggle bit18 of GPR1, used as a workaround of errata
+                * "PCIe PCIe does not support L2 Power Down"
+                */
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
+                               IMX6Q_GPR1_PCIE_TEST_PD, 0);
+       }
+
+       return 0;
+}
+
+static const struct dev_pm_ops pci_imx_pm_ops = {
+       .suspend_noirq = pci_imx_suspend_noirq,
+       .resume_noirq = pci_imx_resume_noirq,
+       .freeze_noirq = pci_imx_suspend_noirq,
+       .thaw_noirq = pci_imx_resume_noirq,
+       .poweroff_noirq = pci_imx_suspend_noirq,
+       .restore_noirq = pci_imx_resume_noirq,
+};
+#endif
+
 static int __init imx6_pcie_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -640,6 +821,28 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
                return PTR_ERR(pp->dbi_base);
 
        /* Fetch GPIOs */
+       imx6_pcie->dis_gpio = of_get_named_gpio(node, "disable-gpio", 0);
+       if (gpio_is_valid(imx6_pcie->dis_gpio)) {
+               ret = devm_gpio_request_one(&pdev->dev, imx6_pcie->dis_gpio,
+                                           GPIOF_OUT_INIT_HIGH, "PCIe DIS");
+               if (ret) {
+                       dev_err(&pdev->dev, "unable to get disable gpio\n");
+                       return ret;
+               }
+       }
+
+       imx6_pcie->power_on_gpio = of_get_named_gpio(node, "power-on-gpio", 0);
+       if (gpio_is_valid(imx6_pcie->power_on_gpio)) {
+               ret = devm_gpio_request_one(&pdev->dev,
+                                           imx6_pcie->power_on_gpio,
+                                           GPIOF_OUT_INIT_LOW,
+                                           "PCIe power enable");
+               if (ret) {
+                       dev_err(&pdev->dev, "unable to get power-on gpio\n");
+                       return ret;
+               }
+       }
+
        imx6_pcie->reset_gpio = of_get_named_gpio(node, "reset-gpio", 0);
        imx6_pcie->gpio_active_high = of_property_read_bool(node,
                                                "reset-gpio-active-high");
@@ -685,8 +888,23 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
        }
 
        /* Grab GPR config register range */
-       imx6_pcie->iomuxc_gpr =
+       if (imx6_pcie->variant == IMX7D) {
+               imx6_pcie->iomuxc_gpr =
+                        syscon_regmap_lookup_by_compatible
+                        ("fsl,imx7d-iomuxc-gpr");
+               imx6_pcie->reg_src =
+                        syscon_regmap_lookup_by_compatible("fsl,imx7d-src");
+               if (IS_ERR(imx6_pcie->reg_src)) {
+                       dev_err(&pdev->dev,
+                               "imx7d pcie phy src missing or invalid\n");
+                       return PTR_ERR(imx6_pcie->reg_src);
+               }
+               imx6_pcie->pcie_phy_regulator = devm_regulator_get(pp->dev,
+                               "pcie-phy");
+       } else {
+               imx6_pcie->iomuxc_gpr =
                 syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
+       }
        if (IS_ERR(imx6_pcie->iomuxc_gpr)) {
                dev_err(dev, "unable to find iomuxc registers\n");
                return PTR_ERR(imx6_pcie->iomuxc_gpr);
@@ -739,6 +957,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, },
        {},
 };
 
@@ -746,6 +965,7 @@ static struct platform_driver imx6_pcie_driver = {
        .driver = {
                .name   = "imx6q-pcie",
                .of_match_table = imx6_pcie_of_match,
+               .pm = &pci_imx_pm_ops,
        },
        .shutdown = imx6_pcie_shutdown,
 };