Commit 178e244c authored by Richard Zhu's avatar Richard Zhu Committed by Lorenzo Pieralisi
Browse files

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: default avatarMarcel Ziswiler <marcel.ziswiler@toradex.com>
Tested-by: default avatarTim Harvey <tharvey@gateworks.com>
Signed-off-by: default avatarRichard Zhu <hongxing.zhu@nxp.com>
Signed-off-by: default avatarLorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Reviewed-by: default avatarTim Harvey <tharvey@gateworks.com>
parent 3e15f623
Loading
Loading
Loading
Loading
+73 −8
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/reset.h>
#include <linux/phy/phy.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>

@@ -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),
@@ -1089,10 +1124,35 @@ static int imx6_pcie_probe(struct platform_device *pdev)
			dev_err(dev, "Failed to get PCIE APPS reset control\n");
			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[] = {
@@ -1210,6 +1274,7 @@ static const struct of_device_id imx6_pcie_of_match[] = {
	{ .compatible = "fsl,imx6qp-pcie", .data = &drvdata[IMX6QP], },
	{ .compatible = "fsl,imx7d-pcie",  .data = &drvdata[IMX7D],  },
	{ .compatible = "fsl,imx8mq-pcie", .data = &drvdata[IMX8MQ], },
	{ .compatible = "fsl,imx8mm-pcie", .data = &drvdata[IMX8MM], },
	{},
};