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 4
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)
138 unsigned long parent_rate;
141 if (!periph->auto_div)
144 if (periph->range.max) {
145 parent = clk_get_parent_by_index(periph->hw.clk, 0);
146 parent_rate = __clk_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;
165 if (periph->id < PERIPHERAL_ID_MIN)
168 pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID) |
170 AT91_PMC_PCR_DIV(periph->div) |
175 static void clk_sam9x5_peripheral_disable(struct clk_hw *hw)
177 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
178 struct at91_pmc *pmc = periph->pmc;
180 if (periph->id < PERIPHERAL_ID_MIN)
183 pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID) |
187 static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw)
189 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
190 struct at91_pmc *pmc = periph->pmc;
193 if (periph->id < PERIPHERAL_ID_MIN)
197 pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID));
198 ret = !!(pmc_read(pmc, AT91_PMC_PCR) & AT91_PMC_PCR_EN);
205 clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw,
206 unsigned long parent_rate)
208 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
209 struct at91_pmc *pmc = periph->pmc;
212 if (periph->id < PERIPHERAL_ID_MIN)
216 pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID));
217 tmp = pmc_read(pmc, AT91_PMC_PCR);
220 if (tmp & AT91_PMC_PCR_EN) {
221 periph->div = PERIPHERAL_RSHIFT(tmp);
222 periph->auto_div = false;
224 clk_sam9x5_peripheral_autodiv(periph);
227 return parent_rate >> periph->div;
230 static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw,
232 unsigned long *parent_rate)
235 unsigned long best_rate;
236 unsigned long best_diff;
237 unsigned long cur_rate = *parent_rate;
238 unsigned long cur_diff;
239 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
241 if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max)
244 if (periph->range.max) {
245 for (; shift < PERIPHERAL_MAX_SHIFT; shift++) {
246 cur_rate = *parent_rate >> shift;
247 if (cur_rate <= periph->range.max)
252 if (rate >= cur_rate)
255 best_diff = cur_rate - rate;
256 best_rate = cur_rate;
257 for (; shift < PERIPHERAL_MAX_SHIFT; shift++) {
258 cur_rate = *parent_rate >> shift;
260 cur_diff = rate - cur_rate;
262 cur_diff = cur_rate - rate;
264 if (cur_diff < best_diff) {
265 best_diff = cur_diff;
266 best_rate = cur_rate;
269 if (!best_diff || cur_rate < rate)
276 static int clk_sam9x5_peripheral_set_rate(struct clk_hw *hw,
278 unsigned long parent_rate)
281 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
282 if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) {
283 if (parent_rate == rate)
289 if (periph->range.max && rate > periph->range.max)
292 for (shift = 0; shift < PERIPHERAL_MAX_SHIFT; shift++) {
293 if (parent_rate >> shift == rate) {
294 periph->auto_div = false;
303 static const struct clk_ops sam9x5_peripheral_ops = {
304 .enable = clk_sam9x5_peripheral_enable,
305 .disable = clk_sam9x5_peripheral_disable,
306 .is_enabled = clk_sam9x5_peripheral_is_enabled,
307 .recalc_rate = clk_sam9x5_peripheral_recalc_rate,
308 .round_rate = clk_sam9x5_peripheral_round_rate,
309 .set_rate = clk_sam9x5_peripheral_set_rate,
312 static struct clk * __init
313 at91_clk_register_sam9x5_peripheral(struct at91_pmc *pmc, const char *name,
314 const char *parent_name, u32 id,
315 const struct clk_range *range)
317 struct clk_sam9x5_peripheral *periph;
318 struct clk *clk = NULL;
319 struct clk_init_data init;
321 if (!pmc || !name || !parent_name)
322 return ERR_PTR(-EINVAL);
324 periph = kzalloc(sizeof(*periph), GFP_KERNEL);
326 return ERR_PTR(-ENOMEM);
329 init.ops = &sam9x5_peripheral_ops;
330 init.parent_names = (parent_name ? &parent_name : NULL);
331 init.num_parents = (parent_name ? 1 : 0);
335 periph->hw.init = &init;
338 periph->auto_div = true;
339 periph->range = *range;
341 clk = clk_register(NULL, &periph->hw);
345 clk_sam9x5_peripheral_autodiv(periph);
351 of_at91_clk_periph_setup(struct device_node *np, struct at91_pmc *pmc, u8 type)
356 const char *parent_name;
358 struct device_node *periphclknp;
360 parent_name = of_clk_get_parent_name(np, 0);
364 num = of_get_child_count(np);
365 if (!num || num > PERIPHERAL_MAX)
368 for_each_child_of_node(np, periphclknp) {
369 if (of_property_read_u32(periphclknp, "reg", &id))
372 if (id >= PERIPHERAL_MAX)
375 if (of_property_read_string(np, "clock-output-names", &name))
376 name = periphclknp->name;
378 if (type == PERIPHERAL_AT91RM9200) {
379 clk = at91_clk_register_peripheral(pmc, name,
382 struct clk_range range = CLK_RANGE(0, 0);
384 of_at91_get_clk_range(periphclknp,
385 "atmel,clk-output-range",
388 clk = at91_clk_register_sam9x5_peripheral(pmc, name,
396 of_clk_add_provider(periphclknp, of_clk_src_simple_get, clk);
400 void __init of_at91rm9200_clk_periph_setup(struct device_node *np,
401 struct at91_pmc *pmc)
403 of_at91_clk_periph_setup(np, pmc, PERIPHERAL_AT91RM9200);
406 void __init of_at91sam9x5_clk_periph_setup(struct device_node *np,
407 struct at91_pmc *pmc)
409 of_at91_clk_periph_setup(np, pmc, PERIPHERAL_AT91SAM9X5);