drivers/phy/samsung/phy-samsung-ufs.c

Source file repositories/reference/linux-study-clean/drivers/phy/samsung/phy-samsung-ufs.c

File Facts

System
Linux kernel
Corpus path
drivers/phy/samsung/phy-samsung-ufs.c
Extension
.c
Size
9553 bytes
Lines
389
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

for_each_phy_lane(ufs_phy, i) {
			samsung_ufs_phy_config(ufs_phy, cfg, i);
		}
	}

	for_each_phy_lane(ufs_phy, i) {
		if (ufs_phy->ufs_phy_state == CFG_PRE_INIT &&
		    ufs_phy->drvdata->wait_for_cal) {
			err = ufs_phy->drvdata->wait_for_cal(phy, i);
			if (err)
				goto out;
		}

		if (ufs_phy->ufs_phy_state == CFG_POST_PWR_HS &&
		    ufs_phy->drvdata->wait_for_cdr) {
			err = ufs_phy->drvdata->wait_for_cdr(phy, i);
			if (err)
				goto out;
		}
	}

	/**
	 * In Samsung ufshci, PHY need to be calibrated at different
	 * stages / state mainly before Linkstartup, after Linkstartup,
	 * before power mode change and after power mode change.
	 * Below state machine to make sure to calibrate PHY in each
	 * state. Here after configuring PHY in a given state, will
	 * change the state to next state so that next state phy
	 * calibration value can be programed
	 */
out:
	switch (ufs_phy->ufs_phy_state) {
	case CFG_PRE_INIT:
		ufs_phy->ufs_phy_state = CFG_POST_INIT;
		break;
	case CFG_POST_INIT:
		ufs_phy->ufs_phy_state = CFG_PRE_PWR_HS;
		break;
	case CFG_PRE_PWR_HS:
		ufs_phy->ufs_phy_state = CFG_POST_PWR_HS;
		break;
	case CFG_POST_PWR_HS:
		/* Change back to INIT state */
		ufs_phy->ufs_phy_state = CFG_PRE_INIT;
		break;
	default:
		dev_err(ufs_phy->dev, "wrong state for phy calibration\n");
	}

	return err;
}

static int samsung_ufs_phy_clks_init(struct samsung_ufs_phy *phy)
{
	int i;
	const struct samsung_ufs_phy_drvdata *drvdata = phy->drvdata;
	int num_clks = drvdata->num_clks;

	phy->clks = devm_kcalloc(phy->dev, num_clks, sizeof(*phy->clks),
				 GFP_KERNEL);
	if (!phy->clks)
		return -ENOMEM;

	for (i = 0; i < num_clks; i++)
		phy->clks[i].id = drvdata->clk_list[i];

	return devm_clk_bulk_get(phy->dev, num_clks, phy->clks);
}

static int samsung_ufs_phy_init(struct phy *phy)
{
	struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy);

	ss_phy->lane_cnt = phy->attrs.bus_width;
	ss_phy->ufs_phy_state = CFG_PRE_INIT;

	return 0;
}

static int samsung_ufs_phy_power_on(struct phy *phy)
{
	struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy);
	int ret;

	samsung_ufs_phy_ctrl_isol(ss_phy, false);

	ret = clk_bulk_prepare_enable(ss_phy->drvdata->num_clks, ss_phy->clks);
	if (ret) {
		dev_err(ss_phy->dev, "failed to enable ufs phy clocks\n");
		return ret;

Annotation

Implementation Notes