drivers/clk/clk-plldig.c

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

File Facts

System
Linux kernel
Corpus path
drivers/clk/clk-plldig.c
Extension
.c
Size
7108 bytes
Lines
287
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 clk_plldig {
	struct clk_hw hw;
	void __iomem *regs;
	unsigned int vco_freq;
};

#define to_clk_plldig(_hw)	container_of(_hw, struct clk_plldig, hw)

static int plldig_enable(struct clk_hw *hw)
{
	struct clk_plldig *data = to_clk_plldig(hw);
	u32 val;

	val = readl(data->regs + PLLDIG_REG_PLLFM);
	/*
	 * Use Bypass mode with PLL off by default, the frequency overshoot
	 * detector output was disable. SSCG Bypass mode should be enable.
	 */
	val |= PLLDIG_SSCGBYP_ENABLE;
	writel(val, data->regs + PLLDIG_REG_PLLFM);

	return 0;
}

static void plldig_disable(struct clk_hw *hw)
{
	struct clk_plldig *data = to_clk_plldig(hw);
	u32 val;

	val = readl(data->regs + PLLDIG_REG_PLLFM);

	val &= ~PLLDIG_SSCGBYP_ENABLE;
	val |= FIELD_PREP(PLLDIG_SSCGBYP_ENABLE, 0x0);

	writel(val, data->regs + PLLDIG_REG_PLLFM);
}

static int plldig_is_enabled(struct clk_hw *hw)
{
	struct clk_plldig *data = to_clk_plldig(hw);

	return readl(data->regs + PLLDIG_REG_PLLFM) &
			      PLLDIG_SSCGBYP_ENABLE;
}

static unsigned long plldig_recalc_rate(struct clk_hw *hw,
					unsigned long parent_rate)
{
	struct clk_plldig *data = to_clk_plldig(hw);
	u32 val, rfdphi1;

	val = readl(data->regs + PLLDIG_REG_PLLDV);

	/* Check if PLL is bypassed */
	if (val & PLLDIG_SSCGBYP_ENABLE)
		return parent_rate;

	rfdphi1 = FIELD_GET(PLLDIG_RFDPHI1_MASK, val);

	/*
	 * If RFDPHI1 has a value of 1 the VCO frequency is also divided by
	 * one.
	 */
	if (!rfdphi1)
		rfdphi1 = 1;

	return DIV_ROUND_UP(data->vco_freq, rfdphi1);
}

static unsigned long plldig_calc_target_div(unsigned long vco_freq,
					    unsigned long target_rate)
{
	unsigned long div;

	div = DIV_ROUND_CLOSEST(vco_freq, target_rate);
	div = clamp(div, 1UL, MAX_RFDPHI1);

	return div;
}

static int plldig_determine_rate(struct clk_hw *hw,
				 struct clk_rate_request *req)
{
	struct clk_plldig *data = to_clk_plldig(hw);
	unsigned int div;

	req->rate = clamp(req->rate, PHI1_MIN_FREQ, PHI1_MAX_FREQ);
	div = plldig_calc_target_div(data->vco_freq, req->rate);
	req->rate = DIV_ROUND_UP(data->vco_freq, div);

Annotation

Implementation Notes