blob: 58f3b568e9cb9024beaf0831c9ac3e0237a93d03 [file] [log] [blame]
Boris BREZILLON61140672013-10-11 11:44:36 +02001/*
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/of.h>
15#include <linux/of_address.h>
16#include <linux/io.h>
17
18#include "pmc.h"
19
20#define PERIPHERAL_MAX 64
21
22#define PERIPHERAL_AT91RM9200 0
23#define PERIPHERAL_AT91SAM9X5 1
24
25#define PERIPHERAL_ID_MIN 2
26#define PERIPHERAL_ID_MAX 31
27#define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX))
28
29#define PERIPHERAL_RSHIFT_MASK 0x3
30#define PERIPHERAL_RSHIFT(val) (((val) >> 16) & PERIPHERAL_RSHIFT_MASK)
31
Boris Brezillon86e44042015-05-28 14:01:08 +020032#define PERIPHERAL_MAX_SHIFT 3
Boris BREZILLON61140672013-10-11 11:44:36 +020033
34struct clk_peripheral {
35 struct clk_hw hw;
36 struct at91_pmc *pmc;
37 u32 id;
38};
39
40#define to_clk_peripheral(hw) container_of(hw, struct clk_peripheral, hw)
41
42struct clk_sam9x5_peripheral {
43 struct clk_hw hw;
44 struct at91_pmc *pmc;
45 struct clk_range range;
46 u32 id;
47 u32 div;
48 bool auto_div;
49};
50
51#define to_clk_sam9x5_peripheral(hw) \
52 container_of(hw, struct clk_sam9x5_peripheral, hw)
53
54static int clk_peripheral_enable(struct clk_hw *hw)
55{
56 struct clk_peripheral *periph = to_clk_peripheral(hw);
57 struct at91_pmc *pmc = periph->pmc;
58 int offset = AT91_PMC_PCER;
59 u32 id = periph->id;
60
61 if (id < PERIPHERAL_ID_MIN)
62 return 0;
63 if (id > PERIPHERAL_ID_MAX)
64 offset = AT91_PMC_PCER1;
65 pmc_write(pmc, offset, PERIPHERAL_MASK(id));
66 return 0;
67}
68
69static void clk_peripheral_disable(struct clk_hw *hw)
70{
71 struct clk_peripheral *periph = to_clk_peripheral(hw);
72 struct at91_pmc *pmc = periph->pmc;
73 int offset = AT91_PMC_PCDR;
74 u32 id = periph->id;
75
76 if (id < PERIPHERAL_ID_MIN)
77 return;
78 if (id > PERIPHERAL_ID_MAX)
79 offset = AT91_PMC_PCDR1;
80 pmc_write(pmc, offset, PERIPHERAL_MASK(id));
81}
82
83static int clk_peripheral_is_enabled(struct clk_hw *hw)
84{
85 struct clk_peripheral *periph = to_clk_peripheral(hw);
86 struct at91_pmc *pmc = periph->pmc;
87 int offset = AT91_PMC_PCSR;
88 u32 id = periph->id;
89
90 if (id < PERIPHERAL_ID_MIN)
91 return 1;
92 if (id > PERIPHERAL_ID_MAX)
93 offset = AT91_PMC_PCSR1;
94 return !!(pmc_read(pmc, offset) & PERIPHERAL_MASK(id));
95}
96
97static const struct clk_ops peripheral_ops = {
98 .enable = clk_peripheral_enable,
99 .disable = clk_peripheral_disable,
100 .is_enabled = clk_peripheral_is_enabled,
101};
102
103static struct clk * __init
104at91_clk_register_peripheral(struct at91_pmc *pmc, const char *name,
105 const char *parent_name, u32 id)
106{
107 struct clk_peripheral *periph;
108 struct clk *clk = NULL;
109 struct clk_init_data init;
110
111 if (!pmc || !name || !parent_name || id > PERIPHERAL_ID_MAX)
112 return ERR_PTR(-EINVAL);
113
114 periph = kzalloc(sizeof(*periph), GFP_KERNEL);
115 if (!periph)
116 return ERR_PTR(-ENOMEM);
117
118 init.name = name;
119 init.ops = &peripheral_ops;
120 init.parent_names = (parent_name ? &parent_name : NULL);
121 init.num_parents = (parent_name ? 1 : 0);
122 init.flags = 0;
123
124 periph->id = id;
125 periph->hw.init = &init;
126 periph->pmc = pmc;
127
128 clk = clk_register(NULL, &periph->hw);
129 if (IS_ERR(clk))
130 kfree(periph);
131
132 return clk;
133}
134
135static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
136{
Stephen Boydd0979332015-07-30 17:20:57 -0700137 struct clk_hw *parent;
Boris BREZILLON61140672013-10-11 11:44:36 +0200138 unsigned long parent_rate;
139 int shift = 0;
140
141 if (!periph->auto_div)
142 return;
143
144 if (periph->range.max) {
Stephen Boydd0979332015-07-30 17:20:57 -0700145 parent = clk_hw_get_parent_by_index(&periph->hw, 0);
146 parent_rate = clk_hw_get_rate(parent);
Boris BREZILLON61140672013-10-11 11:44:36 +0200147 if (!parent_rate)
148 return;
149
150 for (; shift < PERIPHERAL_MAX_SHIFT; shift++) {
151 if (parent_rate >> shift <= periph->range.max)
152 break;
153 }
154 }
155
156 periph->auto_div = false;
157 periph->div = shift;
158}
159
160static int clk_sam9x5_peripheral_enable(struct clk_hw *hw)
161{
162 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
163 struct at91_pmc *pmc = periph->pmc;
Nicolas Ferre36844bd2015-06-17 14:40:39 +0200164 u32 tmp;
Boris BREZILLON61140672013-10-11 11:44:36 +0200165
166 if (periph->id < PERIPHERAL_ID_MIN)
167 return 0;
168
Nicolas Ferre36844bd2015-06-17 14:40:39 +0200169 pmc_lock(pmc);
170 pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID_MASK));
171 tmp = pmc_read(pmc, AT91_PMC_PCR) & ~AT91_PMC_PCR_DIV_MASK;
172 pmc_write(pmc, AT91_PMC_PCR, tmp | AT91_PMC_PCR_DIV(periph->div)
173 | AT91_PMC_PCR_CMD
174 | AT91_PMC_PCR_EN);
175 pmc_unlock(pmc);
Boris BREZILLON61140672013-10-11 11:44:36 +0200176 return 0;
177}
178
179static void clk_sam9x5_peripheral_disable(struct clk_hw *hw)
180{
181 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
182 struct at91_pmc *pmc = periph->pmc;
Nicolas Ferre36844bd2015-06-17 14:40:39 +0200183 u32 tmp;
Boris BREZILLON61140672013-10-11 11:44:36 +0200184
185 if (periph->id < PERIPHERAL_ID_MIN)
186 return;
187
Nicolas Ferre36844bd2015-06-17 14:40:39 +0200188 pmc_lock(pmc);
189 pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID_MASK));
190 tmp = pmc_read(pmc, AT91_PMC_PCR) & ~AT91_PMC_PCR_EN;
191 pmc_write(pmc, AT91_PMC_PCR, tmp | AT91_PMC_PCR_CMD);
192 pmc_unlock(pmc);
Boris BREZILLON61140672013-10-11 11:44:36 +0200193}
194
195static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw)
196{
197 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
198 struct at91_pmc *pmc = periph->pmc;
199 int ret;
200
201 if (periph->id < PERIPHERAL_ID_MIN)
202 return 1;
203
204 pmc_lock(pmc);
Nicolas Ferre96ef36e2015-06-17 14:40:38 +0200205 pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID_MASK));
Boris BREZILLON61140672013-10-11 11:44:36 +0200206 ret = !!(pmc_read(pmc, AT91_PMC_PCR) & AT91_PMC_PCR_EN);
207 pmc_unlock(pmc);
208
209 return ret;
210}
211
212static unsigned long
213clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw,
214 unsigned long parent_rate)
215{
216 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
217 struct at91_pmc *pmc = periph->pmc;
218 u32 tmp;
219
220 if (periph->id < PERIPHERAL_ID_MIN)
221 return parent_rate;
222
223 pmc_lock(pmc);
Nicolas Ferre96ef36e2015-06-17 14:40:38 +0200224 pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID_MASK));
Boris BREZILLON61140672013-10-11 11:44:36 +0200225 tmp = pmc_read(pmc, AT91_PMC_PCR);
226 pmc_unlock(pmc);
227
228 if (tmp & AT91_PMC_PCR_EN) {
229 periph->div = PERIPHERAL_RSHIFT(tmp);
230 periph->auto_div = false;
231 } else {
232 clk_sam9x5_peripheral_autodiv(periph);
233 }
234
235 return parent_rate >> periph->div;
236}
237
238static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw,
239 unsigned long rate,
240 unsigned long *parent_rate)
241{
242 int shift = 0;
243 unsigned long best_rate;
244 unsigned long best_diff;
245 unsigned long cur_rate = *parent_rate;
246 unsigned long cur_diff;
247 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
248
249 if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max)
250 return *parent_rate;
251
252 if (periph->range.max) {
Boris Brezillon86e44042015-05-28 14:01:08 +0200253 for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
Boris BREZILLON61140672013-10-11 11:44:36 +0200254 cur_rate = *parent_rate >> shift;
255 if (cur_rate <= periph->range.max)
256 break;
257 }
258 }
259
260 if (rate >= cur_rate)
261 return cur_rate;
262
263 best_diff = cur_rate - rate;
264 best_rate = cur_rate;
Boris Brezillon86e44042015-05-28 14:01:08 +0200265 for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
Boris BREZILLON61140672013-10-11 11:44:36 +0200266 cur_rate = *parent_rate >> shift;
267 if (cur_rate < rate)
268 cur_diff = rate - cur_rate;
269 else
270 cur_diff = cur_rate - rate;
271
272 if (cur_diff < best_diff) {
273 best_diff = cur_diff;
274 best_rate = cur_rate;
275 }
276
277 if (!best_diff || cur_rate < rate)
278 break;
279 }
280
281 return best_rate;
282}
283
284static int clk_sam9x5_peripheral_set_rate(struct clk_hw *hw,
285 unsigned long rate,
286 unsigned long parent_rate)
287{
288 int shift;
289 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
290 if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) {
291 if (parent_rate == rate)
292 return 0;
293 else
294 return -EINVAL;
295 }
296
297 if (periph->range.max && rate > periph->range.max)
298 return -EINVAL;
299
Boris Brezillon86e44042015-05-28 14:01:08 +0200300 for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
Boris BREZILLON61140672013-10-11 11:44:36 +0200301 if (parent_rate >> shift == rate) {
302 periph->auto_div = false;
303 periph->div = shift;
304 return 0;
305 }
306 }
307
308 return -EINVAL;
309}
310
311static const struct clk_ops sam9x5_peripheral_ops = {
312 .enable = clk_sam9x5_peripheral_enable,
313 .disable = clk_sam9x5_peripheral_disable,
314 .is_enabled = clk_sam9x5_peripheral_is_enabled,
315 .recalc_rate = clk_sam9x5_peripheral_recalc_rate,
316 .round_rate = clk_sam9x5_peripheral_round_rate,
317 .set_rate = clk_sam9x5_peripheral_set_rate,
318};
319
320static struct clk * __init
321at91_clk_register_sam9x5_peripheral(struct at91_pmc *pmc, const char *name,
322 const char *parent_name, u32 id,
323 const struct clk_range *range)
324{
325 struct clk_sam9x5_peripheral *periph;
326 struct clk *clk = NULL;
327 struct clk_init_data init;
328
329 if (!pmc || !name || !parent_name)
330 return ERR_PTR(-EINVAL);
331
332 periph = kzalloc(sizeof(*periph), GFP_KERNEL);
333 if (!periph)
334 return ERR_PTR(-ENOMEM);
335
336 init.name = name;
337 init.ops = &sam9x5_peripheral_ops;
338 init.parent_names = (parent_name ? &parent_name : NULL);
339 init.num_parents = (parent_name ? 1 : 0);
340 init.flags = 0;
341
342 periph->id = id;
343 periph->hw.init = &init;
344 periph->div = 0;
345 periph->pmc = pmc;
346 periph->auto_div = true;
347 periph->range = *range;
348
349 clk = clk_register(NULL, &periph->hw);
350 if (IS_ERR(clk))
351 kfree(periph);
352 else
353 clk_sam9x5_peripheral_autodiv(periph);
354
355 return clk;
356}
357
358static void __init
359of_at91_clk_periph_setup(struct device_node *np, struct at91_pmc *pmc, u8 type)
360{
361 int num;
362 u32 id;
363 struct clk *clk;
364 const char *parent_name;
365 const char *name;
366 struct device_node *periphclknp;
367
368 parent_name = of_clk_get_parent_name(np, 0);
369 if (!parent_name)
370 return;
371
372 num = of_get_child_count(np);
373 if (!num || num > PERIPHERAL_MAX)
374 return;
375
376 for_each_child_of_node(np, periphclknp) {
377 if (of_property_read_u32(periphclknp, "reg", &id))
378 continue;
379
380 if (id >= PERIPHERAL_MAX)
381 continue;
382
383 if (of_property_read_string(np, "clock-output-names", &name))
384 name = periphclknp->name;
385
386 if (type == PERIPHERAL_AT91RM9200) {
387 clk = at91_clk_register_peripheral(pmc, name,
388 parent_name, id);
389 } else {
390 struct clk_range range = CLK_RANGE(0, 0);
391
392 of_at91_get_clk_range(periphclknp,
393 "atmel,clk-output-range",
394 &range);
395
396 clk = at91_clk_register_sam9x5_peripheral(pmc, name,
397 parent_name,
398 id, &range);
399 }
400
401 if (IS_ERR(clk))
402 continue;
403
404 of_clk_add_provider(periphclknp, of_clk_src_simple_get, clk);
405 }
406}
407
408void __init of_at91rm9200_clk_periph_setup(struct device_node *np,
409 struct at91_pmc *pmc)
410{
411 of_at91_clk_periph_setup(np, pmc, PERIPHERAL_AT91RM9200);
412}
413
414void __init of_at91sam9x5_clk_periph_setup(struct device_node *np,
415 struct at91_pmc *pmc)
416{
417 of_at91_clk_periph_setup(np, pmc, PERIPHERAL_AT91SAM9X5);
418}