summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Zhu <Richard.Zhu@freescale.com>2015-02-05 16:13:07 +0800
committerRichard Zhu <Richard.Zhu@freescale.com>2015-02-11 15:19:48 +0800
commit334af1b0fdb473a36a42bd0bb638ef2b7e26033f (patch)
treed47dd323763da586de306efdc96ee74a271684da
parent15290d39e1ba88d9c27c69f4691a14681a5cf42d (diff)
MLK-10203-1 pci: imx: enable imx6qdl pcie support
enable imx6qdl pcie on imx_3.14 kernel and add the pcie pm workaround for imx6qdl. ------ 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" WARNING: This is not official workaround for ERR005723. Fortunately, we don't encounter issue with this workaround. User should take own risk to use it. Signed-off-by: Richard Zhu <Richard.Zhu@freescale.com> (cherry picked from commit 4cdd4fb7fb3b3e746b66838f4cdd24fccd67c78d)
-rw-r--r--drivers/pci/host/pci-imx6.c54
1 files changed, 54 insertions, 0 deletions
diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c
index c120ee1052b2..1b53010ea49c 100644
--- a/drivers/pci/host/pci-imx6.c
+++ b/drivers/pci/host/pci-imx6.c
@@ -45,9 +45,11 @@ static u32 ddr_test_region = 0, test_region_size = SZ_2M;
struct imx6_pcie {
int reset_gpio;
+ int power_on_gpio;
struct clk *pcie_bus;
struct clk *pcie_phy;
struct clk *pcie_inbound_axi;
+ struct clk *ref_100m;
struct clk *pcie;
struct pcie_port pp;
struct regmap *iomuxc_gpr;
@@ -292,6 +294,9 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
int ret;
+ if (gpio_is_valid(imx6_pcie->power_on_gpio))
+ gpio_set_value(imx6_pcie->power_on_gpio, 1);
+
request_bus_freq(BUS_FREQ_HIGH);
ret = clk_prepare_enable(imx6_pcie->pcie_phy);
if (ret) {
@@ -327,6 +332,13 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
/* power up core phy and enable ref clock */
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
IMX6Q_GPR1_PCIE_TEST_PD, 0);
+
+ /* sata_ref is not used by pcie on imx6sx */
+ ret = clk_prepare_enable(imx6_pcie->ref_100m);
+ if (ret) {
+ dev_err(pp->dev, "unable to enable ref_100m\n");
+ goto err_inbound_axi;
+ }
/*
* the async reset input need ref clock to sync internally,
* when the ref clock comes after reset, internal synced
@@ -525,6 +537,9 @@ out:
* Power down PCIe PHY.
*/
regulator_disable(imx6_pcie->pcie_phy_regulator);
+ } else {
+ clk_disable_unprepare(imx6_pcie->ref_100m);
+ release_bus_freq(BUS_FREQ_HIGH);
}
} else {
tmp = readl(pp->dbi_base + 0x80);
@@ -901,6 +916,16 @@ static int pci_imx_suspend_noirq(struct device *dev)
clk_disable_unprepare(imx6_pcie->pcie_inbound_axi);
release_bus_freq(BUS_FREQ_HIGH);
}
+ } 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;
@@ -956,6 +981,15 @@ static int pci_imx_resume_noirq(struct device *dev)
if (IS_ENABLED(CONFIG_PCI_MSI))
dw_pcie_msi_cfg_restore(pp);
}
+ } 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;
@@ -1013,6 +1047,18 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
}
}
+ imx6_pcie->power_on_gpio = of_get_named_gpio(np, "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;
+ }
+ }
+
/* Fetch clocks */
imx6_pcie->pcie_phy = devm_clk_get(&pdev->dev, "pcie_phy");
if (IS_ERR(imx6_pcie->pcie_phy)) {
@@ -1051,6 +1097,14 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
syscon_regmap_lookup_by_compatible
("fsl,imx6sx-iomuxc-gpr");
} else {
+ /* sata_ref is not used by pcie on imx6sx */
+ imx6_pcie->ref_100m = devm_clk_get(&pdev->dev, "ref_100m");
+ if (IS_ERR(imx6_pcie->ref_100m)) {
+ dev_err(&pdev->dev,
+ "ref_100m clock source missing or invalid\n");
+ return PTR_ERR(imx6_pcie->ref_100m);
+ }
+
imx6_pcie->iomuxc_gpr =
syscon_regmap_lookup_by_compatible
("fsl,imx6q-iomuxc-gpr");