]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
MLK-10058-4 pci: imx6: refine imx6sx pcie pm
authorRichard Zhu <Richard.Zhu@freescale.com>
Thu, 25 Dec 2014 06:36:44 +0000 (14:36 +0800)
committerRichard Zhu <Richard.Zhu@freescale.com>
Mon, 19 Jan 2015 01:48:42 +0000 (09:48 +0800)
- Regarding to the pcie design on imx6sx, some gpc
operations are mandatory pre-required when pcie phy
is powered on/off.
In order to DO NOT touch gpc module in pcie driver,
register one pcie phy regulator into gpc, encapsulate
the pcie phy power on/off pre gpc related operations into
regulator's notify and contained in gpc driver
- in order to save power consumption, disable pcie clks and phy
regulator if the pcie link is down.
- remove the PRST set/unset in suspend/resume, because that
usb hub would be reset, and the name of the dev node of the
thumb disk inserted in the port of the pcie2usb device,
would be changed randomly after suspend resume on imx6sx.
- add the extremely power save mode

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

index 71ddbfd71a99587e79860a83e2d47dec1fb4b585..bf9b8493c30e3da2b347e476bf7d6f9ad6843d55 100644 (file)
@@ -30,6 +30,16 @@ config PCI_IMX6
        select PCIEPORTBUS
        select PCIE_DW
 
+config PCI_IMX6SX_EXTREMELY_PWR_SAVE
+       bool "Freescale i.MX6SX PCIe controller extremely power save mode"
+       depends on PCI_IMX6
+       help
+         If you want extremely power save on iMX6SX PCIe, you can enable
+         the ultra low power mode during suspend/resume here. Note: iMX6SX
+         PCIe maybe powered off compeltely during suspend, and would be
+         re-initialized again during resume. Some behaviors of the PCIe EP
+         device would be impacted.
+
 config EP_MODE_IN_EP_RC_SYS
        bool "PCI Express EP mode in the IMX6 RC/EP interconnection system"
        depends on PCI_IMX6
index 3f063455213763684a8e26de2c4fc9d78ae71889..c120ee1052b29af1fc89913b7925e9cc37c7a4bc 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * PCIe host controller driver for Freescale i.MX6 SoCs
  *
- * Copyright (C) 2014 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2014-2015 Freescale Semiconductor, Inc. All Rights Reserved.
  * Copyright (C) 2013 Kosagi
  *             http://www.kosagi.com
  *
@@ -26,6 +26,7 @@
 #include <linux/pci_regs.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
+#include <linux/busfreq-imx6.h>
 #include <linux/regulator/consumer.h>
 
 #include "pcie-designware.h"
@@ -50,7 +51,6 @@ struct imx6_pcie {
        struct clk              *pcie;
        struct pcie_port        pp;
        struct regmap           *iomuxc_gpr;
-       struct regmap           *gpc_ips_reg;
        struct regulator        *pcie_phy_regulator;
        void __iomem            *mem_base;
 };
@@ -91,10 +91,6 @@ struct imx6_pcie {
 #define PHY_RX_OVRD_IN_LO_RX_DATA_EN (1 << 5)
 #define PHY_RX_OVRD_IN_LO_RX_PLL_EN (1 << 3)
 
-/* GPC PCIE PHY bit definitions */
-#define GPC_CNTR                       0
-#define GPC_CNTR_PCIE_PHY_PUP_REQ      BIT(7)
-
 static inline bool is_imx6sx_pcie(struct imx6_pcie *imx6_pcie)
 {
        struct pcie_port *pp = &imx6_pcie->pp;
@@ -296,6 +292,7 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
        struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
        int ret;
 
+       request_bus_freq(BUS_FREQ_HIGH);
        ret = clk_prepare_enable(imx6_pcie->pcie_phy);
        if (ret) {
                dev_err(pp->dev, "unable to enable pcie_phy clock\n");
@@ -343,12 +340,12 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
        }
 
        /* 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)) {
                gpio_set_value(imx6_pcie->reset_gpio, 0);
-               msleep(100);
+               mdelay(100);
                gpio_set_value(imx6_pcie->reset_gpio, 1);
        }
 
@@ -387,9 +384,6 @@ static int imx6_pcie_init_phy(struct pcie_port *pp)
                                IMX6SX_GPR5_PCIE_BTNRST,
                                IMX6SX_GPR5_PCIE_BTNRST);
 
-               regmap_update_bits(imx6_pcie->gpc_ips_reg, GPC_CNTR,
-                               GPC_CNTR_PCIE_PHY_PUP_REQ,
-                               GPC_CNTR_PCIE_PHY_PUP_REQ);
                regulator_set_voltage(imx6_pcie->pcie_phy_regulator,
                                1100000, 1100000);
                ret = regulator_enable(imx6_pcie->pcie_phy_regulator);
@@ -435,7 +429,7 @@ static int imx6_pcie_wait_for_link(struct pcie_port *pp)
        int count = 200;
 
        while (!dw_pcie_link_up(pp)) {
-               usleep_range(100, 1000);
+               udelay(100);
                if (--count)
                        continue;
 
@@ -479,7 +473,7 @@ static int imx6_pcie_start_link(struct pcie_port *pp)
 
        ret = imx6_pcie_wait_for_link(pp);
        if (ret)
-               return ret;
+               goto out;
 
        /* Allow Gen2 mode after the link is up. */
        tmp = readl(pp->dbi_base + PCIE_RC_LCR);
@@ -501,7 +495,7 @@ static int imx6_pcie_start_link(struct pcie_port *pp)
                /* Test if the speed change finished. */
                if (!(tmp & PORT_LOGIC_SPEED_CHANGE))
                        break;
-               usleep_range(100, 1000);
+               udelay(100);
        }
 
        /* Make sure link training is finished as well! */
@@ -510,8 +504,28 @@ static int imx6_pcie_start_link(struct pcie_port *pp)
        else
                ret = -EINVAL;
 
+out:
        if (ret) {
                dev_err(pp->dev, "Failed to bring link up!\n");
+               clk_disable_unprepare(imx6_pcie->pcie);
+               if (!IS_ENABLED(CONFIG_EP_MODE_IN_EP_RC_SYS)
+                       && !IS_ENABLED(CONFIG_RC_MODE_IN_EP_RC_SYS))
+                       clk_disable_unprepare(imx6_pcie->pcie_bus);
+               clk_disable_unprepare(imx6_pcie->pcie_phy);
+               if (is_imx6sx_pcie(imx6_pcie)) {
+                       /* Disable clks and power down PCIe PHY */
+                       clk_disable_unprepare(imx6_pcie->pcie_inbound_axi);
+                       release_bus_freq(BUS_FREQ_HIGH);
+
+                       /* Put PCIe PHY to be isolation */
+                       regmap_update_bits(imx6_pcie->iomuxc_gpr,
+                                       IOMUXC_GPR0, BIT(6), 1 << 6);
+
+                       /*
+                        * Power down PCIe PHY.
+                        */
+                       regulator_disable(imx6_pcie->pcie_phy_regulator);
+               }
        } else {
                tmp = readl(pp->dbi_base + 0x80);
                dev_dbg(pp->dev, "Link up, Gen=%i\n", (tmp >> 16) & 0xf);
@@ -536,7 +550,9 @@ static int imx6_pcie_host_init(struct pcie_port *pp)
 
        dw_pcie_setup_rc(pp);
 
-       imx6_pcie_start_link(pp);
+       ret = imx6_pcie_start_link(pp);
+       if (ret < 0)
+               return ret;
 
        if (IS_ENABLED(CONFIG_PCI_MSI))
                dw_pcie_msi_init(pp);
@@ -553,7 +569,7 @@ static void imx6_pcie_reset_phy(struct pcie_port *pp)
                 PHY_RX_OVRD_IN_LO_RX_PLL_EN);
        pcie_phy_write(pp->dbi_base, PHY_RX_OVRD_IN_LO, temp);
 
-       usleep_range(2000, 3000);
+       udelay(2000);
 
        pcie_phy_read(pp->dbi_base, PHY_RX_OVRD_IN_LO, &temp);
        temp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN |
@@ -564,7 +580,7 @@ static void imx6_pcie_reset_phy(struct pcie_port *pp)
 static int imx6_pcie_link_up(struct pcie_port *pp)
 {
        u32 rc, debug_r0, rx_valid;
-       int count = 5;
+       int count = 500;
 
        /*
         * Test if the PHY reports that the link is up and also that the LTSSM
@@ -595,7 +611,7 @@ static int imx6_pcie_link_up(struct pcie_port *pp)
                 * Wait a little bit, then re-check if the link finished
                 * the training.
                 */
-               usleep_range(1000, 2000);
+               udelay(10);
        }
        /*
         * From L0, initiate MAC entry to gen2 if EP/RC supports gen2.
@@ -853,21 +869,38 @@ static int pci_imx_suspend_noirq(struct device *dev)
                if (IS_ENABLED(CONFIG_PCI_MSI))
                        dw_pcie_msi_cfg_store(pp);
 
-               /* PM_TURN_OFF */
-               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
-                               IMX6SX_GPR12_PCIE_PM_TURN_OFF,
-                               IMX6SX_GPR12_PCIE_PM_TURN_OFF);
-               udelay(10);
-               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
-                               IMX6SX_GPR12_PCIE_PM_TURN_OFF, 0);
-               clk_disable_unprepare(imx6_pcie->pcie);
-               clk_disable_unprepare(imx6_pcie->pcie_bus);
-               clk_disable_unprepare(imx6_pcie->pcie_phy);
-               clk_disable_unprepare(imx6_pcie->pcie_inbound_axi);
-
-               /* Assert per-reset to ep */
-               if (gpio_is_valid(imx6_pcie->reset_gpio))
-                       gpio_set_value(imx6_pcie->reset_gpio, 0);
+               if (IS_ENABLED(CONFIG_PCI_IMX6SX_EXTREMELY_PWR_SAVE)) {
+                       /* Disable clks and power down PCIe PHY */
+                       clk_disable_unprepare(imx6_pcie->pcie);
+                       if (!IS_ENABLED(CONFIG_EP_MODE_IN_EP_RC_SYS)
+                               && !IS_ENABLED(CONFIG_RC_MODE_IN_EP_RC_SYS))
+                               clk_disable_unprepare(imx6_pcie->pcie_bus);
+                       clk_disable_unprepare(imx6_pcie->pcie_phy);
+                       clk_disable_unprepare(imx6_pcie->pcie_inbound_axi);
+                       release_bus_freq(BUS_FREQ_HIGH);
+
+                       /* Put PCIe PHY to be isolation */
+                       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR0,
+                                       BIT(6), 1 << 6);
+
+                       /*
+                        * Power down PCIe PHY.
+                        */
+                       regulator_disable(imx6_pcie->pcie_phy_regulator);
+               } else {
+                       /* PM_TURN_OFF */
+                       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+                                       IMX6SX_GPR12_PCIE_PM_TURN_OFF,
+                                       IMX6SX_GPR12_PCIE_PM_TURN_OFF);
+                       udelay(10);
+                       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+                                       IMX6SX_GPR12_PCIE_PM_TURN_OFF, 0);
+                       clk_disable_unprepare(imx6_pcie->pcie);
+                       clk_disable_unprepare(imx6_pcie->pcie_bus);
+                       clk_disable_unprepare(imx6_pcie->pcie_phy);
+                       clk_disable_unprepare(imx6_pcie->pcie_inbound_axi);
+                       release_bus_freq(BUS_FREQ_HIGH);
+               }
        }
 
        return 0;
@@ -875,31 +908,54 @@ static int pci_imx_suspend_noirq(struct device *dev)
 
 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 (is_imx6sx_pcie(imx6_pcie)) {
-               clk_prepare_enable(imx6_pcie->pcie_inbound_axi);
-               clk_prepare_enable(imx6_pcie->pcie_bus);
-               clk_prepare_enable(imx6_pcie->pcie_phy);
-               clk_prepare_enable(imx6_pcie->pcie);
+               if (IS_ENABLED(CONFIG_PCI_IMX6SX_EXTREMELY_PWR_SAVE)) {
+                       imx6_pcie_assert_core_reset(pp);
 
-               /* Reset iMX6SX PCIe */
-               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5,
-                               IMX6SX_GPR5_PCIE_PERST, IMX6SX_GPR5_PCIE_PERST);
-               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5,
-                               IMX6SX_GPR5_PCIE_PERST, 0);
-               /*
-                * controller maybe turn off, re-configure again
-                */
-               dw_pcie_setup_rc(pp);
+                       ret = imx6_pcie_init_phy(pp);
+                       if (ret < 0)
+                               return ret;
 
-               if (IS_ENABLED(CONFIG_PCI_MSI))
-                       dw_pcie_msi_cfg_restore(pp);
+                       ret = imx6_pcie_deassert_core_reset(pp);
+                       if (ret < 0)
+                               return ret;
+
+                       if (IS_ENABLED(CONFIG_EP_MODE_IN_EP_RC_SYS))
+                               imx6_pcie_setup_ep(pp);
+                       else
+                               dw_pcie_setup_rc(pp);
+
+                       if (IS_ENABLED(CONFIG_PCI_MSI))
+                               dw_pcie_msi_cfg_restore(pp);
+
+                       ret = imx6_pcie_start_link(pp);
+                       if (ret < 0)
+                               return ret;
+               } else {
+                       request_bus_freq(BUS_FREQ_HIGH);
+                       clk_prepare_enable(imx6_pcie->pcie_inbound_axi);
+                       clk_prepare_enable(imx6_pcie->pcie_bus);
+                       clk_prepare_enable(imx6_pcie->pcie_phy);
+                       clk_prepare_enable(imx6_pcie->pcie);
 
-               /* De-assert per-reset to ep */
-               if (gpio_is_valid(imx6_pcie->reset_gpio))
-                       gpio_set_value(imx6_pcie->reset_gpio, 1);
+                       /* Reset iMX6SX PCIe */
+                       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5,
+                                       IMX6SX_GPR5_PCIE_PERST,
+                                       IMX6SX_GPR5_PCIE_PERST);
+                       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5,
+                                       IMX6SX_GPR5_PCIE_PERST, 0);
+                       /*
+                        * controller maybe turn off, re-configure again
+                        */
+                       dw_pcie_setup_rc(pp);
+
+                       if (IS_ENABLED(CONFIG_PCI_MSI))
+                               dw_pcie_msi_cfg_restore(pp);
+               }
        }
 
        return 0;
@@ -994,8 +1050,6 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
                imx6_pcie->iomuxc_gpr =
                         syscon_regmap_lookup_by_compatible
                         ("fsl,imx6sx-iomuxc-gpr");
-               imx6_pcie->gpc_ips_reg =
-                        syscon_regmap_lookup_by_compatible("fsl,imx6sx-gpc");
        } else {
                imx6_pcie->iomuxc_gpr =
                        syscon_regmap_lookup_by_compatible