162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/slab.h>
462306a36Sopenharmony_ci#include <linux/bitops.h>
562306a36Sopenharmony_ci#include <linux/regmap.h>
662306a36Sopenharmony_ci#include <linux/clk.h>
762306a36Sopenharmony_ci#include <linux/clk-provider.h>
862306a36Sopenharmony_ci#include "clk.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_cistruct rockchip_muxgrf_clock {
1162306a36Sopenharmony_ci	struct clk_hw		hw;
1262306a36Sopenharmony_ci	struct regmap		*regmap;
1362306a36Sopenharmony_ci	u32			reg;
1462306a36Sopenharmony_ci	u32			shift;
1562306a36Sopenharmony_ci	u32			width;
1662306a36Sopenharmony_ci	int			flags;
1762306a36Sopenharmony_ci};
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define to_muxgrf_clock(_hw) container_of(_hw, struct rockchip_muxgrf_clock, hw)
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic u8 rockchip_muxgrf_get_parent(struct clk_hw *hw)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	struct rockchip_muxgrf_clock *mux = to_muxgrf_clock(hw);
2462306a36Sopenharmony_ci	unsigned int mask = GENMASK(mux->width - 1, 0);
2562306a36Sopenharmony_ci	unsigned int val;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	regmap_read(mux->regmap, mux->reg, &val);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	val >>= mux->shift;
3062306a36Sopenharmony_ci	val &= mask;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	return val;
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic int rockchip_muxgrf_set_parent(struct clk_hw *hw, u8 index)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	struct rockchip_muxgrf_clock *mux = to_muxgrf_clock(hw);
3862306a36Sopenharmony_ci	unsigned int mask = GENMASK(mux->width + mux->shift - 1, mux->shift);
3962306a36Sopenharmony_ci	unsigned int val;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	val = index;
4262306a36Sopenharmony_ci	val <<= mux->shift;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	if (mux->flags & CLK_MUX_HIWORD_MASK)
4562306a36Sopenharmony_ci		return regmap_write(mux->regmap, mux->reg, val | (mask << 16));
4662306a36Sopenharmony_ci	else
4762306a36Sopenharmony_ci		return regmap_update_bits(mux->regmap, mux->reg, mask, val);
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic const struct clk_ops rockchip_muxgrf_clk_ops = {
5162306a36Sopenharmony_ci	.get_parent = rockchip_muxgrf_get_parent,
5262306a36Sopenharmony_ci	.set_parent = rockchip_muxgrf_set_parent,
5362306a36Sopenharmony_ci	.determine_rate = __clk_mux_determine_rate,
5462306a36Sopenharmony_ci};
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistruct clk *rockchip_clk_register_muxgrf(const char *name,
5762306a36Sopenharmony_ci				const char *const *parent_names, u8 num_parents,
5862306a36Sopenharmony_ci				int flags, struct regmap *regmap, int reg,
5962306a36Sopenharmony_ci				int shift, int width, int mux_flags)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	struct rockchip_muxgrf_clock *muxgrf_clock;
6262306a36Sopenharmony_ci	struct clk_init_data init;
6362306a36Sopenharmony_ci	struct clk *clk;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	if (IS_ERR(regmap)) {
6662306a36Sopenharmony_ci		pr_err("%s: regmap not available\n", __func__);
6762306a36Sopenharmony_ci		return ERR_PTR(-ENOTSUPP);
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	muxgrf_clock = kmalloc(sizeof(*muxgrf_clock), GFP_KERNEL);
7162306a36Sopenharmony_ci	if (!muxgrf_clock)
7262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	init.name = name;
7562306a36Sopenharmony_ci	init.flags = flags;
7662306a36Sopenharmony_ci	init.num_parents = num_parents;
7762306a36Sopenharmony_ci	init.parent_names = parent_names;
7862306a36Sopenharmony_ci	init.ops = &rockchip_muxgrf_clk_ops;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	muxgrf_clock->hw.init = &init;
8162306a36Sopenharmony_ci	muxgrf_clock->regmap = regmap;
8262306a36Sopenharmony_ci	muxgrf_clock->reg = reg;
8362306a36Sopenharmony_ci	muxgrf_clock->shift = shift;
8462306a36Sopenharmony_ci	muxgrf_clock->width = width;
8562306a36Sopenharmony_ci	muxgrf_clock->flags = mux_flags;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	clk = clk_register(NULL, &muxgrf_clock->hw);
8862306a36Sopenharmony_ci	if (IS_ERR(clk))
8962306a36Sopenharmony_ci		kfree(muxgrf_clock);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return clk;
9262306a36Sopenharmony_ci}
93