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