drivers/clk/nuvoton/clk-ma35d1-pll.c

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

File Facts

System
Linux kernel
Corpus path
drivers/clk/nuvoton/clk-ma35d1-pll.c
Extension
.c
Size
9558 bytes
Lines
369
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 ma35d1_clk_pll {
	struct clk_hw hw;
	u32 id;
	u8 mode;
	void __iomem *ctl0_base;
	void __iomem *ctl1_base;
	void __iomem *ctl2_base;
};

static inline struct ma35d1_clk_pll *to_ma35d1_clk_pll(struct clk_hw *_hw)
{
	return container_of(_hw, struct ma35d1_clk_pll, hw);
}

static unsigned long ma35d1_calc_smic_pll_freq(u32 pll0_ctl0,
					       unsigned long parent_rate)
{
	u32 m, n, p, outdiv;
	u64 pll_freq;

	if (pll0_ctl0 & SPLL0_CTL0_BP)
		return parent_rate;

	n = FIELD_GET(SPLL0_CTL0_FBDIV, pll0_ctl0);
	m = FIELD_GET(SPLL0_CTL0_INDIV, pll0_ctl0);
	p = FIELD_GET(SPLL0_CTL0_OUTDIV, pll0_ctl0);
	outdiv = 1 << p;
	pll_freq = (u64)parent_rate * n;
	div_u64(pll_freq, m * outdiv);
	return pll_freq;
}

static unsigned long ma35d1_calc_pll_freq(u8 mode, u32 *reg_ctl, unsigned long parent_rate)
{
	unsigned long pll_freq, x;
	u32 m, n, p;

	if (reg_ctl[1] & PLL_CTL1_BP)
		return parent_rate;

	n = FIELD_GET(PLL_CTL0_FBDIV, reg_ctl[0]);
	m = FIELD_GET(PLL_CTL0_INDIV, reg_ctl[0]);
	p = FIELD_GET(PLL_CTL1_OUTDIV, reg_ctl[1]);

	if (mode == PLL_MODE_INT) {
		pll_freq = (u64)parent_rate * n;
		div_u64(pll_freq, m * p);
	} else {
		x = FIELD_GET(PLL_CTL1_FRAC, reg_ctl[1]);
		/* 2 decimal places floating to integer (ex. 1.23 to 123) */
		n = n * 100 + ((x * 100) / FIELD_MAX(PLL_CTL1_FRAC));
		pll_freq = div_u64(parent_rate * n, 100 * m * p);
	}
	return pll_freq;
}

static int ma35d1_pll_find_closest(struct ma35d1_clk_pll *pll, unsigned long rate,
				   unsigned long parent_rate, u32 *reg_ctl,
				   unsigned long *freq)
{
	unsigned long min_diff = ULONG_MAX;
	int fbdiv_min, fbdiv_max;
	int p, m, n;

	*freq = 0;
	if (rate < PLL_FCLKO_MIN_FREQ || rate > PLL_FCLKO_MAX_FREQ)
		return -EINVAL;

	if (pll->mode == PLL_MODE_INT) {
		fbdiv_min = FBDIV_MIN;
		fbdiv_max = FBDIV_MAX;
	} else {
		fbdiv_min = FBDIV_FRAC_MIN;
		fbdiv_max = FBDIV_FRAC_MAX;
	}

	for (m = INDIV_MIN; m <= INDIV_MAX; m++) {
		for (n = fbdiv_min; n <= fbdiv_max; n++) {
			for (p = OUTDIV_MIN; p <= OUTDIV_MAX; p++) {
				unsigned long tmp, fout, fclk, diff;

				tmp = div_u64(parent_rate, m);
				if (tmp < PLL_FREF_M_MIN_FREQ ||
				    tmp > PLL_FREF_M_MAX_FREQ)
					continue; /* constrain */

				fclk = div_u64(parent_rate * n, m);
				/* for 2 decimal places */
				if (pll->mode != PLL_MODE_INT)
					fclk = div_u64(fclk, 100);

Annotation

Implementation Notes