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