drivers/clk/mmp/clk-mix.c

Source file repositories/reference/linux-study-clean/drivers/clk/mmp/clk-mix.c

File Facts

System
Linux kernel
Corpus path
drivers/clk/mmp/clk-mix.c
Extension
.c
Size
11870 bytes
Lines
505
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 (parent_rate % item->rate) {
			item->valid = 0;
		} else {
			item->divisor = parent_rate / item->rate;
			item->valid = 1;
		}
	}
}

static int _set_rate(struct mmp_clk_mix *mix, u32 mux_val, u32 div_val,
			unsigned int change_mux, unsigned int change_div)
{
	struct mmp_clk_mix_reg_info *ri = &mix->reg_info;
	u8 width, shift;
	u32 mux_div, fc_req;
	int ret, timeout = 50;
	unsigned long flags = 0;

	if (!change_mux && !change_div)
		return -EINVAL;

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

	if (mix->type == MMP_CLK_MIX_TYPE_V1
		|| mix->type == MMP_CLK_MIX_TYPE_V2)
		mux_div = readl(ri->reg_clk_ctrl);
	else
		mux_div = readl(ri->reg_clk_sel);

	if (change_div) {
		width = ri->width_div;
		shift = ri->shift_div;
		mux_div &= ~MMP_CLK_BITS_MASK(width, shift);
		mux_div |= MMP_CLK_BITS_SET_VAL(div_val, width, shift);
	}

	if (change_mux) {
		width = ri->width_mux;
		shift = ri->shift_mux;
		mux_div &= ~MMP_CLK_BITS_MASK(width, shift);
		mux_div |= MMP_CLK_BITS_SET_VAL(mux_val, width, shift);
	}

	if (mix->type == MMP_CLK_MIX_TYPE_V1) {
		writel(mux_div, ri->reg_clk_ctrl);
	} else if (mix->type == MMP_CLK_MIX_TYPE_V2) {
		mux_div |= (1 << ri->bit_fc);
		writel(mux_div, ri->reg_clk_ctrl);

		do {
			fc_req = readl(ri->reg_clk_ctrl);
			timeout--;
			if (!(fc_req & (1 << ri->bit_fc)))
				break;
		} while (timeout);

		if (timeout == 0) {
			pr_err("%s:%s cannot do frequency change\n",
				__func__, clk_hw_get_name(&mix->hw));
			ret = -EBUSY;
			goto error;
		}
	} else {
		fc_req = readl(ri->reg_clk_ctrl);
		fc_req |= 1 << ri->bit_fc;
		writel(fc_req, ri->reg_clk_ctrl);
		writel(mux_div, ri->reg_clk_sel);
		fc_req &= ~(1 << ri->bit_fc);
	}

	ret = 0;
error:
	if (mix->lock)
		spin_unlock_irqrestore(mix->lock, flags);

	return ret;
}

static int mmp_clk_mix_determine_rate(struct clk_hw *hw,
				      struct clk_rate_request *req)
{
	struct mmp_clk_mix *mix = to_clk_mix(hw);
	struct mmp_clk_mix_clk_table *item;
	struct clk_hw *parent, *parent_best;
	unsigned long parent_rate, mix_rate, mix_rate_best, parent_rate_best;
	unsigned long gap, gap_best;
	u32 div_val_max;
	unsigned int div;
	int i, j;

Annotation

Implementation Notes