18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2015 Linaro Ltd.
48c2ecf20Sopenharmony_ci * Author: Pi-Cheng Chen <pi-cheng.chen@linaro.org>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
88c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "clk-mtk.h"
128c2ecf20Sopenharmony_ci#include "clk-cpumux.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistatic inline struct mtk_clk_cpumux *to_mtk_clk_cpumux(struct clk_hw *_hw)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	return container_of(_hw, struct mtk_clk_cpumux, hw);
178c2ecf20Sopenharmony_ci}
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic u8 clk_cpumux_get_parent(struct clk_hw *hw)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	struct mtk_clk_cpumux *mux = to_mtk_clk_cpumux(hw);
228c2ecf20Sopenharmony_ci	unsigned int val;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	regmap_read(mux->regmap, mux->reg, &val);
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	val >>= mux->shift;
278c2ecf20Sopenharmony_ci	val &= mux->mask;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	return val;
308c2ecf20Sopenharmony_ci}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic int clk_cpumux_set_parent(struct clk_hw *hw, u8 index)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	struct mtk_clk_cpumux *mux = to_mtk_clk_cpumux(hw);
358c2ecf20Sopenharmony_ci	u32 mask, val;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	val = index << mux->shift;
388c2ecf20Sopenharmony_ci	mask = mux->mask << mux->shift;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	return regmap_update_bits(mux->regmap, mux->reg, mask, val);
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic const struct clk_ops clk_cpumux_ops = {
448c2ecf20Sopenharmony_ci	.get_parent = clk_cpumux_get_parent,
458c2ecf20Sopenharmony_ci	.set_parent = clk_cpumux_set_parent,
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic struct clk *
498c2ecf20Sopenharmony_cimtk_clk_register_cpumux(const struct mtk_composite *mux,
508c2ecf20Sopenharmony_ci			struct regmap *regmap)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	struct mtk_clk_cpumux *cpumux;
538c2ecf20Sopenharmony_ci	struct clk *clk;
548c2ecf20Sopenharmony_ci	struct clk_init_data init;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	cpumux = kzalloc(sizeof(*cpumux), GFP_KERNEL);
578c2ecf20Sopenharmony_ci	if (!cpumux)
588c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	init.name = mux->name;
618c2ecf20Sopenharmony_ci	init.ops = &clk_cpumux_ops;
628c2ecf20Sopenharmony_ci	init.parent_names = mux->parent_names;
638c2ecf20Sopenharmony_ci	init.num_parents = mux->num_parents;
648c2ecf20Sopenharmony_ci	init.flags = mux->flags;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	cpumux->reg = mux->mux_reg;
678c2ecf20Sopenharmony_ci	cpumux->shift = mux->mux_shift;
688c2ecf20Sopenharmony_ci	cpumux->mask = BIT(mux->mux_width) - 1;
698c2ecf20Sopenharmony_ci	cpumux->regmap = regmap;
708c2ecf20Sopenharmony_ci	cpumux->hw.init = &init;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	clk = clk_register(NULL, &cpumux->hw);
738c2ecf20Sopenharmony_ci	if (IS_ERR(clk))
748c2ecf20Sopenharmony_ci		kfree(cpumux);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	return clk;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ciint mtk_clk_register_cpumuxes(struct device_node *node,
808c2ecf20Sopenharmony_ci			      const struct mtk_composite *clks, int num,
818c2ecf20Sopenharmony_ci			      struct clk_onecell_data *clk_data)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	int i;
848c2ecf20Sopenharmony_ci	struct clk *clk;
858c2ecf20Sopenharmony_ci	struct regmap *regmap;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	regmap = syscon_node_to_regmap(node);
888c2ecf20Sopenharmony_ci	if (IS_ERR(regmap)) {
898c2ecf20Sopenharmony_ci		pr_err("Cannot find regmap for %pOF: %ld\n", node,
908c2ecf20Sopenharmony_ci		       PTR_ERR(regmap));
918c2ecf20Sopenharmony_ci		return PTR_ERR(regmap);
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	for (i = 0; i < num; i++) {
958c2ecf20Sopenharmony_ci		const struct mtk_composite *mux = &clks[i];
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci		clk = mtk_clk_register_cpumux(mux, regmap);
988c2ecf20Sopenharmony_ci		if (IS_ERR(clk)) {
998c2ecf20Sopenharmony_ci			pr_err("Failed to register clk %s: %ld\n",
1008c2ecf20Sopenharmony_ci			       mux->name, PTR_ERR(clk));
1018c2ecf20Sopenharmony_ci			continue;
1028c2ecf20Sopenharmony_ci		}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci		clk_data->clks[mux->id] = clk;
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	return 0;
1088c2ecf20Sopenharmony_ci}
109