Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0
2 : : /*
3 : : * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
4 : : * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org>
5 : : * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
6 : : *
7 : : * Simple multiplexer clock implementation
8 : : */
9 : :
10 : : #include <linux/clk-provider.h>
11 : : #include <linux/module.h>
12 : : #include <linux/slab.h>
13 : : #include <linux/io.h>
14 : : #include <linux/err.h>
15 : :
16 : : /*
17 : : * DOC: basic adjustable multiplexer clock that cannot gate
18 : : *
19 : : * Traits of this clock:
20 : : * prepare - clk_prepare only ensures that parents are prepared
21 : : * enable - clk_enable only ensures that parents are enabled
22 : : * rate - rate is only affected by parent switching. No clk_set_rate support
23 : : * parent - parent is adjustable through clk_set_parent
24 : : */
25 : :
26 : : static inline u32 clk_mux_readl(struct clk_mux *mux)
27 : : {
28 : 0 : if (mux->flags & CLK_MUX_BIG_ENDIAN)
29 : 0 : return ioread32be(mux->reg);
30 : :
31 : 0 : return readl(mux->reg);
32 : : }
33 : :
34 : : static inline void clk_mux_writel(struct clk_mux *mux, u32 val)
35 : : {
36 : 0 : if (mux->flags & CLK_MUX_BIG_ENDIAN)
37 : 0 : iowrite32be(val, mux->reg);
38 : : else
39 : 0 : writel(val, mux->reg);
40 : : }
41 : :
42 : 0 : int clk_mux_val_to_index(struct clk_hw *hw, u32 *table, unsigned int flags,
43 : : unsigned int val)
44 : : {
45 : 0 : int num_parents = clk_hw_get_num_parents(hw);
46 : :
47 : 0 : if (table) {
48 : : int i;
49 : :
50 : 0 : for (i = 0; i < num_parents; i++)
51 : 0 : if (table[i] == val)
52 : 0 : return i;
53 : : return -EINVAL;
54 : : }
55 : :
56 : 0 : if (val && (flags & CLK_MUX_INDEX_BIT))
57 : 0 : val = ffs(val) - 1;
58 : :
59 : 0 : if (val && (flags & CLK_MUX_INDEX_ONE))
60 : 0 : val--;
61 : :
62 : 0 : if (val >= num_parents)
63 : : return -EINVAL;
64 : :
65 : 0 : return val;
66 : : }
67 : : EXPORT_SYMBOL_GPL(clk_mux_val_to_index);
68 : :
69 : 0 : unsigned int clk_mux_index_to_val(u32 *table, unsigned int flags, u8 index)
70 : : {
71 : 0 : unsigned int val = index;
72 : :
73 : 0 : if (table) {
74 : 0 : val = table[index];
75 : : } else {
76 : 0 : if (flags & CLK_MUX_INDEX_BIT)
77 : 0 : val = 1 << index;
78 : :
79 : 0 : if (flags & CLK_MUX_INDEX_ONE)
80 : 0 : val++;
81 : : }
82 : :
83 : 0 : return val;
84 : : }
85 : : EXPORT_SYMBOL_GPL(clk_mux_index_to_val);
86 : :
87 : 0 : static u8 clk_mux_get_parent(struct clk_hw *hw)
88 : : {
89 : : struct clk_mux *mux = to_clk_mux(hw);
90 : : u32 val;
91 : :
92 : 0 : val = clk_mux_readl(mux) >> mux->shift;
93 : 0 : val &= mux->mask;
94 : :
95 : 0 : return clk_mux_val_to_index(hw, mux->table, mux->flags, val);
96 : : }
97 : :
98 : 0 : static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
99 : : {
100 : : struct clk_mux *mux = to_clk_mux(hw);
101 : 0 : u32 val = clk_mux_index_to_val(mux->table, mux->flags, index);
102 : : unsigned long flags = 0;
103 : : u32 reg;
104 : :
105 : 0 : if (mux->lock)
106 : 0 : spin_lock_irqsave(mux->lock, flags);
107 : : else
108 : : __acquire(mux->lock);
109 : :
110 : 0 : if (mux->flags & CLK_MUX_HIWORD_MASK) {
111 : 0 : reg = mux->mask << (mux->shift + 16);
112 : : } else {
113 : : reg = clk_mux_readl(mux);
114 : 0 : reg &= ~(mux->mask << mux->shift);
115 : : }
116 : 0 : val = val << mux->shift;
117 : 0 : reg |= val;
118 : : clk_mux_writel(mux, reg);
119 : :
120 : 0 : if (mux->lock)
121 : : spin_unlock_irqrestore(mux->lock, flags);
122 : : else
123 : : __release(mux->lock);
124 : :
125 : 0 : return 0;
126 : : }
127 : :
128 : 0 : static int clk_mux_determine_rate(struct clk_hw *hw,
129 : : struct clk_rate_request *req)
130 : : {
131 : : struct clk_mux *mux = to_clk_mux(hw);
132 : :
133 : 0 : return clk_mux_determine_rate_flags(hw, req, mux->flags);
134 : : }
135 : :
136 : : const struct clk_ops clk_mux_ops = {
137 : : .get_parent = clk_mux_get_parent,
138 : : .set_parent = clk_mux_set_parent,
139 : : .determine_rate = clk_mux_determine_rate,
140 : : };
141 : : EXPORT_SYMBOL_GPL(clk_mux_ops);
142 : :
143 : : const struct clk_ops clk_mux_ro_ops = {
144 : : .get_parent = clk_mux_get_parent,
145 : : };
146 : : EXPORT_SYMBOL_GPL(clk_mux_ro_ops);
147 : :
148 : 0 : struct clk_hw *clk_hw_register_mux_table(struct device *dev, const char *name,
149 : : const char * const *parent_names, u8 num_parents,
150 : : unsigned long flags,
151 : : void __iomem *reg, u8 shift, u32 mask,
152 : : u8 clk_mux_flags, u32 *table, spinlock_t *lock)
153 : : {
154 : : struct clk_mux *mux;
155 : : struct clk_hw *hw;
156 : : struct clk_init_data init;
157 : : u8 width = 0;
158 : : int ret;
159 : :
160 : 0 : if (clk_mux_flags & CLK_MUX_HIWORD_MASK) {
161 : 0 : width = fls(mask) - ffs(mask) + 1;
162 : 0 : if (width + shift > 16) {
163 : 0 : pr_err("mux value exceeds LOWORD field\n");
164 : 0 : return ERR_PTR(-EINVAL);
165 : : }
166 : : }
167 : :
168 : : /* allocate the mux */
169 : 0 : mux = kzalloc(sizeof(*mux), GFP_KERNEL);
170 : 0 : if (!mux)
171 : : return ERR_PTR(-ENOMEM);
172 : :
173 : 0 : init.name = name;
174 : 0 : if (clk_mux_flags & CLK_MUX_READ_ONLY)
175 : 0 : init.ops = &clk_mux_ro_ops;
176 : : else
177 : 0 : init.ops = &clk_mux_ops;
178 : 0 : init.flags = flags;
179 : 0 : init.parent_names = parent_names;
180 : 0 : init.num_parents = num_parents;
181 : :
182 : : /* struct clk_mux assignments */
183 : 0 : mux->reg = reg;
184 : 0 : mux->shift = shift;
185 : 0 : mux->mask = mask;
186 : 0 : mux->flags = clk_mux_flags;
187 : 0 : mux->lock = lock;
188 : 0 : mux->table = table;
189 : 0 : mux->hw.init = &init;
190 : :
191 : 0 : hw = &mux->hw;
192 : 0 : ret = clk_hw_register(dev, hw);
193 : 0 : if (ret) {
194 : 0 : kfree(mux);
195 : : hw = ERR_PTR(ret);
196 : : }
197 : :
198 : 0 : return hw;
199 : : }
200 : : EXPORT_SYMBOL_GPL(clk_hw_register_mux_table);
201 : :
202 : 0 : struct clk *clk_register_mux_table(struct device *dev, const char *name,
203 : : const char * const *parent_names, u8 num_parents,
204 : : unsigned long flags,
205 : : void __iomem *reg, u8 shift, u32 mask,
206 : : u8 clk_mux_flags, u32 *table, spinlock_t *lock)
207 : : {
208 : : struct clk_hw *hw;
209 : :
210 : 0 : hw = clk_hw_register_mux_table(dev, name, parent_names, num_parents,
211 : : flags, reg, shift, mask, clk_mux_flags,
212 : : table, lock);
213 : 0 : if (IS_ERR(hw))
214 : : return ERR_CAST(hw);
215 : 0 : return hw->clk;
216 : : }
217 : : EXPORT_SYMBOL_GPL(clk_register_mux_table);
218 : :
219 : 0 : struct clk *clk_register_mux(struct device *dev, const char *name,
220 : : const char * const *parent_names, u8 num_parents,
221 : : unsigned long flags,
222 : : void __iomem *reg, u8 shift, u8 width,
223 : : u8 clk_mux_flags, spinlock_t *lock)
224 : : {
225 : 0 : u32 mask = BIT(width) - 1;
226 : :
227 : 0 : return clk_register_mux_table(dev, name, parent_names, num_parents,
228 : : flags, reg, shift, mask, clk_mux_flags,
229 : : NULL, lock);
230 : : }
231 : : EXPORT_SYMBOL_GPL(clk_register_mux);
232 : :
233 : 0 : struct clk_hw *clk_hw_register_mux(struct device *dev, const char *name,
234 : : const char * const *parent_names, u8 num_parents,
235 : : unsigned long flags,
236 : : void __iomem *reg, u8 shift, u8 width,
237 : : u8 clk_mux_flags, spinlock_t *lock)
238 : : {
239 : 0 : u32 mask = BIT(width) - 1;
240 : :
241 : 0 : return clk_hw_register_mux_table(dev, name, parent_names, num_parents,
242 : : flags, reg, shift, mask, clk_mux_flags,
243 : : NULL, lock);
244 : : }
245 : : EXPORT_SYMBOL_GPL(clk_hw_register_mux);
246 : :
247 : 0 : void clk_unregister_mux(struct clk *clk)
248 : : {
249 : : struct clk_mux *mux;
250 : : struct clk_hw *hw;
251 : :
252 : 0 : hw = __clk_get_hw(clk);
253 : 0 : if (!hw)
254 : 0 : return;
255 : :
256 : : mux = to_clk_mux(hw);
257 : :
258 : 0 : clk_unregister(clk);
259 : 0 : kfree(mux);
260 : : }
261 : : EXPORT_SYMBOL_GPL(clk_unregister_mux);
262 : :
263 : 0 : void clk_hw_unregister_mux(struct clk_hw *hw)
264 : : {
265 : : struct clk_mux *mux;
266 : :
267 : : mux = to_clk_mux(hw);
268 : :
269 : 0 : clk_hw_unregister(hw);
270 : 0 : kfree(mux);
271 : 0 : }
272 : : EXPORT_SYMBOL_GPL(clk_hw_unregister_mux);
|