drivers/clk/analogbits/wrpll-cln28hpc.c

Source file repositories/reference/linux-study-clean/drivers/clk/analogbits/wrpll-cln28hpc.c

File Facts

System
Linux kernel
Corpus path
drivers/clk/analogbits/wrpll-cln28hpc.c
Extension
.c
Size
11473 bytes
Lines
377
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

if (__wrpll_update_parent_rate(c, parent_rate)) {
			pr_err("%s: PLL input rate is out of range\n",
			       __func__);
			return -ERANGE;
		}
	}

	c->flags &= ~WRPLL_FLAGS_RESET_MASK;

	/* Put the PLL into bypass if the user requests the parent clock rate */
	if (target_rate == parent_rate) {
		c->flags |= WRPLL_FLAGS_BYPASS_MASK;
		return 0;
	}

	c->flags &= ~WRPLL_FLAGS_BYPASS_MASK;

	/* Calculate the Q shift and target VCO rate */
	divq = __wrpll_calc_divq(target_rate, &target_vco_rate);
	if (!divq)
		return -1;
	c->divq = divq;

	/* Precalculate the pre-Q divider target ratio */
	ratio = div64_u64((target_vco_rate << ROUND_SHIFT), parent_rate);

	fbdiv = __wrpll_calc_fbdiv(c);
	best_r = 0;
	best_f = 0;
	best_delta = MAX_VCO_FREQ;

	/*
	 * Consider all values for R which land within
	 * [MIN_POST_DIVR_FREQ, MAX_POST_DIVR_FREQ]; prefer smaller R
	 */
	for (r = c->init_r; r <= c->max_r; ++r) {
		f_pre_div = ratio * r;
		f = (f_pre_div + (1 << ROUND_SHIFT)) >> ROUND_SHIFT;
		f >>= (fbdiv - 1);

		post_divr_freq = div_u64(parent_rate, r);
		vco_pre = fbdiv * post_divr_freq;
		vco = vco_pre * f;

		/* Ensure rounding didn't take us out of range */
		if (vco > target_vco_rate) {
			--f;
			vco = vco_pre * f;
		} else if (vco < MIN_VCO_FREQ) {
			++f;
			vco = vco_pre * f;
		}

		delta = abs(target_vco_rate - vco);
		if (delta < best_delta) {
			best_delta = delta;
			best_r = r;
			best_f = f;
		}
	}

	c->divr = best_r - 1;
	c->divf = best_f - 1;

	post_divr_freq = div_u64(parent_rate, best_r);

	/* Pick the best PLL jitter filter */
	range = __wrpll_calc_filter_range(post_divr_freq);
	if (range < 0)
		return range;
	c->range = range;

	return 0;
}
EXPORT_SYMBOL_GPL(wrpll_configure_for_rate);

/**
 * wrpll_calc_output_rate() - calculate the PLL's target output rate
 * @c: ptr to a struct wrpll_cfg record to read from
 * @parent_rate: PLL refclk rate
 *
 * Given a pointer to the PLL's current input configuration @c and the
 * PLL's input reference clock rate @parent_rate (before the R
 * pre-divider), calculate the PLL's output clock rate (after the Q
 * post-divider).
 *
 * Context: Any context.  Caller must protect the memory pointed to by @c
 *          from simultaneous modification.
 *
 * Return: the PLL's output clock rate, in Hz.  The return value from

Annotation

Implementation Notes