drivers/clk/ingenic/cgu.c

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

File Facts

System
Linux kernel
Corpus path
drivers/clk/ingenic/cgu.c
Extension
.c
Size
21534 bytes
Lines
854
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 (!(clk_info->div.bypass_mask & BIT(parent))) {
			div_reg = readl(cgu->base + clk_info->div.reg);
			div = (div_reg >> clk_info->div.shift) &
			      GENMASK(clk_info->div.bits - 1, 0);

			if (clk_info->div.div_table)
				div = clk_info->div.div_table[div];
			else
				div = (div + 1) * clk_info->div.div;

			rate /= div;
		}
	} else if (clk_info->type & CGU_CLK_FIXDIV) {
		rate /= clk_info->fixdiv.div;
	}

	return rate;
}

static unsigned int
ingenic_clk_calc_hw_div(const struct ingenic_cgu_clk_info *clk_info,
			unsigned int div)
{
	unsigned int i, best_i = 0, best = (unsigned int)-1;

	for (i = 0; i < (1 << clk_info->div.bits)
				&& clk_info->div.div_table[i]; i++) {
		if (clk_info->div.div_table[i] >= div &&
		    clk_info->div.div_table[i] < best) {
			best = clk_info->div.div_table[i];
			best_i = i;

			if (div == best)
				break;
		}
	}

	return best_i;
}

static unsigned
ingenic_clk_calc_div(struct clk_hw *hw,
		     const struct ingenic_cgu_clk_info *clk_info,
		     unsigned long parent_rate, unsigned long req_rate)
{
	unsigned int div, hw_div;
	u8 parent;

	parent = ingenic_clk_get_parent(hw);
	if (clk_info->div.bypass_mask & BIT(parent))
		return 1;

	/* calculate the divide */
	div = DIV_ROUND_UP(parent_rate, req_rate);

	if (clk_info->div.div_table) {
		hw_div = ingenic_clk_calc_hw_div(clk_info, div);

		return clk_info->div.div_table[hw_div];
	}

	/* Impose hardware constraints */
	div = clamp_t(unsigned int, div, clk_info->div.div,
		      clk_info->div.div << clk_info->div.bits);

	/*
	 * If the divider value itself must be divided before being written to
	 * the divider register, we must ensure we don't have any bits set that
	 * would be lost as a result of doing so.
	 */
	div = DIV_ROUND_UP(div, clk_info->div.div);
	div *= clk_info->div.div;

	return div;
}

static int ingenic_clk_determine_rate(struct clk_hw *hw,
				      struct clk_rate_request *req)
{
	struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
	const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk);
	unsigned int div = 1;

	if (clk_info->type & CGU_CLK_DIV)
		div = ingenic_clk_calc_div(hw, clk_info, req->best_parent_rate,
					   req->rate);
	else if (clk_info->type & CGU_CLK_FIXDIV)
		div = clk_info->fixdiv.div;
	else if (clk_hw_can_set_rate_parent(hw))
		req->best_parent_rate = req->rate;

Annotation

Implementation Notes