diff options
author | Boris BREZILLON <boris.brezillon@free-electrons.com> | 2014-05-07 12:02:15 -0400 |
---|---|---|
committer | Nicolas Ferre <nicolas.ferre@atmel.com> | 2014-05-07 12:27:45 -0400 |
commit | 80eded6ce8bb8bade60955660c6957d6166c44c1 (patch) | |
tree | ceb33e9008052db4f868374f5d179e1ff2f24d7c | |
parent | f9e1716f919f1bce2749a3ec15f34759ded0a56d (diff) |
clk: at91: add slow clks driver
AT91 slow clk is a clk multiplexer.
In some SoCs (sam9x5, sama5, sam9g45 families) this multiplexer can
choose among 2 sources: an internal RC oscillator circuit and an oscillator
using an external crystal.
In other Socs (sam9260 family) the multiplexer source is hardcoded with
the OSCSEL signal.
Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
Acked-by: Mike Turquette <mturquette@linaro.org>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
-rw-r--r-- | drivers/clk/at91/Makefile | 4 | ||||
-rw-r--r-- | drivers/clk/at91/clk-slow.c | 467 | ||||
-rw-r--r-- | drivers/clk/at91/pmc.c | 5 | ||||
-rw-r--r-- | drivers/clk/at91/pmc.h | 3 | ||||
-rw-r--r-- | drivers/clk/at91/sckc.c | 57 | ||||
-rw-r--r-- | drivers/clk/at91/sckc.h | 22 | ||||
-rw-r--r-- | include/linux/clk/at91_pmc.h | 1 |
7 files changed, 557 insertions, 2 deletions
diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 46c1d3d0d66b..4998aee59267 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile | |||
@@ -2,8 +2,8 @@ | |||
2 | # Makefile for at91 specific clk | 2 | # Makefile for at91 specific clk |
3 | # | 3 | # |
4 | 4 | ||
5 | obj-y += pmc.o | 5 | obj-y += pmc.o sckc.o |
6 | obj-y += clk-main.o clk-pll.o clk-plldiv.o clk-master.o | 6 | obj-y += clk-slow.o clk-main.o clk-pll.o clk-plldiv.o clk-master.o |
7 | obj-y += clk-system.o clk-peripheral.o clk-programmable.o | 7 | obj-y += clk-system.o clk-peripheral.o clk-programmable.o |
8 | 8 | ||
9 | obj-$(CONFIG_HAVE_AT91_UTMI) += clk-utmi.o | 9 | obj-$(CONFIG_HAVE_AT91_UTMI) += clk-utmi.o |
diff --git a/drivers/clk/at91/clk-slow.c b/drivers/clk/at91/clk-slow.c new file mode 100644 index 000000000000..0300c46ee247 --- /dev/null +++ b/drivers/clk/at91/clk-slow.c | |||
@@ -0,0 +1,467 @@ | |||
1 | /* | ||
2 | * drivers/clk/at91/clk-slow.c | ||
3 | * | ||
4 | * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | */ | ||
12 | |||
13 | #include <linux/clk-provider.h> | ||
14 | #include <linux/clkdev.h> | ||
15 | #include <linux/clk/at91_pmc.h> | ||
16 | #include <linux/delay.h> | ||
17 | #include <linux/of.h> | ||
18 | #include <linux/of_address.h> | ||
19 | #include <linux/of_irq.h> | ||
20 | #include <linux/io.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <linux/irq.h> | ||
23 | #include <linux/sched.h> | ||
24 | #include <linux/wait.h> | ||
25 | |||
26 | #include "pmc.h" | ||
27 | #include "sckc.h" | ||
28 | |||
29 | #define SLOW_CLOCK_FREQ 32768 | ||
30 | #define SLOWCK_SW_CYCLES 5 | ||
31 | #define SLOWCK_SW_TIME_USEC ((SLOWCK_SW_CYCLES * USEC_PER_SEC) / \ | ||
32 | SLOW_CLOCK_FREQ) | ||
33 | |||
34 | #define AT91_SCKC_CR 0x00 | ||
35 | #define AT91_SCKC_RCEN (1 << 0) | ||
36 | #define AT91_SCKC_OSC32EN (1 << 1) | ||
37 | #define AT91_SCKC_OSC32BYP (1 << 2) | ||
38 | #define AT91_SCKC_OSCSEL (1 << 3) | ||
39 | |||
40 | struct clk_slow_osc { | ||
41 | struct clk_hw hw; | ||
42 | void __iomem *sckcr; | ||
43 | unsigned long startup_usec; | ||
44 | }; | ||
45 | |||
46 | #define to_clk_slow_osc(hw) container_of(hw, struct clk_slow_osc, hw) | ||
47 | |||
48 | struct clk_slow_rc_osc { | ||
49 | struct clk_hw hw; | ||
50 | void __iomem *sckcr; | ||
51 | unsigned long frequency; | ||
52 | unsigned long accuracy; | ||
53 | unsigned long startup_usec; | ||
54 | }; | ||
55 | |||
56 | #define to_clk_slow_rc_osc(hw) container_of(hw, struct clk_slow_rc_osc, hw) | ||
57 | |||
58 | struct clk_sam9260_slow { | ||
59 | struct clk_hw hw; | ||
60 | struct at91_pmc *pmc; | ||
61 | }; | ||
62 | |||
63 | #define to_clk_sam9260_slow(hw) container_of(hw, struct clk_sam9260_slow, hw) | ||
64 | |||
65 | struct clk_sam9x5_slow { | ||
66 | struct clk_hw hw; | ||
67 | void __iomem *sckcr; | ||
68 | u8 parent; | ||
69 | }; | ||
70 | |||
71 | #define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, hw) | ||
72 | |||
73 | |||
74 | static int clk_slow_osc_prepare(struct clk_hw *hw) | ||
75 | { | ||
76 | struct clk_slow_osc *osc = to_clk_slow_osc(hw); | ||
77 | void __iomem *sckcr = osc->sckcr; | ||
78 | u32 tmp = readl(sckcr); | ||
79 | |||
80 | if (tmp & AT91_SCKC_OSC32BYP) | ||
81 | return 0; | ||
82 | |||
83 | writel(tmp | AT91_SCKC_OSC32EN, sckcr); | ||
84 | |||
85 | usleep_range(osc->startup_usec, osc->startup_usec + 1); | ||
86 | |||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | static void clk_slow_osc_unprepare(struct clk_hw *hw) | ||
91 | { | ||
92 | struct clk_slow_osc *osc = to_clk_slow_osc(hw); | ||
93 | void __iomem *sckcr = osc->sckcr; | ||
94 | u32 tmp = readl(sckcr); | ||
95 | |||
96 | if (tmp & AT91_SCKC_OSC32BYP) | ||
97 | return; | ||
98 | |||
99 | writel(tmp & ~AT91_SCKC_OSC32EN, sckcr); | ||
100 | } | ||
101 | |||
102 | static int clk_slow_osc_is_prepared(struct clk_hw *hw) | ||
103 | { | ||
104 | struct clk_slow_osc *osc = to_clk_slow_osc(hw); | ||
105 | void __iomem *sckcr = osc->sckcr; | ||
106 | u32 tmp = readl(sckcr); | ||
107 | |||
108 | if (tmp & AT91_SCKC_OSC32BYP) | ||
109 | return 1; | ||
110 | |||
111 | return !!(tmp & AT91_SCKC_OSC32EN); | ||
112 | } | ||
113 | |||
114 | static const struct clk_ops slow_osc_ops = { | ||
115 | .prepare = clk_slow_osc_prepare, | ||
116 | .unprepare = clk_slow_osc_unprepare, | ||
117 | .is_prepared = clk_slow_osc_is_prepared, | ||
118 | }; | ||
119 | |||
120 | static struct clk * __init | ||
121 | at91_clk_register_slow_osc(void __iomem *sckcr, | ||
122 | const char *name, | ||
123 | const char *parent_name, | ||
124 | unsigned long startup, | ||
125 | bool bypass) | ||
126 | { | ||
127 | struct clk_slow_osc *osc; | ||
128 | struct clk *clk = NULL; | ||
129 | struct clk_init_data init; | ||
130 | |||
131 | if (!sckcr || !name || !parent_name) | ||
132 | return ERR_PTR(-EINVAL); | ||
133 | |||
134 | osc = kzalloc(sizeof(*osc), GFP_KERNEL); | ||
135 | if (!osc) | ||
136 | return ERR_PTR(-ENOMEM); | ||
137 | |||
138 | init.name = name; | ||
139 | init.ops = &slow_osc_ops; | ||
140 | init.parent_names = &parent_name; | ||
141 | init.num_parents = 1; | ||
142 | init.flags = CLK_IGNORE_UNUSED; | ||
143 | |||
144 | osc->hw.init = &init; | ||
145 | osc->sckcr = sckcr; | ||
146 | osc->startup_usec = startup; | ||
147 | |||
148 | if (bypass) | ||
149 | writel((readl(sckcr) & ~AT91_SCKC_OSC32EN) | AT91_SCKC_OSC32BYP, | ||
150 | sckcr); | ||
151 | |||
152 | clk = clk_register(NULL, &osc->hw); | ||
153 | if (IS_ERR(clk)) | ||
154 | kfree(osc); | ||
155 | |||
156 | return clk; | ||
157 | } | ||
158 | |||
159 | void __init of_at91sam9x5_clk_slow_osc_setup(struct device_node *np, | ||
160 | void __iomem *sckcr) | ||
161 | { | ||
162 | struct clk *clk; | ||
163 | const char *parent_name; | ||
164 | const char *name = np->name; | ||
165 | u32 startup; | ||
166 | bool bypass; | ||
167 | |||
168 | parent_name = of_clk_get_parent_name(np, 0); | ||
169 | of_property_read_string(np, "clock-output-names", &name); | ||
170 | of_property_read_u32(np, "atmel,startup-time-usec", &startup); | ||
171 | bypass = of_property_read_bool(np, "atmel,osc-bypass"); | ||
172 | |||
173 | clk = at91_clk_register_slow_osc(sckcr, name, parent_name, startup, | ||
174 | bypass); | ||
175 | if (IS_ERR(clk)) | ||
176 | return; | ||
177 | |||
178 | of_clk_add_provider(np, of_clk_src_simple_get, clk); | ||
179 | } | ||
180 | |||
181 | static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw, | ||
182 | unsigned long parent_rate) | ||
183 | { | ||
184 | struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); | ||
185 | |||
186 | return osc->frequency; | ||
187 | } | ||
188 | |||
189 | static unsigned long clk_slow_rc_osc_recalc_accuracy(struct clk_hw *hw, | ||
190 | unsigned long parent_acc) | ||
191 | { | ||
192 | struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); | ||
193 | |||
194 | return osc->accuracy; | ||
195 | } | ||
196 | |||
197 | static int clk_slow_rc_osc_prepare(struct clk_hw *hw) | ||
198 | { | ||
199 | struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); | ||
200 | void __iomem *sckcr = osc->sckcr; | ||
201 | |||
202 | writel(readl(sckcr) | AT91_SCKC_RCEN, sckcr); | ||
203 | |||
204 | usleep_range(osc->startup_usec, osc->startup_usec + 1); | ||
205 | |||
206 | return 0; | ||
207 | } | ||
208 | |||
209 | static void clk_slow_rc_osc_unprepare(struct clk_hw *hw) | ||
210 | { | ||
211 | struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); | ||
212 | void __iomem *sckcr = osc->sckcr; | ||
213 | |||
214 | writel(readl(sckcr) & ~AT91_SCKC_RCEN, sckcr); | ||
215 | } | ||
216 | |||
217 | static int clk_slow_rc_osc_is_prepared(struct clk_hw *hw) | ||
218 | { | ||
219 | struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); | ||
220 | |||
221 | return !!(readl(osc->sckcr) & AT91_SCKC_RCEN); | ||
222 | } | ||
223 | |||
224 | static const struct clk_ops slow_rc_osc_ops = { | ||
225 | .prepare = clk_slow_rc_osc_prepare, | ||
226 | .unprepare = clk_slow_rc_osc_unprepare, | ||
227 | .is_prepared = clk_slow_rc_osc_is_prepared, | ||
228 | .recalc_rate = clk_slow_rc_osc_recalc_rate, | ||
229 | .recalc_accuracy = clk_slow_rc_osc_recalc_accuracy, | ||
230 | }; | ||
231 | |||
232 | static struct clk * __init | ||
233 | at91_clk_register_slow_rc_osc(void __iomem *sckcr, | ||
234 | const char *name, | ||
235 | unsigned long frequency, | ||
236 | unsigned long accuracy, | ||
237 | unsigned long startup) | ||
238 | { | ||
239 | struct clk_slow_rc_osc *osc; | ||
240 | struct clk *clk = NULL; | ||
241 | struct clk_init_data init; | ||
242 | |||
243 | if (!sckcr || !name) | ||
244 | return ERR_PTR(-EINVAL); | ||
245 | |||
246 | osc = kzalloc(sizeof(*osc), GFP_KERNEL); | ||
247 | if (!osc) | ||
248 | return ERR_PTR(-ENOMEM); | ||
249 | |||
250 | init.name = name; | ||
251 | init.ops = &slow_rc_osc_ops; | ||
252 | init.parent_names = NULL; | ||
253 | init.num_parents = 0; | ||
254 | init.flags = CLK_IS_ROOT | CLK_IGNORE_UNUSED; | ||
255 | |||
256 | osc->hw.init = &init; | ||
257 | osc->sckcr = sckcr; | ||
258 | osc->frequency = frequency; | ||
259 | osc->accuracy = accuracy; | ||
260 | osc->startup_usec = startup; | ||
261 | |||
262 | clk = clk_register(NULL, &osc->hw); | ||
263 | if (IS_ERR(clk)) | ||
264 | kfree(osc); | ||
265 | |||
266 | return clk; | ||
267 | } | ||
268 | |||
269 | void __init of_at91sam9x5_clk_slow_rc_osc_setup(struct device_node *np, | ||
270 | void __iomem *sckcr) | ||
271 | { | ||
272 | struct clk *clk; | ||
273 | u32 frequency = 0; | ||
274 | u32 accuracy = 0; | ||
275 | u32 startup = 0; | ||
276 | const char *name = np->name; | ||
277 | |||
278 | of_property_read_string(np, "clock-output-names", &name); | ||
279 | of_property_read_u32(np, "clock-frequency", &frequency); | ||
280 | of_property_read_u32(np, "clock-accuracy", &accuracy); | ||
281 | of_property_read_u32(np, "atmel,startup-time-usec", &startup); | ||
282 | |||
283 | clk = at91_clk_register_slow_rc_osc(sckcr, name, frequency, accuracy, | ||
284 | startup); | ||
285 | if (IS_ERR(clk)) | ||
286 | return; | ||
287 | |||
288 | of_clk_add_provider(np, of_clk_src_simple_get, clk); | ||
289 | } | ||
290 | |||
291 | static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index) | ||
292 | { | ||
293 | struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); | ||
294 | void __iomem *sckcr = slowck->sckcr; | ||
295 | u32 tmp; | ||
296 | |||
297 | if (index > 1) | ||
298 | return -EINVAL; | ||
299 | |||
300 | tmp = readl(sckcr); | ||
301 | |||
302 | if ((!index && !(tmp & AT91_SCKC_OSCSEL)) || | ||
303 | (index && (tmp & AT91_SCKC_OSCSEL))) | ||
304 | return 0; | ||
305 | |||
306 | if (index) | ||
307 | tmp |= AT91_SCKC_OSCSEL; | ||
308 | else | ||
309 | tmp &= ~AT91_SCKC_OSCSEL; | ||
310 | |||
311 | writel(tmp, sckcr); | ||
312 | |||
313 | usleep_range(SLOWCK_SW_TIME_USEC, SLOWCK_SW_TIME_USEC + 1); | ||
314 | |||
315 | return 0; | ||
316 | } | ||
317 | |||
318 | static u8 clk_sam9x5_slow_get_parent(struct clk_hw *hw) | ||
319 | { | ||
320 | struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); | ||
321 | |||
322 | return !!(readl(slowck->sckcr) & AT91_SCKC_OSCSEL); | ||
323 | } | ||
324 | |||
325 | static const struct clk_ops sam9x5_slow_ops = { | ||
326 | .set_parent = clk_sam9x5_slow_set_parent, | ||
327 | .get_parent = clk_sam9x5_slow_get_parent, | ||
328 | }; | ||
329 | |||
330 | static struct clk * __init | ||
331 | at91_clk_register_sam9x5_slow(void __iomem *sckcr, | ||
332 | const char *name, | ||
333 | const char **parent_names, | ||
334 | int num_parents) | ||
335 | { | ||
336 | struct clk_sam9x5_slow *slowck; | ||
337 | struct clk *clk = NULL; | ||
338 | struct clk_init_data init; | ||
339 | |||
340 | if (!sckcr || !name || !parent_names || !num_parents) | ||
341 | return ERR_PTR(-EINVAL); | ||
342 | |||
343 | slowck = kzalloc(sizeof(*slowck), GFP_KERNEL); | ||
344 | if (!slowck) | ||
345 | return ERR_PTR(-ENOMEM); | ||
346 | |||
347 | init.name = name; | ||
348 | init.ops = &sam9x5_slow_ops; | ||
349 | init.parent_names = parent_names; | ||
350 | init.num_parents = num_parents; | ||
351 | init.flags = 0; | ||
352 | |||
353 | slowck->hw.init = &init; | ||
354 | slowck->sckcr = sckcr; | ||
355 | slowck->parent = !!(readl(sckcr) & AT91_SCKC_OSCSEL); | ||
356 | |||
357 | clk = clk_register(NULL, &slowck->hw); | ||
358 | if (IS_ERR(clk)) | ||
359 | kfree(slowck); | ||
360 | |||
361 | return clk; | ||
362 | } | ||
363 | |||
364 | void __init of_at91sam9x5_clk_slow_setup(struct device_node *np, | ||
365 | void __iomem *sckcr) | ||
366 | { | ||
367 | struct clk *clk; | ||
368 | const char *parent_names[2]; | ||
369 | int num_parents; | ||
370 | const char *name = np->name; | ||
371 | int i; | ||
372 | |||
373 | num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); | ||
374 | if (num_parents <= 0 || num_parents > 2) | ||
375 | return; | ||
376 | |||
377 | for (i = 0; i < num_parents; ++i) { | ||
378 | parent_names[i] = of_clk_get_parent_name(np, i); | ||
379 | if (!parent_names[i]) | ||
380 | return; | ||
381 | } | ||
382 | |||
383 | of_property_read_string(np, "clock-output-names", &name); | ||
384 | |||
385 | clk = at91_clk_register_sam9x5_slow(sckcr, name, parent_names, | ||
386 | num_parents); | ||
387 | if (IS_ERR(clk)) | ||
388 | return; | ||
389 | |||
390 | of_clk_add_provider(np, of_clk_src_simple_get, clk); | ||
391 | } | ||
392 | |||
393 | static u8 clk_sam9260_slow_get_parent(struct clk_hw *hw) | ||
394 | { | ||
395 | struct clk_sam9260_slow *slowck = to_clk_sam9260_slow(hw); | ||
396 | |||
397 | return !!(pmc_read(slowck->pmc, AT91_PMC_SR) & AT91_PMC_OSCSEL); | ||
398 | } | ||
399 | |||
400 | static const struct clk_ops sam9260_slow_ops = { | ||
401 | .get_parent = clk_sam9260_slow_get_parent, | ||
402 | }; | ||
403 | |||
404 | static struct clk * __init | ||
405 | at91_clk_register_sam9260_slow(struct at91_pmc *pmc, | ||
406 | const char *name, | ||
407 | const char **parent_names, | ||
408 | int num_parents) | ||
409 | { | ||
410 | struct clk_sam9260_slow *slowck; | ||
411 | struct clk *clk = NULL; | ||
412 | struct clk_init_data init; | ||
413 | |||
414 | if (!pmc || !name) | ||
415 | return ERR_PTR(-EINVAL); | ||
416 | |||
417 | if (!parent_names || !num_parents) | ||
418 | return ERR_PTR(-EINVAL); | ||
419 | |||
420 | slowck = kzalloc(sizeof(*slowck), GFP_KERNEL); | ||
421 | if (!slowck) | ||
422 | return ERR_PTR(-ENOMEM); | ||
423 | |||
424 | init.name = name; | ||
425 | init.ops = &sam9260_slow_ops; | ||
426 | init.parent_names = parent_names; | ||
427 | init.num_parents = num_parents; | ||
428 | init.flags = 0; | ||
429 | |||
430 | slowck->hw.init = &init; | ||
431 | slowck->pmc = pmc; | ||
432 | |||
433 | clk = clk_register(NULL, &slowck->hw); | ||
434 | if (IS_ERR(clk)) | ||
435 | kfree(slowck); | ||
436 | |||
437 | return clk; | ||
438 | } | ||
439 | |||
440 | void __init of_at91sam9260_clk_slow_setup(struct device_node *np, | ||
441 | struct at91_pmc *pmc) | ||
442 | { | ||
443 | struct clk *clk; | ||
444 | const char *parent_names[2]; | ||
445 | int num_parents; | ||
446 | const char *name = np->name; | ||
447 | int i; | ||
448 | |||
449 | num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); | ||
450 | if (num_parents <= 0 || num_parents > 1) | ||
451 | return; | ||
452 | |||
453 | for (i = 0; i < num_parents; ++i) { | ||
454 | parent_names[i] = of_clk_get_parent_name(np, i); | ||
455 | if (!parent_names[i]) | ||
456 | return; | ||
457 | } | ||
458 | |||
459 | of_property_read_string(np, "clock-output-names", &name); | ||
460 | |||
461 | clk = at91_clk_register_sam9260_slow(pmc, name, parent_names, | ||
462 | num_parents); | ||
463 | if (IS_ERR(clk)) | ||
464 | return; | ||
465 | |||
466 | of_clk_add_provider(np, of_clk_src_simple_get, clk); | ||
467 | } | ||
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index dc5fdde98e1a..524196bb35a5 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c | |||
@@ -229,6 +229,11 @@ out_free_pmc: | |||
229 | } | 229 | } |
230 | 230 | ||
231 | static const struct of_device_id pmc_clk_ids[] __initconst = { | 231 | static const struct of_device_id pmc_clk_ids[] __initconst = { |
232 | /* Slow oscillator */ | ||
233 | { | ||
234 | .compatible = "atmel,at91sam9260-clk-slow", | ||
235 | .data = of_at91sam9260_clk_slow_setup, | ||
236 | }, | ||
232 | /* Main clock */ | 237 | /* Main clock */ |
233 | { | 238 | { |
234 | .compatible = "atmel,at91rm9200-clk-main-osc", | 239 | .compatible = "atmel,at91rm9200-clk-main-osc", |
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index 42cc7cc5e1d3..6c7625976113 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h | |||
@@ -58,6 +58,9 @@ static inline void pmc_write(struct at91_pmc *pmc, int offset, u32 value) | |||
58 | int of_at91_get_clk_range(struct device_node *np, const char *propname, | 58 | int of_at91_get_clk_range(struct device_node *np, const char *propname, |
59 | struct clk_range *range); | 59 | struct clk_range *range); |
60 | 60 | ||
61 | extern void __init of_at91sam9260_clk_slow_setup(struct device_node *np, | ||
62 | struct at91_pmc *pmc); | ||
63 | |||
61 | extern void __init of_at91rm9200_clk_main_osc_setup(struct device_node *np, | 64 | extern void __init of_at91rm9200_clk_main_osc_setup(struct device_node *np, |
62 | struct at91_pmc *pmc); | 65 | struct at91_pmc *pmc); |
63 | extern void __init of_at91sam9x5_clk_main_rc_osc_setup(struct device_node *np, | 66 | extern void __init of_at91sam9x5_clk_main_rc_osc_setup(struct device_node *np, |
diff --git a/drivers/clk/at91/sckc.c b/drivers/clk/at91/sckc.c new file mode 100644 index 000000000000..1184d76a7ab7 --- /dev/null +++ b/drivers/clk/at91/sckc.c | |||
@@ -0,0 +1,57 @@ | |||
1 | /* | ||
2 | * drivers/clk/at91/sckc.c | ||
3 | * | ||
4 | * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | */ | ||
12 | |||
13 | #include <linux/clk-provider.h> | ||
14 | #include <linux/clkdev.h> | ||
15 | #include <linux/of.h> | ||
16 | #include <linux/of_address.h> | ||
17 | #include <linux/io.h> | ||
18 | |||
19 | #include "sckc.h" | ||
20 | |||
21 | static const struct of_device_id sckc_clk_ids[] __initconst = { | ||
22 | /* Slow clock */ | ||
23 | { | ||
24 | .compatible = "atmel,at91sam9x5-clk-slow-osc", | ||
25 | .data = of_at91sam9x5_clk_slow_osc_setup, | ||
26 | }, | ||
27 | { | ||
28 | .compatible = "atmel,at91sam9x5-clk-slow-rc-osc", | ||
29 | .data = of_at91sam9x5_clk_slow_rc_osc_setup, | ||
30 | }, | ||
31 | { | ||
32 | .compatible = "atmel,at91sam9x5-clk-slow", | ||
33 | .data = of_at91sam9x5_clk_slow_setup, | ||
34 | }, | ||
35 | { /*sentinel*/ } | ||
36 | }; | ||
37 | |||
38 | static void __init of_at91sam9x5_sckc_setup(struct device_node *np) | ||
39 | { | ||
40 | struct device_node *childnp; | ||
41 | void (*clk_setup)(struct device_node *, void __iomem *); | ||
42 | const struct of_device_id *clk_id; | ||
43 | void __iomem *regbase = of_iomap(np, 0); | ||
44 | |||
45 | if (!regbase) | ||
46 | return; | ||
47 | |||
48 | for_each_child_of_node(np, childnp) { | ||
49 | clk_id = of_match_node(sckc_clk_ids, childnp); | ||
50 | if (!clk_id) | ||
51 | continue; | ||
52 | clk_setup = clk_id->data; | ||
53 | clk_setup(childnp, regbase); | ||
54 | } | ||
55 | } | ||
56 | CLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc", | ||
57 | of_at91sam9x5_sckc_setup); | ||
diff --git a/drivers/clk/at91/sckc.h b/drivers/clk/at91/sckc.h new file mode 100644 index 000000000000..836fcf59820f --- /dev/null +++ b/drivers/clk/at91/sckc.h | |||
@@ -0,0 +1,22 @@ | |||
1 | /* | ||
2 | * drivers/clk/at91/sckc.h | ||
3 | * | ||
4 | * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #ifndef __AT91_SCKC_H_ | ||
13 | #define __AT91_SCKC_H_ | ||
14 | |||
15 | extern void __init of_at91sam9x5_clk_slow_osc_setup(struct device_node *np, | ||
16 | void __iomem *sckcr); | ||
17 | extern void __init of_at91sam9x5_clk_slow_rc_osc_setup(struct device_node *np, | ||
18 | void __iomem *sckcr); | ||
19 | extern void __init of_at91sam9x5_clk_slow_setup(struct device_node *np, | ||
20 | void __iomem *sckcr); | ||
21 | |||
22 | #endif /* __AT91_SCKC_H_ */ | ||
diff --git a/include/linux/clk/at91_pmc.h b/include/linux/clk/at91_pmc.h index a6911ebbd02a..de4268d4987a 100644 --- a/include/linux/clk/at91_pmc.h +++ b/include/linux/clk/at91_pmc.h | |||
@@ -155,6 +155,7 @@ extern void __iomem *at91_pmc_base; | |||
155 | #define AT91_PMC_LOCKB (1 << 2) /* PLLB Lock */ | 155 | #define AT91_PMC_LOCKB (1 << 2) /* PLLB Lock */ |
156 | #define AT91_PMC_MCKRDY (1 << 3) /* Master Clock */ | 156 | #define AT91_PMC_MCKRDY (1 << 3) /* Master Clock */ |
157 | #define AT91_PMC_LOCKU (1 << 6) /* UPLL Lock [some SAM9] */ | 157 | #define AT91_PMC_LOCKU (1 << 6) /* UPLL Lock [some SAM9] */ |
158 | #define AT91_PMC_OSCSEL (1 << 7) /* Slow Oscillator Selection [some SAM9] */ | ||
158 | #define AT91_PMC_PCK0RDY (1 << 8) /* Programmable Clock 0 */ | 159 | #define AT91_PMC_PCK0RDY (1 << 8) /* Programmable Clock 0 */ |
159 | #define AT91_PMC_PCK1RDY (1 << 9) /* Programmable Clock 1 */ | 160 | #define AT91_PMC_PCK1RDY (1 << 9) /* Programmable Clock 1 */ |
160 | #define AT91_PMC_PCK2RDY (1 << 10) /* Programmable Clock 2 */ | 161 | #define AT91_PMC_PCK2RDY (1 << 10) /* Programmable Clock 2 */ |