xref: /kernel/linux/linux-6.6/drivers/clk/ti/mux.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * TI Multiplexer Clock
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2013 Texas Instruments, Inc.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Tero Kristo <t-kristo@ti.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/clk-provider.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/err.h>
1362306a36Sopenharmony_ci#include <linux/of.h>
1462306a36Sopenharmony_ci#include <linux/of_address.h>
1562306a36Sopenharmony_ci#include <linux/clk/ti.h>
1662306a36Sopenharmony_ci#include "clock.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#undef pr_fmt
1962306a36Sopenharmony_ci#define pr_fmt(fmt) "%s: " fmt, __func__
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic u8 ti_clk_mux_get_parent(struct clk_hw *hw)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	struct clk_omap_mux *mux = to_clk_omap_mux(hw);
2462306a36Sopenharmony_ci	int num_parents = clk_hw_get_num_parents(hw);
2562306a36Sopenharmony_ci	u32 val;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	/*
2862306a36Sopenharmony_ci	 * FIXME need a mux-specific flag to determine if val is bitwise or
2962306a36Sopenharmony_ci	 * numeric. e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges
3062306a36Sopenharmony_ci	 * from 0x1 to 0x7 (index starts at one)
3162306a36Sopenharmony_ci	 * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so
3262306a36Sopenharmony_ci	 * val = 0x4 really means "bit 2, index starts at bit 0"
3362306a36Sopenharmony_ci	 */
3462306a36Sopenharmony_ci	val = ti_clk_ll_ops->clk_readl(&mux->reg) >> mux->shift;
3562306a36Sopenharmony_ci	val &= mux->mask;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	if (mux->table) {
3862306a36Sopenharmony_ci		int i;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci		for (i = 0; i < num_parents; i++)
4162306a36Sopenharmony_ci			if (mux->table[i] == val)
4262306a36Sopenharmony_ci				return i;
4362306a36Sopenharmony_ci		return -EINVAL;
4462306a36Sopenharmony_ci	}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	if (val && (mux->flags & CLK_MUX_INDEX_BIT))
4762306a36Sopenharmony_ci		val = ffs(val) - 1;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	if (val && (mux->flags & CLK_MUX_INDEX_ONE))
5062306a36Sopenharmony_ci		val--;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (val >= num_parents)
5362306a36Sopenharmony_ci		return -EINVAL;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	return val;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic int ti_clk_mux_set_parent(struct clk_hw *hw, u8 index)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct clk_omap_mux *mux = to_clk_omap_mux(hw);
6162306a36Sopenharmony_ci	u32 val;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	if (mux->table) {
6462306a36Sopenharmony_ci		index = mux->table[index];
6562306a36Sopenharmony_ci	} else {
6662306a36Sopenharmony_ci		if (mux->flags & CLK_MUX_INDEX_BIT)
6762306a36Sopenharmony_ci			index = (1 << ffs(index));
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci		if (mux->flags & CLK_MUX_INDEX_ONE)
7062306a36Sopenharmony_ci			index++;
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	if (mux->flags & CLK_MUX_HIWORD_MASK) {
7462306a36Sopenharmony_ci		val = mux->mask << (mux->shift + 16);
7562306a36Sopenharmony_ci	} else {
7662306a36Sopenharmony_ci		val = ti_clk_ll_ops->clk_readl(&mux->reg);
7762306a36Sopenharmony_ci		val &= ~(mux->mask << mux->shift);
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci	val |= index << mux->shift;
8062306a36Sopenharmony_ci	ti_clk_ll_ops->clk_writel(val, &mux->reg);
8162306a36Sopenharmony_ci	ti_clk_latch(&mux->reg, mux->latch);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	return 0;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci/**
8762306a36Sopenharmony_ci * clk_mux_save_context - Save the parent selcted in the mux
8862306a36Sopenharmony_ci * @hw: pointer  struct clk_hw
8962306a36Sopenharmony_ci *
9062306a36Sopenharmony_ci * Save the parent mux value.
9162306a36Sopenharmony_ci */
9262306a36Sopenharmony_cistatic int clk_mux_save_context(struct clk_hw *hw)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	struct clk_omap_mux *mux = to_clk_omap_mux(hw);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	mux->saved_parent = ti_clk_mux_get_parent(hw);
9762306a36Sopenharmony_ci	return 0;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/**
10162306a36Sopenharmony_ci * clk_mux_restore_context - Restore the parent in the mux
10262306a36Sopenharmony_ci * @hw: pointer  struct clk_hw
10362306a36Sopenharmony_ci *
10462306a36Sopenharmony_ci * Restore the saved parent mux value.
10562306a36Sopenharmony_ci */
10662306a36Sopenharmony_cistatic void clk_mux_restore_context(struct clk_hw *hw)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct clk_omap_mux *mux = to_clk_omap_mux(hw);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	ti_clk_mux_set_parent(hw, mux->saved_parent);
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ciconst struct clk_ops ti_clk_mux_ops = {
11462306a36Sopenharmony_ci	.get_parent = ti_clk_mux_get_parent,
11562306a36Sopenharmony_ci	.set_parent = ti_clk_mux_set_parent,
11662306a36Sopenharmony_ci	.determine_rate = __clk_mux_determine_rate,
11762306a36Sopenharmony_ci	.save_context = clk_mux_save_context,
11862306a36Sopenharmony_ci	.restore_context = clk_mux_restore_context,
11962306a36Sopenharmony_ci};
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic struct clk *_register_mux(struct device_node *node, const char *name,
12262306a36Sopenharmony_ci				 const char * const *parent_names,
12362306a36Sopenharmony_ci				 u8 num_parents, unsigned long flags,
12462306a36Sopenharmony_ci				 struct clk_omap_reg *reg, u8 shift, u32 mask,
12562306a36Sopenharmony_ci				 s8 latch, u8 clk_mux_flags, u32 *table)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	struct clk_omap_mux *mux;
12862306a36Sopenharmony_ci	struct clk *clk;
12962306a36Sopenharmony_ci	struct clk_init_data init;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/* allocate the mux */
13262306a36Sopenharmony_ci	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
13362306a36Sopenharmony_ci	if (!mux)
13462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	init.name = name;
13762306a36Sopenharmony_ci	init.ops = &ti_clk_mux_ops;
13862306a36Sopenharmony_ci	init.flags = flags;
13962306a36Sopenharmony_ci	init.parent_names = parent_names;
14062306a36Sopenharmony_ci	init.num_parents = num_parents;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/* struct clk_mux assignments */
14362306a36Sopenharmony_ci	memcpy(&mux->reg, reg, sizeof(*reg));
14462306a36Sopenharmony_ci	mux->shift = shift;
14562306a36Sopenharmony_ci	mux->mask = mask;
14662306a36Sopenharmony_ci	mux->latch = latch;
14762306a36Sopenharmony_ci	mux->flags = clk_mux_flags;
14862306a36Sopenharmony_ci	mux->table = table;
14962306a36Sopenharmony_ci	mux->hw.init = &init;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	clk = of_ti_clk_register(node, &mux->hw, name);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	if (IS_ERR(clk))
15462306a36Sopenharmony_ci		kfree(mux);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	return clk;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci/**
16062306a36Sopenharmony_ci * of_mux_clk_setup - Setup function for simple mux rate clock
16162306a36Sopenharmony_ci * @node: DT node for the clock
16262306a36Sopenharmony_ci *
16362306a36Sopenharmony_ci * Sets up a basic clock multiplexer.
16462306a36Sopenharmony_ci */
16562306a36Sopenharmony_cistatic void of_mux_clk_setup(struct device_node *node)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	struct clk *clk;
16862306a36Sopenharmony_ci	struct clk_omap_reg reg;
16962306a36Sopenharmony_ci	unsigned int num_parents;
17062306a36Sopenharmony_ci	const char **parent_names;
17162306a36Sopenharmony_ci	const char *name;
17262306a36Sopenharmony_ci	u8 clk_mux_flags = 0;
17362306a36Sopenharmony_ci	u32 mask = 0;
17462306a36Sopenharmony_ci	u32 shift = 0;
17562306a36Sopenharmony_ci	s32 latch = -EINVAL;
17662306a36Sopenharmony_ci	u32 flags = CLK_SET_RATE_NO_REPARENT;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	num_parents = of_clk_get_parent_count(node);
17962306a36Sopenharmony_ci	if (num_parents < 2) {
18062306a36Sopenharmony_ci		pr_err("mux-clock %pOFn must have parents\n", node);
18162306a36Sopenharmony_ci		return;
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci	parent_names = kzalloc((sizeof(char *) * num_parents), GFP_KERNEL);
18462306a36Sopenharmony_ci	if (!parent_names)
18562306a36Sopenharmony_ci		goto cleanup;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	of_clk_parent_fill(node, parent_names, num_parents);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	if (ti_clk_get_reg_addr(node, 0, &reg))
19062306a36Sopenharmony_ci		goto cleanup;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	of_property_read_u32(node, "ti,bit-shift", &shift);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	of_property_read_u32(node, "ti,latch-bit", &latch);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	if (of_property_read_bool(node, "ti,index-starts-at-one"))
19762306a36Sopenharmony_ci		clk_mux_flags |= CLK_MUX_INDEX_ONE;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if (of_property_read_bool(node, "ti,set-rate-parent"))
20062306a36Sopenharmony_ci		flags |= CLK_SET_RATE_PARENT;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	/* Generate bit-mask based on parent info */
20362306a36Sopenharmony_ci	mask = num_parents;
20462306a36Sopenharmony_ci	if (!(clk_mux_flags & CLK_MUX_INDEX_ONE))
20562306a36Sopenharmony_ci		mask--;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	mask = (1 << fls(mask)) - 1;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	name = ti_dt_clk_name(node);
21062306a36Sopenharmony_ci	clk = _register_mux(node, name, parent_names, num_parents,
21162306a36Sopenharmony_ci			    flags, &reg, shift, mask, latch, clk_mux_flags,
21262306a36Sopenharmony_ci			    NULL);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	if (!IS_ERR(clk))
21562306a36Sopenharmony_ci		of_clk_add_provider(node, of_clk_src_simple_get, clk);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cicleanup:
21862306a36Sopenharmony_ci	kfree(parent_names);
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ciCLK_OF_DECLARE(mux_clk, "ti,mux-clock", of_mux_clk_setup);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistruct clk_hw *ti_clk_build_component_mux(struct ti_clk_mux *setup)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	struct clk_omap_mux *mux;
22562306a36Sopenharmony_ci	int num_parents;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	if (!setup)
22862306a36Sopenharmony_ci		return NULL;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
23162306a36Sopenharmony_ci	if (!mux)
23262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	mux->shift = setup->bit_shift;
23562306a36Sopenharmony_ci	mux->latch = -EINVAL;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	mux->reg.index = setup->module;
23862306a36Sopenharmony_ci	mux->reg.offset = setup->reg;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (setup->flags & CLKF_INDEX_STARTS_AT_ONE)
24162306a36Sopenharmony_ci		mux->flags |= CLK_MUX_INDEX_ONE;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	num_parents = setup->num_parents;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	mux->mask = num_parents - 1;
24662306a36Sopenharmony_ci	mux->mask = (1 << fls(mux->mask)) - 1;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	return &mux->hw;
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic void __init of_ti_composite_mux_clk_setup(struct device_node *node)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	struct clk_omap_mux *mux;
25462306a36Sopenharmony_ci	unsigned int num_parents;
25562306a36Sopenharmony_ci	u32 val;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
25862306a36Sopenharmony_ci	if (!mux)
25962306a36Sopenharmony_ci		return;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	if (ti_clk_get_reg_addr(node, 0, &mux->reg))
26262306a36Sopenharmony_ci		goto cleanup;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	if (!of_property_read_u32(node, "ti,bit-shift", &val))
26562306a36Sopenharmony_ci		mux->shift = val;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	if (of_property_read_bool(node, "ti,index-starts-at-one"))
26862306a36Sopenharmony_ci		mux->flags |= CLK_MUX_INDEX_ONE;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	num_parents = of_clk_get_parent_count(node);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	if (num_parents < 2) {
27362306a36Sopenharmony_ci		pr_err("%pOFn must have parents\n", node);
27462306a36Sopenharmony_ci		goto cleanup;
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	mux->mask = num_parents - 1;
27862306a36Sopenharmony_ci	mux->mask = (1 << fls(mux->mask)) - 1;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	if (!ti_clk_add_component(node, &mux->hw, CLK_COMPONENT_TYPE_MUX))
28162306a36Sopenharmony_ci		return;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cicleanup:
28462306a36Sopenharmony_ci	kfree(mux);
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ciCLK_OF_DECLARE(ti_composite_mux_clk_setup, "ti,composite-mux-clock",
28762306a36Sopenharmony_ci	       of_ti_composite_mux_clk_setup);
288