blob: 7701183692308e8870a669658e8c24b4d14700e4 [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
Stephen Boydf5644f12016-06-01 14:31:22 -0700107static struct clk_hw * __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;
Boris BREZILLON61140672013-10-11 11:44:36 +0200112 struct clk_init_data init;
Stephen Boydf5644f12016-06-01 14:31:22 -0700113 struct clk_hw *hw;
114 int ret;
Boris BREZILLON61140672013-10-11 11:44:36 +0200115
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200116 if (!name || !parent_name || id > PERIPHERAL_ID_MAX)
Boris BREZILLON61140672013-10-11 11:44:36 +0200117 return ERR_PTR(-EINVAL);
118
119 periph = kzalloc(sizeof(*periph), GFP_KERNEL);
120 if (!periph)
121 return ERR_PTR(-ENOMEM);
122
123 init.name = name;
124 init.ops = &peripheral_ops;
125 init.parent_names = (parent_name ? &parent_name : NULL);
126 init.num_parents = (parent_name ? 1 : 0);
127 init.flags = 0;
128
129 periph->id = id;
130 periph->hw.init = &init;
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200131 periph->regmap = regmap;
Boris BREZILLON61140672013-10-11 11:44:36 +0200132
Stephen Boydf5644f12016-06-01 14:31:22 -0700133 hw = &periph->hw;
134 ret = clk_hw_register(NULL, &periph->hw);
135 if (ret) {
Boris BREZILLON61140672013-10-11 11:44:36 +0200136 kfree(periph);
Stephen Boydf5644f12016-06-01 14:31:22 -0700137 hw = ERR_PTR(ret);
138 }
Boris BREZILLON61140672013-10-11 11:44:36 +0200139
Stephen Boydf5644f12016-06-01 14:31:22 -0700140 return hw;
Boris BREZILLON61140672013-10-11 11:44:36 +0200141}
142
143static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
144{
Stephen Boydd0979332015-07-30 17:20:57 -0700145 struct clk_hw *parent;
Boris BREZILLON61140672013-10-11 11:44:36 +0200146 unsigned long parent_rate;
147 int shift = 0;
148
149 if (!periph->auto_div)
150 return;
151
152 if (periph->range.max) {
Stephen Boydd0979332015-07-30 17:20:57 -0700153 parent = clk_hw_get_parent_by_index(&periph->hw, 0);
154 parent_rate = clk_hw_get_rate(parent);
Boris BREZILLON61140672013-10-11 11:44:36 +0200155 if (!parent_rate)
156 return;
157
158 for (; shift < PERIPHERAL_MAX_SHIFT; shift++) {
159 if (parent_rate >> shift <= periph->range.max)
160 break;
161 }
162 }
163
164 periph->auto_div = false;
165 periph->div = shift;
166}
167
168static int clk_sam9x5_peripheral_enable(struct clk_hw *hw)
169{
170 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200171 unsigned long flags;
Boris BREZILLON61140672013-10-11 11:44:36 +0200172
173 if (periph->id < PERIPHERAL_ID_MIN)
174 return 0;
175
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200176 spin_lock_irqsave(periph->lock, flags);
177 regmap_write(periph->regmap, AT91_PMC_PCR,
178 (periph->id & AT91_PMC_PCR_PID_MASK));
179 regmap_update_bits(periph->regmap, AT91_PMC_PCR,
180 AT91_PMC_PCR_DIV_MASK | AT91_PMC_PCR_CMD |
181 AT91_PMC_PCR_EN,
182 AT91_PMC_PCR_DIV(periph->div) |
183 AT91_PMC_PCR_CMD |
184 AT91_PMC_PCR_EN);
185 spin_unlock_irqrestore(periph->lock, flags);
186
Boris BREZILLON61140672013-10-11 11:44:36 +0200187 return 0;
188}
189
190static void clk_sam9x5_peripheral_disable(struct clk_hw *hw)
191{
192 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200193 unsigned long flags;
Boris BREZILLON61140672013-10-11 11:44:36 +0200194
195 if (periph->id < PERIPHERAL_ID_MIN)
196 return;
197
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200198 spin_lock_irqsave(periph->lock, flags);
199 regmap_write(periph->regmap, AT91_PMC_PCR,
200 (periph->id & AT91_PMC_PCR_PID_MASK));
201 regmap_update_bits(periph->regmap, AT91_PMC_PCR,
202 AT91_PMC_PCR_EN | AT91_PMC_PCR_CMD,
203 AT91_PMC_PCR_CMD);
204 spin_unlock_irqrestore(periph->lock, flags);
Boris BREZILLON61140672013-10-11 11:44:36 +0200205}
206
207static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw)
208{
209 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200210 unsigned long flags;
211 unsigned int status;
Boris BREZILLON61140672013-10-11 11:44:36 +0200212
213 if (periph->id < PERIPHERAL_ID_MIN)
214 return 1;
215
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200216 spin_lock_irqsave(periph->lock, flags);
217 regmap_write(periph->regmap, AT91_PMC_PCR,
218 (periph->id & AT91_PMC_PCR_PID_MASK));
219 regmap_read(periph->regmap, AT91_PMC_PCR, &status);
220 spin_unlock_irqrestore(periph->lock, flags);
Boris BREZILLON61140672013-10-11 11:44:36 +0200221
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200222 return status & AT91_PMC_PCR_EN ? 1 : 0;
Boris BREZILLON61140672013-10-11 11:44:36 +0200223}
224
225static unsigned long
226clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw,
227 unsigned long parent_rate)
228{
229 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200230 unsigned long flags;
231 unsigned int status;
Boris BREZILLON61140672013-10-11 11:44:36 +0200232
233 if (periph->id < PERIPHERAL_ID_MIN)
234 return parent_rate;
235
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200236 spin_lock_irqsave(periph->lock, flags);
237 regmap_write(periph->regmap, AT91_PMC_PCR,
238 (periph->id & AT91_PMC_PCR_PID_MASK));
239 regmap_read(periph->regmap, AT91_PMC_PCR, &status);
240 spin_unlock_irqrestore(periph->lock, flags);
Boris BREZILLON61140672013-10-11 11:44:36 +0200241
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200242 if (status & AT91_PMC_PCR_EN) {
243 periph->div = PERIPHERAL_RSHIFT(status);
Boris BREZILLON61140672013-10-11 11:44:36 +0200244 periph->auto_div = false;
245 } else {
246 clk_sam9x5_peripheral_autodiv(periph);
247 }
248
249 return parent_rate >> periph->div;
250}
251
252static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw,
253 unsigned long rate,
254 unsigned long *parent_rate)
255{
256 int shift = 0;
257 unsigned long best_rate;
258 unsigned long best_diff;
259 unsigned long cur_rate = *parent_rate;
260 unsigned long cur_diff;
261 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
262
263 if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max)
264 return *parent_rate;
265
266 if (periph->range.max) {
Boris Brezillon86e44042015-05-28 14:01:08 +0200267 for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
Boris BREZILLON61140672013-10-11 11:44:36 +0200268 cur_rate = *parent_rate >> shift;
269 if (cur_rate <= periph->range.max)
270 break;
271 }
272 }
273
274 if (rate >= cur_rate)
275 return cur_rate;
276
277 best_diff = cur_rate - rate;
278 best_rate = cur_rate;
Boris Brezillon86e44042015-05-28 14:01:08 +0200279 for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
Boris BREZILLON61140672013-10-11 11:44:36 +0200280 cur_rate = *parent_rate >> shift;
281 if (cur_rate < rate)
282 cur_diff = rate - cur_rate;
283 else
284 cur_diff = cur_rate - rate;
285
286 if (cur_diff < best_diff) {
287 best_diff = cur_diff;
288 best_rate = cur_rate;
289 }
290
291 if (!best_diff || cur_rate < rate)
292 break;
293 }
294
295 return best_rate;
296}
297
298static int clk_sam9x5_peripheral_set_rate(struct clk_hw *hw,
299 unsigned long rate,
300 unsigned long parent_rate)
301{
302 int shift;
303 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
304 if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) {
305 if (parent_rate == rate)
306 return 0;
307 else
308 return -EINVAL;
309 }
310
311 if (periph->range.max && rate > periph->range.max)
312 return -EINVAL;
313
Boris Brezillon86e44042015-05-28 14:01:08 +0200314 for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
Boris BREZILLON61140672013-10-11 11:44:36 +0200315 if (parent_rate >> shift == rate) {
316 periph->auto_div = false;
317 periph->div = shift;
318 return 0;
319 }
320 }
321
322 return -EINVAL;
323}
324
325static const struct clk_ops sam9x5_peripheral_ops = {
326 .enable = clk_sam9x5_peripheral_enable,
327 .disable = clk_sam9x5_peripheral_disable,
328 .is_enabled = clk_sam9x5_peripheral_is_enabled,
329 .recalc_rate = clk_sam9x5_peripheral_recalc_rate,
330 .round_rate = clk_sam9x5_peripheral_round_rate,
331 .set_rate = clk_sam9x5_peripheral_set_rate,
332};
333
Stephen Boydf5644f12016-06-01 14:31:22 -0700334static struct clk_hw * __init
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200335at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
336 const char *name, const char *parent_name,
337 u32 id, const struct clk_range *range)
Boris BREZILLON61140672013-10-11 11:44:36 +0200338{
339 struct clk_sam9x5_peripheral *periph;
Boris BREZILLON61140672013-10-11 11:44:36 +0200340 struct clk_init_data init;
Stephen Boydf5644f12016-06-01 14:31:22 -0700341 struct clk_hw *hw;
342 int ret;
Boris BREZILLON61140672013-10-11 11:44:36 +0200343
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200344 if (!name || !parent_name)
Boris BREZILLON61140672013-10-11 11:44:36 +0200345 return ERR_PTR(-EINVAL);
346
347 periph = kzalloc(sizeof(*periph), GFP_KERNEL);
348 if (!periph)
349 return ERR_PTR(-ENOMEM);
350
351 init.name = name;
352 init.ops = &sam9x5_peripheral_ops;
353 init.parent_names = (parent_name ? &parent_name : NULL);
354 init.num_parents = (parent_name ? 1 : 0);
355 init.flags = 0;
356
357 periph->id = id;
358 periph->hw.init = &init;
359 periph->div = 0;
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200360 periph->regmap = regmap;
361 periph->lock = lock;
Boris BREZILLON61140672013-10-11 11:44:36 +0200362 periph->auto_div = true;
363 periph->range = *range;
364
Stephen Boydf5644f12016-06-01 14:31:22 -0700365 hw = &periph->hw;
366 ret = clk_hw_register(NULL, &periph->hw);
367 if (ret) {
Boris BREZILLON61140672013-10-11 11:44:36 +0200368 kfree(periph);
Stephen Boydf5644f12016-06-01 14:31:22 -0700369 hw = ERR_PTR(ret);
Alexandre Bellonib3b02ea2017-06-08 02:36:47 +0200370 } else {
Boris BREZILLON61140672013-10-11 11:44:36 +0200371 clk_sam9x5_peripheral_autodiv(periph);
Alexandre Bellonib3b02ea2017-06-08 02:36:47 +0200372 pmc_register_id(id);
373 }
Boris BREZILLON61140672013-10-11 11:44:36 +0200374
Stephen Boydf5644f12016-06-01 14:31:22 -0700375 return hw;
Boris BREZILLON61140672013-10-11 11:44:36 +0200376}
377
378static void __init
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200379of_at91_clk_periph_setup(struct device_node *np, u8 type)
Boris BREZILLON61140672013-10-11 11:44:36 +0200380{
381 int num;
382 u32 id;
Stephen Boydf5644f12016-06-01 14:31:22 -0700383 struct clk_hw *hw;
Boris BREZILLON61140672013-10-11 11:44:36 +0200384 const char *parent_name;
385 const char *name;
386 struct device_node *periphclknp;
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200387 struct regmap *regmap;
Boris BREZILLON61140672013-10-11 11:44:36 +0200388
389 parent_name = of_clk_get_parent_name(np, 0);
390 if (!parent_name)
391 return;
392
393 num = of_get_child_count(np);
394 if (!num || num > PERIPHERAL_MAX)
395 return;
396
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200397 regmap = syscon_node_to_regmap(of_get_parent(np));
398 if (IS_ERR(regmap))
399 return;
400
Boris BREZILLON61140672013-10-11 11:44:36 +0200401 for_each_child_of_node(np, periphclknp) {
402 if (of_property_read_u32(periphclknp, "reg", &id))
403 continue;
404
405 if (id >= PERIPHERAL_MAX)
406 continue;
407
408 if (of_property_read_string(np, "clock-output-names", &name))
409 name = periphclknp->name;
410
411 if (type == PERIPHERAL_AT91RM9200) {
Stephen Boydf5644f12016-06-01 14:31:22 -0700412 hw = at91_clk_register_peripheral(regmap, name,
Boris BREZILLON61140672013-10-11 11:44:36 +0200413 parent_name, id);
414 } else {
415 struct clk_range range = CLK_RANGE(0, 0);
416
417 of_at91_get_clk_range(periphclknp,
418 "atmel,clk-output-range",
419 &range);
420
Stephen Boydf5644f12016-06-01 14:31:22 -0700421 hw = at91_clk_register_sam9x5_peripheral(regmap,
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200422 &pmc_pcr_lock,
423 name,
Boris BREZILLON61140672013-10-11 11:44:36 +0200424 parent_name,
425 id, &range);
426 }
427
Stephen Boydf5644f12016-06-01 14:31:22 -0700428 if (IS_ERR(hw))
Boris BREZILLON61140672013-10-11 11:44:36 +0200429 continue;
430
Stephen Boydf5644f12016-06-01 14:31:22 -0700431 of_clk_add_hw_provider(periphclknp, of_clk_hw_simple_get, hw);
Boris BREZILLON61140672013-10-11 11:44:36 +0200432 }
433}
434
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200435static void __init of_at91rm9200_clk_periph_setup(struct device_node *np)
Boris BREZILLON61140672013-10-11 11:44:36 +0200436{
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200437 of_at91_clk_periph_setup(np, PERIPHERAL_AT91RM9200);
Boris BREZILLON61140672013-10-11 11:44:36 +0200438}
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200439CLK_OF_DECLARE(at91rm9200_clk_periph, "atmel,at91rm9200-clk-peripheral",
440 of_at91rm9200_clk_periph_setup);
Boris BREZILLON61140672013-10-11 11:44:36 +0200441
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200442static void __init of_at91sam9x5_clk_periph_setup(struct device_node *np)
Boris BREZILLON61140672013-10-11 11:44:36 +0200443{
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200444 of_at91_clk_periph_setup(np, PERIPHERAL_AT91SAM9X5);
Boris BREZILLON61140672013-10-11 11:44:36 +0200445}
Boris Brezillon1bdf0232014-09-07 08:14:29 +0200446CLK_OF_DECLARE(at91sam9x5_clk_periph, "atmel,at91sam9x5-clk-peripheral",
447 of_at91sam9x5_clk_periph_setup);
448