drivers/clk/at91/clk-sam9x60-pll.c

Source file repositories/reference/linux-study-clean/drivers/clk/at91/clk-sam9x60-pll.c

File Facts

System
Linux kernel
Corpus path
drivers/clk/at91/clk-sam9x60-pll.c
Extension
.c
Size
21850 bytes
Lines
797
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 sam9x60_pll_core {
	struct regmap *regmap;
	spinlock_t *lock;
	const struct clk_pll_characteristics *characteristics;
	const struct clk_pll_layout *layout;
	struct clk_hw hw;
	u8 id;
};

struct sam9x60_frac {
	struct sam9x60_pll_core core;
	struct at91_clk_pms pms;
	u32 frac;
	u16 mul;
};

struct sam9x60_div {
	struct sam9x60_pll_core core;
	struct at91_clk_pms pms;
	u8 div;
	u8 safe_div;
};

#define to_sam9x60_pll_core(hw)	container_of(hw, struct sam9x60_pll_core, hw)
#define to_sam9x60_frac(core)	container_of(core, struct sam9x60_frac, core)
#define to_sam9x60_div(core)	container_of(core, struct sam9x60_div, core)

static struct sam9x60_div *notifier_div;

static inline bool sam9x60_pll_ready(struct regmap *regmap, int id)
{
	unsigned int status;

	regmap_read(regmap, AT91_PMC_PLL_ISR0, &status);

	return !!(status & BIT(id));
}

static bool sam9x60_frac_pll_ready(struct regmap *regmap, u8 id)
{
	return sam9x60_pll_ready(regmap, id);
}

static unsigned long sam9x60_frac_pll_recalc_rate(struct clk_hw *hw,
						  unsigned long parent_rate)
{
	struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
	struct sam9x60_frac *frac = to_sam9x60_frac(core);
	unsigned long freq;

	freq = parent_rate * (frac->mul + 1) +
		DIV_ROUND_CLOSEST_ULL((u64)parent_rate * frac->frac, (1 << 22));

	if (core->layout->div2)
		freq >>= 1;

	return freq;
}

static int sam9x60_frac_pll_set(struct sam9x60_pll_core *core)
{
	struct sam9x60_frac *frac = to_sam9x60_frac(core);
	struct regmap *regmap = core->regmap;
	unsigned int val, cfrac, cmul;
	unsigned long flags;

	spin_lock_irqsave(core->lock, flags);

	regmap_write_bits(regmap, AT91_PMC_PLL_UPDT,
			  AT91_PMC_PLL_UPDT_ID_MSK, core->id);
	regmap_read(regmap, AT91_PMC_PLL_CTRL1, &val);
	cmul = (val & core->layout->mul_mask) >> core->layout->mul_shift;
	cfrac = (val & core->layout->frac_mask) >> core->layout->frac_shift;

	if (sam9x60_frac_pll_ready(regmap, core->id) &&
	    (cmul == frac->mul && cfrac == frac->frac))
		goto unlock;

	/* Load recommended value for PMC_PLL_ACR */
	val = core->characteristics->acr;
	regmap_write(regmap, AT91_PMC_PLL_ACR, val);

	regmap_write(regmap, AT91_PMC_PLL_CTRL1,
		     (frac->mul << core->layout->mul_shift) |
		     (frac->frac << core->layout->frac_shift));

	if (core->characteristics->upll) {
		/* Enable the UTMI internal bandgap */
		val |= AT91_PMC_PLL_ACR_UTMIBG;
		regmap_write(regmap, AT91_PMC_PLL_ACR, val);

Annotation

Implementation Notes