PCI: imx: Add imx6sx suspend/resume support

Enable PCI suspend/resume support on imx6sx SOCs. This is similar to
imx7d with a few differences:

* The PM_Turn_Off bit is exposed through an IOMUX GPR, like all other
pcie control bits on 6sx.
* The pcie_inbound_axi clk needs to be turned off in suspend. On resume
it is restored via resume -> deassert_core_reset -> enable_ref_clk.

Most of the resume logic is shared with the initial reset after probe.

Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Reviewed-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Acked-by: Lucas Stach <l.stach@pengutronix.de>
This commit is contained in:
Leonard Crestez 2018-11-07 13:57:03 +00:00 committed by Lorenzo Pieralisi
parent 3d71746c42
commit 9e56f0df36
2 changed files with 40 additions and 5 deletions

View File

@ -817,8 +817,28 @@ static void imx6_pcie_ltssm_disable(struct device *dev)
static void imx6_pcie_pm_turnoff(struct imx6_pcie *imx6_pcie)
{
reset_control_assert(imx6_pcie->turnoff_reset);
reset_control_deassert(imx6_pcie->turnoff_reset);
struct device *dev = imx6_pcie->pci->dev;
/* Some variants have a turnoff reset in DT */
if (imx6_pcie->turnoff_reset) {
reset_control_assert(imx6_pcie->turnoff_reset);
reset_control_deassert(imx6_pcie->turnoff_reset);
goto pm_turnoff_sleep;
}
/* Others poke directly at IOMUXC registers */
switch (imx6_pcie->variant) {
case IMX6SX:
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
IMX6SX_GPR12_PCIE_PM_TURN_OFF,
IMX6SX_GPR12_PCIE_PM_TURN_OFF);
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
IMX6SX_GPR12_PCIE_PM_TURN_OFF, 0);
break;
default:
dev_err(dev, "PME_Turn_Off not implemented\n");
return;
}
/*
* Components with an upstream port must respond to
@ -827,6 +847,7 @@ static void imx6_pcie_pm_turnoff(struct imx6_pcie *imx6_pcie)
* The standard recommends a 1-10ms timeout after which to
* proceed anyway as if acks were received.
*/
pm_turnoff_sleep:
usleep_range(1000, 10000);
}
@ -836,18 +857,31 @@ static void imx6_pcie_clk_disable(struct imx6_pcie *imx6_pcie)
clk_disable_unprepare(imx6_pcie->pcie_phy);
clk_disable_unprepare(imx6_pcie->pcie_bus);
if (imx6_pcie->variant == IMX7D) {
switch (imx6_pcie->variant) {
case IMX6SX:
clk_disable_unprepare(imx6_pcie->pcie_inbound_axi);
break;
case IMX7D:
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
IMX7D_GPR12_PCIE_PHY_REFCLK_SEL,
IMX7D_GPR12_PCIE_PHY_REFCLK_SEL);
break;
default:
break;
}
}
static inline bool imx6_pcie_supports_suspend(struct imx6_pcie *imx6_pcie)
{
return (imx6_pcie->variant == IMX7D ||
imx6_pcie->variant == IMX6SX);
}
static int imx6_pcie_suspend_noirq(struct device *dev)
{
struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev);
if (imx6_pcie->variant != IMX7D)
if (!imx6_pcie_supports_suspend(imx6_pcie))
return 0;
imx6_pcie_pm_turnoff(imx6_pcie);
@ -863,7 +897,7 @@ static int imx6_pcie_resume_noirq(struct device *dev)
struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev);
struct pcie_port *pp = &imx6_pcie->pci->pp;
if (imx6_pcie->variant != IMX7D)
if (!imx6_pcie_supports_suspend(imx6_pcie))
return 0;
imx6_pcie_assert_core_reset(imx6_pcie);

View File

@ -440,6 +440,7 @@
#define IMX6SX_GPR5_DISP_MUX_DCIC1_MASK (0x1 << 1)
#define IMX6SX_GPR12_PCIE_TEST_POWERDOWN BIT(30)
#define IMX6SX_GPR12_PCIE_PM_TURN_OFF BIT(16)
#define IMX6SX_GPR12_PCIE_RX_EQ_MASK (0x7 << 0)
#define IMX6SX_GPR12_PCIE_RX_EQ_2 (0x2 << 0)