diff options
author | Leonard Crestez <leonard.crestez@nxp.com> | 2018-11-07 08:57:03 -0500 |
---|---|---|
committer | Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> | 2018-12-18 07:01:16 -0500 |
commit | 9e56f0df3684bd752347e7c3df5e8ed1fc55d139 (patch) | |
tree | 003b7f8b7fd62585978928192b120ee7e401b2c7 /drivers/pci/controller/dwc/pci-imx6.c | |
parent | 3d71746c420c1c1c27cf5c4e48f8fa0a6cfdc185 (diff) |
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>
Diffstat (limited to 'drivers/pci/controller/dwc/pci-imx6.c')
-rw-r--r-- | drivers/pci/controller/dwc/pci-imx6.c | 44 |
1 files changed, 39 insertions, 5 deletions
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 4a307cdf20c8..25a2b7683e55 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c | |||
@@ -817,8 +817,28 @@ static void imx6_pcie_ltssm_disable(struct device *dev) | |||
817 | 817 | ||
818 | static void imx6_pcie_pm_turnoff(struct imx6_pcie *imx6_pcie) | 818 | static void imx6_pcie_pm_turnoff(struct imx6_pcie *imx6_pcie) |
819 | { | 819 | { |
820 | reset_control_assert(imx6_pcie->turnoff_reset); | 820 | struct device *dev = imx6_pcie->pci->dev; |
821 | reset_control_deassert(imx6_pcie->turnoff_reset); | 821 | |
822 | /* Some variants have a turnoff reset in DT */ | ||
823 | if (imx6_pcie->turnoff_reset) { | ||
824 | reset_control_assert(imx6_pcie->turnoff_reset); | ||
825 | reset_control_deassert(imx6_pcie->turnoff_reset); | ||
826 | goto pm_turnoff_sleep; | ||
827 | } | ||
828 | |||
829 | /* Others poke directly at IOMUXC registers */ | ||
830 | switch (imx6_pcie->variant) { | ||
831 | case IMX6SX: | ||
832 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, | ||
833 | IMX6SX_GPR12_PCIE_PM_TURN_OFF, | ||
834 | IMX6SX_GPR12_PCIE_PM_TURN_OFF); | ||
835 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, | ||
836 | IMX6SX_GPR12_PCIE_PM_TURN_OFF, 0); | ||
837 | break; | ||
838 | default: | ||
839 | dev_err(dev, "PME_Turn_Off not implemented\n"); | ||
840 | return; | ||
841 | } | ||
822 | 842 | ||
823 | /* | 843 | /* |
824 | * Components with an upstream port must respond to | 844 | * Components with an upstream port must respond to |
@@ -827,6 +847,7 @@ static void imx6_pcie_pm_turnoff(struct imx6_pcie *imx6_pcie) | |||
827 | * The standard recommends a 1-10ms timeout after which to | 847 | * The standard recommends a 1-10ms timeout after which to |
828 | * proceed anyway as if acks were received. | 848 | * proceed anyway as if acks were received. |
829 | */ | 849 | */ |
850 | pm_turnoff_sleep: | ||
830 | usleep_range(1000, 10000); | 851 | usleep_range(1000, 10000); |
831 | } | 852 | } |
832 | 853 | ||
@@ -836,18 +857,31 @@ static void imx6_pcie_clk_disable(struct imx6_pcie *imx6_pcie) | |||
836 | clk_disable_unprepare(imx6_pcie->pcie_phy); | 857 | clk_disable_unprepare(imx6_pcie->pcie_phy); |
837 | clk_disable_unprepare(imx6_pcie->pcie_bus); | 858 | clk_disable_unprepare(imx6_pcie->pcie_bus); |
838 | 859 | ||
839 | if (imx6_pcie->variant == IMX7D) { | 860 | switch (imx6_pcie->variant) { |
861 | case IMX6SX: | ||
862 | clk_disable_unprepare(imx6_pcie->pcie_inbound_axi); | ||
863 | break; | ||
864 | case IMX7D: | ||
840 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, | 865 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, |
841 | IMX7D_GPR12_PCIE_PHY_REFCLK_SEL, | 866 | IMX7D_GPR12_PCIE_PHY_REFCLK_SEL, |
842 | IMX7D_GPR12_PCIE_PHY_REFCLK_SEL); | 867 | IMX7D_GPR12_PCIE_PHY_REFCLK_SEL); |
868 | break; | ||
869 | default: | ||
870 | break; | ||
843 | } | 871 | } |
844 | } | 872 | } |
845 | 873 | ||
874 | static inline bool imx6_pcie_supports_suspend(struct imx6_pcie *imx6_pcie) | ||
875 | { | ||
876 | return (imx6_pcie->variant == IMX7D || | ||
877 | imx6_pcie->variant == IMX6SX); | ||
878 | } | ||
879 | |||
846 | static int imx6_pcie_suspend_noirq(struct device *dev) | 880 | static int imx6_pcie_suspend_noirq(struct device *dev) |
847 | { | 881 | { |
848 | struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev); | 882 | struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev); |
849 | 883 | ||
850 | if (imx6_pcie->variant != IMX7D) | 884 | if (!imx6_pcie_supports_suspend(imx6_pcie)) |
851 | return 0; | 885 | return 0; |
852 | 886 | ||
853 | imx6_pcie_pm_turnoff(imx6_pcie); | 887 | imx6_pcie_pm_turnoff(imx6_pcie); |
@@ -863,7 +897,7 @@ static int imx6_pcie_resume_noirq(struct device *dev) | |||
863 | struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev); | 897 | struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev); |
864 | struct pcie_port *pp = &imx6_pcie->pci->pp; | 898 | struct pcie_port *pp = &imx6_pcie->pci->pp; |
865 | 899 | ||
866 | if (imx6_pcie->variant != IMX7D) | 900 | if (!imx6_pcie_supports_suspend(imx6_pcie)) |
867 | return 0; | 901 | return 0; |
868 | 902 | ||
869 | imx6_pcie_assert_core_reset(imx6_pcie); | 903 | imx6_pcie_assert_core_reset(imx6_pcie); |