drivers/clk/tegra/clk-device.c

Source file repositories/reference/linux-study-clean/drivers/clk/tegra/clk-device.c

File Facts

System
Linux kernel
Corpus path
drivers/clk/tegra/clk-device.c
Extension
.c
Size
5359 bytes
Lines
211
Domain
Driver Families
Bucket
drivers/clk
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 tegra_clk_device {
	struct notifier_block clk_nb;
	struct device *dev;
	struct clk_hw *hw;
	struct mutex lock;
};

static int tegra_clock_set_pd_state(struct tegra_clk_device *clk_dev,
				    unsigned long rate)
{
	struct device *dev = clk_dev->dev;
	struct dev_pm_opp *opp;
	unsigned int pstate;

	opp = dev_pm_opp_find_freq_ceil(dev, &rate);
	if (opp == ERR_PTR(-ERANGE)) {
		/*
		 * Some clocks may be unused by a particular board and they
		 * may have uninitiated clock rate that is overly high.  In
		 * this case clock is expected to be disabled, but still we
		 * need to set up performance state of the power domain and
		 * not error out clk initialization.  A typical example is
		 * a PCIe clock on Android tablets.
		 */
		dev_dbg(dev, "failed to find ceil OPP for %luHz\n", rate);
		opp = dev_pm_opp_find_freq_floor(dev, &rate);
	}

	if (IS_ERR(opp)) {
		dev_err(dev, "failed to find OPP for %luHz: %pe\n", rate, opp);
		return PTR_ERR(opp);
	}

	pstate = dev_pm_opp_get_required_pstate(opp, 0);
	dev_pm_opp_put(opp);

	return dev_pm_genpd_set_performance_state(dev, pstate);
}

static int tegra_clock_change_notify(struct notifier_block *nb,
				     unsigned long msg, void *data)
{
	struct clk_notifier_data *cnd = data;
	struct tegra_clk_device *clk_dev;
	int err = 0;

	clk_dev = container_of(nb, struct tegra_clk_device, clk_nb);

	mutex_lock(&clk_dev->lock);
	switch (msg) {
	case PRE_RATE_CHANGE:
		if (cnd->new_rate > cnd->old_rate)
			err = tegra_clock_set_pd_state(clk_dev, cnd->new_rate);
		break;

	case ABORT_RATE_CHANGE:
		err = tegra_clock_set_pd_state(clk_dev, cnd->old_rate);
		break;

	case POST_RATE_CHANGE:
		if (cnd->new_rate < cnd->old_rate)
			err = tegra_clock_set_pd_state(clk_dev, cnd->new_rate);
		break;

	default:
		break;
	}
	mutex_unlock(&clk_dev->lock);

	return notifier_from_errno(err);
}

static int tegra_clock_sync_pd_state(struct tegra_clk_device *clk_dev)
{
	unsigned long rate;
	int ret;

	mutex_lock(&clk_dev->lock);

	rate = clk_hw_get_rate(clk_dev->hw);
	ret = tegra_clock_set_pd_state(clk_dev, rate);

	mutex_unlock(&clk_dev->lock);

	return ret;
}

static int tegra_clock_probe(struct platform_device *pdev)
{
	struct tegra_core_opp_params opp_params = {};

Annotation

Implementation Notes