1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2018 MediaTek Inc.
4 * Author: Owen Chen <owen.chen@mediatek.com>
5 */
6
7#include <linux/of.h>
8#include <linux/of_address.h>
9#include <linux/slab.h>
10#include <linux/mfd/syscon.h>
11
12#include "clk-mtk.h"
13#include "clk-mux.h"
14
15static inline struct mtk_clk_mux *to_mtk_clk_mux(struct clk_hw *hw)
16{
17	return container_of(hw, struct mtk_clk_mux, hw);
18}
19
20static int mtk_clk_mux_enable(struct clk_hw *hw)
21{
22	struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
23	u32 mask = BIT(mux->data->gate_shift);
24
25	return regmap_update_bits(mux->regmap, mux->data->mux_ofs,
26			mask, ~mask);
27}
28
29static void mtk_clk_mux_disable(struct clk_hw *hw)
30{
31	struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
32	u32 mask = BIT(mux->data->gate_shift);
33
34	regmap_update_bits(mux->regmap, mux->data->mux_ofs, mask, mask);
35}
36
37static int mtk_clk_mux_enable_setclr(struct clk_hw *hw)
38{
39	struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
40
41	return regmap_write(mux->regmap, mux->data->clr_ofs,
42			BIT(mux->data->gate_shift));
43}
44
45static void mtk_clk_mux_disable_setclr(struct clk_hw *hw)
46{
47	struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
48
49	regmap_write(mux->regmap, mux->data->set_ofs,
50			BIT(mux->data->gate_shift));
51}
52
53static int mtk_clk_mux_is_enabled(struct clk_hw *hw)
54{
55	struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
56	u32 val;
57
58	regmap_read(mux->regmap, mux->data->mux_ofs, &val);
59
60	return (val & BIT(mux->data->gate_shift)) == 0;
61}
62
63static u8 mtk_clk_mux_get_parent(struct clk_hw *hw)
64{
65	struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
66	u32 mask = GENMASK(mux->data->mux_width - 1, 0);
67	u32 val;
68
69	regmap_read(mux->regmap, mux->data->mux_ofs, &val);
70	val = (val >> mux->data->mux_shift) & mask;
71
72	return val;
73}
74
75static int mtk_clk_mux_set_parent_lock(struct clk_hw *hw, u8 index)
76{
77	struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
78	u32 mask = GENMASK(mux->data->mux_width - 1, 0);
79	unsigned long flags = 0;
80
81	if (mux->lock)
82		spin_lock_irqsave(mux->lock, flags);
83	else
84		__acquire(mux->lock);
85
86	regmap_update_bits(mux->regmap, mux->data->mux_ofs, mask,
87		index << mux->data->mux_shift);
88
89	if (mux->lock)
90		spin_unlock_irqrestore(mux->lock, flags);
91	else
92		__release(mux->lock);
93
94	return 0;
95}
96
97static int mtk_clk_mux_set_parent_setclr_lock(struct clk_hw *hw, u8 index)
98{
99	struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
100	u32 mask = GENMASK(mux->data->mux_width - 1, 0);
101	u32 val, orig;
102	unsigned long flags = 0;
103
104	if (mux->lock)
105		spin_lock_irqsave(mux->lock, flags);
106	else
107		__acquire(mux->lock);
108
109	regmap_read(mux->regmap, mux->data->mux_ofs, &orig);
110	val = (orig & ~(mask << mux->data->mux_shift))
111			| (index << mux->data->mux_shift);
112
113	if (val != orig) {
114		regmap_write(mux->regmap, mux->data->clr_ofs,
115				mask << mux->data->mux_shift);
116		regmap_write(mux->regmap, mux->data->set_ofs,
117				index << mux->data->mux_shift);
118
119		if (mux->data->upd_shift >= 0)
120			regmap_write(mux->regmap, mux->data->upd_ofs,
121					BIT(mux->data->upd_shift));
122	}
123
124	if (mux->lock)
125		spin_unlock_irqrestore(mux->lock, flags);
126	else
127		__release(mux->lock);
128
129	return 0;
130}
131
132const struct clk_ops mtk_mux_ops = {
133	.get_parent = mtk_clk_mux_get_parent,
134	.set_parent = mtk_clk_mux_set_parent_lock,
135};
136
137const struct clk_ops mtk_mux_clr_set_upd_ops = {
138	.get_parent = mtk_clk_mux_get_parent,
139	.set_parent = mtk_clk_mux_set_parent_setclr_lock,
140};
141
142const struct clk_ops mtk_mux_gate_ops = {
143	.enable = mtk_clk_mux_enable,
144	.disable = mtk_clk_mux_disable,
145	.is_enabled = mtk_clk_mux_is_enabled,
146	.get_parent = mtk_clk_mux_get_parent,
147	.set_parent = mtk_clk_mux_set_parent_lock,
148};
149
150const struct clk_ops mtk_mux_gate_clr_set_upd_ops = {
151	.enable = mtk_clk_mux_enable_setclr,
152	.disable = mtk_clk_mux_disable_setclr,
153	.is_enabled = mtk_clk_mux_is_enabled,
154	.get_parent = mtk_clk_mux_get_parent,
155	.set_parent = mtk_clk_mux_set_parent_setclr_lock,
156};
157
158struct clk *mtk_clk_register_mux(const struct mtk_mux *mux,
159				 struct regmap *regmap,
160				 spinlock_t *lock)
161{
162	struct mtk_clk_mux *clk_mux;
163	struct clk_init_data init = {};
164	struct clk *clk;
165
166	clk_mux = kzalloc(sizeof(*clk_mux), GFP_KERNEL);
167	if (!clk_mux)
168		return ERR_PTR(-ENOMEM);
169
170	init.name = mux->name;
171	init.flags = mux->flags | CLK_SET_RATE_PARENT;
172	init.parent_names = mux->parent_names;
173	init.num_parents = mux->num_parents;
174	init.ops = mux->ops;
175
176	clk_mux->regmap = regmap;
177	clk_mux->data = mux;
178	clk_mux->lock = lock;
179	clk_mux->hw.init = &init;
180
181	clk = clk_register(NULL, &clk_mux->hw);
182	if (IS_ERR(clk)) {
183		kfree(clk_mux);
184		return clk;
185	}
186
187	return clk;
188}
189
190int mtk_clk_register_muxes(const struct mtk_mux *muxes,
191			   int num, struct device_node *node,
192			   spinlock_t *lock,
193			   struct clk_onecell_data *clk_data)
194{
195	struct regmap *regmap;
196	struct clk *clk;
197	int i;
198
199	regmap = syscon_node_to_regmap(node);
200	if (IS_ERR(regmap)) {
201		pr_err("Cannot find regmap for %pOF: %ld\n", node,
202		       PTR_ERR(regmap));
203		return PTR_ERR(regmap);
204	}
205
206	for (i = 0; i < num; i++) {
207		const struct mtk_mux *mux = &muxes[i];
208
209		if (IS_ERR_OR_NULL(clk_data->clks[mux->id])) {
210			clk = mtk_clk_register_mux(mux, regmap, lock);
211
212			if (IS_ERR(clk)) {
213				pr_err("Failed to register clk %s: %ld\n",
214				       mux->name, PTR_ERR(clk));
215				continue;
216			}
217
218			clk_data->clks[mux->id] = clk;
219		}
220	}
221
222	return 0;
223}
224