Re: [PATCH v2 3/3] PCI: imx: Initial imx7d pm support

From: Lucas Stach
Date: Mon Jul 23 2018 - 05:38:18 EST


Hi Leonard,

Am Freitag, den 20.07.2018, 15:47 +0300 schrieb Leonard Crestez:
> On imx7d the pcie-phy power domain is turned off in suspend and this can
> make the system hang after resume when attempting any read from PCI.
>
> Fix this by adding minimal suspend/resume code from the nxp internal
> tree. This will prepare for powering down on suspend and reset the block
> on resume.
>
> Code is only for imx7d but a very similar sequence can be used for
> other socs.
>
> > The original author is mostly Richard Zhu <hongxing.zhu@xxxxxxx>, this
> patch adjusts the code to the upstream imx7d implemention using reset
> controls and power domains.
>
> > Signed-off-by: Leonard Crestez <leonard.crestez@xxxxxxx>
> ---
> Âdrivers/pci/controller/dwc/pci-imx6.c | 95 +++++++++++++++++++++++++--
> Â1 file changed, 90 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
> index 80f604602783..daebee905108 100644
> --- a/drivers/pci/controller/dwc/pci-imx6.c
> +++ b/drivers/pci/controller/dwc/pci-imx6.c
> @@ -540,10 +540,27 @@ static int imx6_pcie_wait_for_speed_change(struct imx6_pcie *imx6_pcie)
> Â
> > Â dev_err(dev, "Speed change timeout\n");
> > Â return -EINVAL;
> Â}
> Â
> +static void imx6_pcie_ltssm_enable(struct device *dev)
> +{
> > + struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev);
> +
> > + switch (imx6_pcie->variant) {
> > + case IMX6Q:
> > + case IMX6SX:
> > + case IMX6QP:
> > + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
> > + ÂÂÂIMX6Q_GPR12_PCIE_CTL_2,
> > + ÂÂÂIMX6Q_GPR12_PCIE_CTL_2);
> > + break;
> > + case IMX7D:
> > + reset_control_deassert(imx6_pcie->apps_reset);
> > + }
> +}
> +
> Âstatic int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
> Â{
> > Â struct dw_pcie *pci = imx6_pcie->pci;
> > Â struct device *dev = pci->dev;
> > Â u32 tmp;
> @@ -558,15 +575,11 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
> > Â tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;
> > Â tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1;
> > Â dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp);
> Â
> > Â /* Start LTSSM. */
> > - if (imx6_pcie->variant == IMX7D)
> > - reset_control_deassert(imx6_pcie->apps_reset);
> > - else
> > - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
> > - ÂÂÂIMX6Q_GPR12_PCIE_CTL_2, 1 << 10);
> > + imx6_pcie_ltssm_enable(dev);
> Â
> > Â ret = imx6_pcie_wait_for_link(imx6_pcie);
> > Â if (ret)
> > Â goto err_reset_phy;
> Â
> @@ -681,10 +694,81 @@ static int imx6_add_pcie_port(struct imx6_pcie *imx6_pcie,
> Â
> Âstatic const struct dw_pcie_ops dw_pcie_ops = {
> > Â .link_up = imx6_pcie_link_up,
> Â};
> Â
> +#ifdef CONFIG_PM_SLEEP
> +static void imx6_pcie_ltssm_disable(struct device *dev)
> +{
> > + struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev);
> +
> > + switch (imx6_pcie->variant) {
> > + case IMX6Q:
> > + case IMX6SX:
> > + case IMX6QP:
> > + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
> + ÂÂÂIMX6Q_GPR12_PCIE_CTL_2, 0);

Has this been tested on i.MX6? LTSSM disable requires a more complex
sequence on this SoC to avoid hanging the system. See commit
3e3e406e3807 "PCI: imx6: Put LTSSM in "Detect" state before disabling
it".

Note that implementing the correct sequence requires the core clocks to
still be on when disabling LTSSM, so would need to switch ordering of
the function calls inÂimx6_pcie_suspend_noirq.

> + break;
> > + case IMX7D:
> > + reset_control_assert(imx6_pcie->apps_reset);
> > + break;
> > + }
> +}
> +
> +static void imx6_pcie_clk_disable(struct imx6_pcie *imx6_pcie)
> +{
> > + clk_disable_unprepare(imx6_pcie->pcie);
> > + clk_disable_unprepare(imx6_pcie->pcie_phy);
> > + clk_disable_unprepare(imx6_pcie->pcie_bus);
> +
> > + if (imx6_pcie->variant == IMX7D) {
> > + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
> > + ÂÂÂIMX7D_GPR12_PCIE_PHY_REFCLK_SEL,
> > + ÂÂÂIMX7D_GPR12_PCIE_PHY_REFCLK_SEL);
> > + }
> +}
> +
> +static int imx6_pcie_suspend_noirq(struct device *dev)
> +{
> > + struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev);
> +
> > + if (imx6_pcie->variant != IMX7D)
> > + return 0;
> +
> > + imx6_pcie_clk_disable(imx6_pcie);
> > + imx6_pcie_ltssm_disable(dev);
> +
> > + return 0;
> +}
> +
> +static int imx6_pcie_resume_noirq(struct device *dev)
> +{
> > + int ret = 0;
> > + struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev);
> > + struct pcie_port *pp = &imx6_pcie->pci->pp;
> +
> > + if (imx6_pcie->variant != IMX7D)
> > + return 0;
> +
> > + imx6_pcie_assert_core_reset(imx6_pcie);
> > + imx6_pcie_init_phy(imx6_pcie);
> > + imx6_pcie_deassert_core_reset(imx6_pcie);
> > + dw_pcie_setup_rc(pp);
> +
> > + ret = imx6_pcie_establish_link(imx6_pcie);
> > + if (ret < 0)
> + pr_err("pcie link is down after resume.\n");

dev_err(), please.

Regards,
Lucas

> +
> > + return 0;
> +}
> +#endif
> +
> +static const struct dev_pm_ops imx6_pcie_pm_ops = {
> > + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx6_pcie_suspend_noirq,
> > + ÂÂÂÂÂÂimx6_pcie_resume_noirq)
> +};
> +
> Âstatic int imx6_pcie_probe(struct platform_device *pdev)
> Â{
> > Â struct device *dev = &pdev->dev;
> > Â struct dw_pcie *pci;
> > Â struct imx6_pcie *imx6_pcie;
> @@ -847,10 +931,11 @@ static const struct of_device_id imx6_pcie_of_match[] = {
> Âstatic struct platform_driver imx6_pcie_driver = {
> > Â .driver = {
> > > Â .name = "imx6q-pcie",
> > Â .of_match_table = imx6_pcie_of_match,
> > Â .suppress_bind_attrs = true,
> > + .pm = &imx6_pcie_pm_ops,
> > Â },
> > Â .probeÂÂÂÂ= imx6_pcie_probe,
> > Â .shutdown = imx6_pcie_shutdown,
> Â};
> Â