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