diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 86b09b5d7f2493b917211309b24d1d993b30e537..95250477849da07c608f6af0c10c5a9287b85227 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -60,6 +60,13 @@ enum imx6_pcie_variants { #define IMX6_PCIE_FLAG_IMX6_PHY BIT(0) #define IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE BIT(1) #define IMX6_PCIE_FLAG_SUPPORTS_SUSPEND BIT(2) +/* + * Because of ERR005723 (PCIe does not support L2 power down) we need to + * workaround suspend resume on some devices which are affected by this errata. + */ +#define IMX6_PCIE_FLAG_BROKEN_SUSPEND BIT(9) + +#define imx6_check_flag(pci, val) (pci->drvdata->flags & val) struct imx6_pcie_drvdata { enum imx6_pcie_variants variant; @@ -1210,9 +1217,19 @@ static int imx6_pcie_suspend_noirq(struct device *dev) return 0; imx6_pcie_msi_save_restore(imx6_pcie, true); - imx6_pcie_pm_turnoff(imx6_pcie); - imx6_pcie_stop_link(imx6_pcie->pci); - imx6_pcie_host_exit(pp); + if (imx6_check_flag(imx6_pcie, IMX6_PCIE_FLAG_BROKEN_SUSPEND)) { + /* + * The minimum for a workaround would be to set PERST# and to + * set the PCIE_TEST_PD flag. However, we can also disable the + * clock which saves some power. + */ + imx6_pcie_assert_core_reset(imx6_pcie); + imx6_pcie_enable_ref_clk(imx6_pcie); + } else { + imx6_pcie_pm_turnoff(imx6_pcie); + imx6_pcie_stop_link(imx6_pcie->pci); + imx6_pcie_host_exit(pp); + } return 0; } @@ -1226,14 +1243,32 @@ static int imx6_pcie_resume_noirq(struct device *dev) if (!(imx6_pcie->drvdata->flags & IMX6_PCIE_FLAG_SUPPORTS_SUSPEND)) return 0; - ret = imx6_pcie_host_init(pp); - if (ret) - return ret; - imx6_pcie_msi_save_restore(imx6_pcie, false); - dw_pcie_setup_rc(pp); + if (imx6_check_flag(imx6_pcie, IMX6_PCIE_FLAG_BROKEN_SUSPEND)) { + ret = imx6_pcie_enable_ref_clk(imx6_pcie); + if (ret) + return ret; + ret = imx6_pcie_deassert_core_reset(imx6_pcie); + if (ret) + return ret; + /* + * Using PCIE_TEST_PD seems to disable MSI and powers down the + * root complex. This is why we have to setup the rc again and + * why we have to restore the MSI register. + */ + ret = dw_pcie_setup_rc(&imx6_pcie->pci->pp); + if (ret) + return ret; + imx6_pcie_msi_save_restore(imx6_pcie, false); + } else { + ret = imx6_pcie_host_init(pp); + if (ret) + return ret; + imx6_pcie_msi_save_restore(imx6_pcie, false); + dw_pcie_setup_rc(pp); - if (imx6_pcie->link_is_up) - imx6_pcie_start_link(imx6_pcie->pci); + if (imx6_pcie->link_is_up) + imx6_pcie_start_link(imx6_pcie->pci); + } return 0; } @@ -1475,7 +1510,9 @@ static const struct imx6_pcie_drvdata drvdata[] = { [IMX6Q] = { .variant = IMX6Q, .flags = IMX6_PCIE_FLAG_IMX6_PHY | - IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE, + IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE | + IMX6_PCIE_FLAG_BROKEN_SUSPEND | + IMX6_PCIE_FLAG_SUPPORTS_SUSPEND, .dbi_length = 0x200, .gpr = "fsl,imx6q-iomuxc-gpr", },