xref: /kernel/linux/linux-5.10/drivers/clk/ti/mux.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * TI Multiplexer Clock
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2013 Texas Instruments, Inc.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Tero Kristo <t-kristo@ti.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
98c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License version 2 as
108c2ecf20Sopenharmony_ci * published by the Free Software Foundation.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any
138c2ecf20Sopenharmony_ci * kind, whether express or implied; without even the implied warranty
148c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
158c2ecf20Sopenharmony_ci * GNU General Public License for more details.
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci#include <linux/err.h>
218c2ecf20Sopenharmony_ci#include <linux/of.h>
228c2ecf20Sopenharmony_ci#include <linux/of_address.h>
238c2ecf20Sopenharmony_ci#include <linux/clk/ti.h>
248c2ecf20Sopenharmony_ci#include "clock.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#undef pr_fmt
278c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "%s: " fmt, __func__
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic u8 ti_clk_mux_get_parent(struct clk_hw *hw)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	struct clk_omap_mux *mux = to_clk_omap_mux(hw);
328c2ecf20Sopenharmony_ci	int num_parents = clk_hw_get_num_parents(hw);
338c2ecf20Sopenharmony_ci	u32 val;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	/*
368c2ecf20Sopenharmony_ci	 * FIXME need a mux-specific flag to determine if val is bitwise or
378c2ecf20Sopenharmony_ci	 * numeric. e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges
388c2ecf20Sopenharmony_ci	 * from 0x1 to 0x7 (index starts at one)
398c2ecf20Sopenharmony_ci	 * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so
408c2ecf20Sopenharmony_ci	 * val = 0x4 really means "bit 2, index starts at bit 0"
418c2ecf20Sopenharmony_ci	 */
428c2ecf20Sopenharmony_ci	val = ti_clk_ll_ops->clk_readl(&mux->reg) >> mux->shift;
438c2ecf20Sopenharmony_ci	val &= mux->mask;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	if (mux->table) {
468c2ecf20Sopenharmony_ci		int i;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci		for (i = 0; i < num_parents; i++)
498c2ecf20Sopenharmony_ci			if (mux->table[i] == val)
508c2ecf20Sopenharmony_ci				return i;
518c2ecf20Sopenharmony_ci		return -EINVAL;
528c2ecf20Sopenharmony_ci	}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	if (val && (mux->flags & CLK_MUX_INDEX_BIT))
558c2ecf20Sopenharmony_ci		val = ffs(val) - 1;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	if (val && (mux->flags & CLK_MUX_INDEX_ONE))
588c2ecf20Sopenharmony_ci		val--;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	if (val >= num_parents)
618c2ecf20Sopenharmony_ci		return -EINVAL;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	return val;
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic int ti_clk_mux_set_parent(struct clk_hw *hw, u8 index)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	struct clk_omap_mux *mux = to_clk_omap_mux(hw);
698c2ecf20Sopenharmony_ci	u32 val;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	if (mux->table) {
728c2ecf20Sopenharmony_ci		index = mux->table[index];
738c2ecf20Sopenharmony_ci	} else {
748c2ecf20Sopenharmony_ci		if (mux->flags & CLK_MUX_INDEX_BIT)
758c2ecf20Sopenharmony_ci			index = (1 << ffs(index));
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci		if (mux->flags & CLK_MUX_INDEX_ONE)
788c2ecf20Sopenharmony_ci			index++;
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (mux->flags & CLK_MUX_HIWORD_MASK) {
828c2ecf20Sopenharmony_ci		val = mux->mask << (mux->shift + 16);
838c2ecf20Sopenharmony_ci	} else {
848c2ecf20Sopenharmony_ci		val = ti_clk_ll_ops->clk_readl(&mux->reg);
858c2ecf20Sopenharmony_ci		val &= ~(mux->mask << mux->shift);
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_ci	val |= index << mux->shift;
888c2ecf20Sopenharmony_ci	ti_clk_ll_ops->clk_writel(val, &mux->reg);
898c2ecf20Sopenharmony_ci	ti_clk_latch(&mux->reg, mux->latch);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	return 0;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci/**
958c2ecf20Sopenharmony_ci * clk_mux_save_context - Save the parent selcted in the mux
968c2ecf20Sopenharmony_ci * @hw: pointer  struct clk_hw
978c2ecf20Sopenharmony_ci *
988c2ecf20Sopenharmony_ci * Save the parent mux value.
998c2ecf20Sopenharmony_ci */
1008c2ecf20Sopenharmony_cistatic int clk_mux_save_context(struct clk_hw *hw)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	struct clk_omap_mux *mux = to_clk_omap_mux(hw);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	mux->saved_parent = ti_clk_mux_get_parent(hw);
1058c2ecf20Sopenharmony_ci	return 0;
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci/**
1098c2ecf20Sopenharmony_ci * clk_mux_restore_context - Restore the parent in the mux
1108c2ecf20Sopenharmony_ci * @hw: pointer  struct clk_hw
1118c2ecf20Sopenharmony_ci *
1128c2ecf20Sopenharmony_ci * Restore the saved parent mux value.
1138c2ecf20Sopenharmony_ci */
1148c2ecf20Sopenharmony_cistatic void clk_mux_restore_context(struct clk_hw *hw)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	struct clk_omap_mux *mux = to_clk_omap_mux(hw);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	ti_clk_mux_set_parent(hw, mux->saved_parent);
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ciconst struct clk_ops ti_clk_mux_ops = {
1228c2ecf20Sopenharmony_ci	.get_parent = ti_clk_mux_get_parent,
1238c2ecf20Sopenharmony_ci	.set_parent = ti_clk_mux_set_parent,
1248c2ecf20Sopenharmony_ci	.determine_rate = __clk_mux_determine_rate,
1258c2ecf20Sopenharmony_ci	.save_context = clk_mux_save_context,
1268c2ecf20Sopenharmony_ci	.restore_context = clk_mux_restore_context,
1278c2ecf20Sopenharmony_ci};
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic struct clk *_register_mux(struct device_node *node, const char *name,
1308c2ecf20Sopenharmony_ci				 const char * const *parent_names,
1318c2ecf20Sopenharmony_ci				 u8 num_parents, unsigned long flags,
1328c2ecf20Sopenharmony_ci				 struct clk_omap_reg *reg, u8 shift, u32 mask,
1338c2ecf20Sopenharmony_ci				 s8 latch, u8 clk_mux_flags, u32 *table)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	struct clk_omap_mux *mux;
1368c2ecf20Sopenharmony_ci	struct clk *clk;
1378c2ecf20Sopenharmony_ci	struct clk_init_data init;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	/* allocate the mux */
1408c2ecf20Sopenharmony_ci	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
1418c2ecf20Sopenharmony_ci	if (!mux)
1428c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	init.name = name;
1458c2ecf20Sopenharmony_ci	init.ops = &ti_clk_mux_ops;
1468c2ecf20Sopenharmony_ci	init.flags = flags;
1478c2ecf20Sopenharmony_ci	init.parent_names = parent_names;
1488c2ecf20Sopenharmony_ci	init.num_parents = num_parents;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	/* struct clk_mux assignments */
1518c2ecf20Sopenharmony_ci	memcpy(&mux->reg, reg, sizeof(*reg));
1528c2ecf20Sopenharmony_ci	mux->shift = shift;
1538c2ecf20Sopenharmony_ci	mux->mask = mask;
1548c2ecf20Sopenharmony_ci	mux->latch = latch;
1558c2ecf20Sopenharmony_ci	mux->flags = clk_mux_flags;
1568c2ecf20Sopenharmony_ci	mux->table = table;
1578c2ecf20Sopenharmony_ci	mux->hw.init = &init;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	clk = of_ti_clk_register(node, &mux->hw, name);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	if (IS_ERR(clk))
1628c2ecf20Sopenharmony_ci		kfree(mux);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	return clk;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci/**
1688c2ecf20Sopenharmony_ci * of_mux_clk_setup - Setup function for simple mux rate clock
1698c2ecf20Sopenharmony_ci * @node: DT node for the clock
1708c2ecf20Sopenharmony_ci *
1718c2ecf20Sopenharmony_ci * Sets up a basic clock multiplexer.
1728c2ecf20Sopenharmony_ci */
1738c2ecf20Sopenharmony_cistatic void of_mux_clk_setup(struct device_node *node)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	struct clk *clk;
1768c2ecf20Sopenharmony_ci	struct clk_omap_reg reg;
1778c2ecf20Sopenharmony_ci	unsigned int num_parents;
1788c2ecf20Sopenharmony_ci	const char **parent_names;
1798c2ecf20Sopenharmony_ci	const char *name;
1808c2ecf20Sopenharmony_ci	u8 clk_mux_flags = 0;
1818c2ecf20Sopenharmony_ci	u32 mask = 0;
1828c2ecf20Sopenharmony_ci	u32 shift = 0;
1838c2ecf20Sopenharmony_ci	s32 latch = -EINVAL;
1848c2ecf20Sopenharmony_ci	u32 flags = CLK_SET_RATE_NO_REPARENT;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	num_parents = of_clk_get_parent_count(node);
1878c2ecf20Sopenharmony_ci	if (num_parents < 2) {
1888c2ecf20Sopenharmony_ci		pr_err("mux-clock %pOFn must have parents\n", node);
1898c2ecf20Sopenharmony_ci		return;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci	parent_names = kzalloc((sizeof(char *) * num_parents), GFP_KERNEL);
1928c2ecf20Sopenharmony_ci	if (!parent_names)
1938c2ecf20Sopenharmony_ci		goto cleanup;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	of_clk_parent_fill(node, parent_names, num_parents);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	if (ti_clk_get_reg_addr(node, 0, &reg))
1988c2ecf20Sopenharmony_ci		goto cleanup;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	of_property_read_u32(node, "ti,bit-shift", &shift);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	of_property_read_u32(node, "ti,latch-bit", &latch);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	if (of_property_read_bool(node, "ti,index-starts-at-one"))
2058c2ecf20Sopenharmony_ci		clk_mux_flags |= CLK_MUX_INDEX_ONE;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	if (of_property_read_bool(node, "ti,set-rate-parent"))
2088c2ecf20Sopenharmony_ci		flags |= CLK_SET_RATE_PARENT;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	/* Generate bit-mask based on parent info */
2118c2ecf20Sopenharmony_ci	mask = num_parents;
2128c2ecf20Sopenharmony_ci	if (!(clk_mux_flags & CLK_MUX_INDEX_ONE))
2138c2ecf20Sopenharmony_ci		mask--;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	mask = (1 << fls(mask)) - 1;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	name = ti_dt_clk_name(node);
2188c2ecf20Sopenharmony_ci	clk = _register_mux(node, name, parent_names, num_parents,
2198c2ecf20Sopenharmony_ci			    flags, &reg, shift, mask, latch, clk_mux_flags,
2208c2ecf20Sopenharmony_ci			    NULL);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	if (!IS_ERR(clk))
2238c2ecf20Sopenharmony_ci		of_clk_add_provider(node, of_clk_src_simple_get, clk);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cicleanup:
2268c2ecf20Sopenharmony_ci	kfree(parent_names);
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ciCLK_OF_DECLARE(mux_clk, "ti,mux-clock", of_mux_clk_setup);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistruct clk_hw *ti_clk_build_component_mux(struct ti_clk_mux *setup)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	struct clk_omap_mux *mux;
2338c2ecf20Sopenharmony_ci	int num_parents;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	if (!setup)
2368c2ecf20Sopenharmony_ci		return NULL;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
2398c2ecf20Sopenharmony_ci	if (!mux)
2408c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	mux->shift = setup->bit_shift;
2438c2ecf20Sopenharmony_ci	mux->latch = -EINVAL;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	mux->reg.index = setup->module;
2468c2ecf20Sopenharmony_ci	mux->reg.offset = setup->reg;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	if (setup->flags & CLKF_INDEX_STARTS_AT_ONE)
2498c2ecf20Sopenharmony_ci		mux->flags |= CLK_MUX_INDEX_ONE;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	num_parents = setup->num_parents;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	mux->mask = num_parents - 1;
2548c2ecf20Sopenharmony_ci	mux->mask = (1 << fls(mux->mask)) - 1;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	return &mux->hw;
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic void __init of_ti_composite_mux_clk_setup(struct device_node *node)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	struct clk_omap_mux *mux;
2628c2ecf20Sopenharmony_ci	unsigned int num_parents;
2638c2ecf20Sopenharmony_ci	u32 val;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
2668c2ecf20Sopenharmony_ci	if (!mux)
2678c2ecf20Sopenharmony_ci		return;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	if (ti_clk_get_reg_addr(node, 0, &mux->reg))
2708c2ecf20Sopenharmony_ci		goto cleanup;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	if (!of_property_read_u32(node, "ti,bit-shift", &val))
2738c2ecf20Sopenharmony_ci		mux->shift = val;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	if (of_property_read_bool(node, "ti,index-starts-at-one"))
2768c2ecf20Sopenharmony_ci		mux->flags |= CLK_MUX_INDEX_ONE;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	num_parents = of_clk_get_parent_count(node);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	if (num_parents < 2) {
2818c2ecf20Sopenharmony_ci		pr_err("%pOFn must have parents\n", node);
2828c2ecf20Sopenharmony_ci		goto cleanup;
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	mux->mask = num_parents - 1;
2868c2ecf20Sopenharmony_ci	mux->mask = (1 << fls(mux->mask)) - 1;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	if (!ti_clk_add_component(node, &mux->hw, CLK_COMPONENT_TYPE_MUX))
2898c2ecf20Sopenharmony_ci		return;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cicleanup:
2928c2ecf20Sopenharmony_ci	kfree(mux);
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ciCLK_OF_DECLARE(ti_composite_mux_clk_setup, "ti,composite-mux-clock",
2958c2ecf20Sopenharmony_ci	       of_ti_composite_mux_clk_setup);
296