diff options
author | Richard Zhu <r65037@freescale.com> | 2014-02-25 13:01:53 +0800 |
---|---|---|
committer | Nitin Garg <nitin.garg@freescale.com> | 2014-04-16 08:57:53 -0500 |
commit | 5ff1a3f7229cee48911f7a33ceedaade7ac90936 (patch) | |
tree | df9c6266de8dea545d5abf530efc87c7720f1e11 /drivers/pci | |
parent | 01e536e51e7c8ddb7d568bce2402966dbb3797ec (diff) |
ENGR00303325-1 pcie: enable pcie on imx6sx
Enable pcie on imx6sx 19x19 socs.
- imx6sx pcie has its own power domain, add the standalone power
control codes for imx6sx pcie.
- the pcie ref (100mhz differential clock) is routed out and used
by pcie ep on imx6sx pcie.
Signed-off-by: Richard Zhu <r65037@freescale.com>
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/host/pci-imx6.c | 163 |
1 files changed, 136 insertions, 27 deletions
diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index 8741ab67b72d..f0fc4f920b4d 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c @@ -21,6 +21,7 @@ #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> #include <linux/module.h> #include <linux/of_gpio.h> +#include <linux/of_device.h> #include <linux/pci.h> #include <linux/platform_device.h> #include <linux/regmap.h> @@ -28,6 +29,7 @@ #include <linux/signal.h> #include <linux/types.h> #include <linux/busfreq-imx6.h> +#include <linux/regulator/consumer.h> #include "pcie-designware.h" @@ -42,17 +44,32 @@ static u32 ddr_test_region = 0x40000000; static u32 test_region_size = SZ_2M; +/* The pcie who have standalone power domain */ +#define PCIE_PHY_HAS_PWR_DOMAIN BIT(0) + +struct imx_pcie_data { + unsigned int flags; +}; + +static const struct imx_pcie_data imx6sx_pcie_data = { + .flags = PCIE_PHY_HAS_PWR_DOMAIN, +}; + struct imx6_pcie { int reset_gpio; int power_on_gpio; int wake_up_gpio; int disable_gpio; + const struct imx_pcie_data *data; struct clk *lvds_gate; struct clk *sata_ref_100m; struct clk *pcie_ref_125m; struct clk *pcie_axi; + struct clk *dis_axi; struct pcie_port pp; struct regmap *iomuxc_gpr; + struct regmap *gpc_ips_reg; + struct regulator *pcie_regulator; void __iomem *mem_base; }; @@ -78,6 +95,11 @@ 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) +static inline bool is_imx6sx_pcie(struct imx6_pcie *imx6_pcie) +{ + return imx6_pcie->data == &imx6sx_pcie_data; +} + static int pcie_phy_poll_ack(void __iomem *dbi_base, int exp_val) { u32 val; @@ -223,16 +245,27 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp) if (gpio_is_valid(imx6_pcie->power_on_gpio)) gpio_set_value(imx6_pcie->power_on_gpio, 1); - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, - IMX6Q_GPR1_PCIE_TEST_PD, 0 << 18); - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, - IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16); request_bus_freq(BUS_FREQ_HIGH); - ret = clk_prepare_enable(imx6_pcie->sata_ref_100m); - if (ret) { - dev_err(pp->dev, "unable to enable sata_ref_100m\n"); - goto err_sata_ref; + if (is_imx6sx_pcie(imx6_pcie)) { + ret = clk_prepare_enable(imx6_pcie->dis_axi); + if (ret) { + dev_err(pp->dev, "unable to enable dis_axi\n"); + goto err_dis_axi; + } + } else { + /* Those bits are not used anymore on imx6sx */ + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_TEST_PD, 0 << 18); + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16); + + /* sata_ref is not used by pcie on imx6sx */ + ret = clk_prepare_enable(imx6_pcie->sata_ref_100m); + if (ret) { + dev_err(pp->dev, "unable to enable sata_ref_100m\n"); + goto err_sata_ref; + } } ret = clk_prepare_enable(imx6_pcie->pcie_ref_125m); @@ -272,8 +305,12 @@ err_pcie_axi: err_lvds_gate: clk_disable_unprepare(imx6_pcie->pcie_ref_125m); err_pcie_ref: - clk_disable_unprepare(imx6_pcie->sata_ref_100m); + if (!is_imx6sx_pcie(imx6_pcie)) + clk_disable_unprepare(imx6_pcie->sata_ref_100m); err_sata_ref: + if (is_imx6sx_pcie(imx6_pcie)) + clk_disable_unprepare(imx6_pcie->dis_axi); +err_dis_axi: release_bus_freq(BUS_FREQ_HIGH); return ret; @@ -282,7 +319,26 @@ err_sata_ref: static void imx6_pcie_init_phy(struct pcie_port *pp) { struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp); + int ret; + /* + * iMX6SX PCIe has the stand-alone power domain + * add the initialization here for iMX6SX PCIe. + */ + if (is_imx6sx_pcie(imx6_pcie)) { + /* Force PCIe PHY reset */ + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5, + BIT(19), 1 << 19); + + /* Power up PCIe PHY, ANATOP_REG_CORE offset 0x140, bit13-9 */ + regulator_set_voltage(imx6_pcie->pcie_regulator, 1100000, 1100000); + ret = regulator_enable(imx6_pcie->pcie_regulator); + if (ret) + dev_info(pp->dev, "failed to enable pcie regulator.\n"); + + /* Disable PCIe isolation, APIS1_GPC_IPS 0x020D_C000 offset 0 */ + regmap_update_bits(imx6_pcie->gpc_ips_reg, 0, BIT(7), 1 << 7); + } regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX6Q_GPR12_PCIE_CTL_2, 0 << 10); @@ -328,6 +384,17 @@ static void imx6_pcie_host_init(struct pcie_port *pp) imx6_pcie_deassert_core_reset(pp); + /* + * iMX6SX PCIe has the stand-alone power domain. + * refer to the initialization for iMX6SX PCIe, + * release the PCIe PHY reset here, + * before LTSSM enable is set. + */ + if (is_imx6sx_pcie(imx6_pcie)) + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5, + BIT(19), 0 << 19); + + dw_pcie_setup_rc(pp); regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, @@ -587,10 +654,19 @@ static struct attribute_group imx_pcie_attrgroup = { .attrs = imx_pcie_attrs, }; +static const struct of_device_id imx6_pcie_of_match[] = { + { .compatible = "fsl,imx6q-pcie", }, + { .compatible = "fsl,imx6sx-pcie", .data = &imx6sx_pcie_data}, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx6_pcie_of_match); + static int __init imx6_pcie_probe(struct platform_device *pdev) { struct imx6_pcie *imx6_pcie; struct pcie_port *pp; + const struct of_device_id *of_id = + of_match_device(imx6_pcie_of_match, &pdev->dev); struct device_node *np = pdev->dev.of_node; struct resource *dbi_base; int ret, i; @@ -605,6 +681,7 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) pp = &imx6_pcie->pp; pp->dev = &pdev->dev; + imx6_pcie->data = of_id->data; if (IS_ENABLED(CONFIG_EP_MODE_IN_EP_RC_SYS)) { /* add attributes for device */ @@ -687,14 +764,6 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) goto err; } - imx6_pcie->sata_ref_100m = devm_clk_get(&pdev->dev, "sata_ref_100m"); - if (IS_ERR(imx6_pcie->sata_ref_100m)) { - dev_err(&pdev->dev, - "sata_ref_100m clock source missing or invalid\n"); - ret = PTR_ERR(imx6_pcie->sata_ref_100m); - goto err; - } - imx6_pcie->pcie_ref_125m = devm_clk_get(&pdev->dev, "pcie_ref_125m"); if (IS_ERR(imx6_pcie->pcie_ref_125m)) { dev_err(&pdev->dev, @@ -711,9 +780,45 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) goto err; } - /* Grab GPR config register range */ - imx6_pcie->iomuxc_gpr = - syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); + if (is_imx6sx_pcie(imx6_pcie)) { + imx6_pcie->dis_axi = devm_clk_get(&pdev->dev, "display_axi"); + if (IS_ERR(imx6_pcie->dis_axi)) { + dev_err(&pdev->dev, + "dis_axi clock source missing or invalid\n"); + ret = PTR_ERR(imx6_pcie->dis_axi); + goto err; + } + + /* Grab GPC IPS config register range */ + imx6_pcie->gpc_ips_reg = + syscon_regmap_lookup_by_compatible("fsl,imx6q-gpc"); + if (IS_ERR(imx6_pcie->gpc_ips_reg)) { + dev_err(&pdev->dev, "unable to find gpc ips registers\n"); + ret = PTR_ERR(imx6_pcie->gpc_ips_reg); + goto err; + } + + /* Get pcie regulator */ + imx6_pcie->pcie_regulator = devm_regulator_get(pp->dev, "pcie"); + + /* Grab GPR config register range */ + imx6_pcie->iomuxc_gpr = + syscon_regmap_lookup_by_compatible("fsl,imx6sx-iomuxc-gpr"); + } else { + /* sata_ref is not used by pcie on imx6sx */ + imx6_pcie->sata_ref_100m = devm_clk_get(&pdev->dev, "sata_ref_100m"); + if (IS_ERR(imx6_pcie->sata_ref_100m)) { + dev_err(&pdev->dev, + "sata_ref_100m clock source missing or invalid\n"); + ret = PTR_ERR(imx6_pcie->sata_ref_100m); + goto err; + } + + /* Grab GPR config register range */ + imx6_pcie->iomuxc_gpr = + syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); + } + if (IS_ERR(imx6_pcie->iomuxc_gpr)) { dev_err(&pdev->dev, "unable to find iomuxc registers\n"); ret = PTR_ERR(imx6_pcie->iomuxc_gpr); @@ -758,11 +863,21 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) imx6_pcie_deassert_core_reset(pp); + /* + * iMX6SX PCIe has the stand-alone power domain. + * refer to the initialization for iMX6SX PCIe, + * release the PCIe PHY reset here, + * before LTSSM enable is set + * . + */ + if (is_imx6sx_pcie(imx6_pcie)) + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5, + BIT(19), 0 << 19); + /* assert LTSSM enable */ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX6Q_GPR12_PCIE_CTL_2, 1 << 10); - dev_info(&pdev->dev, "PCIe EP: waiting for link up...\n"); platform_set_drvdata(pdev, imx6_pcie); @@ -881,12 +996,6 @@ err: return ret; } -static const struct of_device_id imx6_pcie_of_match[] = { - { .compatible = "fsl,imx6q-pcie", }, - {}, -}; -MODULE_DEVICE_TABLE(of, imx6_pcie_of_match); - static struct platform_driver imx6_pcie_driver = { .driver = { .name = "imx6q-pcie", |