1
2
3
4
5
6
7
8
9
10
11
12
13#include <linux/kernel.h>
14#include <linux/clk-provider.h>
15#include <linux/of_address.h>
16#include <linux/slab.h>
17#include <linux/delay.h>
18#include "common.h"
19
20#define CORE_CLK_DIV_RATIO_MASK 0xff
21
22
23
24
25
26
27
28
29struct clk_corediv_desc {
30 unsigned int mask;
31 unsigned int offset;
32 unsigned int fieldbit;
33};
34
35
36
37
38
39
40
41struct clk_corediv_soc_desc {
42 const struct clk_corediv_desc *descs;
43 unsigned int ndescs;
44 const struct clk_ops ops;
45 u32 ratio_reload;
46 u32 enable_bit_offset;
47 u32 ratio_offset;
48};
49
50
51
52
53
54
55struct clk_corediv {
56 struct clk_hw hw;
57 void __iomem *reg;
58 const struct clk_corediv_desc *desc;
59 const struct clk_corediv_soc_desc *soc_desc;
60 spinlock_t lock;
61};
62
63static struct clk_onecell_data clk_data;
64
65
66
67
68
69
70static const struct clk_corediv_desc mvebu_corediv_desc[] = {
71 { .mask = 0x3f, .offset = 8, .fieldbit = 1 },
72};
73
74static const struct clk_corediv_desc mv98dx3236_corediv_desc[] = {
75 { .mask = 0x0f, .offset = 6, .fieldbit = 27 },
76};
77
78#define to_corediv_clk(p) container_of(p, struct clk_corediv, hw)
79
80static int clk_corediv_is_enabled(struct clk_hw *hwclk)
81{
82 struct clk_corediv *corediv = to_corediv_clk(hwclk);
83 const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc;
84 const struct clk_corediv_desc *desc = corediv->desc;
85 u32 enable_mask = BIT(desc->fieldbit) << soc_desc->enable_bit_offset;
86
87 return !!(readl(corediv->reg) & enable_mask);
88}
89
90static int clk_corediv_enable(struct clk_hw *hwclk)
91{
92 struct clk_corediv *corediv = to_corediv_clk(hwclk);
93 const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc;
94 const struct clk_corediv_desc *desc = corediv->desc;
95 unsigned long flags = 0;
96 u32 reg;
97
98 spin_lock_irqsave(&corediv->lock, flags);
99
100 reg = readl(corediv->reg);
101 reg |= (BIT(desc->fieldbit) << soc_desc->enable_bit_offset);
102 writel(reg, corediv->reg);
103
104 spin_unlock_irqrestore(&corediv->lock, flags);
105
106 return 0;
107}
108
109static void clk_corediv_disable(struct clk_hw *hwclk)
110{
111 struct clk_corediv *corediv = to_corediv_clk(hwclk);
112 const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc;
113 const struct clk_corediv_desc *desc = corediv->desc;
114 unsigned long flags = 0;
115 u32 reg;
116
117 spin_lock_irqsave(&corediv->lock, flags);
118
119 reg = readl(corediv->reg);
120 reg &= ~(BIT(desc->fieldbit) << soc_desc->enable_bit_offset);
121 writel(reg, corediv->reg);
122
123 spin_unlock_irqrestore(&corediv->lock, flags);
124}
125
126static unsigned long clk_corediv_recalc_rate(struct clk_hw *hwclk,
127 unsigned long parent_rate)
128{
129 struct clk_corediv *corediv = to_corediv_clk(hwclk);
130 const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc;
131 const struct clk_corediv_desc *desc = corediv->desc;
132 u32 reg, div;
133
134 reg = readl(corediv->reg + soc_desc->ratio_offset);
135 div = (reg >> desc->offset) & desc->mask;
136 return parent_rate / div;
137}
138
139static long clk_corediv_round_rate(struct clk_hw *hwclk, unsigned long rate,
140 unsigned long *parent_rate)
141{
142
143 u32 div;
144
145 div = *parent_rate / rate;
146 if (div < 4)
147 div = 4;
148 else if (div > 6)
149 div = 8;
150
151 return *parent_rate / div;
152}
153
154static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate,
155 unsigned long parent_rate)
156{
157 struct clk_corediv *corediv = to_corediv_clk(hwclk);
158 const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc;
159 const struct clk_corediv_desc *desc = corediv->desc;
160 unsigned long flags = 0;
161 u32 reg, div;
162
163 div = parent_rate / rate;
164
165 spin_lock_irqsave(&corediv->lock, flags);
166
167
168 reg = readl(corediv->reg + soc_desc->ratio_offset);
169 reg &= ~(desc->mask << desc->offset);
170 reg |= (div & desc->mask) << desc->offset;
171 writel(reg, corediv->reg + soc_desc->ratio_offset);
172
173
174 reg = readl(corediv->reg) | BIT(desc->fieldbit);
175 writel(reg, corediv->reg);
176
177
178 reg = readl(corediv->reg) | soc_desc->ratio_reload;
179 writel(reg, corediv->reg);
180
181
182
183
184
185 udelay(1000);
186 reg &= ~(CORE_CLK_DIV_RATIO_MASK | soc_desc->ratio_reload);
187 writel(reg, corediv->reg);
188 udelay(1000);
189
190 spin_unlock_irqrestore(&corediv->lock, flags);
191
192 return 0;
193}
194
195static const struct clk_corediv_soc_desc armada370_corediv_soc = {
196 .descs = mvebu_corediv_desc,
197 .ndescs = ARRAY_SIZE(mvebu_corediv_desc),
198 .ops = {
199 .enable = clk_corediv_enable,
200 .disable = clk_corediv_disable,
201 .is_enabled = clk_corediv_is_enabled,
202 .recalc_rate = clk_corediv_recalc_rate,
203 .round_rate = clk_corediv_round_rate,
204 .set_rate = clk_corediv_set_rate,
205 },
206 .ratio_reload = BIT(8),
207 .enable_bit_offset = 24,
208 .ratio_offset = 0x8,
209};
210
211static const struct clk_corediv_soc_desc armada380_corediv_soc = {
212 .descs = mvebu_corediv_desc,
213 .ndescs = ARRAY_SIZE(mvebu_corediv_desc),
214 .ops = {
215 .enable = clk_corediv_enable,
216 .disable = clk_corediv_disable,
217 .is_enabled = clk_corediv_is_enabled,
218 .recalc_rate = clk_corediv_recalc_rate,
219 .round_rate = clk_corediv_round_rate,
220 .set_rate = clk_corediv_set_rate,
221 },
222 .ratio_reload = BIT(8),
223 .enable_bit_offset = 16,
224 .ratio_offset = 0x4,
225};
226
227static const struct clk_corediv_soc_desc armada375_corediv_soc = {
228 .descs = mvebu_corediv_desc,
229 .ndescs = ARRAY_SIZE(mvebu_corediv_desc),
230 .ops = {
231 .recalc_rate = clk_corediv_recalc_rate,
232 .round_rate = clk_corediv_round_rate,
233 .set_rate = clk_corediv_set_rate,
234 },
235 .ratio_reload = BIT(8),
236 .ratio_offset = 0x4,
237};
238
239static const struct clk_corediv_soc_desc mv98dx3236_corediv_soc = {
240 .descs = mv98dx3236_corediv_desc,
241 .ndescs = ARRAY_SIZE(mv98dx3236_corediv_desc),
242 .ops = {
243 .recalc_rate = clk_corediv_recalc_rate,
244 .round_rate = clk_corediv_round_rate,
245 .set_rate = clk_corediv_set_rate,
246 },
247 .ratio_reload = BIT(10),
248 .ratio_offset = 0x8,
249};
250
251static void __init
252mvebu_corediv_clk_init(struct device_node *node,
253 const struct clk_corediv_soc_desc *soc_desc)
254{
255 struct clk_init_data init;
256 struct clk_corediv *corediv;
257 struct clk **clks;
258 void __iomem *base;
259 const char *parent_name;
260 const char *clk_name;
261 int i;
262
263 base = of_iomap(node, 0);
264 if (WARN_ON(!base))
265 return;
266
267 parent_name = of_clk_get_parent_name(node, 0);
268
269 clk_data.clk_num = soc_desc->ndescs;
270
271
272 clks = kcalloc(clk_data.clk_num, sizeof(struct clk *),
273 GFP_KERNEL);
274 if (WARN_ON(!clks))
275 goto err_unmap;
276
277 corediv = kcalloc(clk_data.clk_num, sizeof(struct clk_corediv),
278 GFP_KERNEL);
279 if (WARN_ON(!corediv))
280 goto err_free_clks;
281
282 spin_lock_init(&corediv->lock);
283
284 for (i = 0; i < clk_data.clk_num; i++) {
285 of_property_read_string_index(node, "clock-output-names",
286 i, &clk_name);
287 init.num_parents = 1;
288 init.parent_names = &parent_name;
289 init.name = clk_name;
290 init.ops = &soc_desc->ops;
291 init.flags = 0;
292
293 corediv[i].soc_desc = soc_desc;
294 corediv[i].desc = soc_desc->descs + i;
295 corediv[i].reg = base;
296 corediv[i].hw.init = &init;
297
298 clks[i] = clk_register(NULL, &corediv[i].hw);
299 WARN_ON(IS_ERR(clks[i]));
300 }
301
302 clk_data.clks = clks;
303 of_clk_add_provider(node, of_clk_src_onecell_get, &clk_data);
304 return;
305
306err_free_clks:
307 kfree(clks);
308err_unmap:
309 iounmap(base);
310}
311
312static void __init armada370_corediv_clk_init(struct device_node *node)
313{
314 return mvebu_corediv_clk_init(node, &armada370_corediv_soc);
315}
316CLK_OF_DECLARE(armada370_corediv_clk, "marvell,armada-370-corediv-clock",
317 armada370_corediv_clk_init);
318
319static void __init armada375_corediv_clk_init(struct device_node *node)
320{
321 return mvebu_corediv_clk_init(node, &armada375_corediv_soc);
322}
323CLK_OF_DECLARE(armada375_corediv_clk, "marvell,armada-375-corediv-clock",
324 armada375_corediv_clk_init);
325
326static void __init armada380_corediv_clk_init(struct device_node *node)
327{
328 return mvebu_corediv_clk_init(node, &armada380_corediv_soc);
329}
330CLK_OF_DECLARE(armada380_corediv_clk, "marvell,armada-380-corediv-clock",
331 armada380_corediv_clk_init);
332
333static void __init mv98dx3236_corediv_clk_init(struct device_node *node)
334{
335 return mvebu_corediv_clk_init(node, &mv98dx3236_corediv_soc);
336}
337CLK_OF_DECLARE(mv98dx3236_corediv_clk, "marvell,mv98dx3236-corediv-clock",
338 mv98dx3236_corediv_clk_init);
339