blob: cd831e19ba72905c4b0ea43da7524e5c980bd938 [file] [log] [blame]
Boris BREZILLON80eded62014-05-07 18:02:15 +02001/*
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>
Boris Brezillon1bdf0232014-09-07 08:14:29 +020018#include <linux/mfd/syscon.h>
19#include <linux/regmap.h>
Boris BREZILLON80eded62014-05-07 18:02:15 +020020
21#include "pmc.h"
22#include "sckc.h"
23
24#define SLOW_CLOCK_FREQ 32768
25#define SLOWCK_SW_CYCLES 5
26#define SLOWCK_SW_TIME_USEC ((SLOWCK_SW_CYCLES * USEC_PER_SEC) / \
27 SLOW_CLOCK_FREQ)
28
29#define AT91_SCKC_CR 0x00
30#define AT91_SCKC_RCEN (1 << 0)
31#define AT91_SCKC_OSC32EN (1 << 1)
32#define AT91_SCKC_OSC32BYP (1 << 2)
33#define AT91_SCKC_OSCSEL (1 << 3)
34
35struct clk_slow_osc {
36 struct clk_hw hw;
37 void __iomem *sckcr;
38 unsigned long startup_usec;
39};
40
41#define to_clk_slow_osc(hw) container_of(hw, struct clk_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_sam9260_slow {
54 struct clk_hw hw;
Boris Brezillon1bdf0232014-09-07 08:14:29 +020055 struct regmap *regmap;
Boris BREZILLON80eded62014-05-07 18:02:15 +020056};
57
58#define to_clk_sam9260_slow(hw) container_of(hw, struct clk_sam9260_slow, hw)
59
60struct clk_sam9x5_slow {
61 struct clk_hw hw;
62 void __iomem *sckcr;
63 u8 parent;
64};
65
66#define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, hw)
67
Boris BREZILLON80eded62014-05-07 18:02:15 +020068static int clk_slow_osc_prepare(struct clk_hw *hw)
69{
70 struct clk_slow_osc *osc = to_clk_slow_osc(hw);
71 void __iomem *sckcr = osc->sckcr;
72 u32 tmp = readl(sckcr);
73
74 if (tmp & AT91_SCKC_OSC32BYP)
75 return 0;
76
77 writel(tmp | AT91_SCKC_OSC32EN, sckcr);
78
79 usleep_range(osc->startup_usec, osc->startup_usec + 1);
80
81 return 0;
82}
83
84static void clk_slow_osc_unprepare(struct clk_hw *hw)
85{
86 struct clk_slow_osc *osc = to_clk_slow_osc(hw);
87 void __iomem *sckcr = osc->sckcr;
88 u32 tmp = readl(sckcr);
89
90 if (tmp & AT91_SCKC_OSC32BYP)
91 return;
92
93 writel(tmp & ~AT91_SCKC_OSC32EN, sckcr);
94}
95
96static int clk_slow_osc_is_prepared(struct clk_hw *hw)
97{
98 struct clk_slow_osc *osc = to_clk_slow_osc(hw);
99 void __iomem *sckcr = osc->sckcr;
100 u32 tmp = readl(sckcr);
101
102 if (tmp & AT91_SCKC_OSC32BYP)
103 return 1;
104
105 return !!(tmp & AT91_SCKC_OSC32EN);
106}
107
108static const struct clk_ops slow_osc_ops = {
109 .prepare = clk_slow_osc_prepare,
110 .unprepare = clk_slow_osc_unprepare,
111 .is_prepared = clk_slow_osc_is_prepared,
112};
113
Stephen Boydf5644f12016-06-01 14:31:22 -0700114static struct clk_hw * __init
Boris BREZILLON80eded62014-05-07 18:02:15 +0200115at91_clk_register_slow_osc(void __iomem *sckcr,
116 const char *name,
117 const char *parent_name,
118 unsigned long startup,
119 bool bypass)
120{
121 struct clk_slow_osc *osc;
Stephen Boydf5644f12016-06-01 14:31:22 -0700122 struct clk_hw *hw;
Boris BREZILLON80eded62014-05-07 18:02:15 +0200123 struct clk_init_data init;
Stephen Boydf5644f12016-06-01 14:31:22 -0700124 int ret;
Boris BREZILLON80eded62014-05-07 18:02:15 +0200125
126 if (!sckcr || !name || !parent_name)
127 return ERR_PTR(-EINVAL);
128
129 osc = kzalloc(sizeof(*osc), GFP_KERNEL);
130 if (!osc)
131 return ERR_PTR(-ENOMEM);
132
133 init.name = name;
134 init.ops = &slow_osc_ops;
135 init.parent_names = &parent_name;
136 init.num_parents = 1;
137 init.flags = CLK_IGNORE_UNUSED;
138
139 osc->hw.init = &init;
140 osc->sckcr = sckcr;
141 osc->startup_usec = startup;
142
143 if (bypass)
144 writel((readl(sckcr) & ~AT91_SCKC_OSC32EN) | AT91_SCKC_OSC32BYP,
145 sckcr);
146
Stephen Boydf5644f12016-06-01 14:31:22 -0700147 hw = &osc->hw;
148 ret = clk_hw_register(NULL, &osc->hw);
149 if (ret) {
Boris BREZILLON80eded62014-05-07 18:02:15 +0200150 kfree(osc);
Stephen Boydf5644f12016-06-01 14:31:22 -0700151 hw = ERR_PTR(ret);
152 }
Boris BREZILLON80eded62014-05-07 18:02:15 +0200153
Stephen Boydf5644f12016-06-01 14:31:22 -0700154 return hw;
Boris BREZILLON80eded62014-05-07 18:02:15 +0200155}
156
157void __init of_at91sam9x5_clk_slow_osc_setup(struct device_node *np,
158 void __iomem *sckcr)
159{
Stephen Boydf5644f12016-06-01 14:31:22 -0700160 struct clk_hw *hw;
Boris BREZILLON80eded62014-05-07 18:02:15 +0200161 const char *parent_name;
162 const char *name = np->name;
163 u32 startup;
164 bool bypass;
165
166 parent_name = of_clk_get_parent_name(np, 0);
167 of_property_read_string(np, "clock-output-names", &name);
168 of_property_read_u32(np, "atmel,startup-time-usec", &startup);
169 bypass = of_property_read_bool(np, "atmel,osc-bypass");
170
Stephen Boydf5644f12016-06-01 14:31:22 -0700171 hw = at91_clk_register_slow_osc(sckcr, name, parent_name, startup,
Boris BREZILLON80eded62014-05-07 18:02:15 +0200172 bypass);
Stephen Boydf5644f12016-06-01 14:31:22 -0700173 if (IS_ERR(hw))
Boris BREZILLON80eded62014-05-07 18:02:15 +0200174 return;
175
Stephen Boydf5644f12016-06-01 14:31:22 -0700176 of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
Boris BREZILLON80eded62014-05-07 18:02:15 +0200177}
178
179static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw,
180 unsigned long parent_rate)
181{
182 struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
183
184 return osc->frequency;
185}
186
187static unsigned long clk_slow_rc_osc_recalc_accuracy(struct clk_hw *hw,
188 unsigned long parent_acc)
189{
190 struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
191
192 return osc->accuracy;
193}
194
195static int clk_slow_rc_osc_prepare(struct clk_hw *hw)
196{
197 struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
198 void __iomem *sckcr = osc->sckcr;
199
200 writel(readl(sckcr) | AT91_SCKC_RCEN, sckcr);
201
202 usleep_range(osc->startup_usec, osc->startup_usec + 1);
203
204 return 0;
205}
206
207static void clk_slow_rc_osc_unprepare(struct clk_hw *hw)
208{
209 struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
210 void __iomem *sckcr = osc->sckcr;
211
212 writel(readl(sckcr) & ~AT91_SCKC_RCEN, sckcr);
213}
214
215static int clk_slow_rc_osc_is_prepared(struct clk_hw *hw)
216{
217 struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
218
219 return !!(readl(osc->sckcr) & AT91_SCKC_RCEN);
220}
221
222static const struct clk_ops slow_rc_osc_ops = {
223 .prepare = clk_slow_rc_osc_prepare,
224 .unprepare = clk_slow_rc_osc_unprepare,
225 .is_prepared = clk_slow_rc_osc_is_prepared,
226 .recalc_rate = clk_slow_rc_osc_recalc_rate,
227 .recalc_accuracy = clk_slow_rc_osc_recalc_accuracy,
228};
229
Stephen Boydf5644f12016-06-01 14:31:22 -0700230static struct clk_hw * __init
Boris BREZILLON80eded62014-05-07 18:02:15 +0200231at91_clk_register_slow_rc_osc(void __iomem *sckcr,
232 const char *name,
233 unsigned long frequency,
234 unsigned long accuracy,
235 unsigned long startup)
236{
237 struct clk_slow_rc_osc *osc;
Stephen Boydf5644f12016-06-01 14:31:22 -0700238 struct clk_hw *hw;
Boris BREZILLON80eded62014-05-07 18:02:15 +0200239 struct clk_init_data init;
Stephen Boydf5644f12016-06-01 14:31:22 -0700240 int ret;
Boris BREZILLON80eded62014-05-07 18:02:15 +0200241
242 if (!sckcr || !name)
243 return ERR_PTR(-EINVAL);
244
245 osc = kzalloc(sizeof(*osc), GFP_KERNEL);
246 if (!osc)
247 return ERR_PTR(-ENOMEM);
248
249 init.name = name;
250 init.ops = &slow_rc_osc_ops;
251 init.parent_names = NULL;
252 init.num_parents = 0;
Stephen Boyda9bb2ef2016-03-01 10:59:46 -0800253 init.flags = CLK_IGNORE_UNUSED;
Boris BREZILLON80eded62014-05-07 18:02:15 +0200254
255 osc->hw.init = &init;
256 osc->sckcr = sckcr;
257 osc->frequency = frequency;
258 osc->accuracy = accuracy;
259 osc->startup_usec = startup;
260
Stephen Boydf5644f12016-06-01 14:31:22 -0700261 hw = &osc->hw;
262 ret = clk_hw_register(NULL, &osc->hw);
263 if (ret) {
Boris BREZILLON80eded62014-05-07 18:02:15 +0200264 kfree(osc);
Stephen Boydf5644f12016-06-01 14:31:22 -0700265 hw = ERR_PTR(ret);
266 }
Boris BREZILLON80eded62014-05-07 18:02:15 +0200267
Stephen Boydf5644f12016-06-01 14:31:22 -0700268 return hw;
Boris BREZILLON80eded62014-05-07 18:02:15 +0200269}
270
271void __init of_at91sam9x5_clk_slow_rc_osc_setup(struct device_node *np,
272 void __iomem *sckcr)
273{
Stephen Boydf5644f12016-06-01 14:31:22 -0700274 struct clk_hw *hw;
Boris BREZILLON80eded62014-05-07 18:02:15 +0200275 u32 frequency = 0;
276 u32 accuracy = 0;
277 u32 startup = 0;
278 const char *name = np->name;
279
280 of_property_read_string(np, "clock-output-names", &name);
281 of_property_read_u32(np, "clock-frequency", &frequency);
282 of_property_read_u32(np, "clock-accuracy", &accuracy);
283 of_property_read_u32(np, "atmel,startup-time-usec", &startup);
284
Stephen Boydf5644f12016-06-01 14:31:22 -0700285 hw = at91_clk_register_slow_rc_osc(sckcr, name, frequency, accuracy,
Boris BREZILLON80eded62014-05-07 18:02:15 +0200286 startup);
Stephen Boydf5644f12016-06-01 14:31:22 -0700287 if (IS_ERR(hw))
Boris BREZILLON80eded62014-05-07 18:02:15 +0200288 return;
289
Stephen Boydf5644f12016-06-01 14:31:22 -0700290 of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
Boris BREZILLON80eded62014-05-07 18:02:15 +0200291}
292
293static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index)
294{
295 struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
296 void __iomem *sckcr = slowck->sckcr;
297 u32 tmp;
298
299 if (index > 1)
300 return -EINVAL;
301
302 tmp = readl(sckcr);
303
304 if ((!index && !(tmp & AT91_SCKC_OSCSEL)) ||
305 (index && (tmp & AT91_SCKC_OSCSEL)))
306 return 0;
307
308 if (index)
309 tmp |= AT91_SCKC_OSCSEL;
310 else
311 tmp &= ~AT91_SCKC_OSCSEL;
312
313 writel(tmp, sckcr);
314
315 usleep_range(SLOWCK_SW_TIME_USEC, SLOWCK_SW_TIME_USEC + 1);
316
317 return 0;
318}
319
320static u8 clk_sam9x5_slow_get_parent(struct clk_hw *hw)
321{
322 struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
323
324 return !!(readl(slowck->sckcr) & AT91_SCKC_OSCSEL);
325}
326
327static const struct clk_ops sam9x5_slow_ops = {
328 .set_parent = clk_sam9x5_slow_set_parent,
329 .get_parent = clk_sam9x5_slow_get_parent,
330};
331
Stephen Boydf5644f12016-06-01 14:31:22 -0700332static struct clk_hw * __init
Boris BREZILLON80eded62014-05-07 18:02:15 +0200333at91_clk_register_sam9x5_slow(void __iomem *sckcr,
334 const char *name,
335 const char **parent_names,
336 int num_parents)
337{
338 struct clk_sam9x5_slow *slowck;
Stephen Boydf5644f12016-06-01 14:31:22 -0700339 struct clk_hw *hw;
Boris BREZILLON80eded62014-05-07 18:02:15 +0200340 struct clk_init_data init;
Stephen Boydf5644f12016-06-01 14:31:22 -0700341 int ret;
Boris BREZILLON80eded62014-05-07 18:02:15 +0200342
343 if (!sckcr || !name || !parent_names || !num_parents)
344 return ERR_PTR(-EINVAL);
345
346 slowck = kzalloc(sizeof(*slowck), GFP_KERNEL);
347 if (!slowck)
348 return ERR_PTR(-ENOMEM);
349
350 init.name = name;
351 init.ops = &sam9x5_slow_ops;
352 init.parent_names = parent_names;
353 init.num_parents = num_parents;
354 init.flags = 0;
355
356 slowck->hw.init = &init;
357 slowck->sckcr = sckcr;
358 slowck->parent = !!(readl(sckcr) & AT91_SCKC_OSCSEL);
359
Stephen Boydf5644f12016-06-01 14:31:22 -0700360 hw = &slowck->hw;
361 ret = clk_hw_register(NULL, &slowck->hw);
362 if (ret) {
Boris BREZILLON80eded62014-05-07 18:02:15 +0200363 kfree(slowck);
Stephen Boydf5644f12016-06-01 14:31:22 -0700364 hw = ERR_PTR(ret);
365 }
Boris BREZILLON80eded62014-05-07 18:02:15 +0200366
Stephen Boydf5644f12016-06-01 14:31:22 -0700367 return hw;
Boris BREZILLON80eded62014-05-07 18:02:15 +0200368}
369
370void __init of_at91sam9x5_clk_slow_setup(struct device_node *np,
371 void __iomem *sckcr)
372{
Stephen Boydf5644f12016-06-01 14:31:22 -0700373 struct clk_hw *hw;
Boris BREZILLON80eded62014-05-07 18:02:15 +0200374 const char *parent_names[2];
Stephen Boyd8c1b1e52016-02-19 17:29:17 -0800375 unsigned int num_parents;
Boris BREZILLON80eded62014-05-07 18:02:15 +0200376 const char *name = np->name;
Boris BREZILLON80eded62014-05-07 18:02:15 +0200377
Geert Uytterhoeven51a43be2015-05-29 11:25:45 +0200378 num_parents = of_clk_get_parent_count(np);
Stephen Boyd8c1b1e52016-02-19 17:29:17 -0800379 if (num_parents == 0 || num_parents > 2)
Boris BREZILLON80eded62014-05-07 18:02:15 +0200380 return;
381
Dinh Nguyenf0557fb2015-07-06 22:59:01 -0500382 of_clk_parent_fill(np, parent_names, num_parents);
Boris BREZILLON80eded62014-05-07 18:02:15 +0200383
384 of_property_read_string(np, "clock-output-names", &name);
385
Stephen Boydf5644f12016-06-01 14:31:22 -0700386 hw = at91_clk_register_sam9x5_slow(sckcr, name, parent_names,
Boris BREZILLON80eded62014-05-07 18:02:15 +0200387 num_parents);
Stephen Boydf5644f12016-06-01 14:31:22 -0700388 if (IS_ERR(hw))
Boris BREZILLON80eded62014-05-07 18:02:15 +0200389 return;
390
Stephen Boydf5644f12016-06-01 14:31:22 -0700391 of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
Boris BREZILLON80eded62014-05-07 18:02:15 +0200392}
393
394static u8 clk_sam9260_slow_get_parent(struct clk_hw *hw)
395{
396 struct clk_sam9260_slow *slowck = to_clk_sam9260_slow(hw);
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200397 unsigned int status;
Boris BREZILLON80eded62014-05-07 18:02:15 +0200398
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200399 regmap_read(slowck->regmap, AT91_PMC_SR, &status);
400
401 return status & AT91_PMC_OSCSEL ? 1 : 0;
Boris BREZILLON80eded62014-05-07 18:02:15 +0200402}
403
404static const struct clk_ops sam9260_slow_ops = {
405 .get_parent = clk_sam9260_slow_get_parent,
406};
407
Stephen Boydf5644f12016-06-01 14:31:22 -0700408static struct clk_hw * __init
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200409at91_clk_register_sam9260_slow(struct regmap *regmap,
Boris BREZILLON80eded62014-05-07 18:02:15 +0200410 const char *name,
411 const char **parent_names,
412 int num_parents)
413{
414 struct clk_sam9260_slow *slowck;
Stephen Boydf5644f12016-06-01 14:31:22 -0700415 struct clk_hw *hw;
Boris BREZILLON80eded62014-05-07 18:02:15 +0200416 struct clk_init_data init;
Stephen Boydf5644f12016-06-01 14:31:22 -0700417 int ret;
Boris BREZILLON80eded62014-05-07 18:02:15 +0200418
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200419 if (!name)
Boris BREZILLON80eded62014-05-07 18:02:15 +0200420 return ERR_PTR(-EINVAL);
421
422 if (!parent_names || !num_parents)
423 return ERR_PTR(-EINVAL);
424
425 slowck = kzalloc(sizeof(*slowck), GFP_KERNEL);
426 if (!slowck)
427 return ERR_PTR(-ENOMEM);
428
429 init.name = name;
430 init.ops = &sam9260_slow_ops;
431 init.parent_names = parent_names;
432 init.num_parents = num_parents;
433 init.flags = 0;
434
435 slowck->hw.init = &init;
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200436 slowck->regmap = regmap;
Boris BREZILLON80eded62014-05-07 18:02:15 +0200437
Stephen Boydf5644f12016-06-01 14:31:22 -0700438 hw = &slowck->hw;
439 ret = clk_hw_register(NULL, &slowck->hw);
440 if (ret) {
Boris BREZILLON80eded62014-05-07 18:02:15 +0200441 kfree(slowck);
Stephen Boydf5644f12016-06-01 14:31:22 -0700442 hw = ERR_PTR(ret);
443 }
Boris BREZILLON80eded62014-05-07 18:02:15 +0200444
Stephen Boydf5644f12016-06-01 14:31:22 -0700445 return hw;
Boris BREZILLON80eded62014-05-07 18:02:15 +0200446}
447
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200448static void __init of_at91sam9260_clk_slow_setup(struct device_node *np)
Boris BREZILLON80eded62014-05-07 18:02:15 +0200449{
Stephen Boydf5644f12016-06-01 14:31:22 -0700450 struct clk_hw *hw;
Boris BREZILLON80eded62014-05-07 18:02:15 +0200451 const char *parent_names[2];
Stephen Boyd8c1b1e52016-02-19 17:29:17 -0800452 unsigned int num_parents;
Boris BREZILLON80eded62014-05-07 18:02:15 +0200453 const char *name = np->name;
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200454 struct regmap *regmap;
Boris BREZILLON80eded62014-05-07 18:02:15 +0200455
Geert Uytterhoeven51a43be2015-05-29 11:25:45 +0200456 num_parents = of_clk_get_parent_count(np);
Boris BREZILLONe8531ac2014-09-02 17:27:51 +0200457 if (num_parents != 2)
Boris BREZILLON80eded62014-05-07 18:02:15 +0200458 return;
459
Dinh Nguyenf0557fb2015-07-06 22:59:01 -0500460 of_clk_parent_fill(np, parent_names, num_parents);
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200461 regmap = syscon_node_to_regmap(of_get_parent(np));
462 if (IS_ERR(regmap))
463 return;
Boris BREZILLON80eded62014-05-07 18:02:15 +0200464
465 of_property_read_string(np, "clock-output-names", &name);
466
Stephen Boydf5644f12016-06-01 14:31:22 -0700467 hw = at91_clk_register_sam9260_slow(regmap, name, parent_names,
Boris BREZILLON80eded62014-05-07 18:02:15 +0200468 num_parents);
Stephen Boydf5644f12016-06-01 14:31:22 -0700469 if (IS_ERR(hw))
Boris BREZILLON80eded62014-05-07 18:02:15 +0200470 return;
471
Stephen Boydf5644f12016-06-01 14:31:22 -0700472 of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
Boris BREZILLON80eded62014-05-07 18:02:15 +0200473}
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200474
475CLK_OF_DECLARE(at91sam9260_clk_slow, "atmel,at91sam9260-clk-slow",
476 of_at91sam9260_clk_slow_setup);