drivers/clk/hisilicon/clkdivider-hi6220.c

Source file repositories/reference/linux-study-clean/drivers/clk/hisilicon/clkdivider-hi6220.c

File Facts

System
Linux kernel
Corpus path
drivers/clk/hisilicon/clkdivider-hi6220.c
Extension
.c
Size
3705 bytes
Lines
155
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 hi6220_clk_divider {
	struct clk_hw	hw;
	void __iomem	*reg;
	u8		shift;
	u8		width;
	u32		mask;
	const struct clk_div_table *table;
	spinlock_t	*lock;
};

#define to_hi6220_clk_divider(_hw)	\
	container_of(_hw, struct hi6220_clk_divider, hw)

static unsigned long hi6220_clkdiv_recalc_rate(struct clk_hw *hw,
					unsigned long parent_rate)
{
	unsigned int val;
	struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw);

	val = readl_relaxed(dclk->reg) >> dclk->shift;
	val &= div_mask(dclk->width);

	return divider_recalc_rate(hw, parent_rate, val, dclk->table,
				   CLK_DIVIDER_ROUND_CLOSEST, dclk->width);
}

static int hi6220_clkdiv_determine_rate(struct clk_hw *hw,
					struct clk_rate_request *req)
{
	struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw);

	return divider_determine_rate(hw, req, dclk->table, dclk->width,
				      CLK_DIVIDER_ROUND_CLOSEST);
}

static int hi6220_clkdiv_set_rate(struct clk_hw *hw, unsigned long rate,
					unsigned long parent_rate)
{
	int value;
	unsigned long flags = 0;
	u32 data;
	struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw);

	value = divider_get_val(rate, parent_rate, dclk->table,
				dclk->width, CLK_DIVIDER_ROUND_CLOSEST);

	if (dclk->lock)
		spin_lock_irqsave(dclk->lock, flags);

	data = readl_relaxed(dclk->reg);
	data &= ~(div_mask(dclk->width) << dclk->shift);
	data |= value << dclk->shift;
	data |= dclk->mask;

	writel_relaxed(data, dclk->reg);

	if (dclk->lock)
		spin_unlock_irqrestore(dclk->lock, flags);

	return 0;
}

static const struct clk_ops hi6220_clkdiv_ops = {
	.recalc_rate = hi6220_clkdiv_recalc_rate,
	.determine_rate = hi6220_clkdiv_determine_rate,
	.set_rate = hi6220_clkdiv_set_rate,
};

struct clk *hi6220_register_clkdiv(struct device *dev, const char *name,
	const char *parent_name, unsigned long flags, void __iomem *reg,
	u8 shift, u8 width, u32 mask_bit, spinlock_t *lock)
{
	struct hi6220_clk_divider *div;
	struct clk *clk;
	struct clk_init_data init;
	struct clk_div_table *table;
	u32 max_div, min_div;
	int i;

	/* allocate the divider */
	div = kzalloc_obj(*div);
	if (!div)
		return ERR_PTR(-ENOMEM);

	/* Init the divider table */
	max_div = div_mask(width) + 1;
	min_div = 1;

	table = kzalloc_objs(*table, max_div + 1);
	if (!table) {

Annotation

Implementation Notes