drivers/phy/freescale/phy-fsl-imx8m-pcie.c

Source file repositories/reference/linux-study-clean/drivers/phy/freescale/phy-fsl-imx8m-pcie.c

File Facts

System
Linux kernel
Corpus path
drivers/phy/freescale/phy-fsl-imx8m-pcie.c
Extension
.c
Size
8647 bytes
Lines
292
Domain
Driver Families
Bucket
drivers/phy
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct imx8_pcie_phy_drvdata {
	const	char			*gpr;
	enum	imx8_pcie_phy_type	variant;
};

struct imx8_pcie_phy {
	void __iomem		*base;
	struct clk		*clk;
	struct phy		*phy;
	struct regmap		*iomuxc_gpr;
	struct reset_control	*perst;
	struct reset_control	*reset;
	u32			refclk_pad_mode;
	u32			tx_deemph_gen1;
	u32			tx_deemph_gen2;
	bool			clkreq_unused;
	const struct imx8_pcie_phy_drvdata	*drvdata;
};

static int imx8_pcie_phy_power_on(struct phy *phy)
{
	int ret;
	u32 val, pad_mode;
	struct imx8_pcie_phy *imx8_phy = phy_get_drvdata(phy);

	pad_mode = imx8_phy->refclk_pad_mode;
	switch (imx8_phy->drvdata->variant) {
	case IMX8MM:
		reset_control_assert(imx8_phy->reset);

		/* Tune PHY de-emphasis setting to pass PCIe compliance. */
		if (imx8_phy->tx_deemph_gen1)
			writel(imx8_phy->tx_deemph_gen1,
			       imx8_phy->base + PCIE_PHY_TRSV_REG5);
		if (imx8_phy->tx_deemph_gen2)
			writel(imx8_phy->tx_deemph_gen2,
			       imx8_phy->base + PCIE_PHY_TRSV_REG6);
		break;
	case IMX8MP:
		reset_control_assert(imx8_phy->reset);
		break;
	}

	if (pad_mode == IMX8_PCIE_REFCLK_PAD_INPUT ||
	    pad_mode == IMX8_PCIE_REFCLK_PAD_UNUSED) {
		/* Configure the pad as input */
		val = readl(imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG061);
		writel(val & ~ANA_PLL_CLK_OUT_TO_EXT_IO_EN,
		       imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG061);
	} else {
		/* Configure the PHY to output the refclock via pad */
		writel(ANA_PLL_CLK_OUT_TO_EXT_IO_EN,
		       imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG061);
	}

	if (pad_mode == IMX8_PCIE_REFCLK_PAD_OUTPUT ||
	    pad_mode == IMX8_PCIE_REFCLK_PAD_UNUSED) {
		/* Source clock from SoC internal PLL */
		writel(ANA_PLL_CLK_OUT_TO_EXT_IO_SEL,
		       imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG062);
		if (imx8_phy->drvdata->variant != IMX8MM) {
			writel(AUX_PLL_REFCLK_SEL_SYS_PLL,
			       imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG063);
		}
		val = ANA_AUX_RX_TX_SEL_TX | ANA_AUX_TX_TERM;
		writel(val | ANA_AUX_RX_TERM_GND_EN,
		       imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG064);
		writel(ANA_AUX_RX_TERM | ANA_AUX_TX_LVL,
		       imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG065);
	}

	/* Set AUX_EN_OVERRIDE 1'b0, when the CLKREQ# isn't hooked */
	regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
			   IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE,
			   imx8_phy->clkreq_unused ?
			   0 : IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE);
	regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
			   IMX8MM_GPR_PCIE_AUX_EN,
			   IMX8MM_GPR_PCIE_AUX_EN);
	regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
			   IMX8MM_GPR_PCIE_POWER_OFF, 0);
	regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
			   IMX8MM_GPR_PCIE_SSC_EN, 0);

	regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
			   IMX8MM_GPR_PCIE_REF_CLK_SEL,
			   pad_mode == IMX8_PCIE_REFCLK_PAD_INPUT ?
			   IMX8MM_GPR_PCIE_REF_CLK_EXT :
			   IMX8MM_GPR_PCIE_REF_CLK_PLL);
	usleep_range(100, 200);

Annotation

Implementation Notes