drivers/clk/renesas/r9a09g077-cpg.c

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

File Facts

System
Linux kernel
Corpus path
drivers/clk/renesas/r9a09g077-cpg.c
Extension
.c
Size
16555 bytes
Lines
519
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

if (best_rate == rate) {
			ff = to_clk_fixed_factor(parent_hw);
			return ff->div;
		}
	}

	/* No parent could provide the exact rate - this should not happen */
	return 0;
}

static int r9a09g077_cpg_fselxspi_determine_rate(struct clk_hw *hw,
						 struct clk_rate_request *req)
{
	struct clk_divider *divider = to_clk_divider(hw);
	unsigned long parent_rate, best = 0, now;
	const struct clk_div_table *clkt;
	unsigned long rate = req->rate;
	unsigned int num_parents;
	unsigned int divselxspi;
	unsigned int div = 0;

	if (!rate)
		rate = 1;

	/* Get the number of parents for FSELXSPIn */
	num_parents = clk_hw_get_num_parents(req->best_parent_hw);

	for (clkt = divider->table; clkt->div; clkt++) {
		parent_rate = clk_hw_round_rate(req->best_parent_hw, rate * clkt->div);
		/* Skip if parent can't provide any valid rate */
		if (!parent_rate)
			continue;

		/* Determine which DIVSELXSPIn divider (3 or 4) provides this parent_rate */
		divselxspi = r9a09g077_cpg_fselxspi_get_divider(req->best_parent_hw, parent_rate,
								num_parents);
		if (!divselxspi)
			continue;

		/*
		 * DIVSELXSPIx supports 800MHz and 600MHz operation.
		 * When divselxspi is 4 (600MHz operation), only FSELXSPIn dividers of 8 and 16
		 * are supported. Otherwise, when divselxspi is 3 (800MHz operation),
		 * dividers of 6, 8, 16, 32, and 64 are supported. This check ensures that
		 * FSELXSPIx is set correctly based on hardware limitations.
		 */
		if (divselxspi == 4 && (clkt->div != 8 && clkt->div != 16))
			continue;

		now = DIV_ROUND_UP_ULL(parent_rate, clkt->div);
		if (abs(rate - now) < abs(rate - best)) {
			div = clkt->div;
			best = now;
			req->best_parent_rate = parent_rate;
		}
	}

	if (!div) {
		req->best_parent_rate = clk_hw_round_rate(req->best_parent_hw, 1);
		divselxspi = r9a09g077_cpg_fselxspi_get_divider(req->best_parent_hw,
								req->best_parent_rate,
								num_parents);
		/* default to divider 3 which will result DIVSELXSPIn = 800 MHz */
		if (!divselxspi)
			divselxspi = 3;

		/*
		 * Use the maximum divider based on the parent clock rate:
		 *  - 64 when DIVSELXSPIx is 800 MHz (divider = 3)
		 *  - 16 when DIVSELXSPIx is 600 MHz (divider = 4)
		 */
		div = divselxspi == 3 ? 64 : 16;
	}

	req->rate = DIV_ROUND_UP_ULL(req->best_parent_rate, div);

	return 0;
}

static struct clk * __init
r9a09g077_cpg_fselxspi_div_clk_register(struct device *dev,
					const struct cpg_core_clk *core,
					void __iomem *addr,
					struct cpg_mssr_pub *pub)
{
	static struct clk_ops *xspi_div_ops;
	struct clk_init_data init = {};
	const struct clk *parent;
	const char *parent_name;
	struct clk_divider *div;

Annotation

Implementation Notes