drivers/clk/rockchip/clk-half-divider.c

Source file repositories/reference/linux-study-clean/drivers/clk/rockchip/clk-half-divider.c

File Facts

System
Linux kernel
Corpus path
drivers/clk/rockchip/clk-half-divider.c
Extension
.c
Size
5750 bytes
Lines
232
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 (((u64)rate * (i * 2 + 3)) == ((u64)parent_rate_saved * 2)) {
			/*
			 * It's the most ideal case if the requested rate can be
			 * divided from parent clock without needing to change
			 * parent rate, so return the divider immediately.
			 */
			*best_parent_rate = parent_rate_saved;
			return i;
		}
		parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw),
						((u64)rate * (i * 2 + 3)) / 2);
		now = DIV_ROUND_UP_ULL(((u64)parent_rate * 2),
				       (i * 2 + 3));

		if (_is_best_half_div(rate, now, best, flags)) {
			bestdiv = i;
			best = now;
			*best_parent_rate = parent_rate;
		}
	}

	if (!bestdiv) {
		bestdiv = div_mask(width);
		*best_parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), 1);
	}

	return bestdiv;
}

static int clk_half_divider_determine_rate(struct clk_hw *hw,
					   struct clk_rate_request *req)
{
	struct clk_divider *divider = to_clk_divider(hw);
	int div;

	div = clk_half_divider_bestdiv(hw, req->rate, &req->best_parent_rate,
				       divider->width,
				       divider->flags);

	req->rate = DIV_ROUND_UP_ULL(((u64)req->best_parent_rate * 2), div * 2 + 3);

	return 0;
}

static int clk_half_divider_set_rate(struct clk_hw *hw, unsigned long rate,
				     unsigned long parent_rate)
{
	struct clk_divider *divider = to_clk_divider(hw);
	unsigned int value;
	unsigned long flags = 0;
	u32 val;

	value = DIV_ROUND_UP_ULL(((u64)parent_rate * 2), rate);
	value = (value - 3) / 2;
	value =  min_t(unsigned int, value, div_mask(divider->width));

	if (divider->lock)
		spin_lock_irqsave(divider->lock, flags);
	else
		__acquire(divider->lock);

	if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
		val = div_mask(divider->width) << (divider->shift + 16);
	} else {
		val = readl(divider->reg);
		val &= ~(div_mask(divider->width) << divider->shift);
	}
	val |= value << divider->shift;
	writel(val, divider->reg);

	if (divider->lock)
		spin_unlock_irqrestore(divider->lock, flags);
	else
		__release(divider->lock);

	return 0;
}

static const struct clk_ops clk_half_divider_ops = {
	.recalc_rate = clk_half_divider_recalc_rate,
	.determine_rate = clk_half_divider_determine_rate,
	.set_rate = clk_half_divider_set_rate,
};

/*
 * Register a clock branch.
 * Most clock branches have a form like
 *
 * src1 --|--\
 *        |M |--[GATE]-[DIV]-

Annotation

Implementation Notes