blob: fd160728e99022a06f8083198fe7a997a2375af2 [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>
Boris Brezillon1bdf0232014-09-07 08:14:29 +020015#include <linux/mfd/syscon.h>
16#include <linux/regmap.h>
Boris BREZILLON61140672013-10-11 11:44:36 +020017
18#include "pmc.h"
19
Boris Brezillon1bdf0232014-09-07 08:14:29 +020020DEFINE_SPINLOCK(pmc_pcr_lock);
21
Boris BREZILLON61140672013-10-11 11:44:36 +020022#define PERIPHERAL_MAX 64
23
24#define PERIPHERAL_AT91RM9200 0
25#define PERIPHERAL_AT91SAM9X5 1
26
27#define PERIPHERAL_ID_MIN 2
28#define PERIPHERAL_ID_MAX 31
29#define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX))
30
31#define PERIPHERAL_RSHIFT_MASK 0x3
32#define PERIPHERAL_RSHIFT(val) (((val) >> 16) & PERIPHERAL_RSHIFT_MASK)
33
Boris Brezillon86e44042015-05-28 14:01:08 +020034#define PERIPHERAL_MAX_SHIFT 3
Boris BREZILLON61140672013-10-11 11:44:36 +020035
36struct clk_peripheral {
37 struct clk_hw hw;
Boris Brezillon1bdf0232014-09-07 08:14:29 +020038 struct regmap *regmap;
Boris BREZILLON61140672013-10-11 11:44:36 +020039 u32 id;
40};
41
42#define to_clk_peripheral(hw) container_of(hw, struct clk_peripheral, hw)
43
44struct clk_sam9x5_peripheral {
45 struct clk_hw hw;
Boris Brezillon1bdf0232014-09-07 08:14:29 +020046 struct regmap *regmap;
Boris BREZILLON61140672013-10-11 11:44:36 +020047 struct clk_range range;
Boris Brezillon1bdf0232014-09-07 08:14:29 +020048 spinlock_t *lock;
Boris BREZILLON61140672013-10-11 11:44:36 +020049 u32 id;
50 u32 div;
51 bool auto_div;
52};
53
54#define to_clk_sam9x5_peripheral(hw) \
55 container_of(hw, struct clk_sam9x5_peripheral, hw)
56
57static int clk_peripheral_enable(struct clk_hw *hw)
58{
59 struct clk_peripheral *periph = to_clk_peripheral(hw);
Boris BREZILLON61140672013-10-11 11:44:36 +020060 int offset = AT91_PMC_PCER;
61 u32 id = periph->id;
62
63 if (id < PERIPHERAL_ID_MIN)
64 return 0;
65 if (id > PERIPHERAL_ID_MAX)
66 offset = AT91_PMC_PCER1;
Boris Brezillon1bdf0232014-09-07 08:14:29 +020067 regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id));
68
Boris BREZILLON61140672013-10-11 11:44:36 +020069 return 0;
70}
71
72static void clk_peripheral_disable(struct clk_hw *hw)
73{
74 struct clk_peripheral *periph = to_clk_peripheral(hw);
Boris BREZILLON61140672013-10-11 11:44:36 +020075 int offset = AT91_PMC_PCDR;
76 u32 id = periph->id;
77
78 if (id < PERIPHERAL_ID_MIN)
79 return;
80 if (id > PERIPHERAL_ID_MAX)
81 offset = AT91_PMC_PCDR1;
Boris Brezillon1bdf0232014-09-07 08:14:29 +020082 regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id));
Boris BREZILLON61140672013-10-11 11:44:36 +020083}
84
85static int clk_peripheral_is_enabled(struct clk_hw *hw)
86{
87 struct clk_peripheral *periph = to_clk_peripheral(hw);
Boris BREZILLON61140672013-10-11 11:44:36 +020088 int offset = AT91_PMC_PCSR;
Boris Brezillon1bdf0232014-09-07 08:14:29 +020089 unsigned int status;
Boris BREZILLON61140672013-10-11 11:44:36 +020090 u32 id = periph->id;
91
92 if (id < PERIPHERAL_ID_MIN)
93 return 1;
94 if (id > PERIPHERAL_ID_MAX)
95 offset = AT91_PMC_PCSR1;
Boris Brezillon1bdf0232014-09-07 08:14:29 +020096 regmap_read(periph->regmap, offset, &status);
97
98 return status & PERIPHERAL_MASK(id) ? 1 : 0;
Boris BREZILLON61140672013-10-11 11:44:36 +020099}
100
101static const struct clk_ops peripheral_ops = {
102 .enable = clk_peripheral_enable,
103 .disable = clk_peripheral_disable,
104 .is_enabled = clk_peripheral_is_enabled,
105};
106
107static struct clk * __init
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200108at91_clk_register_peripheral(struct regmap *regmap, const char *name,
Boris BREZILLON61140672013-10-11 11:44:36 +0200109 const char *parent_name, u32 id)
110{
111 struct clk_peripheral *periph;
112 struct clk *clk = NULL;
113 struct clk_init_data init;
114
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200115 if (!name || !parent_name || id > PERIPHERAL_ID_MAX)
Boris BREZILLON61140672013-10-11 11:44:36 +0200116 return ERR_PTR(-EINVAL);
117
118 periph = kzalloc(sizeof(*periph), GFP_KERNEL);
119 if (!periph)
120 return ERR_PTR(-ENOMEM);
121
122 init.name = name;
123 init.ops = &peripheral_ops;
124 init.parent_names = (parent_name ? &parent_name : NULL);
125 init.num_parents = (parent_name ? 1 : 0);
126 init.flags = 0;
127
128 periph->id = id;
129 periph->hw.init = &init;
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200130 periph->regmap = regmap;
Boris BREZILLON61140672013-10-11 11:44:36 +0200131
132 clk = clk_register(NULL, &periph->hw);
133 if (IS_ERR(clk))
134 kfree(periph);
135
136 return clk;
137}
138
139static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
140{
Stephen Boydd0979332015-07-30 17:20:57 -0700141 struct clk_hw *parent;
Boris BREZILLON61140672013-10-11 11:44:36 +0200142 unsigned long parent_rate;
143 int shift = 0;
144
145 if (!periph->auto_div)
146 return;
147
148 if (periph->range.max) {
Stephen Boydd0979332015-07-30 17:20:57 -0700149 parent = clk_hw_get_parent_by_index(&periph->hw, 0);
150 parent_rate = clk_hw_get_rate(parent);
Boris BREZILLON61140672013-10-11 11:44:36 +0200151 if (!parent_rate)
152 return;
153
154 for (; shift < PERIPHERAL_MAX_SHIFT; shift++) {
155 if (parent_rate >> shift <= periph->range.max)
156 break;
157 }
158 }
159
160 periph->auto_div = false;
161 periph->div = shift;
162}
163
164static int clk_sam9x5_peripheral_enable(struct clk_hw *hw)
165{
166 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200167 unsigned long flags;
Boris BREZILLON61140672013-10-11 11:44:36 +0200168
169 if (periph->id < PERIPHERAL_ID_MIN)
170 return 0;
171
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200172 spin_lock_irqsave(periph->lock, flags);
173 regmap_write(periph->regmap, AT91_PMC_PCR,
174 (periph->id & AT91_PMC_PCR_PID_MASK));
175 regmap_update_bits(periph->regmap, AT91_PMC_PCR,
176 AT91_PMC_PCR_DIV_MASK | AT91_PMC_PCR_CMD |
177 AT91_PMC_PCR_EN,
178 AT91_PMC_PCR_DIV(periph->div) |
179 AT91_PMC_PCR_CMD |
180 AT91_PMC_PCR_EN);
181 spin_unlock_irqrestore(periph->lock, flags);
182
Boris BREZILLON61140672013-10-11 11:44:36 +0200183 return 0;
184}
185
186static void clk_sam9x5_peripheral_disable(struct clk_hw *hw)
187{
188 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200189 unsigned long flags;
Boris BREZILLON61140672013-10-11 11:44:36 +0200190
191 if (periph->id < PERIPHERAL_ID_MIN)
192 return;
193
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200194 spin_lock_irqsave(periph->lock, flags);
195 regmap_write(periph->regmap, AT91_PMC_PCR,
196 (periph->id & AT91_PMC_PCR_PID_MASK));
197 regmap_update_bits(periph->regmap, AT91_PMC_PCR,
198 AT91_PMC_PCR_EN | AT91_PMC_PCR_CMD,
199 AT91_PMC_PCR_CMD);
200 spin_unlock_irqrestore(periph->lock, flags);
Boris BREZILLON61140672013-10-11 11:44:36 +0200201}
202
203static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw)
204{
205 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200206 unsigned long flags;
207 unsigned int status;
Boris BREZILLON61140672013-10-11 11:44:36 +0200208
209 if (periph->id < PERIPHERAL_ID_MIN)
210 return 1;
211
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200212 spin_lock_irqsave(periph->lock, flags);
213 regmap_write(periph->regmap, AT91_PMC_PCR,
214 (periph->id & AT91_PMC_PCR_PID_MASK));
215 regmap_read(periph->regmap, AT91_PMC_PCR, &status);
216 spin_unlock_irqrestore(periph->lock, flags);
Boris BREZILLON61140672013-10-11 11:44:36 +0200217
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200218 return status & AT91_PMC_PCR_EN ? 1 : 0;
Boris BREZILLON61140672013-10-11 11:44:36 +0200219}
220
221static unsigned long
222clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw,
223 unsigned long parent_rate)
224{
225 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200226 unsigned long flags;
227 unsigned int status;
Boris BREZILLON61140672013-10-11 11:44:36 +0200228
229 if (periph->id < PERIPHERAL_ID_MIN)
230 return parent_rate;
231
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200232 spin_lock_irqsave(periph->lock, flags);
233 regmap_write(periph->regmap, AT91_PMC_PCR,
234 (periph->id & AT91_PMC_PCR_PID_MASK));
235 regmap_read(periph->regmap, AT91_PMC_PCR, &status);
236 spin_unlock_irqrestore(periph->lock, flags);
Boris BREZILLON61140672013-10-11 11:44:36 +0200237
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200238 if (status & AT91_PMC_PCR_EN) {
239 periph->div = PERIPHERAL_RSHIFT(status);
Boris BREZILLON61140672013-10-11 11:44:36 +0200240 periph->auto_div = false;
241 } else {
242 clk_sam9x5_peripheral_autodiv(periph);
243 }
244
245 return parent_rate >> periph->div;
246}
247
248static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw,
249 unsigned long rate,
250 unsigned long *parent_rate)
251{
252 int shift = 0;
253 unsigned long best_rate;
254 unsigned long best_diff;
255 unsigned long cur_rate = *parent_rate;
256 unsigned long cur_diff;
257 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
258
259 if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max)
260 return *parent_rate;
261
262 if (periph->range.max) {
Boris Brezillon86e44042015-05-28 14:01:08 +0200263 for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
Boris BREZILLON61140672013-10-11 11:44:36 +0200264 cur_rate = *parent_rate >> shift;
265 if (cur_rate <= periph->range.max)
266 break;
267 }
268 }
269
270 if (rate >= cur_rate)
271 return cur_rate;
272
273 best_diff = cur_rate - rate;
274 best_rate = cur_rate;
Boris Brezillon86e44042015-05-28 14:01:08 +0200275 for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
Boris BREZILLON61140672013-10-11 11:44:36 +0200276 cur_rate = *parent_rate >> shift;
277 if (cur_rate < rate)
278 cur_diff = rate - cur_rate;
279 else
280 cur_diff = cur_rate - rate;
281
282 if (cur_diff < best_diff) {
283 best_diff = cur_diff;
284 best_rate = cur_rate;
285 }
286
287 if (!best_diff || cur_rate < rate)
288 break;
289 }
290
291 return best_rate;
292}
293
294static int clk_sam9x5_peripheral_set_rate(struct clk_hw *hw,
295 unsigned long rate,
296 unsigned long parent_rate)
297{
298 int shift;
299 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
300 if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) {
301 if (parent_rate == rate)
302 return 0;
303 else
304 return -EINVAL;
305 }
306
307 if (periph->range.max && rate > periph->range.max)
308 return -EINVAL;
309
Boris Brezillon86e44042015-05-28 14:01:08 +0200310 for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
Boris BREZILLON61140672013-10-11 11:44:36 +0200311 if (parent_rate >> shift == rate) {
312 periph->auto_div = false;
313 periph->div = shift;
314 return 0;
315 }
316 }
317
318 return -EINVAL;
319}
320
321static const struct clk_ops sam9x5_peripheral_ops = {
322 .enable = clk_sam9x5_peripheral_enable,
323 .disable = clk_sam9x5_peripheral_disable,
324 .is_enabled = clk_sam9x5_peripheral_is_enabled,
325 .recalc_rate = clk_sam9x5_peripheral_recalc_rate,
326 .round_rate = clk_sam9x5_peripheral_round_rate,
327 .set_rate = clk_sam9x5_peripheral_set_rate,
328};
329
330static struct clk * __init
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200331at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
332 const char *name, const char *parent_name,
333 u32 id, const struct clk_range *range)
Boris BREZILLON61140672013-10-11 11:44:36 +0200334{
335 struct clk_sam9x5_peripheral *periph;
336 struct clk *clk = NULL;
337 struct clk_init_data init;
338
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200339 if (!name || !parent_name)
Boris BREZILLON61140672013-10-11 11:44:36 +0200340 return ERR_PTR(-EINVAL);
341
342 periph = kzalloc(sizeof(*periph), GFP_KERNEL);
343 if (!periph)
344 return ERR_PTR(-ENOMEM);
345
346 init.name = name;
347 init.ops = &sam9x5_peripheral_ops;
348 init.parent_names = (parent_name ? &parent_name : NULL);
349 init.num_parents = (parent_name ? 1 : 0);
350 init.flags = 0;
351
352 periph->id = id;
353 periph->hw.init = &init;
354 periph->div = 0;
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200355 periph->regmap = regmap;
356 periph->lock = lock;
Boris BREZILLON61140672013-10-11 11:44:36 +0200357 periph->auto_div = true;
358 periph->range = *range;
359
360 clk = clk_register(NULL, &periph->hw);
361 if (IS_ERR(clk))
362 kfree(periph);
363 else
364 clk_sam9x5_peripheral_autodiv(periph);
365
366 return clk;
367}
368
369static void __init
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200370of_at91_clk_periph_setup(struct device_node *np, u8 type)
Boris BREZILLON61140672013-10-11 11:44:36 +0200371{
372 int num;
373 u32 id;
374 struct clk *clk;
375 const char *parent_name;
376 const char *name;
377 struct device_node *periphclknp;
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200378 struct regmap *regmap;
Boris BREZILLON61140672013-10-11 11:44:36 +0200379
380 parent_name = of_clk_get_parent_name(np, 0);
381 if (!parent_name)
382 return;
383
384 num = of_get_child_count(np);
385 if (!num || num > PERIPHERAL_MAX)
386 return;
387
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200388 regmap = syscon_node_to_regmap(of_get_parent(np));
389 if (IS_ERR(regmap))
390 return;
391
Boris BREZILLON61140672013-10-11 11:44:36 +0200392 for_each_child_of_node(np, periphclknp) {
393 if (of_property_read_u32(periphclknp, "reg", &id))
394 continue;
395
396 if (id >= PERIPHERAL_MAX)
397 continue;
398
399 if (of_property_read_string(np, "clock-output-names", &name))
400 name = periphclknp->name;
401
402 if (type == PERIPHERAL_AT91RM9200) {
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200403 clk = at91_clk_register_peripheral(regmap, name,
Boris BREZILLON61140672013-10-11 11:44:36 +0200404 parent_name, id);
405 } else {
406 struct clk_range range = CLK_RANGE(0, 0);
407
408 of_at91_get_clk_range(periphclknp,
409 "atmel,clk-output-range",
410 &range);
411
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200412 clk = at91_clk_register_sam9x5_peripheral(regmap,
413 &pmc_pcr_lock,
414 name,
Boris BREZILLON61140672013-10-11 11:44:36 +0200415 parent_name,
416 id, &range);
417 }
418
419 if (IS_ERR(clk))
420 continue;
421
422 of_clk_add_provider(periphclknp, of_clk_src_simple_get, clk);
423 }
424}
425
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200426static void __init of_at91rm9200_clk_periph_setup(struct device_node *np)
Boris BREZILLON61140672013-10-11 11:44:36 +0200427{
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200428 of_at91_clk_periph_setup(np, PERIPHERAL_AT91RM9200);
Boris BREZILLON61140672013-10-11 11:44:36 +0200429}
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200430CLK_OF_DECLARE(at91rm9200_clk_periph, "atmel,at91rm9200-clk-peripheral",
431 of_at91rm9200_clk_periph_setup);
Boris BREZILLON61140672013-10-11 11:44:36 +0200432
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200433static void __init of_at91sam9x5_clk_periph_setup(struct device_node *np)
Boris BREZILLON61140672013-10-11 11:44:36 +0200434{
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200435 of_at91_clk_periph_setup(np, PERIPHERAL_AT91SAM9X5);
Boris BREZILLON61140672013-10-11 11:44:36 +0200436}
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200437CLK_OF_DECLARE(at91sam9x5_clk_periph, "atmel,at91sam9x5-clk-peripheral",
438 of_at91sam9x5_clk_periph_setup);
439