diff options
author | Leonard Crestez <leonard.crestez@nxp.com> | 2018-08-27 07:28:37 -0400 |
---|---|---|
committer | Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> | 2018-09-18 05:13:14 -0400 |
commit | 0ee2c1f2429f74328c82ea559b127c96d5224ccd (patch) | |
tree | 3c413ccf68b77946ffe4ed4f01d1413bd343307d /drivers/pci/controller/dwc | |
parent | f18f42d7497dbbde3ff314d0ad585b827ea74e48 (diff) |
PCI: imx: Initial imx7d pm support
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. 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.
Original-by: Richard Zhu <hongxing.zhu@nxp.com>
Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>
[lorenzo.pieralisi@arm.com: commit log update]
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Reviewed-by: Lucas Stach <l.stach@pengutronix.de>
Diffstat (limited to 'drivers/pci/controller/dwc')
-rw-r--r-- | drivers/pci/controller/dwc/pci-imx6.c | 97 |
1 files changed, 92 insertions, 5 deletions
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 21e03c6567da..d13dae50dc99 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c | |||
@@ -596,6 +596,24 @@ static int imx6_pcie_wait_for_speed_change(struct imx6_pcie *imx6_pcie) | |||
596 | return -EINVAL; | 596 | return -EINVAL; |
597 | } | 597 | } |
598 | 598 | ||
599 | static void imx6_pcie_ltssm_enable(struct device *dev) | ||
600 | { | ||
601 | struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev); | ||
602 | |||
603 | switch (imx6_pcie->variant) { | ||
604 | case IMX6Q: | ||
605 | case IMX6SX: | ||
606 | case IMX6QP: | ||
607 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, | ||
608 | IMX6Q_GPR12_PCIE_CTL_2, | ||
609 | IMX6Q_GPR12_PCIE_CTL_2); | ||
610 | break; | ||
611 | case IMX7D: | ||
612 | reset_control_deassert(imx6_pcie->apps_reset); | ||
613 | break; | ||
614 | } | ||
615 | } | ||
616 | |||
599 | static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie) | 617 | static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie) |
600 | { | 618 | { |
601 | struct dw_pcie *pci = imx6_pcie->pci; | 619 | struct dw_pcie *pci = imx6_pcie->pci; |
@@ -614,11 +632,7 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie) | |||
614 | dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp); | 632 | dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp); |
615 | 633 | ||
616 | /* Start LTSSM. */ | 634 | /* Start LTSSM. */ |
617 | if (imx6_pcie->variant == IMX7D) | 635 | imx6_pcie_ltssm_enable(dev); |
618 | reset_control_deassert(imx6_pcie->apps_reset); | ||
619 | else | ||
620 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, | ||
621 | IMX6Q_GPR12_PCIE_CTL_2, 1 << 10); | ||
622 | 636 | ||
623 | ret = imx6_pcie_wait_for_link(imx6_pcie); | 637 | ret = imx6_pcie_wait_for_link(imx6_pcie); |
624 | if (ret) | 638 | if (ret) |
@@ -737,6 +751,78 @@ static const struct dw_pcie_ops dw_pcie_ops = { | |||
737 | .link_up = imx6_pcie_link_up, | 751 | .link_up = imx6_pcie_link_up, |
738 | }; | 752 | }; |
739 | 753 | ||
754 | #ifdef CONFIG_PM_SLEEP | ||
755 | static void imx6_pcie_ltssm_disable(struct device *dev) | ||
756 | { | ||
757 | struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev); | ||
758 | |||
759 | switch (imx6_pcie->variant) { | ||
760 | case IMX6SX: | ||
761 | case IMX6QP: | ||
762 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, | ||
763 | IMX6Q_GPR12_PCIE_CTL_2, 0); | ||
764 | break; | ||
765 | case IMX7D: | ||
766 | reset_control_assert(imx6_pcie->apps_reset); | ||
767 | break; | ||
768 | default: | ||
769 | dev_err(dev, "ltssm_disable not supported\n"); | ||
770 | } | ||
771 | } | ||
772 | |||
773 | static void imx6_pcie_clk_disable(struct imx6_pcie *imx6_pcie) | ||
774 | { | ||
775 | clk_disable_unprepare(imx6_pcie->pcie); | ||
776 | clk_disable_unprepare(imx6_pcie->pcie_phy); | ||
777 | clk_disable_unprepare(imx6_pcie->pcie_bus); | ||
778 | |||
779 | if (imx6_pcie->variant == IMX7D) { | ||
780 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, | ||
781 | IMX7D_GPR12_PCIE_PHY_REFCLK_SEL, | ||
782 | IMX7D_GPR12_PCIE_PHY_REFCLK_SEL); | ||
783 | } | ||
784 | } | ||
785 | |||
786 | static int imx6_pcie_suspend_noirq(struct device *dev) | ||
787 | { | ||
788 | struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev); | ||
789 | |||
790 | if (imx6_pcie->variant != IMX7D) | ||
791 | return 0; | ||
792 | |||
793 | imx6_pcie_clk_disable(imx6_pcie); | ||
794 | imx6_pcie_ltssm_disable(dev); | ||
795 | |||
796 | return 0; | ||
797 | } | ||
798 | |||
799 | static int imx6_pcie_resume_noirq(struct device *dev) | ||
800 | { | ||
801 | int ret; | ||
802 | struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev); | ||
803 | struct pcie_port *pp = &imx6_pcie->pci->pp; | ||
804 | |||
805 | if (imx6_pcie->variant != IMX7D) | ||
806 | return 0; | ||
807 | |||
808 | imx6_pcie_assert_core_reset(imx6_pcie); | ||
809 | imx6_pcie_init_phy(imx6_pcie); | ||
810 | imx6_pcie_deassert_core_reset(imx6_pcie); | ||
811 | dw_pcie_setup_rc(pp); | ||
812 | |||
813 | ret = imx6_pcie_establish_link(imx6_pcie); | ||
814 | if (ret < 0) | ||
815 | dev_info(dev, "pcie link is down after resume.\n"); | ||
816 | |||
817 | return 0; | ||
818 | } | ||
819 | #endif | ||
820 | |||
821 | static const struct dev_pm_ops imx6_pcie_pm_ops = { | ||
822 | SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx6_pcie_suspend_noirq, | ||
823 | imx6_pcie_resume_noirq) | ||
824 | }; | ||
825 | |||
740 | static int imx6_pcie_probe(struct platform_device *pdev) | 826 | static int imx6_pcie_probe(struct platform_device *pdev) |
741 | { | 827 | { |
742 | struct device *dev = &pdev->dev; | 828 | struct device *dev = &pdev->dev; |
@@ -903,6 +989,7 @@ static struct platform_driver imx6_pcie_driver = { | |||
903 | .name = "imx6q-pcie", | 989 | .name = "imx6q-pcie", |
904 | .of_match_table = imx6_pcie_of_match, | 990 | .of_match_table = imx6_pcie_of_match, |
905 | .suppress_bind_attrs = true, | 991 | .suppress_bind_attrs = true, |
992 | .pm = &imx6_pcie_pm_ops, | ||
906 | }, | 993 | }, |
907 | .probe = imx6_pcie_probe, | 994 | .probe = imx6_pcie_probe, |
908 | .shutdown = imx6_pcie_shutdown, | 995 | .shutdown = imx6_pcie_shutdown, |