From 178e244cb6e209971b0ded9977e5146b73fe1f45 Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Fri, 24 Dec 2021 10:28:05 +0800 Subject: [PATCH] PCI: imx: Add the imx8mm pcie support i.MX8MM PCIe works mostly like the i.MX8MQ one, but has a different PHY and allows to output the internal PHY reference clock via the refclk pad. Add the i.MX8MM PCIe support based on the standalone PHY driver. Link: https://lore.kernel.org/r/1640312885-31142-2-git-send-email-hongxing.zhu@nxp.com Tested-by: Marcel Ziswiler Tested-by: Tim Harvey Signed-off-by: Richard Zhu Signed-off-by: Lorenzo Pieralisi Reviewed-by: Tim Harvey --- drivers/pci/controller/dwc/pci-imx6.c | 81 +++++++++++++++++++++++++++++++---- 1 file changed, 73 insertions(+), 8 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 26f49f7..6974bd5 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -49,6 +50,7 @@ enum imx6_pcie_variants { IMX6QP, IMX7D, IMX8MQ, + IMX8MM, }; #define IMX6_PCIE_FLAG_IMX6_PHY BIT(0) @@ -88,6 +90,7 @@ struct imx6_pcie { struct device *pd_pcie; /* power domain for pcie phy */ struct device *pd_pcie_phy; + struct phy *phy; const struct imx6_pcie_drvdata *drvdata; }; @@ -372,6 +375,8 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie) case IMX7D: case IMX8MQ: reset_control_assert(imx6_pcie->pciephy_reset); + fallthrough; + case IMX8MM: reset_control_assert(imx6_pcie->apps_reset); break; case IMX6SX: @@ -407,7 +412,8 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie) static unsigned int imx6_pcie_grp_offset(const struct imx6_pcie *imx6_pcie) { - WARN_ON(imx6_pcie->drvdata->variant != IMX8MQ); + WARN_ON(imx6_pcie->drvdata->variant != IMX8MQ && + imx6_pcie->drvdata->variant != IMX8MM); return imx6_pcie->controller_id == 1 ? IOMUXC_GPR16 : IOMUXC_GPR14; } @@ -446,6 +452,11 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie) break; case IMX7D: break; + case IMX8MM: + ret = clk_prepare_enable(imx6_pcie->pcie_aux); + if (ret) + dev_err(dev, "unable to enable pcie_aux clock\n"); + break; case IMX8MQ: ret = clk_prepare_enable(imx6_pcie->pcie_aux); if (ret) { @@ -522,6 +533,14 @@ static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie) goto err_ref_clk; } + switch (imx6_pcie->drvdata->variant) { + case IMX8MM: + if (phy_power_on(imx6_pcie->phy)) + dev_err(dev, "unable to power on PHY\n"); + break; + default: + break; + } /* allow the clocks to stabilize */ usleep_range(200, 500); @@ -538,6 +557,10 @@ static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie) case IMX8MQ: reset_control_deassert(imx6_pcie->pciephy_reset); break; + case IMX8MM: + if (phy_init(imx6_pcie->phy)) + dev_err(dev, "waiting for phy ready timeout!\n"); + break; case IMX7D: reset_control_deassert(imx6_pcie->pciephy_reset); @@ -614,6 +637,12 @@ static void imx6_pcie_configure_type(struct imx6_pcie *imx6_pcie) static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie) { switch (imx6_pcie->drvdata->variant) { + case IMX8MM: + /* + * The PHY initialization had been done in the PHY + * driver, break here directly. + */ + break; case IMX8MQ: /* * TODO: Currently this code assumes external @@ -753,6 +782,7 @@ static void imx6_pcie_ltssm_enable(struct device *dev) break; case IMX7D: case IMX8MQ: + case IMX8MM: reset_control_deassert(imx6_pcie->apps_reset); break; } @@ -871,6 +901,7 @@ static void imx6_pcie_ltssm_disable(struct device *dev) IMX6Q_GPR12_PCIE_CTL_2, 0); break; case IMX7D: + case IMX8MM: reset_control_assert(imx6_pcie->apps_reset); break; default: @@ -930,6 +961,7 @@ static void imx6_pcie_clk_disable(struct imx6_pcie *imx6_pcie) IMX7D_GPR12_PCIE_PHY_REFCLK_SEL); break; case IMX8MQ: + case IMX8MM: clk_disable_unprepare(imx6_pcie->pcie_aux); break; default: @@ -945,8 +977,16 @@ static int imx6_pcie_suspend_noirq(struct device *dev) return 0; imx6_pcie_pm_turnoff(imx6_pcie); - imx6_pcie_clk_disable(imx6_pcie); imx6_pcie_ltssm_disable(dev); + imx6_pcie_clk_disable(imx6_pcie); + switch (imx6_pcie->drvdata->variant) { + case IMX8MM: + if (phy_power_off(imx6_pcie->phy)) + dev_err(dev, "unable to power off PHY\n"); + break; + default: + break; + } return 0; } @@ -1043,11 +1083,6 @@ static int imx6_pcie_probe(struct platform_device *pdev) } /* Fetch clocks */ - imx6_pcie->pcie_phy = devm_clk_get(dev, "pcie_phy"); - if (IS_ERR(imx6_pcie->pcie_phy)) - return dev_err_probe(dev, PTR_ERR(imx6_pcie->pcie_phy), - "pcie_phy clock source missing or invalid\n"); - imx6_pcie->pcie_bus = devm_clk_get(dev, "pcie_bus"); if (IS_ERR(imx6_pcie->pcie_bus)) return dev_err_probe(dev, PTR_ERR(imx6_pcie->pcie_bus), @@ -1090,9 +1125,34 @@ static int imx6_pcie_probe(struct platform_device *pdev) return PTR_ERR(imx6_pcie->apps_reset); } break; + case IMX8MM: + imx6_pcie->pcie_aux = devm_clk_get(dev, "pcie_aux"); + if (IS_ERR(imx6_pcie->pcie_aux)) + return dev_err_probe(dev, PTR_ERR(imx6_pcie->pcie_aux), + "pcie_aux clock source missing or invalid\n"); + imx6_pcie->apps_reset = devm_reset_control_get_exclusive(dev, + "apps"); + if (IS_ERR(imx6_pcie->apps_reset)) + return dev_err_probe(dev, PTR_ERR(imx6_pcie->apps_reset), + "failed to get pcie apps reset control\n"); + + imx6_pcie->phy = devm_phy_get(dev, "pcie-phy"); + if (IS_ERR(imx6_pcie->phy)) + return dev_err_probe(dev, PTR_ERR(imx6_pcie->phy), + "failed to get pcie phy\n"); + + break; default: break; } + /* Don't fetch the pcie_phy clock, if it has abstract PHY driver */ + if (imx6_pcie->phy == NULL) { + imx6_pcie->pcie_phy = devm_clk_get(dev, "pcie_phy"); + if (IS_ERR(imx6_pcie->pcie_phy)) + return dev_err_probe(dev, PTR_ERR(imx6_pcie->pcie_phy), + "pcie_phy clock source missing or invalid\n"); + } + /* Grab turnoff reset */ imx6_pcie->turnoff_reset = devm_reset_control_get_optional_exclusive(dev, "turnoff"); @@ -1202,6 +1262,10 @@ static const struct imx6_pcie_drvdata drvdata[] = { [IMX8MQ] = { .variant = IMX8MQ, }, + [IMX8MM] = { + .variant = IMX8MM, + .flags = IMX6_PCIE_FLAG_SUPPORTS_SUSPEND, + }, }; static const struct of_device_id imx6_pcie_of_match[] = { @@ -1209,7 +1273,8 @@ static const struct of_device_id imx6_pcie_of_match[] = { { .compatible = "fsl,imx6sx-pcie", .data = &drvdata[IMX6SX], }, { .compatible = "fsl,imx6qp-pcie", .data = &drvdata[IMX6QP], }, { .compatible = "fsl,imx7d-pcie", .data = &drvdata[IMX7D], }, - { .compatible = "fsl,imx8mq-pcie", .data = &drvdata[IMX8MQ], } , + { .compatible = "fsl,imx8mq-pcie", .data = &drvdata[IMX8MQ], }, + { .compatible = "fsl,imx8mm-pcie", .data = &drvdata[IMX8MM], }, {}, }; -- 2.7.4