18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci#include <linux/slab.h>
48c2ecf20Sopenharmony_ci#include <linux/bitops.h>
58c2ecf20Sopenharmony_ci#include <linux/regmap.h>
68c2ecf20Sopenharmony_ci#include <linux/clk.h>
78c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
88c2ecf20Sopenharmony_ci#include "clk.h"
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_cistruct rockchip_muxgrf_clock {
118c2ecf20Sopenharmony_ci	struct clk_hw		hw;
128c2ecf20Sopenharmony_ci	struct regmap		*regmap;
138c2ecf20Sopenharmony_ci	u32			reg;
148c2ecf20Sopenharmony_ci	u32			shift;
158c2ecf20Sopenharmony_ci	u32			width;
168c2ecf20Sopenharmony_ci	int			flags;
178c2ecf20Sopenharmony_ci};
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define to_muxgrf_clock(_hw) container_of(_hw, struct rockchip_muxgrf_clock, hw)
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic u8 rockchip_muxgrf_get_parent(struct clk_hw *hw)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	struct rockchip_muxgrf_clock *mux = to_muxgrf_clock(hw);
248c2ecf20Sopenharmony_ci	unsigned int mask = GENMASK(mux->width - 1, 0);
258c2ecf20Sopenharmony_ci	unsigned int val;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	regmap_read(mux->regmap, mux->reg, &val);
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	val >>= mux->shift;
308c2ecf20Sopenharmony_ci	val &= mask;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	return val;
338c2ecf20Sopenharmony_ci}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic int rockchip_muxgrf_set_parent(struct clk_hw *hw, u8 index)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	struct rockchip_muxgrf_clock *mux = to_muxgrf_clock(hw);
388c2ecf20Sopenharmony_ci	unsigned int mask = GENMASK(mux->width + mux->shift - 1, mux->shift);
398c2ecf20Sopenharmony_ci	unsigned int val;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	val = index;
428c2ecf20Sopenharmony_ci	val <<= mux->shift;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	if (mux->flags & CLK_MUX_HIWORD_MASK)
458c2ecf20Sopenharmony_ci		return regmap_write(mux->regmap, mux->reg, val | (mask << 16));
468c2ecf20Sopenharmony_ci	else
478c2ecf20Sopenharmony_ci		return regmap_update_bits(mux->regmap, mux->reg, mask, val);
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic const struct clk_ops rockchip_muxgrf_clk_ops = {
518c2ecf20Sopenharmony_ci	.get_parent = rockchip_muxgrf_get_parent,
528c2ecf20Sopenharmony_ci	.set_parent = rockchip_muxgrf_set_parent,
538c2ecf20Sopenharmony_ci	.determine_rate = __clk_mux_determine_rate,
548c2ecf20Sopenharmony_ci};
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistruct clk *rockchip_clk_register_muxgrf(const char *name,
578c2ecf20Sopenharmony_ci				const char *const *parent_names, u8 num_parents,
588c2ecf20Sopenharmony_ci				int flags, struct regmap *regmap, int reg,
598c2ecf20Sopenharmony_ci				int shift, int width, int mux_flags)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	struct rockchip_muxgrf_clock *muxgrf_clock;
628c2ecf20Sopenharmony_ci	struct clk_init_data init;
638c2ecf20Sopenharmony_ci	struct clk *clk;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	if (IS_ERR(regmap)) {
668c2ecf20Sopenharmony_ci		pr_err("%s: regmap not available\n", __func__);
678c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOTSUPP);
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	muxgrf_clock = kmalloc(sizeof(*muxgrf_clock), GFP_KERNEL);
718c2ecf20Sopenharmony_ci	if (!muxgrf_clock)
728c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	init.name = name;
758c2ecf20Sopenharmony_ci	init.flags = flags;
768c2ecf20Sopenharmony_ci	init.num_parents = num_parents;
778c2ecf20Sopenharmony_ci	init.parent_names = parent_names;
788c2ecf20Sopenharmony_ci	init.ops = &rockchip_muxgrf_clk_ops;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	muxgrf_clock->hw.init = &init;
818c2ecf20Sopenharmony_ci	muxgrf_clock->regmap = regmap;
828c2ecf20Sopenharmony_ci	muxgrf_clock->reg = reg;
838c2ecf20Sopenharmony_ci	muxgrf_clock->shift = shift;
848c2ecf20Sopenharmony_ci	muxgrf_clock->width = width;
858c2ecf20Sopenharmony_ci	muxgrf_clock->flags = mux_flags;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	clk = clk_register(NULL, &muxgrf_clock->hw);
888c2ecf20Sopenharmony_ci	if (IS_ERR(clk))
898c2ecf20Sopenharmony_ci		kfree(muxgrf_clock);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	return clk;
928c2ecf20Sopenharmony_ci}
93