drivers/clk/samsung/clk-pll.c

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

File Facts

System
Linux kernel
Corpus path
drivers/clk/samsung/clk-pll.c
Extension
.c
Size
50953 bytes
Lines
1781
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 samsung_clk_pll {
	struct clk_hw		hw;
	void __iomem		*lock_reg;
	void __iomem		*con_reg;
	/* PLL enable control bit offset in @con_reg register */
	unsigned short		enable_offs;
	/* PLL lock status bit offset in @con_reg register */
	unsigned short		lock_offs;
	enum samsung_pll_type	type;
	unsigned int		rate_count;
	const struct samsung_pll_rate_table *rate_table;
};

#define to_clk_pll(_hw) container_of(_hw, struct samsung_clk_pll, hw)

static const struct samsung_pll_rate_table *samsung_get_pll_settings(
				struct samsung_clk_pll *pll, unsigned long rate)
{
	const struct samsung_pll_rate_table  *rate_table = pll->rate_table;
	int i;

	for (i = 0; i < pll->rate_count; i++) {
		if (rate == rate_table[i].rate)
			return &rate_table[i];
	}

	return NULL;
}

static int samsung_pll_determine_rate(struct clk_hw *hw,
				      struct clk_rate_request *req)
{
	struct samsung_clk_pll *pll = to_clk_pll(hw);
	const struct samsung_pll_rate_table *rate_table = pll->rate_table;
	int i;

	/* Assuming rate_table is in descending order */
	for (i = 0; i < pll->rate_count; i++) {
		if (req->rate >= rate_table[i].rate) {
			req->rate = rate_table[i].rate;

			return 0;
		}
	}

	/* return minimum supported value */
	req->rate = rate_table[i - 1].rate;

	return 0;
}

/* Wait until the PLL is locked */
static int samsung_pll_lock_wait(struct samsung_clk_pll *pll,
				 unsigned int reg_mask)
{
	int ret;
	u32 val;

	/*
	 * This function might be called when the timekeeping API can't be used
	 * to detect timeouts. One situation is when the clocksource is not yet
	 * initialized, another when the timekeeping is suspended. udelay() also
	 * cannot be used when the clocksource is not running on arm64, since
	 * the current timer is used as cycle counter. So a simple busy loop
	 * is used here.
	 * The limit of iterations has been derived from experimental
	 * measurements of various PLLs on multiple Exynos SoC variants. Single
	 * register read time was usually in range 0.4...1.5 us, never less than
	 * 0.4 us.
	 */
	ret = readl_relaxed_poll_timeout_atomic(pll->con_reg, val,
						val & reg_mask, 0,
						PLL_TIMEOUT_LOOPS);
	if (ret < 0)
		pr_err("Could not lock PLL %s\n", clk_hw_get_name(&pll->hw));

	return ret;
}

static int samsung_pll3xxx_enable(struct clk_hw *hw)
{
	struct samsung_clk_pll *pll = to_clk_pll(hw);
	u32 tmp;

	tmp = readl_relaxed(pll->con_reg);
	tmp |= BIT(pll->enable_offs);
	writel_relaxed(tmp, pll->con_reg);

	return samsung_pll_lock_wait(pll, BIT(pll->lock_offs));
}

Annotation

Implementation Notes