drivers/clk/clk-apple-nco.c

Source file repositories/reference/linux-study-clean/drivers/clk/clk-apple-nco.c

File Facts

System
Linux kernel
Corpus path
drivers/clk/clk-apple-nco.c
Extension
.c
Size
8923 bytes
Lines
341
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 applnco_tables {
	u16 fwd[LFSR_TBLSIZE];
	u16 inv[LFSR_TBLSIZE];
};

struct applnco_channel {
	void __iomem *base;
	struct applnco_tables *tbl;
	struct clk_hw hw;

	spinlock_t lock;
};

#define to_applnco_channel(_hw) container_of(_hw, struct applnco_channel, hw)

static void applnco_enable_nolock(struct clk_hw *hw)
{
	struct applnco_channel *chan = to_applnco_channel(hw);
	u32 val;

	val = readl_relaxed(chan->base + REG_CTRL);
	writel_relaxed(val | CTRL_ENABLE, chan->base + REG_CTRL);
}

static void applnco_disable_nolock(struct clk_hw *hw)
{
	struct applnco_channel *chan = to_applnco_channel(hw);
	u32 val;

	val = readl_relaxed(chan->base + REG_CTRL);
	writel_relaxed(val & ~CTRL_ENABLE, chan->base + REG_CTRL);
}

static int applnco_is_enabled(struct clk_hw *hw)
{
	struct applnco_channel *chan = to_applnco_channel(hw);

	return (readl_relaxed(chan->base + REG_CTRL) & CTRL_ENABLE) != 0;
}

static void applnco_compute_tables(struct applnco_tables *tbl)
{
	int i;
	u32 state = LFSR_INIT;

	/*
	 * Go through the states of a Galois LFSR and build
	 * a coarse divisor translation table.
	 */
	for (i = LFSR_PERIOD; i > 0; i--) {
		if (state & 1)
			state = (state >> 1) ^ (LFSR_POLY >> 1);
		else
			state = (state >> 1);
		tbl->fwd[i] = state;
		tbl->inv[state] = i;
	}

	/* Zero value is special-cased */
	tbl->fwd[0] = 0;
	tbl->inv[0] = 0;
}

static bool applnco_div_out_of_range(unsigned int div)
{
	unsigned int coarse = div / 4;

	return coarse < COARSE_DIV_OFFSET ||
		coarse >= COARSE_DIV_OFFSET + LFSR_TBLSIZE;
}

static u32 applnco_div_translate(struct applnco_tables *tbl, unsigned int div)
{
	unsigned int coarse = div / 4;

	if (WARN_ON(applnco_div_out_of_range(div)))
		return 0;

	return FIELD_PREP(DIV_COARSE, tbl->fwd[coarse - COARSE_DIV_OFFSET]) |
			FIELD_PREP(DIV_FINE, div % 4);
}

static unsigned int applnco_div_translate_inv(struct applnco_tables *tbl, u32 regval)
{
	unsigned int coarse, fine;

	coarse = tbl->inv[FIELD_GET(DIV_COARSE, regval)] + COARSE_DIV_OFFSET;
	fine = FIELD_GET(DIV_FINE, regval);

	return coarse * 4 + fine;

Annotation

Implementation Notes