drivers/phy/intel/phy-intel-lgm-emmc.c

Source file repositories/reference/linux-study-clean/drivers/phy/intel/phy-intel-lgm-emmc.c

File Facts

System
Linux kernel
Corpus path
drivers/phy/intel/phy-intel-lgm-emmc.c
Extension
.c
Size
7760 bytes
Lines
285
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 intel_emmc_phy {
	struct regmap *syscfg;
	struct clk *emmcclk;
};

static int intel_emmc_phy_power(struct phy *phy, bool on_off)
{
	struct intel_emmc_phy *priv = phy_get_drvdata(phy);
	unsigned int caldone;
	unsigned int dllrdy;
	unsigned int freqsel;
	unsigned long rate;
	int ret, quot;

	/*
	 * Keep phyctrl_pdb and phyctrl_endll low to allow
	 * initialization of CALIO state M/C DFFs
	 */
	ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL1_REG, PDB_MASK,
				 PDB_SHIFT(0));
	if (ret) {
		dev_err(&phy->dev, "CALIO power down bar failed: %d\n", ret);
		return ret;
	}

	/* Already finish power_off above */
	if (!on_off)
		return 0;

	rate = clk_get_rate(priv->emmcclk);
	quot = DIV_ROUND_CLOSEST(rate, 50000000);
	if (quot > FRQSEL_150M)
		dev_warn(&phy->dev, "Unsupported rate: %lu\n", rate);
	freqsel = clamp_t(int, quot, FRQSEL_25M, FRQSEL_150M);

	/*
	 * According to the user manual, calpad calibration
	 * cycle takes more than 2us without the minimal recommended
	 * value, so we may need a little margin here
	 */
	udelay(5);

	ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL1_REG, PDB_MASK,
				 PDB_SHIFT(1));
	if (ret) {
		dev_err(&phy->dev, "CALIO power down bar failed: %d\n", ret);
		return ret;
	}

	/*
	 * According to the user manual, it asks driver to wait 5us for
	 * calpad busy trimming. However it is documented that this value is
	 * PVT(A.K.A process,voltage and temperature) relevant, so some
	 * failure cases are found which indicates we should be more tolerant
	 * to calpad busy trimming.
	 */
	ret = regmap_read_poll_timeout(priv->syscfg, EMMC_PHYSTAT_REG,
				       caldone, IS_CALDONE(caldone),
				       0, 50);
	if (ret) {
		dev_err(&phy->dev, "caldone failed, ret=%d\n", ret);
		return ret;
	}

	/* Set the frequency of the DLL operation */
	ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL2_REG, FRQSEL_MASK,
				 FRQSEL_SHIFT(freqsel));
	if (ret) {
		dev_err(&phy->dev, "set the frequency of dll failed:%d\n", ret);
		return ret;
	}

	/* Turn on the DLL */
	ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL1_REG, ENDLL_MASK,
				 ENDLL_SHIFT(1));
	if (ret) {
		dev_err(&phy->dev, "turn on the dll failed: %d\n", ret);
		return ret;
	}

	/*
	 * After enabling analog DLL circuits docs say that we need 10.2 us if
	 * our source clock is at 50 MHz and that lock time scales linearly
	 * with clock speed.  If we are powering on the PHY and the card clock
	 * is super slow (like 100 kHZ) this could take as long as 5.1 ms as
	 * per the math: 10.2 us * (50000000 Hz / 100000 Hz) => 5.1 ms
	 * Hopefully we won't be running at 100 kHz, but we should still make
	 * sure we wait long enough.
	 *
	 * NOTE: There appear to be corner cases where the DLL seems to take

Annotation

Implementation Notes