Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * drivers/clk/at91/sckc.c
4 *
5 * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
6 */
7
8#include <linux/clk-provider.h>
9#include <linux/clkdev.h>
10#include <linux/delay.h>
11#include <linux/of.h>
12#include <linux/of_address.h>
13#include <linux/io.h>
14
15#define SLOW_CLOCK_FREQ 32768
16#define SLOWCK_SW_CYCLES 5
17#define SLOWCK_SW_TIME_USEC ((SLOWCK_SW_CYCLES * USEC_PER_SEC) / \
18 SLOW_CLOCK_FREQ)
19
20#define AT91_SCKC_CR 0x00
21#define AT91_SCKC_RCEN (1 << 0)
22#define AT91_SCKC_OSC32EN (1 << 1)
23#define AT91_SCKC_OSC32BYP (1 << 2)
24#define AT91_SCKC_OSCSEL (1 << 3)
25
26struct clk_slow_osc {
27 struct clk_hw hw;
28 void __iomem *sckcr;
29 unsigned long startup_usec;
30};
31
32#define to_clk_slow_osc(hw) container_of(hw, struct clk_slow_osc, hw)
33
34struct clk_sama5d4_slow_osc {
35 struct clk_hw hw;
36 void __iomem *sckcr;
37 unsigned long startup_usec;
38 bool prepared;
39};
40
41#define to_clk_sama5d4_slow_osc(hw) container_of(hw, struct clk_sama5d4_slow_osc, hw)
42
43struct clk_slow_rc_osc {
44 struct clk_hw hw;
45 void __iomem *sckcr;
46 unsigned long frequency;
47 unsigned long accuracy;
48 unsigned long startup_usec;
49};
50
51#define to_clk_slow_rc_osc(hw) container_of(hw, struct clk_slow_rc_osc, hw)
52
53struct clk_sam9x5_slow {
54 struct clk_hw hw;
55 void __iomem *sckcr;
56 u8 parent;
57};
58
59#define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, hw)
60
61static int clk_slow_osc_prepare(struct clk_hw *hw)
62{
63 struct clk_slow_osc *osc = to_clk_slow_osc(hw);
64 void __iomem *sckcr = osc->sckcr;
65 u32 tmp = readl(sckcr);
66
67 if (tmp & (AT91_SCKC_OSC32BYP | AT91_SCKC_OSC32EN))
68 return 0;
69
70 writel(tmp | AT91_SCKC_OSC32EN, sckcr);
71
72 usleep_range(osc->startup_usec, osc->startup_usec + 1);
73
74 return 0;
75}
76
77static void clk_slow_osc_unprepare(struct clk_hw *hw)
78{
79 struct clk_slow_osc *osc = to_clk_slow_osc(hw);
80 void __iomem *sckcr = osc->sckcr;
81 u32 tmp = readl(sckcr);
82
83 if (tmp & AT91_SCKC_OSC32BYP)
84 return;
85
86 writel(tmp & ~AT91_SCKC_OSC32EN, sckcr);
87}
88
89static int clk_slow_osc_is_prepared(struct clk_hw *hw)
90{
91 struct clk_slow_osc *osc = to_clk_slow_osc(hw);
92 void __iomem *sckcr = osc->sckcr;
93 u32 tmp = readl(sckcr);
94
95 if (tmp & AT91_SCKC_OSC32BYP)
96 return 1;
97
98 return !!(tmp & AT91_SCKC_OSC32EN);
99}
100
101static const struct clk_ops slow_osc_ops = {
102 .prepare = clk_slow_osc_prepare,
103 .unprepare = clk_slow_osc_unprepare,
104 .is_prepared = clk_slow_osc_is_prepared,
105};
106
107static struct clk_hw * __init
108at91_clk_register_slow_osc(void __iomem *sckcr,
109 const char *name,
110 const char *parent_name,
111 unsigned long startup,
112 bool bypass)
113{
114 struct clk_slow_osc *osc;
115 struct clk_hw *hw;
116 struct clk_init_data init;
117 int ret;
118
119 if (!sckcr || !name || !parent_name)
120 return ERR_PTR(-EINVAL);
121
122 osc = kzalloc(sizeof(*osc), GFP_KERNEL);
123 if (!osc)
124 return ERR_PTR(-ENOMEM);
125
126 init.name = name;
127 init.ops = &slow_osc_ops;
128 init.parent_names = &parent_name;
129 init.num_parents = 1;
130 init.flags = CLK_IGNORE_UNUSED;
131
132 osc->hw.init = &init;
133 osc->sckcr = sckcr;
134 osc->startup_usec = startup;
135
136 if (bypass)
137 writel((readl(sckcr) & ~AT91_SCKC_OSC32EN) | AT91_SCKC_OSC32BYP,
138 sckcr);
139
140 hw = &osc->hw;
141 ret = clk_hw_register(NULL, &osc->hw);
142 if (ret) {
143 kfree(osc);
144 hw = ERR_PTR(ret);
145 }
146
147 return hw;
148}
149
150static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw,
151 unsigned long parent_rate)
152{
153 struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
154
155 return osc->frequency;
156}
157
158static unsigned long clk_slow_rc_osc_recalc_accuracy(struct clk_hw *hw,
159 unsigned long parent_acc)
160{
161 struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
162
163 return osc->accuracy;
164}
165
166static int clk_slow_rc_osc_prepare(struct clk_hw *hw)
167{
168 struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
169 void __iomem *sckcr = osc->sckcr;
170
171 writel(readl(sckcr) | AT91_SCKC_RCEN, sckcr);
172
173 usleep_range(osc->startup_usec, osc->startup_usec + 1);
174
175 return 0;
176}
177
178static void clk_slow_rc_osc_unprepare(struct clk_hw *hw)
179{
180 struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
181 void __iomem *sckcr = osc->sckcr;
182
183 writel(readl(sckcr) & ~AT91_SCKC_RCEN, sckcr);
184}
185
186static int clk_slow_rc_osc_is_prepared(struct clk_hw *hw)
187{
188 struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
189
190 return !!(readl(osc->sckcr) & AT91_SCKC_RCEN);
191}
192
193static const struct clk_ops slow_rc_osc_ops = {
194 .prepare = clk_slow_rc_osc_prepare,
195 .unprepare = clk_slow_rc_osc_unprepare,
196 .is_prepared = clk_slow_rc_osc_is_prepared,
197 .recalc_rate = clk_slow_rc_osc_recalc_rate,
198 .recalc_accuracy = clk_slow_rc_osc_recalc_accuracy,
199};
200
201static struct clk_hw * __init
202at91_clk_register_slow_rc_osc(void __iomem *sckcr,
203 const char *name,
204 unsigned long frequency,
205 unsigned long accuracy,
206 unsigned long startup)
207{
208 struct clk_slow_rc_osc *osc;
209 struct clk_hw *hw;
210 struct clk_init_data init;
211 int ret;
212
213 if (!sckcr || !name)
214 return ERR_PTR(-EINVAL);
215
216 osc = kzalloc(sizeof(*osc), GFP_KERNEL);
217 if (!osc)
218 return ERR_PTR(-ENOMEM);
219
220 init.name = name;
221 init.ops = &slow_rc_osc_ops;
222 init.parent_names = NULL;
223 init.num_parents = 0;
224 init.flags = CLK_IGNORE_UNUSED;
225
226 osc->hw.init = &init;
227 osc->sckcr = sckcr;
228 osc->frequency = frequency;
229 osc->accuracy = accuracy;
230 osc->startup_usec = startup;
231
232 hw = &osc->hw;
233 ret = clk_hw_register(NULL, &osc->hw);
234 if (ret) {
235 kfree(osc);
236 hw = ERR_PTR(ret);
237 }
238
239 return hw;
240}
241
242static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index)
243{
244 struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
245 void __iomem *sckcr = slowck->sckcr;
246 u32 tmp;
247
248 if (index > 1)
249 return -EINVAL;
250
251 tmp = readl(sckcr);
252
253 if ((!index && !(tmp & AT91_SCKC_OSCSEL)) ||
254 (index && (tmp & AT91_SCKC_OSCSEL)))
255 return 0;
256
257 if (index)
258 tmp |= AT91_SCKC_OSCSEL;
259 else
260 tmp &= ~AT91_SCKC_OSCSEL;
261
262 writel(tmp, sckcr);
263
264 usleep_range(SLOWCK_SW_TIME_USEC, SLOWCK_SW_TIME_USEC + 1);
265
266 return 0;
267}
268
269static u8 clk_sam9x5_slow_get_parent(struct clk_hw *hw)
270{
271 struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
272
273 return !!(readl(slowck->sckcr) & AT91_SCKC_OSCSEL);
274}
275
276static const struct clk_ops sam9x5_slow_ops = {
277 .set_parent = clk_sam9x5_slow_set_parent,
278 .get_parent = clk_sam9x5_slow_get_parent,
279};
280
281static struct clk_hw * __init
282at91_clk_register_sam9x5_slow(void __iomem *sckcr,
283 const char *name,
284 const char **parent_names,
285 int num_parents)
286{
287 struct clk_sam9x5_slow *slowck;
288 struct clk_hw *hw;
289 struct clk_init_data init;
290 int ret;
291
292 if (!sckcr || !name || !parent_names || !num_parents)
293 return ERR_PTR(-EINVAL);
294
295 slowck = kzalloc(sizeof(*slowck), GFP_KERNEL);
296 if (!slowck)
297 return ERR_PTR(-ENOMEM);
298
299 init.name = name;
300 init.ops = &sam9x5_slow_ops;
301 init.parent_names = parent_names;
302 init.num_parents = num_parents;
303 init.flags = 0;
304
305 slowck->hw.init = &init;
306 slowck->sckcr = sckcr;
307 slowck->parent = !!(readl(sckcr) & AT91_SCKC_OSCSEL);
308
309 hw = &slowck->hw;
310 ret = clk_hw_register(NULL, &slowck->hw);
311 if (ret) {
312 kfree(slowck);
313 hw = ERR_PTR(ret);
314 }
315
316 return hw;
317}
318
319static void __init at91sam9x5_sckc_register(struct device_node *np,
320 unsigned int rc_osc_startup_us)
321{
322 const char *parent_names[2] = { "slow_rc_osc", "slow_osc" };
323 void __iomem *regbase = of_iomap(np, 0);
324 struct device_node *child = NULL;
325 const char *xtal_name;
326 struct clk_hw *hw;
327 bool bypass;
328
329 if (!regbase)
330 return;
331
332 hw = at91_clk_register_slow_rc_osc(regbase, parent_names[0], 32768,
333 50000000, rc_osc_startup_us);
334 if (IS_ERR(hw))
335 return;
336
337 xtal_name = of_clk_get_parent_name(np, 0);
338 if (!xtal_name) {
339 /* DT backward compatibility */
340 child = of_get_compatible_child(np, "atmel,at91sam9x5-clk-slow-osc");
341 if (!child)
342 return;
343
344 xtal_name = of_clk_get_parent_name(child, 0);
345 bypass = of_property_read_bool(child, "atmel,osc-bypass");
346
347 child = of_get_compatible_child(np, "atmel,at91sam9x5-clk-slow");
348 } else {
349 bypass = of_property_read_bool(np, "atmel,osc-bypass");
350 }
351
352 if (!xtal_name)
353 return;
354
355 hw = at91_clk_register_slow_osc(regbase, parent_names[1], xtal_name,
356 1200000, bypass);
357 if (IS_ERR(hw))
358 return;
359
360 hw = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_names, 2);
361 if (IS_ERR(hw))
362 return;
363
364 of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
365
366 /* DT backward compatibility */
367 if (child)
368 of_clk_add_hw_provider(child, of_clk_hw_simple_get, hw);
369}
370
371static void __init of_at91sam9x5_sckc_setup(struct device_node *np)
372{
373 at91sam9x5_sckc_register(np, 75);
374}
375CLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc",
376 of_at91sam9x5_sckc_setup);
377
378static void __init of_sama5d3_sckc_setup(struct device_node *np)
379{
380 at91sam9x5_sckc_register(np, 500);
381}
382CLK_OF_DECLARE(sama5d3_clk_sckc, "atmel,sama5d3-sckc",
383 of_sama5d3_sckc_setup);
384
385static int clk_sama5d4_slow_osc_prepare(struct clk_hw *hw)
386{
387 struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw);
388
389 if (osc->prepared)
390 return 0;
391
392 /*
393 * Assume that if it has already been selected (for example by the
394 * bootloader), enough time has aready passed.
395 */
396 if ((readl(osc->sckcr) & AT91_SCKC_OSCSEL)) {
397 osc->prepared = true;
398 return 0;
399 }
400
401 usleep_range(osc->startup_usec, osc->startup_usec + 1);
402 osc->prepared = true;
403
404 return 0;
405}
406
407static int clk_sama5d4_slow_osc_is_prepared(struct clk_hw *hw)
408{
409 struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw);
410
411 return osc->prepared;
412}
413
414static const struct clk_ops sama5d4_slow_osc_ops = {
415 .prepare = clk_sama5d4_slow_osc_prepare,
416 .is_prepared = clk_sama5d4_slow_osc_is_prepared,
417};
418
419static void __init of_sama5d4_sckc_setup(struct device_node *np)
420{
421 void __iomem *regbase = of_iomap(np, 0);
422 struct clk_hw *hw;
423 struct clk_sama5d4_slow_osc *osc;
424 struct clk_init_data init;
425 const char *xtal_name;
426 const char *parent_names[2] = { "slow_rc_osc", "slow_osc" };
427 bool bypass;
428 int ret;
429
430 if (!regbase)
431 return;
432
433 hw = clk_hw_register_fixed_rate_with_accuracy(NULL, parent_names[0],
434 NULL, 0, 32768,
435 250000000);
436 if (IS_ERR(hw))
437 return;
438
439 xtal_name = of_clk_get_parent_name(np, 0);
440
441 bypass = of_property_read_bool(np, "atmel,osc-bypass");
442
443 osc = kzalloc(sizeof(*osc), GFP_KERNEL);
444 if (!osc)
445 return;
446
447 init.name = parent_names[1];
448 init.ops = &sama5d4_slow_osc_ops;
449 init.parent_names = &xtal_name;
450 init.num_parents = 1;
451 init.flags = CLK_IGNORE_UNUSED;
452
453 osc->hw.init = &init;
454 osc->sckcr = regbase;
455 osc->startup_usec = 1200000;
456
457 if (bypass)
458 writel((readl(regbase) | AT91_SCKC_OSC32BYP), regbase);
459
460 hw = &osc->hw;
461 ret = clk_hw_register(NULL, &osc->hw);
462 if (ret) {
463 kfree(osc);
464 return;
465 }
466
467 hw = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_names, 2);
468 if (IS_ERR(hw))
469 return;
470
471 of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
472}
473CLK_OF_DECLARE(sama5d4_clk_sckc, "atmel,sama5d4-sckc",
474 of_sama5d4_sckc_setup);