drivers/clk/imx/clk-sscg-pll.c

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

File Facts

System
Linux kernel
Corpus path
drivers/clk/imx/clk-sscg-pll.c
Extension
.c
Size
13023 bytes
Lines
541
Domain
Driver Families
Bucket
drivers/clk
Inferred role
Driver Families: exported/initcall integration point
Status
integration 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 clk_sscg_pll_setup {
	int divr1, divf1;
	int divr2, divf2;
	int divq;
	int bypass;
	uint64_t vco1;
	uint64_t vco2;
	uint64_t fout;
	uint64_t ref;
	uint64_t ref_div1;
	uint64_t ref_div2;
	uint64_t fout_request;
	int fout_error;
};

struct clk_sscg_pll {
	struct clk_hw	hw;
	const struct clk_ops  ops;
	void __iomem *base;
	struct clk_sscg_pll_setup setup;
	u8 parent;
	u8 bypass1;
	u8 bypass2;
};

#define to_clk_sscg_pll(_hw) container_of(_hw, struct clk_sscg_pll, hw)

static int clk_sscg_pll_wait_lock(struct clk_sscg_pll *pll)
{
	u32 val;

	val = readl_relaxed(pll->base + PLL_CFG0);

	/* don't wait for lock if all plls are bypassed */
	if (!(val & SSCG_PLL_BYPASS2_MASK))
		return readl_poll_timeout(pll->base, val, val & PLL_LOCK_MASK,
						0, PLL_SCCG_LOCK_TIMEOUT);

	return 0;
}

static int clk_sscg_pll2_check_match(struct clk_sscg_pll_setup *setup,
					struct clk_sscg_pll_setup *temp_setup)
{
	int new_diff = temp_setup->fout - temp_setup->fout_request;
	int diff = temp_setup->fout_error;

	if (abs(diff) > abs(new_diff)) {
		temp_setup->fout_error = new_diff;
		memcpy(setup, temp_setup, sizeof(struct clk_sscg_pll_setup));

		if (temp_setup->fout_request == temp_setup->fout)
			return 0;
	}
	return -1;
}

static int clk_sscg_divq_lookup(struct clk_sscg_pll_setup *setup,
				struct clk_sscg_pll_setup *temp_setup)
{
	int ret = -EINVAL;

	for (temp_setup->divq = 0; temp_setup->divq <= PLL_DIVQ_MAX;
	     temp_setup->divq++) {
		temp_setup->vco2 = temp_setup->vco1;
		do_div(temp_setup->vco2, temp_setup->divr2 + 1);
		temp_setup->vco2 *= 2;
		temp_setup->vco2 *= temp_setup->divf2 + 1;
		if (temp_setup->vco2 >= PLL_STAGE2_MIN_FREQ &&
				temp_setup->vco2 <= PLL_STAGE2_MAX_FREQ) {
			temp_setup->fout = temp_setup->vco2;
			do_div(temp_setup->fout, 2 * (temp_setup->divq + 1));

			ret = clk_sscg_pll2_check_match(setup, temp_setup);
			if (!ret) {
				temp_setup->bypass = PLL_BYPASS1;
				return ret;
			}
		}
	}

	return ret;
}

static int clk_sscg_divf2_lookup(struct clk_sscg_pll_setup *setup,
					struct clk_sscg_pll_setup *temp_setup)
{
	int ret = -EINVAL;

	for (temp_setup->divf2 = 0; temp_setup->divf2 <= PLL_DIVF2_MAX;

Annotation

Implementation Notes