blob: d28775d64d33ff8c939391bfab2701bbcec2e148 [file] [log] [blame]
Claudiu Beznea1f9023a2020-09-07 17:46:43 +03001// 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>
Claudiu Beznea8b5bbad2021-07-16 08:43:48 +030014#include <div64.h>
Claudiu Beznea1f9023a2020-09-07 17:46:43 +030015#include <dm.h>
16#include <linux/clk-provider.h>
17#include <linux/clk/at91_pmc.h>
18
19#include "pmc.h"
20
Claudiu Beznea8b5bbad2021-07-16 08:43:48 +030021#define UBOOT_DM_CLK_AT91_MASTER_PRES "at91-master-clk-pres"
22#define UBOOT_DM_CLK_AT91_MASTER_DIV "at91-master-clk-div"
Claudiu Bezneae812b022020-09-07 17:46:44 +030023#define UBOOT_DM_CLK_AT91_SAMA7G5_MASTER "at91-sama7g5-master-clk"
Claudiu Beznea1f9023a2020-09-07 17:46:43 +030024
25#define MASTER_PRES_MASK 0x7
26#define MASTER_PRES_MAX MASTER_PRES_MASK
27#define MASTER_DIV_SHIFT 8
Eugen Hristev227fa122020-07-01 10:42:58 +030028#define MASTER_DIV_MASK 0x7
Claudiu Beznea1f9023a2020-09-07 17:46:43 +030029
Claudiu Bezneae812b022020-09-07 17:46:44 +030030#define PMC_MCR 0x30
31#define PMC_MCR_ID_MSK GENMASK(3, 0)
32#define PMC_MCR_CMD BIT(7)
33#define PMC_MCR_DIV GENMASK(10, 8)
34#define PMC_MCR_CSS GENMASK(20, 16)
35#define PMC_MCR_CSS_SHIFT (16)
36#define PMC_MCR_EN BIT(28)
37
38#define PMC_MCR_ID(x) ((x) & PMC_MCR_ID_MSK)
39
40#define MASTER_MAX_ID 4
41
Claudiu Beznea1f9023a2020-09-07 17:46:43 +030042struct clk_master {
43 void __iomem *base;
44 const struct clk_master_layout *layout;
45 const struct clk_master_characteristics *characteristics;
46 const u32 *mux_table;
47 const u32 *clk_mux_table;
48 u32 num_parents;
49 struct clk clk;
50 u8 id;
51};
52
53#define to_clk_master(_clk) container_of(_clk, struct clk_master, clk)
54
55static inline bool clk_master_ready(struct clk_master *master)
56{
Claudiu Bezneae812b022020-09-07 17:46:44 +030057 unsigned int bit = master->id ? AT91_PMC_MCKXRDY : AT91_PMC_MCKRDY;
Claudiu Beznea1f9023a2020-09-07 17:46:43 +030058 unsigned int status;
59
60 pmc_read(master->base, AT91_PMC_SR, &status);
61
Claudiu Bezneae812b022020-09-07 17:46:44 +030062 return !!(status & bit);
Claudiu Beznea1f9023a2020-09-07 17:46:43 +030063}
64
65static int clk_master_enable(struct clk *clk)
66{
67 struct clk_master *master = to_clk_master(clk);
68
69 while (!clk_master_ready(master)) {
70 debug("waiting for mck %d\n", master->id);
71 cpu_relax();
72 }
73
74 return 0;
75}
76
Claudiu Beznea8b5bbad2021-07-16 08:43:48 +030077static ulong clk_master_pres_get_rate(struct clk *clk)
Claudiu Beznea1f9023a2020-09-07 17:46:43 +030078{
79 struct clk_master *master = to_clk_master(clk);
80 const struct clk_master_layout *layout = master->layout;
81 const struct clk_master_characteristics *characteristics =
82 master->characteristics;
83 ulong rate = clk_get_parent_rate(clk);
84 unsigned int mckr;
Claudiu Beznea8b5bbad2021-07-16 08:43:48 +030085 u8 pres;
Claudiu Beznea1f9023a2020-09-07 17:46:43 +030086
87 if (!rate)
88 return 0;
89
90 pmc_read(master->base, master->layout->offset, &mckr);
91 mckr &= layout->mask;
92
93 pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK;
Claudiu Beznea1f9023a2020-09-07 17:46:43 +030094
95 if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX)
Claudiu Beznea8b5bbad2021-07-16 08:43:48 +030096 pres = 3;
Claudiu Beznea1f9023a2020-09-07 17:46:43 +030097 else
Claudiu Beznea8b5bbad2021-07-16 08:43:48 +030098 pres = (1 << pres);
Claudiu Beznea1f9023a2020-09-07 17:46:43 +030099
Claudiu Beznea8b5bbad2021-07-16 08:43:48 +0300100 return DIV_ROUND_CLOSEST_ULL(rate, pres);
Claudiu Beznea1f9023a2020-09-07 17:46:43 +0300101}
102
Claudiu Beznea8b5bbad2021-07-16 08:43:48 +0300103static const struct clk_ops master_pres_ops = {
Claudiu Beznea1f9023a2020-09-07 17:46:43 +0300104 .enable = clk_master_enable,
Claudiu Beznea8b5bbad2021-07-16 08:43:48 +0300105 .get_rate = clk_master_pres_get_rate,
Claudiu Beznea1f9023a2020-09-07 17:46:43 +0300106};
107
Claudiu Beznea8b5bbad2021-07-16 08:43:48 +0300108struct clk *at91_clk_register_master_pres(void __iomem *base,
Claudiu Beznea1f9023a2020-09-07 17:46:43 +0300109 const char *name, const char * const *parent_names,
110 int num_parents, const struct clk_master_layout *layout,
111 const struct clk_master_characteristics *characteristics,
112 const u32 *mux_table)
113{
114 struct clk_master *master;
115 struct clk *clk;
116 unsigned int val;
117 int ret;
118
119 if (!base || !name || !num_parents || !parent_names ||
120 !layout || !characteristics || !mux_table)
121 return ERR_PTR(-EINVAL);
122
123 master = kzalloc(sizeof(*master), GFP_KERNEL);
124 if (!master)
125 return ERR_PTR(-ENOMEM);
126
127 master->layout = layout;
128 master->characteristics = characteristics;
129 master->base = base;
130 master->num_parents = num_parents;
131 master->mux_table = mux_table;
132
133 pmc_read(master->base, master->layout->offset, &val);
134 clk = &master->clk;
135 clk->flags = CLK_GET_RATE_NOCACHE | CLK_IS_CRITICAL;
Claudiu Beznea8b5bbad2021-07-16 08:43:48 +0300136 ret = clk_register(clk, UBOOT_DM_CLK_AT91_MASTER_PRES, name,
Claudiu Beznea1f9023a2020-09-07 17:46:43 +0300137 parent_names[val & AT91_PMC_CSS]);
138 if (ret) {
139 kfree(master);
140 clk = ERR_PTR(ret);
141 }
142
143 return clk;
144}
145
Claudiu Beznea8b5bbad2021-07-16 08:43:48 +0300146U_BOOT_DRIVER(at91_master_pres_clk) = {
147 .name = UBOOT_DM_CLK_AT91_MASTER_PRES,
148 .id = UCLASS_CLK,
149 .ops = &master_pres_ops,
150 .flags = DM_FLAG_PRE_RELOC,
151};
152
153static ulong clk_master_div_get_rate(struct clk *clk)
154{
155 struct clk_master *master = to_clk_master(clk);
156 const struct clk_master_layout *layout = master->layout;
157 const struct clk_master_characteristics *characteristics =
158 master->characteristics;
159 ulong rate = clk_get_parent_rate(clk);
160 unsigned int mckr;
161 u8 div;
162
163 if (!rate)
164 return 0;
165
166 pmc_read(master->base, master->layout->offset, &mckr);
167 mckr &= layout->mask;
168 div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
169
170 rate = DIV_ROUND_CLOSEST_ULL(rate, characteristics->divisors[div]);
171 if (rate < characteristics->output.min)
172 pr_warn("master clk is underclocked");
173 else if (rate > characteristics->output.max)
174 pr_warn("master clk is overclocked");
175
176 return rate;
177}
178
179static const struct clk_ops master_div_ops = {
180 .enable = clk_master_enable,
181 .get_rate = clk_master_div_get_rate,
182};
183
184struct clk *at91_clk_register_master_div(void __iomem *base,
185 const char *name, const char *parent_name,
186 const struct clk_master_layout *layout,
187 const struct clk_master_characteristics *characteristics)
188{
189 struct clk_master *master;
190 struct clk *clk;
191 int ret;
192
193 if (!base || !name || !parent_name || !layout || !characteristics)
194 return ERR_PTR(-EINVAL);
195
196 master = kzalloc(sizeof(*master), GFP_KERNEL);
197 if (!master)
198 return ERR_PTR(-ENOMEM);
199
200 master->layout = layout;
201 master->characteristics = characteristics;
202 master->base = base;
203 master->num_parents = 1;
204
205 clk = &master->clk;
206 clk->flags = CLK_GET_RATE_NOCACHE | CLK_IS_CRITICAL;
207 ret = clk_register(clk, UBOOT_DM_CLK_AT91_MASTER_DIV, name,
208 parent_name);
209 if (ret) {
210 kfree(master);
211 clk = ERR_PTR(ret);
212 }
213
214 return clk;
215}
216
217U_BOOT_DRIVER(at91_master_div_clk) = {
218 .name = UBOOT_DM_CLK_AT91_MASTER_DIV,
Claudiu Beznea1f9023a2020-09-07 17:46:43 +0300219 .id = UCLASS_CLK,
Claudiu Beznea8b5bbad2021-07-16 08:43:48 +0300220 .ops = &master_div_ops,
Claudiu Beznea1f9023a2020-09-07 17:46:43 +0300221 .flags = DM_FLAG_PRE_RELOC,
222};
223
Claudiu Bezneae812b022020-09-07 17:46:44 +0300224static int clk_sama7g5_master_set_parent(struct clk *clk, struct clk *parent)
225{
226 struct clk_master *master = to_clk_master(clk);
227 int index;
228
229 index = at91_clk_mux_val_to_index(master->clk_mux_table,
230 master->num_parents, parent->id);
231 if (index < 0)
232 return index;
233
234 index = at91_clk_mux_index_to_val(master->mux_table,
235 master->num_parents, index);
236 if (index < 0)
237 return index;
238
239 pmc_write(master->base, PMC_MCR, PMC_MCR_ID(master->id));
240 pmc_update_bits(master->base, PMC_MCR,
241 PMC_MCR_CSS | PMC_MCR_CMD | PMC_MCR_ID_MSK,
242 (index << PMC_MCR_CSS_SHIFT) | PMC_MCR_CMD |
243 PMC_MCR_ID(master->id));
244 return 0;
245}
246
247static int clk_sama7g5_master_enable(struct clk *clk)
248{
249 struct clk_master *master = to_clk_master(clk);
250
251 pmc_write(master->base, PMC_MCR, PMC_MCR_ID(master->id));
252 pmc_update_bits(master->base, PMC_MCR,
253 PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK,
254 PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID(master->id));
255
256 return 0;
257}
258
259static int clk_sama7g5_master_disable(struct clk *clk)
260{
261 struct clk_master *master = to_clk_master(clk);
262
263 pmc_write(master->base, PMC_MCR, master->id);
264 pmc_update_bits(master->base, PMC_MCR,
265 PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK,
266 PMC_MCR_CMD | PMC_MCR_ID(master->id));
267
268 return 0;
269}
270
271static ulong clk_sama7g5_master_set_rate(struct clk *clk, ulong rate)
272{
273 struct clk_master *master = to_clk_master(clk);
274 ulong parent_rate = clk_get_parent_rate(clk);
275 ulong div, rrate;
276
277 if (!parent_rate)
278 return 0;
279
280 div = DIV_ROUND_CLOSEST(parent_rate, rate);
281 if ((div > (1 << (MASTER_PRES_MAX - 1))) || (div & (div - 1))) {
282 return 0;
283 } else if (div == 3) {
284 rrate = DIV_ROUND_CLOSEST(parent_rate, MASTER_PRES_MAX);
285 div = MASTER_PRES_MAX;
286 } else {
287 rrate = DIV_ROUND_CLOSEST(parent_rate, div);
288 div = ffs(div) - 1;
289 }
290
291 pmc_write(master->base, PMC_MCR, master->id);
292 pmc_update_bits(master->base, PMC_MCR,
293 PMC_MCR_DIV | PMC_MCR_CMD | PMC_MCR_ID_MSK,
294 (div << MASTER_DIV_SHIFT) | PMC_MCR_CMD |
295 PMC_MCR_ID(master->id));
296
297 return rrate;
298}
299
300static ulong clk_sama7g5_master_get_rate(struct clk *clk)
301{
302 struct clk_master *master = to_clk_master(clk);
303 ulong parent_rate = clk_get_parent_rate(clk);
304 unsigned int val;
305 ulong div;
306
307 if (!parent_rate)
308 return 0;
309
310 pmc_write(master->base, PMC_MCR, master->id);
311 pmc_read(master->base, PMC_MCR, &val);
312
313 div = (val >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
314
315 if (div == MASTER_PRES_MAX)
316 div = 3;
317 else
318 div = 1 << div;
319
320 return DIV_ROUND_CLOSEST(parent_rate, div);
321}
322
323static const struct clk_ops sama7g5_master_ops = {
324 .enable = clk_sama7g5_master_enable,
325 .disable = clk_sama7g5_master_disable,
326 .set_rate = clk_sama7g5_master_set_rate,
327 .get_rate = clk_sama7g5_master_get_rate,
328 .set_parent = clk_sama7g5_master_set_parent,
329};
330
331struct clk *at91_clk_sama7g5_register_master(void __iomem *base,
332 const char *name, const char * const *parent_names,
333 int num_parents, const u32 *mux_table, const u32 *clk_mux_table,
334 bool critical, u8 id)
335{
336 struct clk_master *master;
337 struct clk *clk;
338 u32 val, index;
339 int ret;
340
341 if (!base || !name || !num_parents || !parent_names ||
342 !mux_table || !clk_mux_table || id > MASTER_MAX_ID)
343 return ERR_PTR(-EINVAL);
344
345 master = kzalloc(sizeof(*master), GFP_KERNEL);
346 if (!master)
347 return ERR_PTR(-ENOMEM);
348
349 master->base = base;
350 master->id = id;
351 master->mux_table = mux_table;
352 master->clk_mux_table = clk_mux_table;
353 master->num_parents = num_parents;
354
355 pmc_write(master->base, PMC_MCR, master->id);
356 pmc_read(master->base, PMC_MCR, &val);
357
358 index = at91_clk_mux_val_to_index(master->mux_table,
359 master->num_parents,
360 (val & PMC_MCR_CSS) >> PMC_MCR_CSS_SHIFT);
361 if (index < 0) {
362 kfree(master);
363 return ERR_PTR(index);
364 }
365
366 clk = &master->clk;
367 clk->flags = CLK_GET_RATE_NOCACHE | (critical ? CLK_IS_CRITICAL : 0);
368
369 ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAMA7G5_MASTER, name,
370 parent_names[index]);
371 if (ret) {
372 kfree(master);
373 clk = ERR_PTR(ret);
374 }
375
376 return clk;
377}
378
379U_BOOT_DRIVER(at91_sama7g5_master_clk) = {
380 .name = UBOOT_DM_CLK_AT91_SAMA7G5_MASTER,
381 .id = UCLASS_CLK,
382 .ops = &sama7g5_master_ops,
383 .flags = DM_FLAG_PRE_RELOC,
384};
385
Claudiu Beznea1f9023a2020-09-07 17:46:43 +0300386const struct clk_master_layout at91rm9200_master_layout = {
387 .mask = 0x31F,
388 .pres_shift = 2,
389 .offset = AT91_PMC_MCKR,
390};
391
392const struct clk_master_layout at91sam9x5_master_layout = {
393 .mask = 0x373,
394 .pres_shift = 4,
395 .offset = AT91_PMC_MCKR,
396};