Commit | Line | Data |
---|---|---|
38d34c31 BB |
1 | /* |
2 | * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | */ | |
10 | ||
11 | #include <linux/clk-provider.h> | |
12 | #include <linux/clkdev.h> | |
13 | #include <linux/clk/at91_pmc.h> | |
14 | #include <linux/delay.h> | |
15 | #include <linux/of.h> | |
1bdf0232 BB |
16 | #include <linux/mfd/syscon.h> |
17 | #include <linux/regmap.h> | |
38d34c31 BB |
18 | |
19 | #include "pmc.h" | |
20 | ||
21 | #define SLOW_CLOCK_FREQ 32768 | |
22 | #define MAINF_DIV 16 | |
23 | #define MAINFRDY_TIMEOUT (((MAINF_DIV + 1) * USEC_PER_SEC) / \ | |
24 | SLOW_CLOCK_FREQ) | |
25 | #define MAINF_LOOP_MIN_WAIT (USEC_PER_SEC / SLOW_CLOCK_FREQ) | |
26 | #define MAINF_LOOP_MAX_WAIT MAINFRDY_TIMEOUT | |
27 | ||
27cb1c20 BB |
28 | #define MOR_KEY_MASK (0xff << 16) |
29 | ||
30 | struct clk_main_osc { | |
38d34c31 | 31 | struct clk_hw hw; |
1bdf0232 | 32 | struct regmap *regmap; |
38d34c31 BB |
33 | }; |
34 | ||
27cb1c20 BB |
35 | #define to_clk_main_osc(hw) container_of(hw, struct clk_main_osc, hw) |
36 | ||
37 | struct clk_main_rc_osc { | |
38 | struct clk_hw hw; | |
1bdf0232 | 39 | struct regmap *regmap; |
27cb1c20 BB |
40 | unsigned long frequency; |
41 | unsigned long accuracy; | |
42 | }; | |
43 | ||
44 | #define to_clk_main_rc_osc(hw) container_of(hw, struct clk_main_rc_osc, hw) | |
45 | ||
46 | struct clk_rm9200_main { | |
47 | struct clk_hw hw; | |
1bdf0232 | 48 | struct regmap *regmap; |
27cb1c20 BB |
49 | }; |
50 | ||
51 | #define to_clk_rm9200_main(hw) container_of(hw, struct clk_rm9200_main, hw) | |
38d34c31 | 52 | |
27cb1c20 BB |
53 | struct clk_sam9x5_main { |
54 | struct clk_hw hw; | |
1bdf0232 | 55 | struct regmap *regmap; |
27cb1c20 BB |
56 | u8 parent; |
57 | }; | |
58 | ||
59 | #define to_clk_sam9x5_main(hw) container_of(hw, struct clk_sam9x5_main, hw) | |
60 | ||
1bdf0232 BB |
61 | static inline bool clk_main_osc_ready(struct regmap *regmap) |
62 | { | |
63 | unsigned int status; | |
64 | ||
65 | regmap_read(regmap, AT91_PMC_SR, &status); | |
66 | ||
67 | return status & AT91_PMC_MOSCS; | |
68 | } | |
69 | ||
27cb1c20 | 70 | static int clk_main_osc_prepare(struct clk_hw *hw) |
38d34c31 | 71 | { |
27cb1c20 | 72 | struct clk_main_osc *osc = to_clk_main_osc(hw); |
1bdf0232 | 73 | struct regmap *regmap = osc->regmap; |
38d34c31 BB |
74 | u32 tmp; |
75 | ||
1bdf0232 BB |
76 | regmap_read(regmap, AT91_CKGR_MOR, &tmp); |
77 | tmp &= ~MOR_KEY_MASK; | |
78 | ||
27cb1c20 BB |
79 | if (tmp & AT91_PMC_OSCBYPASS) |
80 | return 0; | |
81 | ||
82 | if (!(tmp & AT91_PMC_MOSCEN)) { | |
83 | tmp |= AT91_PMC_MOSCEN | AT91_PMC_KEY; | |
1bdf0232 | 84 | regmap_write(regmap, AT91_CKGR_MOR, tmp); |
27cb1c20 BB |
85 | } |
86 | ||
99a81706 AB |
87 | while (!clk_main_osc_ready(regmap)) |
88 | cpu_relax(); | |
38d34c31 | 89 | |
27cb1c20 BB |
90 | return 0; |
91 | } | |
92 | ||
93 | static void clk_main_osc_unprepare(struct clk_hw *hw) | |
94 | { | |
95 | struct clk_main_osc *osc = to_clk_main_osc(hw); | |
1bdf0232 BB |
96 | struct regmap *regmap = osc->regmap; |
97 | u32 tmp; | |
27cb1c20 | 98 | |
1bdf0232 | 99 | regmap_read(regmap, AT91_CKGR_MOR, &tmp); |
27cb1c20 BB |
100 | if (tmp & AT91_PMC_OSCBYPASS) |
101 | return; | |
102 | ||
103 | if (!(tmp & AT91_PMC_MOSCEN)) | |
104 | return; | |
105 | ||
106 | tmp &= ~(AT91_PMC_KEY | AT91_PMC_MOSCEN); | |
1bdf0232 | 107 | regmap_write(regmap, AT91_CKGR_MOR, tmp | AT91_PMC_KEY); |
27cb1c20 BB |
108 | } |
109 | ||
110 | static int clk_main_osc_is_prepared(struct clk_hw *hw) | |
111 | { | |
112 | struct clk_main_osc *osc = to_clk_main_osc(hw); | |
1bdf0232 BB |
113 | struct regmap *regmap = osc->regmap; |
114 | u32 tmp, status; | |
27cb1c20 | 115 | |
1bdf0232 | 116 | regmap_read(regmap, AT91_CKGR_MOR, &tmp); |
27cb1c20 BB |
117 | if (tmp & AT91_PMC_OSCBYPASS) |
118 | return 1; | |
119 | ||
1bdf0232 BB |
120 | regmap_read(regmap, AT91_PMC_SR, &status); |
121 | ||
122 | return (status & AT91_PMC_MOSCS) && (tmp & AT91_PMC_MOSCEN); | |
27cb1c20 BB |
123 | } |
124 | ||
125 | static const struct clk_ops main_osc_ops = { | |
126 | .prepare = clk_main_osc_prepare, | |
127 | .unprepare = clk_main_osc_unprepare, | |
128 | .is_prepared = clk_main_osc_is_prepared, | |
129 | }; | |
130 | ||
131 | static struct clk * __init | |
1bdf0232 | 132 | at91_clk_register_main_osc(struct regmap *regmap, |
27cb1c20 BB |
133 | const char *name, |
134 | const char *parent_name, | |
135 | bool bypass) | |
136 | { | |
27cb1c20 BB |
137 | struct clk_main_osc *osc; |
138 | struct clk *clk = NULL; | |
139 | struct clk_init_data init; | |
140 | ||
99a81706 | 141 | if (!name || !parent_name) |
27cb1c20 BB |
142 | return ERR_PTR(-EINVAL); |
143 | ||
144 | osc = kzalloc(sizeof(*osc), GFP_KERNEL); | |
145 | if (!osc) | |
146 | return ERR_PTR(-ENOMEM); | |
147 | ||
148 | init.name = name; | |
149 | init.ops = &main_osc_ops; | |
150 | init.parent_names = &parent_name; | |
151 | init.num_parents = 1; | |
152 | init.flags = CLK_IGNORE_UNUSED; | |
153 | ||
154 | osc->hw.init = &init; | |
1bdf0232 | 155 | osc->regmap = regmap; |
27cb1c20 BB |
156 | |
157 | if (bypass) | |
1bdf0232 BB |
158 | regmap_update_bits(regmap, |
159 | AT91_CKGR_MOR, MOR_KEY_MASK | | |
160 | AT91_PMC_MOSCEN, | |
161 | AT91_PMC_OSCBYPASS | AT91_PMC_KEY); | |
27cb1c20 BB |
162 | |
163 | clk = clk_register(NULL, &osc->hw); | |
99a81706 | 164 | if (IS_ERR(clk)) |
27cb1c20 | 165 | kfree(osc); |
27cb1c20 BB |
166 | |
167 | return clk; | |
168 | } | |
169 | ||
1bdf0232 | 170 | static void __init of_at91rm9200_clk_main_osc_setup(struct device_node *np) |
27cb1c20 BB |
171 | { |
172 | struct clk *clk; | |
27cb1c20 BB |
173 | const char *name = np->name; |
174 | const char *parent_name; | |
1bdf0232 | 175 | struct regmap *regmap; |
27cb1c20 BB |
176 | bool bypass; |
177 | ||
178 | of_property_read_string(np, "clock-output-names", &name); | |
179 | bypass = of_property_read_bool(np, "atmel,osc-bypass"); | |
180 | parent_name = of_clk_get_parent_name(np, 0); | |
181 | ||
1bdf0232 BB |
182 | regmap = syscon_node_to_regmap(of_get_parent(np)); |
183 | if (IS_ERR(regmap)) | |
184 | return; | |
185 | ||
99a81706 | 186 | clk = at91_clk_register_main_osc(regmap, name, parent_name, bypass); |
27cb1c20 BB |
187 | if (IS_ERR(clk)) |
188 | return; | |
189 | ||
190 | of_clk_add_provider(np, of_clk_src_simple_get, clk); | |
191 | } | |
1bdf0232 BB |
192 | CLK_OF_DECLARE(at91rm9200_clk_main_osc, "atmel,at91rm9200-clk-main-osc", |
193 | of_at91rm9200_clk_main_osc_setup); | |
27cb1c20 | 194 | |
1bdf0232 BB |
195 | static bool clk_main_rc_osc_ready(struct regmap *regmap) |
196 | { | |
197 | unsigned int status; | |
198 | ||
199 | regmap_read(regmap, AT91_PMC_SR, &status); | |
200 | ||
201 | return status & AT91_PMC_MOSCRCS; | |
202 | } | |
203 | ||
27cb1c20 BB |
204 | static int clk_main_rc_osc_prepare(struct clk_hw *hw) |
205 | { | |
206 | struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw); | |
1bdf0232 BB |
207 | struct regmap *regmap = osc->regmap; |
208 | unsigned int mor; | |
27cb1c20 | 209 | |
1bdf0232 | 210 | regmap_read(regmap, AT91_CKGR_MOR, &mor); |
27cb1c20 | 211 | |
1bdf0232 BB |
212 | if (!(mor & AT91_PMC_MOSCRCEN)) |
213 | regmap_update_bits(regmap, AT91_CKGR_MOR, | |
214 | MOR_KEY_MASK | AT91_PMC_MOSCRCEN, | |
215 | AT91_PMC_MOSCRCEN | AT91_PMC_KEY); | |
27cb1c20 | 216 | |
99a81706 AB |
217 | while (!clk_main_rc_osc_ready(regmap)) |
218 | cpu_relax(); | |
27cb1c20 BB |
219 | |
220 | return 0; | |
221 | } | |
222 | ||
223 | static void clk_main_rc_osc_unprepare(struct clk_hw *hw) | |
224 | { | |
225 | struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw); | |
1bdf0232 BB |
226 | struct regmap *regmap = osc->regmap; |
227 | unsigned int mor; | |
27cb1c20 | 228 | |
1bdf0232 BB |
229 | regmap_read(regmap, AT91_CKGR_MOR, &mor); |
230 | ||
231 | if (!(mor & AT91_PMC_MOSCRCEN)) | |
27cb1c20 BB |
232 | return; |
233 | ||
1bdf0232 BB |
234 | regmap_update_bits(regmap, AT91_CKGR_MOR, |
235 | MOR_KEY_MASK | AT91_PMC_MOSCRCEN, AT91_PMC_KEY); | |
27cb1c20 BB |
236 | } |
237 | ||
238 | static int clk_main_rc_osc_is_prepared(struct clk_hw *hw) | |
239 | { | |
240 | struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw); | |
1bdf0232 BB |
241 | struct regmap *regmap = osc->regmap; |
242 | unsigned int mor, status; | |
243 | ||
244 | regmap_read(regmap, AT91_CKGR_MOR, &mor); | |
245 | regmap_read(regmap, AT91_PMC_SR, &status); | |
27cb1c20 | 246 | |
1bdf0232 | 247 | return (mor & AT91_PMC_MOSCRCEN) && (status & AT91_PMC_MOSCRCS); |
27cb1c20 BB |
248 | } |
249 | ||
250 | static unsigned long clk_main_rc_osc_recalc_rate(struct clk_hw *hw, | |
251 | unsigned long parent_rate) | |
252 | { | |
253 | struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw); | |
254 | ||
255 | return osc->frequency; | |
256 | } | |
257 | ||
258 | static unsigned long clk_main_rc_osc_recalc_accuracy(struct clk_hw *hw, | |
259 | unsigned long parent_acc) | |
260 | { | |
261 | struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw); | |
262 | ||
263 | return osc->accuracy; | |
264 | } | |
265 | ||
266 | static const struct clk_ops main_rc_osc_ops = { | |
267 | .prepare = clk_main_rc_osc_prepare, | |
268 | .unprepare = clk_main_rc_osc_unprepare, | |
269 | .is_prepared = clk_main_rc_osc_is_prepared, | |
270 | .recalc_rate = clk_main_rc_osc_recalc_rate, | |
271 | .recalc_accuracy = clk_main_rc_osc_recalc_accuracy, | |
272 | }; | |
273 | ||
274 | static struct clk * __init | |
1bdf0232 | 275 | at91_clk_register_main_rc_osc(struct regmap *regmap, |
27cb1c20 BB |
276 | const char *name, |
277 | u32 frequency, u32 accuracy) | |
278 | { | |
27cb1c20 BB |
279 | struct clk_main_rc_osc *osc; |
280 | struct clk *clk = NULL; | |
281 | struct clk_init_data init; | |
282 | ||
1bdf0232 | 283 | if (!name || !frequency) |
27cb1c20 BB |
284 | return ERR_PTR(-EINVAL); |
285 | ||
286 | osc = kzalloc(sizeof(*osc), GFP_KERNEL); | |
287 | if (!osc) | |
288 | return ERR_PTR(-ENOMEM); | |
289 | ||
290 | init.name = name; | |
291 | init.ops = &main_rc_osc_ops; | |
292 | init.parent_names = NULL; | |
293 | init.num_parents = 0; | |
a9bb2ef7 | 294 | init.flags = CLK_IGNORE_UNUSED; |
27cb1c20 BB |
295 | |
296 | osc->hw.init = &init; | |
1bdf0232 | 297 | osc->regmap = regmap; |
27cb1c20 BB |
298 | osc->frequency = frequency; |
299 | osc->accuracy = accuracy; | |
300 | ||
27cb1c20 | 301 | clk = clk_register(NULL, &osc->hw); |
99a81706 | 302 | if (IS_ERR(clk)) |
27cb1c20 | 303 | kfree(osc); |
27cb1c20 BB |
304 | |
305 | return clk; | |
306 | } | |
307 | ||
1bdf0232 | 308 | static void __init of_at91sam9x5_clk_main_rc_osc_setup(struct device_node *np) |
27cb1c20 BB |
309 | { |
310 | struct clk *clk; | |
27cb1c20 BB |
311 | u32 frequency = 0; |
312 | u32 accuracy = 0; | |
313 | const char *name = np->name; | |
1bdf0232 | 314 | struct regmap *regmap; |
27cb1c20 BB |
315 | |
316 | of_property_read_string(np, "clock-output-names", &name); | |
317 | of_property_read_u32(np, "clock-frequency", &frequency); | |
318 | of_property_read_u32(np, "clock-accuracy", &accuracy); | |
319 | ||
1bdf0232 BB |
320 | regmap = syscon_node_to_regmap(of_get_parent(np)); |
321 | if (IS_ERR(regmap)) | |
322 | return; | |
323 | ||
99a81706 | 324 | clk = at91_clk_register_main_rc_osc(regmap, name, frequency, accuracy); |
27cb1c20 BB |
325 | if (IS_ERR(clk)) |
326 | return; | |
327 | ||
328 | of_clk_add_provider(np, of_clk_src_simple_get, clk); | |
329 | } | |
1bdf0232 BB |
330 | CLK_OF_DECLARE(at91sam9x5_clk_main_rc_osc, "atmel,at91sam9x5-clk-main-rc-osc", |
331 | of_at91sam9x5_clk_main_rc_osc_setup); | |
27cb1c20 BB |
332 | |
333 | ||
1bdf0232 | 334 | static int clk_main_probe_frequency(struct regmap *regmap) |
27cb1c20 BB |
335 | { |
336 | unsigned long prep_time, timeout; | |
1bdf0232 | 337 | unsigned int mcfr; |
38d34c31 BB |
338 | |
339 | timeout = jiffies + usecs_to_jiffies(MAINFRDY_TIMEOUT); | |
340 | do { | |
27cb1c20 | 341 | prep_time = jiffies; |
1bdf0232 BB |
342 | regmap_read(regmap, AT91_CKGR_MCFR, &mcfr); |
343 | if (mcfr & AT91_PMC_MAINRDY) | |
38d34c31 BB |
344 | return 0; |
345 | usleep_range(MAINF_LOOP_MIN_WAIT, MAINF_LOOP_MAX_WAIT); | |
27cb1c20 | 346 | } while (time_before(prep_time, timeout)); |
38d34c31 | 347 | |
27cb1c20 | 348 | return -ETIMEDOUT; |
38d34c31 BB |
349 | } |
350 | ||
1bdf0232 | 351 | static unsigned long clk_main_recalc_rate(struct regmap *regmap, |
27cb1c20 | 352 | unsigned long parent_rate) |
38d34c31 | 353 | { |
1bdf0232 | 354 | unsigned int mcfr; |
27cb1c20 BB |
355 | |
356 | if (parent_rate) | |
357 | return parent_rate; | |
358 | ||
4da66b63 | 359 | pr_warn("Main crystal frequency not set, using approximate value\n"); |
1bdf0232 BB |
360 | regmap_read(regmap, AT91_CKGR_MCFR, &mcfr); |
361 | if (!(mcfr & AT91_PMC_MAINRDY)) | |
27cb1c20 | 362 | return 0; |
38d34c31 | 363 | |
1bdf0232 | 364 | return ((mcfr & AT91_PMC_MAINF) * SLOW_CLOCK_FREQ) / MAINF_DIV; |
38d34c31 BB |
365 | } |
366 | ||
27cb1c20 | 367 | static int clk_rm9200_main_prepare(struct clk_hw *hw) |
38d34c31 | 368 | { |
27cb1c20 BB |
369 | struct clk_rm9200_main *clkmain = to_clk_rm9200_main(hw); |
370 | ||
1bdf0232 | 371 | return clk_main_probe_frequency(clkmain->regmap); |
27cb1c20 BB |
372 | } |
373 | ||
374 | static int clk_rm9200_main_is_prepared(struct clk_hw *hw) | |
375 | { | |
376 | struct clk_rm9200_main *clkmain = to_clk_rm9200_main(hw); | |
1bdf0232 BB |
377 | unsigned int status; |
378 | ||
379 | regmap_read(clkmain->regmap, AT91_CKGR_MCFR, &status); | |
27cb1c20 | 380 | |
1bdf0232 | 381 | return status & AT91_PMC_MAINRDY ? 1 : 0; |
27cb1c20 BB |
382 | } |
383 | ||
384 | static unsigned long clk_rm9200_main_recalc_rate(struct clk_hw *hw, | |
385 | unsigned long parent_rate) | |
386 | { | |
387 | struct clk_rm9200_main *clkmain = to_clk_rm9200_main(hw); | |
388 | ||
1bdf0232 | 389 | return clk_main_recalc_rate(clkmain->regmap, parent_rate); |
27cb1c20 BB |
390 | } |
391 | ||
392 | static const struct clk_ops rm9200_main_ops = { | |
393 | .prepare = clk_rm9200_main_prepare, | |
394 | .is_prepared = clk_rm9200_main_is_prepared, | |
395 | .recalc_rate = clk_rm9200_main_recalc_rate, | |
396 | }; | |
397 | ||
398 | static struct clk * __init | |
1bdf0232 | 399 | at91_clk_register_rm9200_main(struct regmap *regmap, |
27cb1c20 BB |
400 | const char *name, |
401 | const char *parent_name) | |
402 | { | |
403 | struct clk_rm9200_main *clkmain; | |
404 | struct clk *clk = NULL; | |
405 | struct clk_init_data init; | |
406 | ||
1bdf0232 | 407 | if (!name) |
27cb1c20 BB |
408 | return ERR_PTR(-EINVAL); |
409 | ||
410 | if (!parent_name) | |
411 | return ERR_PTR(-EINVAL); | |
412 | ||
413 | clkmain = kzalloc(sizeof(*clkmain), GFP_KERNEL); | |
414 | if (!clkmain) | |
415 | return ERR_PTR(-ENOMEM); | |
416 | ||
417 | init.name = name; | |
418 | init.ops = &rm9200_main_ops; | |
419 | init.parent_names = &parent_name; | |
420 | init.num_parents = 1; | |
421 | init.flags = 0; | |
422 | ||
423 | clkmain->hw.init = &init; | |
1bdf0232 | 424 | clkmain->regmap = regmap; |
27cb1c20 BB |
425 | |
426 | clk = clk_register(NULL, &clkmain->hw); | |
427 | if (IS_ERR(clk)) | |
428 | kfree(clkmain); | |
429 | ||
430 | return clk; | |
431 | } | |
432 | ||
1bdf0232 | 433 | static void __init of_at91rm9200_clk_main_setup(struct device_node *np) |
27cb1c20 BB |
434 | { |
435 | struct clk *clk; | |
436 | const char *parent_name; | |
437 | const char *name = np->name; | |
1bdf0232 | 438 | struct regmap *regmap; |
27cb1c20 BB |
439 | |
440 | parent_name = of_clk_get_parent_name(np, 0); | |
441 | of_property_read_string(np, "clock-output-names", &name); | |
442 | ||
1bdf0232 BB |
443 | regmap = syscon_node_to_regmap(of_get_parent(np)); |
444 | if (IS_ERR(regmap)) | |
445 | return; | |
446 | ||
447 | clk = at91_clk_register_rm9200_main(regmap, name, parent_name); | |
27cb1c20 BB |
448 | if (IS_ERR(clk)) |
449 | return; | |
450 | ||
451 | of_clk_add_provider(np, of_clk_src_simple_get, clk); | |
452 | } | |
1bdf0232 BB |
453 | CLK_OF_DECLARE(at91rm9200_clk_main, "atmel,at91rm9200-clk-main", |
454 | of_at91rm9200_clk_main_setup); | |
27cb1c20 | 455 | |
1bdf0232 BB |
456 | static inline bool clk_sam9x5_main_ready(struct regmap *regmap) |
457 | { | |
458 | unsigned int status; | |
459 | ||
460 | regmap_read(regmap, AT91_PMC_SR, &status); | |
461 | ||
462 | return status & AT91_PMC_MOSCSELS ? 1 : 0; | |
463 | } | |
464 | ||
27cb1c20 BB |
465 | static int clk_sam9x5_main_prepare(struct clk_hw *hw) |
466 | { | |
467 | struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw); | |
1bdf0232 | 468 | struct regmap *regmap = clkmain->regmap; |
38d34c31 | 469 | |
99a81706 AB |
470 | while (!clk_sam9x5_main_ready(regmap)) |
471 | cpu_relax(); | |
27cb1c20 | 472 | |
1bdf0232 | 473 | return clk_main_probe_frequency(regmap); |
27cb1c20 | 474 | } |
38d34c31 | 475 | |
27cb1c20 BB |
476 | static int clk_sam9x5_main_is_prepared(struct clk_hw *hw) |
477 | { | |
478 | struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw); | |
38d34c31 | 479 | |
1bdf0232 | 480 | return clk_sam9x5_main_ready(clkmain->regmap); |
38d34c31 BB |
481 | } |
482 | ||
27cb1c20 BB |
483 | static unsigned long clk_sam9x5_main_recalc_rate(struct clk_hw *hw, |
484 | unsigned long parent_rate) | |
485 | { | |
486 | struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw); | |
487 | ||
1bdf0232 | 488 | return clk_main_recalc_rate(clkmain->regmap, parent_rate); |
27cb1c20 BB |
489 | } |
490 | ||
491 | static int clk_sam9x5_main_set_parent(struct clk_hw *hw, u8 index) | |
492 | { | |
493 | struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw); | |
1bdf0232 BB |
494 | struct regmap *regmap = clkmain->regmap; |
495 | unsigned int tmp; | |
27cb1c20 BB |
496 | |
497 | if (index > 1) | |
498 | return -EINVAL; | |
499 | ||
1bdf0232 BB |
500 | regmap_read(regmap, AT91_CKGR_MOR, &tmp); |
501 | tmp &= ~MOR_KEY_MASK; | |
27cb1c20 BB |
502 | |
503 | if (index && !(tmp & AT91_PMC_MOSCSEL)) | |
1bdf0232 | 504 | regmap_write(regmap, AT91_CKGR_MOR, tmp | AT91_PMC_MOSCSEL); |
27cb1c20 | 505 | else if (!index && (tmp & AT91_PMC_MOSCSEL)) |
1bdf0232 | 506 | regmap_write(regmap, AT91_CKGR_MOR, tmp & ~AT91_PMC_MOSCSEL); |
27cb1c20 | 507 | |
99a81706 AB |
508 | while (!clk_sam9x5_main_ready(regmap)) |
509 | cpu_relax(); | |
27cb1c20 BB |
510 | |
511 | return 0; | |
512 | } | |
513 | ||
514 | static u8 clk_sam9x5_main_get_parent(struct clk_hw *hw) | |
515 | { | |
516 | struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw); | |
1bdf0232 | 517 | unsigned int status; |
27cb1c20 | 518 | |
1bdf0232 BB |
519 | regmap_read(clkmain->regmap, AT91_CKGR_MOR, &status); |
520 | ||
521 | return status & AT91_PMC_MOSCEN ? 1 : 0; | |
27cb1c20 BB |
522 | } |
523 | ||
524 | static const struct clk_ops sam9x5_main_ops = { | |
525 | .prepare = clk_sam9x5_main_prepare, | |
526 | .is_prepared = clk_sam9x5_main_is_prepared, | |
527 | .recalc_rate = clk_sam9x5_main_recalc_rate, | |
528 | .set_parent = clk_sam9x5_main_set_parent, | |
529 | .get_parent = clk_sam9x5_main_get_parent, | |
38d34c31 BB |
530 | }; |
531 | ||
532 | static struct clk * __init | |
1bdf0232 | 533 | at91_clk_register_sam9x5_main(struct regmap *regmap, |
27cb1c20 BB |
534 | const char *name, |
535 | const char **parent_names, | |
536 | int num_parents) | |
38d34c31 | 537 | { |
27cb1c20 | 538 | struct clk_sam9x5_main *clkmain; |
38d34c31 BB |
539 | struct clk *clk = NULL; |
540 | struct clk_init_data init; | |
1bdf0232 | 541 | unsigned int status; |
38d34c31 | 542 | |
1bdf0232 | 543 | if (!name) |
38d34c31 BB |
544 | return ERR_PTR(-EINVAL); |
545 | ||
27cb1c20 | 546 | if (!parent_names || !num_parents) |
38d34c31 BB |
547 | return ERR_PTR(-EINVAL); |
548 | ||
549 | clkmain = kzalloc(sizeof(*clkmain), GFP_KERNEL); | |
550 | if (!clkmain) | |
551 | return ERR_PTR(-ENOMEM); | |
552 | ||
553 | init.name = name; | |
27cb1c20 BB |
554 | init.ops = &sam9x5_main_ops; |
555 | init.parent_names = parent_names; | |
556 | init.num_parents = num_parents; | |
557 | init.flags = CLK_SET_PARENT_GATE; | |
38d34c31 BB |
558 | |
559 | clkmain->hw.init = &init; | |
1bdf0232 | 560 | clkmain->regmap = regmap; |
1bdf0232 BB |
561 | regmap_read(clkmain->regmap, AT91_CKGR_MOR, &status); |
562 | clkmain->parent = status & AT91_PMC_MOSCEN ? 1 : 0; | |
38d34c31 BB |
563 | |
564 | clk = clk_register(NULL, &clkmain->hw); | |
99a81706 | 565 | if (IS_ERR(clk)) |
38d34c31 | 566 | kfree(clkmain); |
38d34c31 BB |
567 | |
568 | return clk; | |
569 | } | |
570 | ||
1bdf0232 | 571 | static void __init of_at91sam9x5_clk_main_setup(struct device_node *np) |
38d34c31 BB |
572 | { |
573 | struct clk *clk; | |
27cb1c20 | 574 | const char *parent_names[2]; |
8c1b1e54 | 575 | unsigned int num_parents; |
38d34c31 | 576 | const char *name = np->name; |
1bdf0232 | 577 | struct regmap *regmap; |
27cb1c20 | 578 | |
51a43be9 | 579 | num_parents = of_clk_get_parent_count(np); |
8c1b1e54 | 580 | if (num_parents == 0 || num_parents > 2) |
27cb1c20 BB |
581 | return; |
582 | ||
f0557fbe | 583 | of_clk_parent_fill(np, parent_names, num_parents); |
1bdf0232 BB |
584 | regmap = syscon_node_to_regmap(of_get_parent(np)); |
585 | if (IS_ERR(regmap)) | |
586 | return; | |
38d34c31 | 587 | |
38d34c31 | 588 | of_property_read_string(np, "clock-output-names", &name); |
27cb1c20 | 589 | |
99a81706 | 590 | clk = at91_clk_register_sam9x5_main(regmap, name, parent_names, |
27cb1c20 | 591 | num_parents); |
38d34c31 BB |
592 | if (IS_ERR(clk)) |
593 | return; | |
594 | ||
595 | of_clk_add_provider(np, of_clk_src_simple_get, clk); | |
596 | } | |
1bdf0232 BB |
597 | CLK_OF_DECLARE(at91sam9x5_clk_main, "atmel,at91sam9x5-clk-main", |
598 | of_at91sam9x5_clk_main_setup); |