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/bitops.h>
12 #include <linux/clk-provider.h>
13 #include <linux/clkdev.h>
14 #include <linux/clk/at91_pmc.h>
15 #include <linux/of.h>
16 #include <linux/mfd/syscon.h>
17 #include <linux/regmap.h>
18 
19 #include "pmc.h"
20 
21 DEFINE_SPINLOCK(pmc_pcr_lock);
22 
23 #define PERIPHERAL_ID_MIN	2
24 #define PERIPHERAL_ID_MAX	31
25 #define PERIPHERAL_MASK(id)	(1 << ((id) & PERIPHERAL_ID_MAX))
26 
27 #define PERIPHERAL_MAX_SHIFT	3
28 
29 struct clk_peripheral {
30 	struct clk_hw hw;
31 	struct regmap *regmap;
32 	u32 id;
33 };
34 
35 #define to_clk_peripheral(hw) container_of(hw, struct clk_peripheral, hw)
36 
37 struct clk_sam9x5_peripheral {
38 	struct clk_hw hw;
39 	struct regmap *regmap;
40 	struct clk_range range;
41 	spinlock_t *lock;
42 	u32 id;
43 	u32 div;
44 	const struct clk_pcr_layout *layout;
45 	bool auto_div;
46 };
47 
48 #define to_clk_sam9x5_peripheral(hw) \
49 	container_of(hw, struct clk_sam9x5_peripheral, hw)
50 
51 static int clk_peripheral_enable(struct clk_hw *hw)
52 {
53 	struct clk_peripheral *periph = to_clk_peripheral(hw);
54 	int offset = AT91_PMC_PCER;
55 	u32 id = periph->id;
56 
57 	if (id < PERIPHERAL_ID_MIN)
58 		return 0;
59 	if (id > PERIPHERAL_ID_MAX)
60 		offset = AT91_PMC_PCER1;
61 	regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id));
62 
63 	return 0;
64 }
65 
66 static void clk_peripheral_disable(struct clk_hw *hw)
67 {
68 	struct clk_peripheral *periph = to_clk_peripheral(hw);
69 	int offset = AT91_PMC_PCDR;
70 	u32 id = periph->id;
71 
72 	if (id < PERIPHERAL_ID_MIN)
73 		return;
74 	if (id > PERIPHERAL_ID_MAX)
75 		offset = AT91_PMC_PCDR1;
76 	regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id));
77 }
78 
79 static int clk_peripheral_is_enabled(struct clk_hw *hw)
80 {
81 	struct clk_peripheral *periph = to_clk_peripheral(hw);
82 	int offset = AT91_PMC_PCSR;
83 	unsigned int status;
84 	u32 id = periph->id;
85 
86 	if (id < PERIPHERAL_ID_MIN)
87 		return 1;
88 	if (id > PERIPHERAL_ID_MAX)
89 		offset = AT91_PMC_PCSR1;
90 	regmap_read(periph->regmap, offset, &status);
91 
92 	return status & PERIPHERAL_MASK(id) ? 1 : 0;
93 }
94 
95 static const struct clk_ops peripheral_ops = {
96 	.enable = clk_peripheral_enable,
97 	.disable = clk_peripheral_disable,
98 	.is_enabled = clk_peripheral_is_enabled,
99 };
100 
101 struct clk_hw * __init
102 at91_clk_register_peripheral(struct regmap *regmap, const char *name,
103 			     const char *parent_name, u32 id)
104 {
105 	struct clk_peripheral *periph;
106 	struct clk_init_data init;
107 	struct clk_hw *hw;
108 	int ret;
109 
110 	if (!name || !parent_name || id > PERIPHERAL_ID_MAX)
111 		return ERR_PTR(-EINVAL);
112 
113 	periph = kzalloc(sizeof(*periph), GFP_KERNEL);
114 	if (!periph)
115 		return ERR_PTR(-ENOMEM);
116 
117 	init.name = name;
118 	init.ops = &peripheral_ops;
119 	init.parent_names = (parent_name ? &parent_name : NULL);
120 	init.num_parents = (parent_name ? 1 : 0);
121 	init.flags = 0;
122 
123 	periph->id = id;
124 	periph->hw.init = &init;
125 	periph->regmap = regmap;
126 
127 	hw = &periph->hw;
128 	ret = clk_hw_register(NULL, &periph->hw);
129 	if (ret) {
130 		kfree(periph);
131 		hw = ERR_PTR(ret);
132 	}
133 
134 	return hw;
135 }
136 
137 static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
138 {
139 	struct clk_hw *parent;
140 	unsigned long parent_rate;
141 	int shift = 0;
142 
143 	if (!periph->auto_div)
144 		return;
145 
146 	if (periph->range.max) {
147 		parent = clk_hw_get_parent_by_index(&periph->hw, 0);
148 		parent_rate = clk_hw_get_rate(parent);
149 		if (!parent_rate)
150 			return;
151 
152 		for (; shift < PERIPHERAL_MAX_SHIFT; shift++) {
153 			if (parent_rate >> shift <= periph->range.max)
154 				break;
155 		}
156 	}
157 
158 	periph->auto_div = false;
159 	periph->div = shift;
160 }
161 
162 static int clk_sam9x5_peripheral_enable(struct clk_hw *hw)
163 {
164 	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
165 	unsigned long flags;
166 
167 	if (periph->id < PERIPHERAL_ID_MIN)
168 		return 0;
169 
170 	spin_lock_irqsave(periph->lock, flags);
171 	regmap_write(periph->regmap, periph->layout->offset,
172 		     (periph->id & periph->layout->pid_mask));
173 	regmap_update_bits(periph->regmap, periph->layout->offset,
174 			   periph->layout->div_mask | periph->layout->cmd |
175 			   AT91_PMC_PCR_EN,
176 			   field_prep(periph->layout->div_mask, periph->div) |
177 			   periph->layout->cmd |
178 			   AT91_PMC_PCR_EN);
179 	spin_unlock_irqrestore(periph->lock, flags);
180 
181 	return 0;
182 }
183 
184 static void clk_sam9x5_peripheral_disable(struct clk_hw *hw)
185 {
186 	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
187 	unsigned long flags;
188 
189 	if (periph->id < PERIPHERAL_ID_MIN)
190 		return;
191 
192 	spin_lock_irqsave(periph->lock, flags);
193 	regmap_write(periph->regmap, periph->layout->offset,
194 		     (periph->id & periph->layout->pid_mask));
195 	regmap_update_bits(periph->regmap, periph->layout->offset,
196 			   AT91_PMC_PCR_EN | periph->layout->cmd,
197 			   periph->layout->cmd);
198 	spin_unlock_irqrestore(periph->lock, flags);
199 }
200 
201 static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw)
202 {
203 	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
204 	unsigned long flags;
205 	unsigned int status;
206 
207 	if (periph->id < PERIPHERAL_ID_MIN)
208 		return 1;
209 
210 	spin_lock_irqsave(periph->lock, flags);
211 	regmap_write(periph->regmap, periph->layout->offset,
212 		     (periph->id & periph->layout->pid_mask));
213 	regmap_read(periph->regmap, periph->layout->offset, &status);
214 	spin_unlock_irqrestore(periph->lock, flags);
215 
216 	return status & AT91_PMC_PCR_EN ? 1 : 0;
217 }
218 
219 static unsigned long
220 clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw,
221 				  unsigned long parent_rate)
222 {
223 	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
224 	unsigned long flags;
225 	unsigned int status;
226 
227 	if (periph->id < PERIPHERAL_ID_MIN)
228 		return parent_rate;
229 
230 	spin_lock_irqsave(periph->lock, flags);
231 	regmap_write(periph->regmap, periph->layout->offset,
232 		     (periph->id & periph->layout->pid_mask));
233 	regmap_read(periph->regmap, periph->layout->offset, &status);
234 	spin_unlock_irqrestore(periph->lock, flags);
235 
236 	if (status & AT91_PMC_PCR_EN) {
237 		periph->div = field_get(periph->layout->div_mask, status);
238 		periph->auto_div = false;
239 	} else {
240 		clk_sam9x5_peripheral_autodiv(periph);
241 	}
242 
243 	return parent_rate >> periph->div;
244 }
245 
246 static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw,
247 					     unsigned long rate,
248 					     unsigned long *parent_rate)
249 {
250 	int shift = 0;
251 	unsigned long best_rate;
252 	unsigned long best_diff;
253 	unsigned long cur_rate = *parent_rate;
254 	unsigned long cur_diff;
255 	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
256 
257 	if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max)
258 		return *parent_rate;
259 
260 	if (periph->range.max) {
261 		for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
262 			cur_rate = *parent_rate >> shift;
263 			if (cur_rate <= periph->range.max)
264 				break;
265 		}
266 	}
267 
268 	if (rate >= cur_rate)
269 		return cur_rate;
270 
271 	best_diff = cur_rate - rate;
272 	best_rate = cur_rate;
273 	for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
274 		cur_rate = *parent_rate >> shift;
275 		if (cur_rate < rate)
276 			cur_diff = rate - cur_rate;
277 		else
278 			cur_diff = cur_rate - rate;
279 
280 		if (cur_diff < best_diff) {
281 			best_diff = cur_diff;
282 			best_rate = cur_rate;
283 		}
284 
285 		if (!best_diff || cur_rate < rate)
286 			break;
287 	}
288 
289 	return best_rate;
290 }
291 
292 static int clk_sam9x5_peripheral_set_rate(struct clk_hw *hw,
293 					  unsigned long rate,
294 					  unsigned long parent_rate)
295 {
296 	int shift;
297 	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
298 	if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) {
299 		if (parent_rate == rate)
300 			return 0;
301 		else
302 			return -EINVAL;
303 	}
304 
305 	if (periph->range.max && rate > periph->range.max)
306 		return -EINVAL;
307 
308 	for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
309 		if (parent_rate >> shift == rate) {
310 			periph->auto_div = false;
311 			periph->div = shift;
312 			return 0;
313 		}
314 	}
315 
316 	return -EINVAL;
317 }
318 
319 static const struct clk_ops sam9x5_peripheral_ops = {
320 	.enable = clk_sam9x5_peripheral_enable,
321 	.disable = clk_sam9x5_peripheral_disable,
322 	.is_enabled = clk_sam9x5_peripheral_is_enabled,
323 	.recalc_rate = clk_sam9x5_peripheral_recalc_rate,
324 	.round_rate = clk_sam9x5_peripheral_round_rate,
325 	.set_rate = clk_sam9x5_peripheral_set_rate,
326 };
327 
328 struct clk_hw * __init
329 at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
330 				    const struct clk_pcr_layout *layout,
331 				    const char *name, const char *parent_name,
332 				    u32 id, const struct clk_range *range)
333 {
334 	struct clk_sam9x5_peripheral *periph;
335 	struct clk_init_data init;
336 	struct clk_hw *hw;
337 	int ret;
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 	if (layout->div_mask)
358 		periph->auto_div = true;
359 	periph->layout = layout;
360 	periph->range = *range;
361 
362 	hw = &periph->hw;
363 	ret = clk_hw_register(NULL, &periph->hw);
364 	if (ret) {
365 		kfree(periph);
366 		hw = ERR_PTR(ret);
367 	} else {
368 		clk_sam9x5_peripheral_autodiv(periph);
369 		pmc_register_id(id);
370 	}
371 
372 	return hw;
373 }
374