drivers/clk/davinci/pll.c

Source file repositories/reference/linux-study-clean/drivers/clk/davinci/pll.c

File Facts

System
Linux kernel
Corpus path
drivers/clk/davinci/pll.c
Extension
.c
Size
23635 bytes
Lines
956
Domain
Driver Families
Bucket
drivers/clk
Inferred role
Driver Families: exported/initcall integration point
Status
integration 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 davinci_pll_clk {
	struct clk_hw hw;
	void __iomem *base;
	u32 pllm_min;
	u32 pllm_max;
	u32 pllm_mask;
};

#define to_davinci_pll_clk(_hw) \
	container_of((_hw), struct davinci_pll_clk, hw)

static unsigned long davinci_pll_recalc_rate(struct clk_hw *hw,
					     unsigned long parent_rate)
{
	struct davinci_pll_clk *pll = to_davinci_pll_clk(hw);
	unsigned long rate = parent_rate;
	u32 mult;

	mult = readl(pll->base + PLLM) & pll->pllm_mask;
	rate *= mult + 1;

	return rate;
}

static int davinci_pll_determine_rate(struct clk_hw *hw,
				      struct clk_rate_request *req)
{
	struct davinci_pll_clk *pll = to_davinci_pll_clk(hw);
	struct clk_hw *parent = req->best_parent_hw;
	unsigned long parent_rate = req->best_parent_rate;
	unsigned long rate = req->rate;
	unsigned long best_rate, r;
	u32 mult;

	/* there is a limited range of valid outputs (see datasheet) */
	if (rate < req->min_rate)
		return -EINVAL;

	rate = min(rate, req->max_rate);
	mult = rate / parent_rate;
	best_rate = parent_rate * mult;

	/* easy case when there is no PREDIV */
	if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) {
		if (best_rate < req->min_rate)
			return -EINVAL;

		if (mult < pll->pllm_min || mult > pll->pllm_max)
			return -EINVAL;

		req->rate = best_rate;

		return 0;
	}

	/* see if the PREDIV clock can help us */
	best_rate = 0;

	for (mult = pll->pllm_min; mult <= pll->pllm_max; mult++) {
		parent_rate = clk_hw_round_rate(parent, rate / mult);
		r = parent_rate * mult;
		if (r < req->min_rate)
			continue;
		if (r > rate || r > req->max_rate)
			break;
		if (r > best_rate) {
			best_rate = r;
			req->rate = best_rate;
			req->best_parent_rate = parent_rate;
			if (best_rate == rate)
				break;
		}
	}

	return 0;
}

static int davinci_pll_set_rate(struct clk_hw *hw, unsigned long rate,
				unsigned long parent_rate)
{
	struct davinci_pll_clk *pll = to_davinci_pll_clk(hw);
	u32 mult;

	mult = rate / parent_rate;
	writel(mult - 1, pll->base + PLLM);

	return 0;
}

#ifdef CONFIG_DEBUG_FS

Annotation

Implementation Notes