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

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

File Facts

System
Linux kernel
Corpus path
drivers/clk/renesas/rcar-gen4-cpg.c
Extension
.c
Size
14823 bytes
Lines
547
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 *pllcr0_reg;
	void __iomem *pllcr1_reg;
	void __iomem *pllecr_reg;
	u32 pllecr_pllst_mask;
};

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

static unsigned long cpg_pll_8_25_clk_recalc_rate(struct clk_hw *hw,
						  unsigned long parent_rate)
{
	struct cpg_pll_clk *pll_clk = to_pll_clk(hw);
	u32 cr0 = readl(pll_clk->pllcr0_reg);
	unsigned int ni, nf;
	unsigned long rate;

	ni = (FIELD_GET(CPG_PLLxCR0_NI8, cr0) + 1) * 2;
	rate = parent_rate * ni;
	if (cr0 & CPG_PLLxCR0_SSMODE_FM) {
		nf = FIELD_GET(CPG_PLLxCR1_NF25, readl(pll_clk->pllcr1_reg));
		rate += mul_u64_u32_shr(parent_rate, nf, 24);
	}

	return rate;
}

static int cpg_pll_8_25_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, ni, nf;
	u32 cr0 = readl(pll_clk->pllcr0_reg);
	unsigned long prate;

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

	if (cr0 & CPG_PLLxCR0_SSMODE_FM) {
		ni = div64_ul(req->rate, prate);
		if (ni < min_mult) {
			ni = min_mult;
			nf = 0;
		} else {
			ni = min(ni, max_mult);
			nf = div64_ul((u64)(req->rate - prate * ni) << 24,
				      req->best_parent_rate);
		}
	} else {
		ni = DIV_ROUND_CLOSEST_ULL(req->rate, prate);
		ni = clamp(ni, min_mult, max_mult);
		nf = 0;
	}
	req->rate = prate * ni + mul_u64_u32_shr(req->best_parent_rate, nf, 24);

	return 0;
}

static int cpg_pll_8_25_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 long prate = parent_rate * 2;
	u32 cr0 = readl(pll_clk->pllcr0_reg);
	unsigned int ni, nf;
	u32 val;

	if (cr0 & CPG_PLLxCR0_SSMODE_FM) {
		ni = div64_ul(rate, prate);
		if (ni < 1) {
			ni = 1;
			nf = 0;
		} else {
			ni = min(ni, 256U);
			nf = div64_ul((u64)(rate - prate * ni) << 24,
				      parent_rate);
		}
	} else {
		ni = DIV_ROUND_CLOSEST_ULL(rate, prate);
		ni = clamp(ni, 1U, 256U);
	}

	if (readl(pll_clk->pllcr0_reg) & CPG_PLLxCR0_KICK)
		return -EBUSY;

	cpg_reg_modify(pll_clk->pllcr0_reg, CPG_PLLxCR0_NI8,

Annotation

Implementation Notes