2 * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
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.
11 #include <linux/clk-provider.h>
12 #include <linux/clkdev.h>
13 #include <linux/clk/at91_pmc.h>
15 #include <linux/of_address.h>
20 #define PERIPHERAL_MAX 64
22 #define PERIPHERAL_AT91RM9200 0
23 #define PERIPHERAL_AT91SAM9X5 1
25 #define PERIPHERAL_ID_MIN 2
26 #define PERIPHERAL_ID_MAX 31
27 #define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX))
29 #define PERIPHERAL_RSHIFT_MASK 0x3
30 #define PERIPHERAL_RSHIFT(val) (((val) >> 16) & PERIPHERAL_RSHIFT_MASK)
32 #define PERIPHERAL_MAX_SHIFT 3
34 struct clk_peripheral
{
40 #define to_clk_peripheral(hw) container_of(hw, struct clk_peripheral, hw)
42 struct clk_sam9x5_peripheral
{
45 struct clk_range range
;
51 #define to_clk_sam9x5_peripheral(hw) \
52 container_of(hw, struct clk_sam9x5_peripheral, hw)
54 static int clk_peripheral_enable(struct clk_hw
*hw
)
56 struct clk_peripheral
*periph
= to_clk_peripheral(hw
);
57 struct at91_pmc
*pmc
= periph
->pmc
;
58 int offset
= AT91_PMC_PCER
;
61 if (id
< PERIPHERAL_ID_MIN
)
63 if (id
> PERIPHERAL_ID_MAX
)
64 offset
= AT91_PMC_PCER1
;
65 pmc_write(pmc
, offset
, PERIPHERAL_MASK(id
));
69 static void clk_peripheral_disable(struct clk_hw
*hw
)
71 struct clk_peripheral
*periph
= to_clk_peripheral(hw
);
72 struct at91_pmc
*pmc
= periph
->pmc
;
73 int offset
= AT91_PMC_PCDR
;
76 if (id
< PERIPHERAL_ID_MIN
)
78 if (id
> PERIPHERAL_ID_MAX
)
79 offset
= AT91_PMC_PCDR1
;
80 pmc_write(pmc
, offset
, PERIPHERAL_MASK(id
));
83 static int clk_peripheral_is_enabled(struct clk_hw
*hw
)
85 struct clk_peripheral
*periph
= to_clk_peripheral(hw
);
86 struct at91_pmc
*pmc
= periph
->pmc
;
87 int offset
= AT91_PMC_PCSR
;
90 if (id
< PERIPHERAL_ID_MIN
)
92 if (id
> PERIPHERAL_ID_MAX
)
93 offset
= AT91_PMC_PCSR1
;
94 return !!(pmc_read(pmc
, offset
) & PERIPHERAL_MASK(id
));
97 static const struct clk_ops peripheral_ops
= {
98 .enable
= clk_peripheral_enable
,
99 .disable
= clk_peripheral_disable
,
100 .is_enabled
= clk_peripheral_is_enabled
,
103 static struct clk
* __init
104 at91_clk_register_peripheral(struct at91_pmc
*pmc
, const char *name
,
105 const char *parent_name
, u32 id
)
107 struct clk_peripheral
*periph
;
108 struct clk
*clk
= NULL
;
109 struct clk_init_data init
;
111 if (!pmc
|| !name
|| !parent_name
|| id
> PERIPHERAL_ID_MAX
)
112 return ERR_PTR(-EINVAL
);
114 periph
= kzalloc(sizeof(*periph
), GFP_KERNEL
);
116 return ERR_PTR(-ENOMEM
);
119 init
.ops
= &peripheral_ops
;
120 init
.parent_names
= (parent_name
? &parent_name
: NULL
);
121 init
.num_parents
= (parent_name
? 1 : 0);
125 periph
->hw
.init
= &init
;
128 clk
= clk_register(NULL
, &periph
->hw
);
135 static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral
*periph
)
137 struct clk_hw
*parent
;
138 unsigned long parent_rate
;
141 if (!periph
->auto_div
)
144 if (periph
->range
.max
) {
145 parent
= clk_hw_get_parent_by_index(&periph
->hw
, 0);
146 parent_rate
= clk_hw_get_rate(parent
);
150 for (; shift
< PERIPHERAL_MAX_SHIFT
; shift
++) {
151 if (parent_rate
>> shift
<= periph
->range
.max
)
156 periph
->auto_div
= false;
160 static int clk_sam9x5_peripheral_enable(struct clk_hw
*hw
)
162 struct clk_sam9x5_peripheral
*periph
= to_clk_sam9x5_peripheral(hw
);
163 struct at91_pmc
*pmc
= periph
->pmc
;
166 if (periph
->id
< PERIPHERAL_ID_MIN
)
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
)
179 static void clk_sam9x5_peripheral_disable(struct clk_hw
*hw
)
181 struct clk_sam9x5_peripheral
*periph
= to_clk_sam9x5_peripheral(hw
);
182 struct at91_pmc
*pmc
= periph
->pmc
;
185 if (periph
->id
< PERIPHERAL_ID_MIN
)
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
);
195 static int clk_sam9x5_peripheral_is_enabled(struct clk_hw
*hw
)
197 struct clk_sam9x5_peripheral
*periph
= to_clk_sam9x5_peripheral(hw
);
198 struct at91_pmc
*pmc
= periph
->pmc
;
201 if (periph
->id
< PERIPHERAL_ID_MIN
)
205 pmc_write(pmc
, AT91_PMC_PCR
, (periph
->id
& AT91_PMC_PCR_PID_MASK
));
206 ret
= !!(pmc_read(pmc
, AT91_PMC_PCR
) & AT91_PMC_PCR_EN
);
213 clk_sam9x5_peripheral_recalc_rate(struct clk_hw
*hw
,
214 unsigned long parent_rate
)
216 struct clk_sam9x5_peripheral
*periph
= to_clk_sam9x5_peripheral(hw
);
217 struct at91_pmc
*pmc
= periph
->pmc
;
220 if (periph
->id
< PERIPHERAL_ID_MIN
)
224 pmc_write(pmc
, AT91_PMC_PCR
, (periph
->id
& AT91_PMC_PCR_PID_MASK
));
225 tmp
= pmc_read(pmc
, AT91_PMC_PCR
);
228 if (tmp
& AT91_PMC_PCR_EN
) {
229 periph
->div
= PERIPHERAL_RSHIFT(tmp
);
230 periph
->auto_div
= false;
232 clk_sam9x5_peripheral_autodiv(periph
);
235 return parent_rate
>> periph
->div
;
238 static long clk_sam9x5_peripheral_round_rate(struct clk_hw
*hw
,
240 unsigned long *parent_rate
)
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
);
249 if (periph
->id
< PERIPHERAL_ID_MIN
|| !periph
->range
.max
)
252 if (periph
->range
.max
) {
253 for (; shift
<= PERIPHERAL_MAX_SHIFT
; shift
++) {
254 cur_rate
= *parent_rate
>> shift
;
255 if (cur_rate
<= periph
->range
.max
)
260 if (rate
>= cur_rate
)
263 best_diff
= cur_rate
- rate
;
264 best_rate
= cur_rate
;
265 for (; shift
<= PERIPHERAL_MAX_SHIFT
; shift
++) {
266 cur_rate
= *parent_rate
>> shift
;
268 cur_diff
= rate
- cur_rate
;
270 cur_diff
= cur_rate
- rate
;
272 if (cur_diff
< best_diff
) {
273 best_diff
= cur_diff
;
274 best_rate
= cur_rate
;
277 if (!best_diff
|| cur_rate
< rate
)
284 static int clk_sam9x5_peripheral_set_rate(struct clk_hw
*hw
,
286 unsigned long parent_rate
)
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
)
297 if (periph
->range
.max
&& rate
> periph
->range
.max
)
300 for (shift
= 0; shift
<= PERIPHERAL_MAX_SHIFT
; shift
++) {
301 if (parent_rate
>> shift
== rate
) {
302 periph
->auto_div
= false;
311 static 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
,
320 static struct clk
* __init
321 at91_clk_register_sam9x5_peripheral(struct at91_pmc
*pmc
, const char *name
,
322 const char *parent_name
, u32 id
,
323 const struct clk_range
*range
)
325 struct clk_sam9x5_peripheral
*periph
;
326 struct clk
*clk
= NULL
;
327 struct clk_init_data init
;
329 if (!pmc
|| !name
|| !parent_name
)
330 return ERR_PTR(-EINVAL
);
332 periph
= kzalloc(sizeof(*periph
), GFP_KERNEL
);
334 return ERR_PTR(-ENOMEM
);
337 init
.ops
= &sam9x5_peripheral_ops
;
338 init
.parent_names
= (parent_name
? &parent_name
: NULL
);
339 init
.num_parents
= (parent_name
? 1 : 0);
343 periph
->hw
.init
= &init
;
346 periph
->auto_div
= true;
347 periph
->range
= *range
;
349 clk
= clk_register(NULL
, &periph
->hw
);
353 clk_sam9x5_peripheral_autodiv(periph
);
359 of_at91_clk_periph_setup(struct device_node
*np
, struct at91_pmc
*pmc
, u8 type
)
364 const char *parent_name
;
366 struct device_node
*periphclknp
;
368 parent_name
= of_clk_get_parent_name(np
, 0);
372 num
= of_get_child_count(np
);
373 if (!num
|| num
> PERIPHERAL_MAX
)
376 for_each_child_of_node(np
, periphclknp
) {
377 if (of_property_read_u32(periphclknp
, "reg", &id
))
380 if (id
>= PERIPHERAL_MAX
)
383 if (of_property_read_string(np
, "clock-output-names", &name
))
384 name
= periphclknp
->name
;
386 if (type
== PERIPHERAL_AT91RM9200
) {
387 clk
= at91_clk_register_peripheral(pmc
, name
,
390 struct clk_range range
= CLK_RANGE(0, 0);
392 of_at91_get_clk_range(periphclknp
,
393 "atmel,clk-output-range",
396 clk
= at91_clk_register_sam9x5_peripheral(pmc
, name
,
404 of_clk_add_provider(periphclknp
, of_clk_src_simple_get
, clk
);
408 void __init
of_at91rm9200_clk_periph_setup(struct device_node
*np
,
409 struct at91_pmc
*pmc
)
411 of_at91_clk_periph_setup(np
, pmc
, PERIPHERAL_AT91RM9200
);
414 void __init
of_at91sam9x5_clk_periph_setup(struct device_node
*np
,
415 struct at91_pmc
*pmc
)
417 of_at91_clk_periph_setup(np
, pmc
, PERIPHERAL_AT91SAM9X5
);