diff options
Diffstat (limited to 'drivers/pci/host/pci-imx6.c')
-rw-r--r-- | drivers/pci/host/pci-imx6.c | 160 |
1 files changed, 107 insertions, 53 deletions
diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index 3f0634552137..c120ee1052b2 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c @@ -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 |