drivers/clk/mvebu/dove-divider.c

Source file repositories/reference/linux-study-clean/drivers/clk/mvebu/dove-divider.c

File Facts

System
Linux kernel
Corpus path
drivers/clk/mvebu/dove-divider.c
Extension
.c
Size
5896 bytes
Lines
263
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

struct dove_clk {
	const char *name;
	struct clk_hw hw;
	void __iomem *base;
	spinlock_t *lock;
	u8 div_bit_start;
	u8 div_bit_end;
	u8 div_bit_load;
	u8 div_bit_size;
	u32 *divider_table;
};

enum {
	DIV_CTRL0 = 0,
	DIV_CTRL1 = 4,
	DIV_CTRL1_N_RESET_MASK = BIT(10),
};

#define to_dove_clk(hw) container_of(hw, struct dove_clk, hw)

static void dove_load_divider(void __iomem *base, u32 val, u32 mask, u32 load)
{
	u32 v;

	v = readl_relaxed(base + DIV_CTRL1) | DIV_CTRL1_N_RESET_MASK;
	writel_relaxed(v, base + DIV_CTRL1);

	v = (readl_relaxed(base + DIV_CTRL0) & ~(mask | load)) | val;
	writel_relaxed(v, base + DIV_CTRL0);
	writel_relaxed(v | load, base + DIV_CTRL0);
	ndelay(250);
	writel_relaxed(v, base + DIV_CTRL0);
}

static unsigned int dove_get_divider(struct dove_clk *dc)
{
	unsigned int divider;
	u32 val;

	val = readl_relaxed(dc->base + DIV_CTRL0);
	val >>= dc->div_bit_start;

	divider = val & ~(~0 << dc->div_bit_size);

	if (dc->divider_table)
		divider = dc->divider_table[divider];

	return divider;
}

static int dove_calc_divider(const struct dove_clk *dc, unsigned long rate,
			     unsigned long parent_rate, bool set)
{
	unsigned int divider, max;

	divider = DIV_ROUND_CLOSEST(parent_rate, rate);

	if (dc->divider_table) {
		unsigned int i;

		for (i = 0; dc->divider_table[i]; i++)
			if (divider == dc->divider_table[i]) {
				divider = i;
				break;
			}

		if (!dc->divider_table[i])
			return -EINVAL;
	} else {
		max = 1 << dc->div_bit_size;

		if (set && (divider == 0 || divider >= max))
			return -EINVAL;
		if (divider >= max)
			divider = max - 1;
		else if (divider == 0)
			divider = 1;
	}

	return divider;
}

static unsigned long dove_recalc_rate(struct clk_hw *hw, unsigned long parent)
{
	struct dove_clk *dc = to_dove_clk(hw);
	unsigned int divider = dove_get_divider(dc);
	unsigned long rate = DIV_ROUND_CLOSEST(parent, divider);

	pr_debug("%s(): %s divider=%u parent=%lu rate=%lu\n",
		 __func__, dc->name, divider, parent, rate);

Annotation

Implementation Notes