Merge tag 'leds-5.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/pavel...
[linux-2.6-microblaze.git] / drivers / clk / mediatek / clk-mux.c
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
15 static 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
20 static int mtk_clk_mux_enable_setclr(struct clk_hw *hw)
21 {
22         struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
23         unsigned long flags = 0;
24
25         if (mux->lock)
26                 spin_lock_irqsave(mux->lock, flags);
27         else
28                 __acquire(mux->lock);
29
30         regmap_write(mux->regmap, mux->data->clr_ofs,
31                      BIT(mux->data->gate_shift));
32
33         /*
34          * If the parent has been changed when the clock was disabled, it will
35          * not be effective yet. Set the update bit to ensure the mux gets
36          * updated.
37          */
38         if (mux->reparent && mux->data->upd_shift >= 0) {
39                 regmap_write(mux->regmap, mux->data->upd_ofs,
40                              BIT(mux->data->upd_shift));
41                 mux->reparent = false;
42         }
43
44         if (mux->lock)
45                 spin_unlock_irqrestore(mux->lock, flags);
46         else
47                 __release(mux->lock);
48
49         return 0;
50 }
51
52 static void mtk_clk_mux_disable_setclr(struct clk_hw *hw)
53 {
54         struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
55
56         regmap_write(mux->regmap, mux->data->set_ofs,
57                         BIT(mux->data->gate_shift));
58 }
59
60 static int mtk_clk_mux_is_enabled(struct clk_hw *hw)
61 {
62         struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
63         u32 val;
64
65         regmap_read(mux->regmap, mux->data->mux_ofs, &val);
66
67         return (val & BIT(mux->data->gate_shift)) == 0;
68 }
69
70 static u8 mtk_clk_mux_get_parent(struct clk_hw *hw)
71 {
72         struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
73         u32 mask = GENMASK(mux->data->mux_width - 1, 0);
74         u32 val;
75
76         regmap_read(mux->regmap, mux->data->mux_ofs, &val);
77         val = (val >> mux->data->mux_shift) & mask;
78
79         return val;
80 }
81
82 static int mtk_clk_mux_set_parent_setclr_lock(struct clk_hw *hw, u8 index)
83 {
84         struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
85         u32 mask = GENMASK(mux->data->mux_width - 1, 0);
86         u32 val, orig;
87         unsigned long flags = 0;
88
89         if (mux->lock)
90                 spin_lock_irqsave(mux->lock, flags);
91         else
92                 __acquire(mux->lock);
93
94         regmap_read(mux->regmap, mux->data->mux_ofs, &orig);
95         val = (orig & ~(mask << mux->data->mux_shift))
96                         | (index << mux->data->mux_shift);
97
98         if (val != orig) {
99                 regmap_write(mux->regmap, mux->data->clr_ofs,
100                                 mask << mux->data->mux_shift);
101                 regmap_write(mux->regmap, mux->data->set_ofs,
102                                 index << mux->data->mux_shift);
103
104                 if (mux->data->upd_shift >= 0) {
105                         regmap_write(mux->regmap, mux->data->upd_ofs,
106                                         BIT(mux->data->upd_shift));
107                         mux->reparent = true;
108                 }
109         }
110
111         if (mux->lock)
112                 spin_unlock_irqrestore(mux->lock, flags);
113         else
114                 __release(mux->lock);
115
116         return 0;
117 }
118
119 static const struct clk_ops mtk_mux_ops = {
120         .enable = mtk_clk_mux_enable_setclr,
121         .disable = mtk_clk_mux_disable_setclr,
122         .is_enabled = mtk_clk_mux_is_enabled,
123         .get_parent = mtk_clk_mux_get_parent,
124         .set_parent = mtk_clk_mux_set_parent_setclr_lock,
125 };
126
127 static struct clk *mtk_clk_register_mux(const struct mtk_mux *mux,
128                                  struct regmap *regmap,
129                                  spinlock_t *lock)
130 {
131         struct mtk_clk_mux *clk_mux;
132         struct clk_init_data init = {};
133         struct clk *clk;
134
135         clk_mux = kzalloc(sizeof(*clk_mux), GFP_KERNEL);
136         if (!clk_mux)
137                 return ERR_PTR(-ENOMEM);
138
139         init.name = mux->name;
140         init.flags = mux->flags | CLK_SET_RATE_PARENT;
141         init.parent_names = mux->parent_names;
142         init.num_parents = mux->num_parents;
143         init.ops = &mtk_mux_ops;
144
145         clk_mux->regmap = regmap;
146         clk_mux->data = mux;
147         clk_mux->lock = lock;
148         clk_mux->hw.init = &init;
149
150         clk = clk_register(NULL, &clk_mux->hw);
151         if (IS_ERR(clk)) {
152                 kfree(clk_mux);
153                 return clk;
154         }
155
156         return clk;
157 }
158
159 int mtk_clk_register_muxes(const struct mtk_mux *muxes,
160                            int num, struct device_node *node,
161                            spinlock_t *lock,
162                            struct clk_onecell_data *clk_data)
163 {
164         struct regmap *regmap;
165         struct clk *clk;
166         int i;
167
168         regmap = syscon_node_to_regmap(node);
169         if (IS_ERR(regmap)) {
170                 pr_err("Cannot find regmap for %pOF: %ld\n", node,
171                        PTR_ERR(regmap));
172                 return PTR_ERR(regmap);
173         }
174
175         for (i = 0; i < num; i++) {
176                 const struct mtk_mux *mux = &muxes[i];
177
178                 if (IS_ERR_OR_NULL(clk_data->clks[mux->id])) {
179                         clk = mtk_clk_register_mux(mux, regmap, lock);
180
181                         if (IS_ERR(clk)) {
182                                 pr_err("Failed to register clk %s: %ld\n",
183                                        mux->name, PTR_ERR(clk));
184                                 continue;
185                         }
186
187                         clk_data->clks[mux->id] = clk;
188                 }
189         }
190
191         return 0;
192 }