Message ID | 1413445963-24706-10-git-send-email-richard.zhu@freescale.com (mailing list archive) |
---|---|
State | New, archived |
Delegated to: | Bjorn Helgaas |
Headers | show |
Am Donnerstag, den 16.10.2014, 15:52 +0800 schrieb Richard Zhu: > From: Richard Zhu <r65037@freescale.com> > > - imx6sx pcie has its own standalone pcie power supply. > In order to turn on the imx6sx pcie power during > initialization. Add the pcie regulator and the gpc regmap > into the imx6sx pcie structure. > - imx6sx pcie has the new added reset mechanism, add the > reset operations into the initialization. > - register one PM call-back, enter/exit L2 state during > system suspend/resume. > use noirq pm_ops instead of the general pm_ops in dev_pm_ops, > since cfg read/write may occurs after suspend and before resume. > do msi store/re-store in suspend/resume callbacks, since > controller maybe turned off, and these msi cfg maybe lost > in suspend. > - disp_axi clock is required by pcie inbound axi port actually. > Add one more clock named pcie_inbound_axi for imx6sx pcie. > - host init maybe failed, return negative value when > there is a failure in the host init. > > Signed-off-by: Richard Zhu <richard.zhu@freescale.com> > --- > drivers/pci/host/pci-imx6.c | 204 +++++++++++++++++++++++++++++++++++++++----- > 1 file changed, 183 insertions(+), 21 deletions(-) > [...] > > +#ifdef CONFIG_PM_SLEEP > +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_imx6sx_pcie(imx6_pcie)) { > + 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); > + } > + > + return 0; > +} > + > +static int pci_imx_resume_noirq(struct device *dev) > +{ > + 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); > + > + /* 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); > + > + /* RESET EP */ > + gpio_set_value(imx6_pcie->reset_gpio, 0); > + udelay(10); You are violating the spec here: "When PERST# is asserted it must remain active for a minimum of 100 us (Tperst)." Also you probably want to put the EP in reset at suspend and only release the reset in the resume path. > + gpio_set_value(imx6_pcie->reset_gpio, 1); > + } > + > + 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 imx6_pcie *imx6_pcie; > @@ -610,9 +751,28 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) > return PTR_ERR(imx6_pcie->pcie); > } > > - /* 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->pcie_inbound_axi = devm_clk_get(&pdev->dev, > + "pcie_inbound_axi"); > + if (IS_ERR(imx6_pcie->pcie_inbound_axi)) { > + dev_err(&pdev->dev, > + "pcie clock source missing or invalid\n"); > + return PTR_ERR(imx6_pcie->pcie_inbound_axi); > + } > + > + imx6_pcie->pcie_phy_regulator = devm_regulator_get(pp->dev, > + "pcie-phy"); > + > + 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 > + ("fsl,imx6q-iomuxc-gpr"); > + } > if (IS_ERR(imx6_pcie->iomuxc_gpr)) { > dev_err(&pdev->dev, "unable to find iomuxc registers\n"); > return PTR_ERR(imx6_pcie->iomuxc_gpr); > @@ -636,6 +796,7 @@ static void imx6_pcie_shutdown(struct platform_device *pdev) > > static const struct of_device_id imx6_pcie_of_match[] = { > { .compatible = "fsl,imx6q-pcie", }, > + { .compatible = "fsl,imx6sx-pcie", }, > {}, > }; > MODULE_DEVICE_TABLE(of, imx6_pcie_of_match); > @@ -645,6 +806,7 @@ static struct platform_driver imx6_pcie_driver = { > .name = "imx6q-pcie", > .owner = THIS_MODULE, > .of_match_table = imx6_pcie_of_match, > + .pm = &pci_imx_pm_ops, > }, > .shutdown = imx6_pcie_shutdown, > }; -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
> -----Original Message----- > From: Lucas Stach [mailto:l.stach@pengutronix.de] > Sent: Thursday, October 16, 2014 6:54 PM > To: Richard Zhu > Cc: linux-pci@vger.kernel.org; Guo Shawn-R65073; festevam@gmail.com; > tharvey@gateworks.com; Zhu Richard-R65037 > Subject: Re: [PATCH v6 09/13] PCI: imx6: Add imx6sx pcie support > > Am Donnerstag, den 16.10.2014, 15:52 +0800 schrieb Richard Zhu: > > From: Richard Zhu <r65037@freescale.com> > > > > - imx6sx pcie has its own standalone pcie power supply. > > In order to turn on the imx6sx pcie power during initialization. Add > > the pcie regulator and the gpc regmap into the imx6sx pcie structure. > > - imx6sx pcie has the new added reset mechanism, add the reset > > operations into the initialization. > > - register one PM call-back, enter/exit L2 state during system > > suspend/resume. > > use noirq pm_ops instead of the general pm_ops in dev_pm_ops, since > > cfg read/write may occurs after suspend and before resume. > > do msi store/re-store in suspend/resume callbacks, since controller > > maybe turned off, and these msi cfg maybe lost in suspend. > > - disp_axi clock is required by pcie inbound axi port actually. > > Add one more clock named pcie_inbound_axi for imx6sx pcie. > > - host init maybe failed, return negative value when there is a > > failure in the host init. > > > > Signed-off-by: Richard Zhu <richard.zhu@freescale.com> > > --- > > drivers/pci/host/pci-imx6.c | 204 > > +++++++++++++++++++++++++++++++++++++++----- > > 1 file changed, 183 insertions(+), 21 deletions(-) > > > [...] > > > > +#ifdef CONFIG_PM_SLEEP > > +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_imx6sx_pcie(imx6_pcie)) { > > + 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); > > + } > > + > > + return 0; > > +} > > + > > +static int pci_imx_resume_noirq(struct device *dev) { > > + 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); > > + > > + /* 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); > > + > > + /* RESET EP */ > > + gpio_set_value(imx6_pcie->reset_gpio, 0); > > + udelay(10); > > You are violating the spec here: "When PERST# is asserted it must remain > active for a minimum of 100 us (Tperst)." Also you probably want to put the EP > in reset at suspend and only release the reset in the resume path. > [Richard] Oops, sorry. Would be changed immediately. Assert the PERST# in suspend, and de-assert it in resume. Thanks a lot for your detailed review. > > + gpio_set_value(imx6_pcie->reset_gpio, 1); > > + } > > + > > + 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 imx6_pcie *imx6_pcie; > > @@ -610,9 +751,28 @@ static int __init imx6_pcie_probe(struct > platform_device *pdev) > > return PTR_ERR(imx6_pcie->pcie); > > } > > > > - /* 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->pcie_inbound_axi = devm_clk_get(&pdev->dev, > > + "pcie_inbound_axi"); > > + if (IS_ERR(imx6_pcie->pcie_inbound_axi)) { > > + dev_err(&pdev->dev, > > + "pcie clock source missing or invalid\n"); > > + return PTR_ERR(imx6_pcie->pcie_inbound_axi); > > + } > > + > > + imx6_pcie->pcie_phy_regulator = devm_regulator_get(pp->dev, > > + "pcie-phy"); > > + > > + 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 > > + ("fsl,imx6q-iomuxc-gpr"); > > + } > > if (IS_ERR(imx6_pcie->iomuxc_gpr)) { > > dev_err(&pdev->dev, "unable to find iomuxc registers\n"); > > return PTR_ERR(imx6_pcie->iomuxc_gpr); @@ -636,6 +796,7 @@ static > > void imx6_pcie_shutdown(struct platform_device *pdev) > > > > static const struct of_device_id imx6_pcie_of_match[] = { > > { .compatible = "fsl,imx6q-pcie", }, > > + { .compatible = "fsl,imx6sx-pcie", }, > > {}, > > }; > > MODULE_DEVICE_TABLE(of, imx6_pcie_of_match); @@ -645,6 +806,7 @@ > > static struct platform_driver imx6_pcie_driver = { > > .name = "imx6q-pcie", > > .owner = THIS_MODULE, > > .of_match_table = imx6_pcie_of_match, > > + .pm = &pci_imx_pm_ops, > > }, > > .shutdown = imx6_pcie_shutdown, > > }; > Best Regards Richard Zhu
diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index eac96fb..dfe2334 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c @@ -22,6 +22,7 @@ #include <linux/pci.h> #include <linux/platform_device.h> #include <linux/regmap.h> +#include <linux/regulator/consumer.h> #include <linux/resource.h> #include <linux/signal.h> #include <linux/types.h> @@ -35,9 +36,12 @@ struct imx6_pcie { int reset_gpio; struct clk *pcie_bus; struct clk *pcie_phy; + struct clk *pcie_inbound_axi; 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; }; @@ -77,6 +81,18 @@ 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; + struct device_node *np = pp->dev->of_node; + + return of_device_is_compatible(np, "fsl,imx6sx-pcie"); +} + static int pcie_phy_poll_ack(void __iomem *dbi_base, int exp_val) { u32 val; @@ -275,18 +291,30 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp) goto err_pcie; } - /* power up core phy and enable ref clock */ - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, - IMX6Q_GPR1_PCIE_TEST_PD, 0 << 18); - /* - * the async reset input need ref clock to sync internally, - * when the ref clock comes after reset, internal synced - * reset time is too short , cannot meet the requirement. - * add one ~10us delay here. - */ - udelay(10); - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, - IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16); + if (is_imx6sx_pcie(imx6_pcie)) { + ret = clk_prepare_enable(imx6_pcie->pcie_inbound_axi); + if (ret) { + dev_err(pp->dev, "unable to enable pcie clock\n"); + goto err_inbound_axi; + } + + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX6SX_GPR12_PCIE_TEST_PD, 0); + } else { + /* power up core phy and enable ref clock */ + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_TEST_PD, 0); + /* + * the async reset input need ref clock to sync internally, + * when the ref clock comes after reset, internal synced + * reset time is too short , cannot meet the requirement. + * add one ~10us delay here. + */ + udelay(10); + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_REF_CLK_EN, + IMX6Q_GPR1_PCIE_REF_CLK_EN); + } /* allow the clocks to stabilize */ usleep_range(200, 500); @@ -297,8 +325,19 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp) msleep(100); gpio_set_value(imx6_pcie->reset_gpio, 1); } + + /* + * Release the PCIe PHY reset here, that we have set in + * imx6_pcie_init_phy() now + */ + if (is_imx6sx_pcie(imx6_pcie)) + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5, + IMX6SX_GPR5_PCIE_BTNRST, 0); + return 0; +err_inbound_axi: + clk_disable_unprepare(imx6_pcie->pcie); err_pcie: clk_disable_unprepare(imx6_pcie->pcie_bus); err_pcie_bus: @@ -308,9 +347,31 @@ err_pcie_phy: } -static void imx6_pcie_init_phy(struct pcie_port *pp) +static int imx6_pcie_init_phy(struct pcie_port *pp) { struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp); + int ret; + + /* Power up the separate domain available on i.MX6SX */ + if (is_imx6sx_pcie(imx6_pcie)) { + /* Force PCIe PHY reset */ + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5, + 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); + if (ret) { + dev_err(pp->dev, "failed to enable pcie regulator.\n"); + return ret; + } + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX6SX_GPR12_RX_EQ_MASK, IMX6SX_GPR12_RX_EQ_2); + } regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX6Q_GPR12_PCIE_CTL_2, 0 << 10); @@ -319,7 +380,7 @@ static void imx6_pcie_init_phy(struct pcie_port *pp) 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); + IMX6Q_GPR12_LOS_LEVEL, IMX6Q_GPR12_LOS_LEVEL_9); regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, IMX6Q_GPR8_TX_DEEMPH_GEN1, 0 << 0); @@ -331,6 +392,8 @@ static void imx6_pcie_init_phy(struct pcie_port *pp) IMX6Q_GPR8_TX_SWING_FULL, 127 << 18); regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, IMX6Q_GPR8_TX_SWING_LOW, 127 << 25); + + return 0; } static int imx6_pcie_wait_for_link(struct pcie_port *pp) @@ -377,7 +440,8 @@ static int imx6_pcie_start_link(struct pcie_port *pp) /* Start LTSSM. */ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, - IMX6Q_GPR12_PCIE_CTL_2, 1 << 10); + IMX6Q_GPR12_PCIE_CTL_2, + IMX6Q_GPR12_PCIE_CTL_2); ret = imx6_pcie_wait_for_link(pp); if (ret) @@ -422,13 +486,19 @@ static int imx6_pcie_start_link(struct pcie_port *pp) return ret; } -static void imx6_pcie_host_init(struct pcie_port *pp) +static int imx6_pcie_host_init(struct pcie_port *pp) { + int ret; + imx6_pcie_assert_core_reset(pp); - imx6_pcie_init_phy(pp); + ret = imx6_pcie_init_phy(pp); + if (ret < 0) + return ret; - imx6_pcie_deassert_core_reset(pp); + ret = imx6_pcie_deassert_core_reset(pp); + if (ret < 0) + return ret; dw_pcie_setup_rc(pp); @@ -436,6 +506,8 @@ static void imx6_pcie_host_init(struct pcie_port *pp) if (IS_ENABLED(CONFIG_PCI_MSI)) dw_pcie_msi_init(pp); + + return 0; } static void imx6_pcie_reset_phy(struct pcie_port *pp) @@ -553,6 +625,75 @@ static int __init imx6_add_pcie_port(struct pcie_port *pp, return 0; } +#ifdef CONFIG_PM_SLEEP +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_imx6sx_pcie(imx6_pcie)) { + 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); + } + + return 0; +} + +static int pci_imx_resume_noirq(struct device *dev) +{ + 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); + + /* 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); + + /* RESET EP */ + gpio_set_value(imx6_pcie->reset_gpio, 0); + udelay(10); + gpio_set_value(imx6_pcie->reset_gpio, 1); + } + + 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 imx6_pcie *imx6_pcie; @@ -610,9 +751,28 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) return PTR_ERR(imx6_pcie->pcie); } - /* 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->pcie_inbound_axi = devm_clk_get(&pdev->dev, + "pcie_inbound_axi"); + if (IS_ERR(imx6_pcie->pcie_inbound_axi)) { + dev_err(&pdev->dev, + "pcie clock source missing or invalid\n"); + return PTR_ERR(imx6_pcie->pcie_inbound_axi); + } + + imx6_pcie->pcie_phy_regulator = devm_regulator_get(pp->dev, + "pcie-phy"); + + 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 + ("fsl,imx6q-iomuxc-gpr"); + } if (IS_ERR(imx6_pcie->iomuxc_gpr)) { dev_err(&pdev->dev, "unable to find iomuxc registers\n"); return PTR_ERR(imx6_pcie->iomuxc_gpr); @@ -636,6 +796,7 @@ static void imx6_pcie_shutdown(struct platform_device *pdev) static const struct of_device_id imx6_pcie_of_match[] = { { .compatible = "fsl,imx6q-pcie", }, + { .compatible = "fsl,imx6sx-pcie", }, {}, }; MODULE_DEVICE_TABLE(of, imx6_pcie_of_match); @@ -645,6 +806,7 @@ static struct platform_driver imx6_pcie_driver = { .name = "imx6q-pcie", .owner = THIS_MODULE, .of_match_table = imx6_pcie_of_match, + .pm = &pci_imx_pm_ops, }, .shutdown = imx6_pcie_shutdown, };