1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Master clock support for AT91 architectures.
4 *
5 * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
6 *
7 * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
8 *
9 * Based on drivers/clk/at91/clk-master.c from Linux.
10 */
11
12 #include <asm/processor.h>
13 #include <clk-uclass.h>
14 #include <common.h>
15 #include <div64.h>
16 #include <dm.h>
17 #include <linux/clk-provider.h>
18 #include <linux/clk/at91_pmc.h>
19
20 #include "pmc.h"
21
22 #define UBOOT_DM_CLK_AT91_MASTER_PRES "at91-master-clk-pres"
23 #define UBOOT_DM_CLK_AT91_MASTER_DIV "at91-master-clk-div"
24 #define UBOOT_DM_CLK_AT91_SAMA7G5_MASTER "at91-sama7g5-master-clk"
25
26 #define MASTER_PRES_MASK 0x7
27 #define MASTER_PRES_MAX MASTER_PRES_MASK
28 #define MASTER_DIV_SHIFT 8
29 #define MASTER_DIV_MASK 0x7
30
31 #define PMC_MCR 0x30
32 #define PMC_MCR_ID_MSK GENMASK(3, 0)
33 #define PMC_MCR_CMD BIT(7)
34 #define PMC_MCR_DIV GENMASK(10, 8)
35 #define PMC_MCR_CSS GENMASK(20, 16)
36 #define PMC_MCR_CSS_SHIFT (16)
37 #define PMC_MCR_EN BIT(28)
38
39 #define PMC_MCR_ID(x) ((x) & PMC_MCR_ID_MSK)
40
41 #define MASTER_MAX_ID 4
42
43 struct clk_master {
44 void __iomem *base;
45 const struct clk_master_layout *layout;
46 const struct clk_master_characteristics *characteristics;
47 const u32 *mux_table;
48 const u32 *clk_mux_table;
49 u32 num_parents;
50 struct clk clk;
51 u8 id;
52 };
53
54 #define to_clk_master(_clk) container_of(_clk, struct clk_master, clk)
55
clk_master_ready(struct clk_master * master)56 static inline bool clk_master_ready(struct clk_master *master)
57 {
58 unsigned int bit = master->id ? AT91_PMC_MCKXRDY : AT91_PMC_MCKRDY;
59 unsigned int status;
60
61 pmc_read(master->base, AT91_PMC_SR, &status);
62
63 return !!(status & bit);
64 }
65
clk_master_enable(struct clk * clk)66 static int clk_master_enable(struct clk *clk)
67 {
68 struct clk_master *master = to_clk_master(clk);
69
70 while (!clk_master_ready(master)) {
71 debug("waiting for mck %d\n", master->id);
72 cpu_relax();
73 }
74
75 return 0;
76 }
77
clk_master_pres_get_rate(struct clk * clk)78 static ulong clk_master_pres_get_rate(struct clk *clk)
79 {
80 struct clk_master *master = to_clk_master(clk);
81 const struct clk_master_layout *layout = master->layout;
82 const struct clk_master_characteristics *characteristics =
83 master->characteristics;
84 ulong rate = clk_get_parent_rate(clk);
85 unsigned int mckr;
86 u8 pres;
87
88 if (!rate)
89 return 0;
90
91 pmc_read(master->base, master->layout->offset, &mckr);
92 mckr &= layout->mask;
93
94 pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK;
95
96 if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX)
97 pres = 3;
98 else
99 pres = (1 << pres);
100
101 return DIV_ROUND_CLOSEST_ULL(rate, pres);
102 }
103
104 static const struct clk_ops master_pres_ops = {
105 .enable = clk_master_enable,
106 .get_rate = clk_master_pres_get_rate,
107 };
108
at91_clk_register_master_pres(void __iomem * base,const char * name,const char * const * parent_names,int num_parents,const struct clk_master_layout * layout,const struct clk_master_characteristics * characteristics,const u32 * mux_table)109 struct clk *at91_clk_register_master_pres(void __iomem *base,
110 const char *name, const char * const *parent_names,
111 int num_parents, const struct clk_master_layout *layout,
112 const struct clk_master_characteristics *characteristics,
113 const u32 *mux_table)
114 {
115 struct clk_master *master;
116 struct clk *clk;
117 unsigned int val;
118 int ret;
119
120 if (!base || !name || !num_parents || !parent_names ||
121 !layout || !characteristics || !mux_table)
122 return ERR_PTR(-EINVAL);
123
124 master = kzalloc(sizeof(*master), GFP_KERNEL);
125 if (!master)
126 return ERR_PTR(-ENOMEM);
127
128 master->layout = layout;
129 master->characteristics = characteristics;
130 master->base = base;
131 master->num_parents = num_parents;
132 master->mux_table = mux_table;
133
134 pmc_read(master->base, master->layout->offset, &val);
135 clk = &master->clk;
136 clk->flags = CLK_GET_RATE_NOCACHE | CLK_IS_CRITICAL;
137 ret = clk_register(clk, UBOOT_DM_CLK_AT91_MASTER_PRES, name,
138 parent_names[val & AT91_PMC_CSS]);
139 if (ret) {
140 kfree(master);
141 clk = ERR_PTR(ret);
142 }
143
144 return clk;
145 }
146
147 U_BOOT_DRIVER(at91_master_pres_clk) = {
148 .name = UBOOT_DM_CLK_AT91_MASTER_PRES,
149 .id = UCLASS_CLK,
150 .ops = &master_pres_ops,
151 .flags = DM_FLAG_PRE_RELOC,
152 };
153
clk_master_div_get_rate(struct clk * clk)154 static ulong clk_master_div_get_rate(struct clk *clk)
155 {
156 struct clk_master *master = to_clk_master(clk);
157 const struct clk_master_layout *layout = master->layout;
158 const struct clk_master_characteristics *characteristics =
159 master->characteristics;
160 ulong rate = clk_get_parent_rate(clk);
161 unsigned int mckr;
162 u8 div;
163
164 if (!rate)
165 return 0;
166
167 pmc_read(master->base, master->layout->offset, &mckr);
168 mckr &= layout->mask;
169 div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
170
171 rate = DIV_ROUND_CLOSEST_ULL(rate, characteristics->divisors[div]);
172 if (rate < characteristics->output.min)
173 pr_warn("master clk is underclocked");
174 else if (rate > characteristics->output.max)
175 pr_warn("master clk is overclocked");
176
177 return rate;
178 }
179
180 static const struct clk_ops master_div_ops = {
181 .enable = clk_master_enable,
182 .get_rate = clk_master_div_get_rate,
183 };
184
at91_clk_register_master_div(void __iomem * base,const char * name,const char * parent_name,const struct clk_master_layout * layout,const struct clk_master_characteristics * characteristics)185 struct clk *at91_clk_register_master_div(void __iomem *base,
186 const char *name, const char *parent_name,
187 const struct clk_master_layout *layout,
188 const struct clk_master_characteristics *characteristics)
189 {
190 struct clk_master *master;
191 struct clk *clk;
192 int ret;
193
194 if (!base || !name || !parent_name || !layout || !characteristics)
195 return ERR_PTR(-EINVAL);
196
197 master = kzalloc(sizeof(*master), GFP_KERNEL);
198 if (!master)
199 return ERR_PTR(-ENOMEM);
200
201 master->layout = layout;
202 master->characteristics = characteristics;
203 master->base = base;
204 master->num_parents = 1;
205
206 clk = &master->clk;
207 clk->flags = CLK_GET_RATE_NOCACHE | CLK_IS_CRITICAL;
208 ret = clk_register(clk, UBOOT_DM_CLK_AT91_MASTER_DIV, name,
209 parent_name);
210 if (ret) {
211 kfree(master);
212 clk = ERR_PTR(ret);
213 }
214
215 return clk;
216 }
217
218 U_BOOT_DRIVER(at91_master_div_clk) = {
219 .name = UBOOT_DM_CLK_AT91_MASTER_DIV,
220 .id = UCLASS_CLK,
221 .ops = &master_div_ops,
222 .flags = DM_FLAG_PRE_RELOC,
223 };
224
clk_sama7g5_master_set_parent(struct clk * clk,struct clk * parent)225 static int clk_sama7g5_master_set_parent(struct clk *clk, struct clk *parent)
226 {
227 struct clk_master *master = to_clk_master(clk);
228 int index;
229
230 index = at91_clk_mux_val_to_index(master->clk_mux_table,
231 master->num_parents, parent->id);
232 if (index < 0)
233 return index;
234
235 index = at91_clk_mux_index_to_val(master->mux_table,
236 master->num_parents, index);
237 if (index < 0)
238 return index;
239
240 pmc_write(master->base, PMC_MCR, PMC_MCR_ID(master->id));
241 pmc_update_bits(master->base, PMC_MCR,
242 PMC_MCR_CSS | PMC_MCR_CMD | PMC_MCR_ID_MSK,
243 (index << PMC_MCR_CSS_SHIFT) | PMC_MCR_CMD |
244 PMC_MCR_ID(master->id));
245 return 0;
246 }
247
clk_sama7g5_master_enable(struct clk * clk)248 static int clk_sama7g5_master_enable(struct clk *clk)
249 {
250 struct clk_master *master = to_clk_master(clk);
251
252 pmc_write(master->base, PMC_MCR, PMC_MCR_ID(master->id));
253 pmc_update_bits(master->base, PMC_MCR,
254 PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK,
255 PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID(master->id));
256
257 return 0;
258 }
259
clk_sama7g5_master_disable(struct clk * clk)260 static int clk_sama7g5_master_disable(struct clk *clk)
261 {
262 struct clk_master *master = to_clk_master(clk);
263
264 pmc_write(master->base, PMC_MCR, master->id);
265 pmc_update_bits(master->base, PMC_MCR,
266 PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK,
267 PMC_MCR_CMD | PMC_MCR_ID(master->id));
268
269 return 0;
270 }
271
clk_sama7g5_master_set_rate(struct clk * clk,ulong rate)272 static ulong clk_sama7g5_master_set_rate(struct clk *clk, ulong rate)
273 {
274 struct clk_master *master = to_clk_master(clk);
275 ulong parent_rate = clk_get_parent_rate(clk);
276 ulong div, rrate;
277
278 if (!parent_rate)
279 return 0;
280
281 div = DIV_ROUND_CLOSEST(parent_rate, rate);
282 if ((div > (1 << (MASTER_PRES_MAX - 1))) || (div & (div - 1))) {
283 return 0;
284 } else if (div == 3) {
285 rrate = DIV_ROUND_CLOSEST(parent_rate, MASTER_PRES_MAX);
286 div = MASTER_PRES_MAX;
287 } else {
288 rrate = DIV_ROUND_CLOSEST(parent_rate, div);
289 div = ffs(div) - 1;
290 }
291
292 pmc_write(master->base, PMC_MCR, master->id);
293 pmc_update_bits(master->base, PMC_MCR,
294 PMC_MCR_DIV | PMC_MCR_CMD | PMC_MCR_ID_MSK,
295 (div << MASTER_DIV_SHIFT) | PMC_MCR_CMD |
296 PMC_MCR_ID(master->id));
297
298 return rrate;
299 }
300
clk_sama7g5_master_get_rate(struct clk * clk)301 static ulong clk_sama7g5_master_get_rate(struct clk *clk)
302 {
303 struct clk_master *master = to_clk_master(clk);
304 ulong parent_rate = clk_get_parent_rate(clk);
305 unsigned int val;
306 ulong div;
307
308 if (!parent_rate)
309 return 0;
310
311 pmc_write(master->base, PMC_MCR, master->id);
312 pmc_read(master->base, PMC_MCR, &val);
313
314 div = (val >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
315
316 if (div == MASTER_PRES_MAX)
317 div = 3;
318 else
319 div = 1 << div;
320
321 return DIV_ROUND_CLOSEST(parent_rate, div);
322 }
323
324 static const struct clk_ops sama7g5_master_ops = {
325 .enable = clk_sama7g5_master_enable,
326 .disable = clk_sama7g5_master_disable,
327 .set_rate = clk_sama7g5_master_set_rate,
328 .get_rate = clk_sama7g5_master_get_rate,
329 .set_parent = clk_sama7g5_master_set_parent,
330 };
331
at91_clk_sama7g5_register_master(void __iomem * base,const char * name,const char * const * parent_names,int num_parents,const u32 * mux_table,const u32 * clk_mux_table,bool critical,u8 id)332 struct clk *at91_clk_sama7g5_register_master(void __iomem *base,
333 const char *name, const char * const *parent_names,
334 int num_parents, const u32 *mux_table, const u32 *clk_mux_table,
335 bool critical, u8 id)
336 {
337 struct clk_master *master;
338 struct clk *clk;
339 u32 val, index;
340 int ret;
341
342 if (!base || !name || !num_parents || !parent_names ||
343 !mux_table || !clk_mux_table || id > MASTER_MAX_ID)
344 return ERR_PTR(-EINVAL);
345
346 master = kzalloc(sizeof(*master), GFP_KERNEL);
347 if (!master)
348 return ERR_PTR(-ENOMEM);
349
350 master->base = base;
351 master->id = id;
352 master->mux_table = mux_table;
353 master->clk_mux_table = clk_mux_table;
354 master->num_parents = num_parents;
355
356 pmc_write(master->base, PMC_MCR, master->id);
357 pmc_read(master->base, PMC_MCR, &val);
358
359 index = at91_clk_mux_val_to_index(master->mux_table,
360 master->num_parents,
361 (val & PMC_MCR_CSS) >> PMC_MCR_CSS_SHIFT);
362 if (index < 0) {
363 kfree(master);
364 return ERR_PTR(index);
365 }
366
367 clk = &master->clk;
368 clk->flags = CLK_GET_RATE_NOCACHE | (critical ? CLK_IS_CRITICAL : 0);
369
370 ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAMA7G5_MASTER, name,
371 parent_names[index]);
372 if (ret) {
373 kfree(master);
374 clk = ERR_PTR(ret);
375 }
376
377 return clk;
378 }
379
380 U_BOOT_DRIVER(at91_sama7g5_master_clk) = {
381 .name = UBOOT_DM_CLK_AT91_SAMA7G5_MASTER,
382 .id = UCLASS_CLK,
383 .ops = &sama7g5_master_ops,
384 .flags = DM_FLAG_PRE_RELOC,
385 };
386
387 const struct clk_master_layout at91rm9200_master_layout = {
388 .mask = 0x31F,
389 .pres_shift = 2,
390 .offset = AT91_PMC_MCKR,
391 };
392
393 const struct clk_master_layout at91sam9x5_master_layout = {
394 .mask = 0x373,
395 .pres_shift = 4,
396 .offset = AT91_PMC_MCKR,
397 };
398