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 
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 
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 
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 * __init
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 *clk = NULL;
113 	struct clk_init_data init;
114 
115 	if (!name || !parent_name || id > PERIPHERAL_ID_MAX)
116 		return ERR_PTR(-EINVAL);
117 
118 	periph = kzalloc(sizeof(*periph), GFP_KERNEL);
119 	if (!periph)
120 		return ERR_PTR(-ENOMEM);
121 
122 	init.name = name;
123 	init.ops = &peripheral_ops;
124 	init.parent_names = (parent_name ? &parent_name : NULL);
125 	init.num_parents = (parent_name ? 1 : 0);
126 	init.flags = 0;
127 
128 	periph->id = id;
129 	periph->hw.init = &init;
130 	periph->regmap = regmap;
131 
132 	clk = clk_register(NULL, &periph->hw);
133 	if (IS_ERR(clk))
134 		kfree(periph);
135 
136 	return clk;
137 }
138 
139 static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
140 {
141 	struct clk_hw *parent;
142 	unsigned long parent_rate;
143 	int shift = 0;
144 
145 	if (!periph->auto_div)
146 		return;
147 
148 	if (periph->range.max) {
149 		parent = clk_hw_get_parent_by_index(&periph->hw, 0);
150 		parent_rate = clk_hw_get_rate(parent);
151 		if (!parent_rate)
152 			return;
153 
154 		for (; shift < PERIPHERAL_MAX_SHIFT; shift++) {
155 			if (parent_rate >> shift <= periph->range.max)
156 				break;
157 		}
158 	}
159 
160 	periph->auto_div = false;
161 	periph->div = shift;
162 }
163 
164 static int clk_sam9x5_peripheral_enable(struct clk_hw *hw)
165 {
166 	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
167 	unsigned long flags;
168 
169 	if (periph->id < PERIPHERAL_ID_MIN)
170 		return 0;
171 
172 	spin_lock_irqsave(periph->lock, flags);
173 	regmap_write(periph->regmap, AT91_PMC_PCR,
174 		     (periph->id & AT91_PMC_PCR_PID_MASK));
175 	regmap_update_bits(periph->regmap, AT91_PMC_PCR,
176 			   AT91_PMC_PCR_DIV_MASK | AT91_PMC_PCR_CMD |
177 			   AT91_PMC_PCR_EN,
178 			   AT91_PMC_PCR_DIV(periph->div) |
179 			   AT91_PMC_PCR_CMD |
180 			   AT91_PMC_PCR_EN);
181 	spin_unlock_irqrestore(periph->lock, flags);
182 
183 	return 0;
184 }
185 
186 static void clk_sam9x5_peripheral_disable(struct clk_hw *hw)
187 {
188 	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
189 	unsigned long flags;
190 
191 	if (periph->id < PERIPHERAL_ID_MIN)
192 		return;
193 
194 	spin_lock_irqsave(periph->lock, flags);
195 	regmap_write(periph->regmap, AT91_PMC_PCR,
196 		     (periph->id & AT91_PMC_PCR_PID_MASK));
197 	regmap_update_bits(periph->regmap, AT91_PMC_PCR,
198 			   AT91_PMC_PCR_EN | AT91_PMC_PCR_CMD,
199 			   AT91_PMC_PCR_CMD);
200 	spin_unlock_irqrestore(periph->lock, flags);
201 }
202 
203 static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw)
204 {
205 	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
206 	unsigned long flags;
207 	unsigned int status;
208 
209 	if (periph->id < PERIPHERAL_ID_MIN)
210 		return 1;
211 
212 	spin_lock_irqsave(periph->lock, flags);
213 	regmap_write(periph->regmap, AT91_PMC_PCR,
214 		     (periph->id & AT91_PMC_PCR_PID_MASK));
215 	regmap_read(periph->regmap, AT91_PMC_PCR, &status);
216 	spin_unlock_irqrestore(periph->lock, flags);
217 
218 	return status & AT91_PMC_PCR_EN ? 1 : 0;
219 }
220 
221 static unsigned long
222 clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw,
223 				  unsigned long parent_rate)
224 {
225 	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
226 	unsigned long flags;
227 	unsigned int status;
228 
229 	if (periph->id < PERIPHERAL_ID_MIN)
230 		return parent_rate;
231 
232 	spin_lock_irqsave(periph->lock, flags);
233 	regmap_write(periph->regmap, AT91_PMC_PCR,
234 		     (periph->id & AT91_PMC_PCR_PID_MASK));
235 	regmap_read(periph->regmap, AT91_PMC_PCR, &status);
236 	spin_unlock_irqrestore(periph->lock, flags);
237 
238 	if (status & AT91_PMC_PCR_EN) {
239 		periph->div = PERIPHERAL_RSHIFT(status);
240 		periph->auto_div = false;
241 	} else {
242 		clk_sam9x5_peripheral_autodiv(periph);
243 	}
244 
245 	return parent_rate >> periph->div;
246 }
247 
248 static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw,
249 					     unsigned long rate,
250 					     unsigned long *parent_rate)
251 {
252 	int shift = 0;
253 	unsigned long best_rate;
254 	unsigned long best_diff;
255 	unsigned long cur_rate = *parent_rate;
256 	unsigned long cur_diff;
257 	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
258 
259 	if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max)
260 		return *parent_rate;
261 
262 	if (periph->range.max) {
263 		for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
264 			cur_rate = *parent_rate >> shift;
265 			if (cur_rate <= periph->range.max)
266 				break;
267 		}
268 	}
269 
270 	if (rate >= cur_rate)
271 		return cur_rate;
272 
273 	best_diff = cur_rate - rate;
274 	best_rate = cur_rate;
275 	for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
276 		cur_rate = *parent_rate >> shift;
277 		if (cur_rate < rate)
278 			cur_diff = rate - cur_rate;
279 		else
280 			cur_diff = cur_rate - rate;
281 
282 		if (cur_diff < best_diff) {
283 			best_diff = cur_diff;
284 			best_rate = cur_rate;
285 		}
286 
287 		if (!best_diff || cur_rate < rate)
288 			break;
289 	}
290 
291 	return best_rate;
292 }
293 
294 static int clk_sam9x5_peripheral_set_rate(struct clk_hw *hw,
295 					  unsigned long rate,
296 					  unsigned long parent_rate)
297 {
298 	int shift;
299 	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
300 	if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) {
301 		if (parent_rate == rate)
302 			return 0;
303 		else
304 			return -EINVAL;
305 	}
306 
307 	if (periph->range.max && rate > periph->range.max)
308 		return -EINVAL;
309 
310 	for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
311 		if (parent_rate >> shift == rate) {
312 			periph->auto_div = false;
313 			periph->div = shift;
314 			return 0;
315 		}
316 	}
317 
318 	return -EINVAL;
319 }
320 
321 static const struct clk_ops sam9x5_peripheral_ops = {
322 	.enable = clk_sam9x5_peripheral_enable,
323 	.disable = clk_sam9x5_peripheral_disable,
324 	.is_enabled = clk_sam9x5_peripheral_is_enabled,
325 	.recalc_rate = clk_sam9x5_peripheral_recalc_rate,
326 	.round_rate = clk_sam9x5_peripheral_round_rate,
327 	.set_rate = clk_sam9x5_peripheral_set_rate,
328 };
329 
330 static struct clk * __init
331 at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
332 				    const char *name, const char *parent_name,
333 				    u32 id, const struct clk_range *range)
334 {
335 	struct clk_sam9x5_peripheral *periph;
336 	struct clk *clk = NULL;
337 	struct clk_init_data init;
338 
339 	if (!name || !parent_name)
340 		return ERR_PTR(-EINVAL);
341 
342 	periph = kzalloc(sizeof(*periph), GFP_KERNEL);
343 	if (!periph)
344 		return ERR_PTR(-ENOMEM);
345 
346 	init.name = name;
347 	init.ops = &sam9x5_peripheral_ops;
348 	init.parent_names = (parent_name ? &parent_name : NULL);
349 	init.num_parents = (parent_name ? 1 : 0);
350 	init.flags = 0;
351 
352 	periph->id = id;
353 	periph->hw.init = &init;
354 	periph->div = 0;
355 	periph->regmap = regmap;
356 	periph->lock = lock;
357 	periph->auto_div = true;
358 	periph->range = *range;
359 
360 	clk = clk_register(NULL, &periph->hw);
361 	if (IS_ERR(clk))
362 		kfree(periph);
363 	else
364 		clk_sam9x5_peripheral_autodiv(periph);
365 
366 	return clk;
367 }
368 
369 static void __init
370 of_at91_clk_periph_setup(struct device_node *np, u8 type)
371 {
372 	int num;
373 	u32 id;
374 	struct clk *clk;
375 	const char *parent_name;
376 	const char *name;
377 	struct device_node *periphclknp;
378 	struct regmap *regmap;
379 
380 	parent_name = of_clk_get_parent_name(np, 0);
381 	if (!parent_name)
382 		return;
383 
384 	num = of_get_child_count(np);
385 	if (!num || num > PERIPHERAL_MAX)
386 		return;
387 
388 	regmap = syscon_node_to_regmap(of_get_parent(np));
389 	if (IS_ERR(regmap))
390 		return;
391 
392 	for_each_child_of_node(np, periphclknp) {
393 		if (of_property_read_u32(periphclknp, "reg", &id))
394 			continue;
395 
396 		if (id >= PERIPHERAL_MAX)
397 			continue;
398 
399 		if (of_property_read_string(np, "clock-output-names", &name))
400 			name = periphclknp->name;
401 
402 		if (type == PERIPHERAL_AT91RM9200) {
403 			clk = at91_clk_register_peripheral(regmap, name,
404 							   parent_name, id);
405 		} else {
406 			struct clk_range range = CLK_RANGE(0, 0);
407 
408 			of_at91_get_clk_range(periphclknp,
409 					      "atmel,clk-output-range",
410 					      &range);
411 
412 			clk = at91_clk_register_sam9x5_peripheral(regmap,
413 								  &pmc_pcr_lock,
414 								  name,
415 								  parent_name,
416 								  id, &range);
417 		}
418 
419 		if (IS_ERR(clk))
420 			continue;
421 
422 		of_clk_add_provider(periphclknp, of_clk_src_simple_get, clk);
423 	}
424 }
425 
426 static void __init of_at91rm9200_clk_periph_setup(struct device_node *np)
427 {
428 	of_at91_clk_periph_setup(np, PERIPHERAL_AT91RM9200);
429 }
430 CLK_OF_DECLARE(at91rm9200_clk_periph, "atmel,at91rm9200-clk-peripheral",
431 	       of_at91rm9200_clk_periph_setup);
432 
433 static void __init of_at91sam9x5_clk_periph_setup(struct device_node *np)
434 {
435 	of_at91_clk_periph_setup(np, PERIPHERAL_AT91SAM9X5);
436 }
437 CLK_OF_DECLARE(at91sam9x5_clk_periph, "atmel,at91sam9x5-clk-peripheral",
438 	       of_at91sam9x5_clk_periph_setup);
439 
440