drivers/clk/renesas/rcar-gen3-cpg.c

Source file repositories/reference/linux-study-clean/drivers/clk/renesas/rcar-gen3-cpg.c

File Facts

System
Linux kernel
Corpus path
drivers/clk/renesas/rcar-gen3-cpg.c
Extension
.c
Size
14093 bytes
Lines
552
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 cpg_pll_clk {
	struct clk_hw hw;
	void __iomem *pllcr_reg;
	void __iomem *pllecr_reg;
	unsigned int fixed_mult;
	u32 pllecr_pllst_mask;
};

#define to_pll_clk(_hw)   container_of(_hw, struct cpg_pll_clk, hw)

static unsigned long cpg_pll_clk_recalc_rate(struct clk_hw *hw,
					     unsigned long parent_rate)
{
	struct cpg_pll_clk *pll_clk = to_pll_clk(hw);
	unsigned int mult;

	mult = FIELD_GET(CPG_PLLnCR_STC_MASK, readl(pll_clk->pllcr_reg)) + 1;

	return parent_rate * mult * pll_clk->fixed_mult;
}

static int cpg_pll_clk_determine_rate(struct clk_hw *hw,
				      struct clk_rate_request *req)
{
	struct cpg_pll_clk *pll_clk = to_pll_clk(hw);
	unsigned int min_mult, max_mult, mult;
	unsigned long prate;

	prate = req->best_parent_rate * pll_clk->fixed_mult;
	min_mult = max(div64_ul(req->min_rate, prate), 1ULL);
	max_mult = min(div64_ul(req->max_rate, prate), 128ULL);
	if (max_mult < min_mult)
		return -EINVAL;

	mult = DIV_ROUND_CLOSEST_ULL(req->rate, prate);
	mult = clamp(mult, min_mult, max_mult);

	req->rate = prate * mult;
	return 0;
}

static int cpg_pll_clk_set_rate(struct clk_hw *hw, unsigned long rate,
				unsigned long parent_rate)
{
	struct cpg_pll_clk *pll_clk = to_pll_clk(hw);
	unsigned int mult, i;
	u32 val;

	mult = DIV_ROUND_CLOSEST_ULL(rate, parent_rate * pll_clk->fixed_mult);
	mult = clamp(mult, 1U, 128U);

	val = readl(pll_clk->pllcr_reg);
	val &= ~CPG_PLLnCR_STC_MASK;
	val |= FIELD_PREP(CPG_PLLnCR_STC_MASK, mult - 1);
	writel(val, pll_clk->pllcr_reg);

	for (i = 1000; i; i--) {
		if (readl(pll_clk->pllecr_reg) & pll_clk->pllecr_pllst_mask)
			return 0;

		cpu_relax();
	}

	return -ETIMEDOUT;
}

static const struct clk_ops cpg_pll_clk_ops = {
	.recalc_rate = cpg_pll_clk_recalc_rate,
	.determine_rate = cpg_pll_clk_determine_rate,
	.set_rate = cpg_pll_clk_set_rate,
};

static struct clk * __init cpg_pll_clk_register(const char *name,
						const char *parent_name,
						void __iomem *base,
						unsigned int mult,
						unsigned int offset,
						unsigned int index)

{
	struct cpg_pll_clk *pll_clk;
	struct clk_init_data init = {};
	struct clk *clk;

	pll_clk = kzalloc_obj(*pll_clk);
	if (!pll_clk)
		return ERR_PTR(-ENOMEM);

	init.name = name;
	init.ops = &cpg_pll_clk_ops;

Annotation

Implementation Notes