• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/mfd/syscon.h>
16 #include <linux/regmap.h>
17 
18 #include "pmc.h"
19 
20 DEFINE_SPINLOCK(pmc_pcr_lock);
21 
22 #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 
34 #define PERIPHERAL_MAX_SHIFT	3
35 
36 struct clk_peripheral {
37 	struct clk_hw hw;
38 	struct regmap *regmap;
39 	u32 id;
40 };
41 
42 #define to_clk_peripheral(hw) container_of(hw, struct clk_peripheral, hw)
43 
44 struct clk_sam9x5_peripheral {
45 	struct clk_hw hw;
46 	struct regmap *regmap;
47 	struct clk_range range;
48 	spinlock_t *lock;
49 	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 
clk_peripheral_enable(struct clk_hw * hw)57 static int clk_peripheral_enable(struct clk_hw *hw)
58 {
59 	struct clk_peripheral *periph = to_clk_peripheral(hw);
60 	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;
67 	regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id));
68 
69 	return 0;
70 }
71 
clk_peripheral_disable(struct clk_hw * hw)72 static void clk_peripheral_disable(struct clk_hw *hw)
73 {
74 	struct clk_peripheral *periph = to_clk_peripheral(hw);
75 	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;
82 	regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id));
83 }
84 
clk_peripheral_is_enabled(struct clk_hw * hw)85 static int clk_peripheral_is_enabled(struct clk_hw *hw)
86 {
87 	struct clk_peripheral *periph = to_clk_peripheral(hw);
88 	int offset = AT91_PMC_PCSR;
89 	unsigned int status;
90 	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;
96 	regmap_read(periph->regmap, offset, &status);
97 
98 	return status & PERIPHERAL_MASK(id) ? 1 : 0;
99 }
100 
101 static 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 
107 static struct clk_hw * __init
at91_clk_register_peripheral(struct regmap * regmap,const char * name,const char * parent_name,u32 id)108 at91_clk_register_peripheral(struct regmap *regmap, const char *name,
109 			     const char *parent_name, u32 id)
110 {
111 	struct clk_peripheral *periph;
112 	struct clk_init_data init;
113 	struct clk_hw *hw;
114 	int ret;
115 
116 	if (!name || !parent_name || id > PERIPHERAL_ID_MAX)
117 		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;
131 	periph->regmap = regmap;
132 
133 	hw = &periph->hw;
134 	ret = clk_hw_register(NULL, &periph->hw);
135 	if (ret) {
136 		kfree(periph);
137 		hw = ERR_PTR(ret);
138 	}
139 
140 	return hw;
141 }
142 
clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral * periph)143 static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
144 {
145 	struct clk_hw *parent;
146 	unsigned long parent_rate;
147 	int shift = 0;
148 
149 	if (!periph->auto_div)
150 		return;
151 
152 	if (periph->range.max) {
153 		parent = clk_hw_get_parent_by_index(&periph->hw, 0);
154 		parent_rate = clk_hw_get_rate(parent);
155 		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 
clk_sam9x5_peripheral_enable(struct clk_hw * hw)168 static int clk_sam9x5_peripheral_enable(struct clk_hw *hw)
169 {
170 	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
171 	unsigned long flags;
172 
173 	if (periph->id < PERIPHERAL_ID_MIN)
174 		return 0;
175 
176 	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 
187 	return 0;
188 }
189 
clk_sam9x5_peripheral_disable(struct clk_hw * hw)190 static void clk_sam9x5_peripheral_disable(struct clk_hw *hw)
191 {
192 	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
193 	unsigned long flags;
194 
195 	if (periph->id < PERIPHERAL_ID_MIN)
196 		return;
197 
198 	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);
205 }
206 
clk_sam9x5_peripheral_is_enabled(struct clk_hw * hw)207 static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw)
208 {
209 	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
210 	unsigned long flags;
211 	unsigned int status;
212 
213 	if (periph->id < PERIPHERAL_ID_MIN)
214 		return 1;
215 
216 	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);
221 
222 	return status & AT91_PMC_PCR_EN ? 1 : 0;
223 }
224 
225 static unsigned long
clk_sam9x5_peripheral_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)226 clk_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);
230 	unsigned long flags;
231 	unsigned int status;
232 
233 	if (periph->id < PERIPHERAL_ID_MIN)
234 		return parent_rate;
235 
236 	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);
241 
242 	if (status & AT91_PMC_PCR_EN) {
243 		periph->div = PERIPHERAL_RSHIFT(status);
244 		periph->auto_div = false;
245 	} else {
246 		clk_sam9x5_peripheral_autodiv(periph);
247 	}
248 
249 	return parent_rate >> periph->div;
250 }
251 
clk_sam9x5_peripheral_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * parent_rate)252 static 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) {
267 		for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
268 			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;
279 	for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
280 		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 
clk_sam9x5_peripheral_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)298 static 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 
314 	for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
315 		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 
325 static 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 
334 static struct clk_hw * __init
at91_clk_register_sam9x5_peripheral(struct regmap * regmap,spinlock_t * lock,const char * name,const char * parent_name,u32 id,const struct clk_range * range)335 at91_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)
338 {
339 	struct clk_sam9x5_peripheral *periph;
340 	struct clk_init_data init;
341 	struct clk_hw *hw;
342 	int ret;
343 
344 	if (!name || !parent_name)
345 		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;
360 	periph->regmap = regmap;
361 	periph->lock = lock;
362 	periph->auto_div = true;
363 	periph->range = *range;
364 
365 	hw = &periph->hw;
366 	ret = clk_hw_register(NULL, &periph->hw);
367 	if (ret) {
368 		kfree(periph);
369 		hw = ERR_PTR(ret);
370 	} else
371 		clk_sam9x5_peripheral_autodiv(periph);
372 
373 	return hw;
374 }
375 
376 static void __init
of_at91_clk_periph_setup(struct device_node * np,u8 type)377 of_at91_clk_periph_setup(struct device_node *np, u8 type)
378 {
379 	int num;
380 	u32 id;
381 	struct clk_hw *hw;
382 	const char *parent_name;
383 	const char *name;
384 	struct device_node *periphclknp;
385 	struct regmap *regmap;
386 
387 	parent_name = of_clk_get_parent_name(np, 0);
388 	if (!parent_name)
389 		return;
390 
391 	num = of_get_child_count(np);
392 	if (!num || num > PERIPHERAL_MAX)
393 		return;
394 
395 	regmap = syscon_node_to_regmap(of_get_parent(np));
396 	if (IS_ERR(regmap))
397 		return;
398 
399 	for_each_child_of_node(np, periphclknp) {
400 		if (of_property_read_u32(periphclknp, "reg", &id))
401 			continue;
402 
403 		if (id >= PERIPHERAL_MAX)
404 			continue;
405 
406 		if (of_property_read_string(np, "clock-output-names", &name))
407 			name = periphclknp->name;
408 
409 		if (type == PERIPHERAL_AT91RM9200) {
410 			hw = at91_clk_register_peripheral(regmap, name,
411 							   parent_name, id);
412 		} else {
413 			struct clk_range range = CLK_RANGE(0, 0);
414 
415 			of_at91_get_clk_range(periphclknp,
416 					      "atmel,clk-output-range",
417 					      &range);
418 
419 			hw = at91_clk_register_sam9x5_peripheral(regmap,
420 								  &pmc_pcr_lock,
421 								  name,
422 								  parent_name,
423 								  id, &range);
424 		}
425 
426 		if (IS_ERR(hw))
427 			continue;
428 
429 		of_clk_add_hw_provider(periphclknp, of_clk_hw_simple_get, hw);
430 	}
431 }
432 
of_at91rm9200_clk_periph_setup(struct device_node * np)433 static void __init of_at91rm9200_clk_periph_setup(struct device_node *np)
434 {
435 	of_at91_clk_periph_setup(np, PERIPHERAL_AT91RM9200);
436 }
437 CLK_OF_DECLARE(at91rm9200_clk_periph, "atmel,at91rm9200-clk-peripheral",
438 	       of_at91rm9200_clk_periph_setup);
439 
of_at91sam9x5_clk_periph_setup(struct device_node * np)440 static void __init of_at91sam9x5_clk_periph_setup(struct device_node *np)
441 {
442 	of_at91_clk_periph_setup(np, PERIPHERAL_AT91SAM9X5);
443 }
444 CLK_OF_DECLARE(at91sam9x5_clk_periph, "atmel,at91sam9x5-clk-peripheral",
445 	       of_at91sam9x5_clk_periph_setup);
446 
447