drivers/clk/mstar/clk-msc313-cpupll.c

Source file repositories/reference/linux-study-clean/drivers/clk/mstar/clk-msc313-cpupll.c

File Facts

System
Linux kernel
Corpus path
drivers/clk/mstar/clk-msc313-cpupll.c
Extension
.c
Size
6528 bytes
Lines
223
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 msc313_cpupll {
	void __iomem *base;
	struct clk_hw clk_hw;
};

#define to_cpupll(_hw) container_of(_hw, struct msc313_cpupll, clk_hw)

static u32 msc313_cpupll_reg_read32(struct msc313_cpupll *cpupll, unsigned int reg)
{
	u32 value;

	value = ioread16(cpupll->base + reg + 4) << 16;
	value |= ioread16(cpupll->base + reg);

	return value;
}

static void msc313_cpupll_reg_write32(struct msc313_cpupll *cpupll, unsigned int reg, u32 value)
{
	u16 l = value & 0xffff, h = (value >> 16) & 0xffff;

	iowrite16(l, cpupll->base + reg);
	iowrite16(h, cpupll->base + reg + 4);
}

static void msc313_cpupll_setfreq(struct msc313_cpupll *cpupll, u32 regvalue)
{
	ktime_t timeout;

	msc313_cpupll_reg_write32(cpupll, REG_LPF_HIGH_BOTTOM, regvalue);

	iowrite16(0x1, cpupll->base + REG_LPF_MYSTERYONE);
	iowrite16(0x6, cpupll->base + REG_LPF_MYSTERYTWO);
	iowrite16(0x8, cpupll->base + REG_LPF_UPDATE_COUNT);
	iowrite16(BIT(12), cpupll->base + REG_LPF_TRANSITIONCTRL);

	iowrite16(0, cpupll->base + REG_LPF_TOGGLE);
	iowrite16(1, cpupll->base + REG_LPF_TOGGLE);

	timeout = ktime_add_ns(ktime_get(), LPF_LOCK_TIMEOUT);
	while (!(ioread16(cpupll->base + REG_LPF_LOCK))) {
		if (ktime_after(ktime_get(), timeout)) {
			pr_err("timeout waiting for LPF_LOCK\n");
			return;
		}
		cpu_relax();
	}

	iowrite16(0, cpupll->base + REG_LPF_TOGGLE);

	msc313_cpupll_reg_write32(cpupll, REG_LPF_LOW_L, regvalue);
}

static unsigned long msc313_cpupll_frequencyforreg(u32 reg, unsigned long parent_rate)
{
	unsigned long long prescaled = ((unsigned long long)parent_rate) * MULTIPLIER;

	if (prescaled == 0 || reg == 0)
		return 0;
	return DIV_ROUND_DOWN_ULL(prescaled, reg);
}

static u32 msc313_cpupll_regforfrequecy(unsigned long rate, unsigned long parent_rate)
{
	unsigned long long prescaled = ((unsigned long long)parent_rate) * MULTIPLIER;

	if (prescaled == 0 || rate == 0)
		return 0;
	return DIV_ROUND_UP_ULL(prescaled, rate);
}

static unsigned long msc313_cpupll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
	struct msc313_cpupll *cpupll = to_cpupll(hw);

	return msc313_cpupll_frequencyforreg(msc313_cpupll_reg_read32(cpupll, REG_LPF_LOW_L),
					     parent_rate);
}

static int msc313_cpupll_determine_rate(struct clk_hw *hw,
					struct clk_rate_request *req)
{
	u32 reg = msc313_cpupll_regforfrequecy(req->rate, req->best_parent_rate);
	long rounded = msc313_cpupll_frequencyforreg(reg, req->best_parent_rate);

	/*
	 * This is my poor attempt at making sure the resulting
	 * rate doesn't overshoot the requested rate.
	 */
	for (; rounded >= req->rate && reg > 0; reg--)

Annotation

Implementation Notes