drivers/clk/qcom/clk-cbf-8996.c

Source file repositories/reference/linux-study-clean/drivers/clk/qcom/clk-cbf-8996.c

File Facts

System
Linux kernel
Corpus path
drivers/clk/qcom/clk-cbf-8996.c
Extension
.c
Size
9556 bytes
Lines
371
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_cbf_8996_mux {
	u32 reg;
	struct notifier_block nb;
	struct clk_regmap clkr;
};

static struct clk_cbf_8996_mux *to_clk_cbf_8996_mux(struct clk_regmap *clkr)
{
	return container_of(clkr, struct clk_cbf_8996_mux, clkr);
}

static int cbf_clk_notifier_cb(struct notifier_block *nb, unsigned long event,
			       void *data);

static u8 clk_cbf_8996_mux_get_parent(struct clk_hw *hw)
{
	struct clk_regmap *clkr = to_clk_regmap(hw);
	struct clk_cbf_8996_mux *mux = to_clk_cbf_8996_mux(clkr);
	u32 val;

	regmap_read(clkr->regmap, mux->reg, &val);

	return FIELD_GET(CBF_MUX_PARENT_MASK, val);
}

static int clk_cbf_8996_mux_set_parent(struct clk_hw *hw, u8 index)
{
	struct clk_regmap *clkr = to_clk_regmap(hw);
	struct clk_cbf_8996_mux *mux = to_clk_cbf_8996_mux(clkr);
	u32 val;

	val = FIELD_PREP(CBF_MUX_PARENT_MASK, index);

	return regmap_update_bits(clkr->regmap, mux->reg, CBF_MUX_PARENT_MASK, val);
}

static int clk_cbf_8996_mux_determine_rate(struct clk_hw *hw,
					   struct clk_rate_request *req)
{
	struct clk_hw *parent;

	if (req->rate < (DIV_THRESHOLD / cbf_pll_postdiv.div))
		return -EINVAL;

	if (req->rate < DIV_THRESHOLD)
		parent = clk_hw_get_parent_by_index(hw, CBF_DIV_INDEX);
	else
		parent = clk_hw_get_parent_by_index(hw, CBF_PLL_INDEX);

	if (!parent)
		return -EINVAL;

	req->best_parent_rate = clk_hw_round_rate(parent, req->rate);
	req->best_parent_hw = parent;

	return 0;
}

static const struct clk_ops clk_cbf_8996_mux_ops = {
	.set_parent = clk_cbf_8996_mux_set_parent,
	.get_parent = clk_cbf_8996_mux_get_parent,
	.determine_rate = clk_cbf_8996_mux_determine_rate,
};

static struct clk_cbf_8996_mux cbf_mux = {
	.reg = CBF_MUX_OFFSET,
	.nb.notifier_call = cbf_clk_notifier_cb,
	.clkr.hw.init = &(struct clk_init_data) {
		.name = "cbf_mux",
		.parent_data = cbf_mux_parent_data,
		.num_parents = ARRAY_SIZE(cbf_mux_parent_data),
		.ops = &clk_cbf_8996_mux_ops,
		/* CPU clock is critical and should never be gated */
		.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
	},
};

static int cbf_clk_notifier_cb(struct notifier_block *nb, unsigned long event,
			       void *data)
{
	struct clk_notifier_data *cnd = data;

	switch (event) {
	case PRE_RATE_CHANGE:
		/*
		 * Avoid overvolting. clk_core_set_rate_nolock() walks from top
		 * to bottom, so it will change the rate of the PLL before
		 * chaging the parent of PMUX. This can result in pmux getting
		 * clocked twice the expected rate.
		 *

Annotation

Implementation Notes