drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c

Source file repositories/reference/linux-study-clean/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c

File Facts

System
Linux kernel
Corpus path
drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c
Extension
.c
Size
11347 bytes
Lines
447
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 mixel_lvds_phy {
	struct phy *phy;
	struct phy_configure_opts_lvds cfg;
	unsigned int id;
};

struct mixel_lvds_phy_priv {
	struct regmap *regmap;
	struct mutex lock;	/* protect remap access and cfg of our own */
	struct clk *phy_ref_clk;
	struct mixel_lvds_phy *phys[PHY_NUM];
};

static int mixel_lvds_phy_init(struct phy *phy)
{
	struct mixel_lvds_phy_priv *priv = dev_get_drvdata(phy->dev.parent);

	mutex_lock(&priv->lock);
	regmap_update_bits(priv->regmap,
			   PHY_CTRL, CTRL_INIT_MASK, CTRL_INIT_VAL);
	mutex_unlock(&priv->lock);

	return 0;
}

static int mixel_lvds_phy_power_on(struct phy *phy)
{
	struct mixel_lvds_phy_priv *priv = dev_get_drvdata(phy->dev.parent);
	struct mixel_lvds_phy *lvds_phy = phy_get_drvdata(phy);
	struct mixel_lvds_phy *companion = priv->phys[lvds_phy->id ^ 1];
	struct phy_configure_opts_lvds *cfg = &lvds_phy->cfg;
	u32 val = 0;
	u32 locked;
	int ret;

	/* The master PHY would power on the slave PHY. */
	if (cfg->is_slave)
		return 0;

	ret = clk_prepare_enable(priv->phy_ref_clk);
	if (ret < 0) {
		dev_err(&phy->dev,
			"failed to enable PHY reference clock: %d\n", ret);
		return ret;
	}

	mutex_lock(&priv->lock);
	if (cfg->bits_per_lane_and_dclk_cycle == 7) {
		if (cfg->differential_clk_rate < 44000000)
			val |= M(0x2);
		else if (cfg->differential_clk_rate < 90000000)
			val |= M(0x1);
		else
			val |= M(0x0);
	} else {
		val = NB;

		if (cfg->differential_clk_rate < 32000000)
			val |= M(0x2);
		else if (cfg->differential_clk_rate < 63000000)
			val |= M(0x1);
		else
			val |= M(0x0);
	}
	regmap_update_bits(priv->regmap, PHY_CTRL, M_MASK | NB, val);

	/*
	 * Enable two channels synchronously,
	 * if the companion PHY is a slave PHY.
	 */
	if (companion->cfg.is_slave)
		val = CH_EN(0) | CH_EN(1);
	else
		val = CH_EN(lvds_phy->id);
	regmap_write(priv->regmap, PHY_CTRL + REG_SET, val);

	ret = regmap_read_poll_timeout(priv->regmap, PHY_STATUS, locked,
				       locked, PLL_LOCK_SLEEP,
				       PLL_LOCK_TIMEOUT);
	if (ret < 0) {
		dev_err(&phy->dev, "failed to get PHY lock: %d\n", ret);
		clk_disable_unprepare(priv->phy_ref_clk);
	}
	mutex_unlock(&priv->lock);

	return ret;
}

static int mixel_lvds_phy_power_off(struct phy *phy)
{

Annotation

Implementation Notes