This source file includes following definitions.
- clk_peripheral_enable
- clk_peripheral_disable
- clk_peripheral_is_enabled
- at91_clk_register_peripheral
- clk_sam9x5_peripheral_autodiv
- clk_sam9x5_peripheral_enable
- clk_sam9x5_peripheral_disable
- clk_sam9x5_peripheral_is_enabled
- clk_sam9x5_peripheral_recalc_rate
- clk_sam9x5_peripheral_round_rate
- clk_sam9x5_peripheral_set_rate
- at91_clk_register_sam9x5_peripheral
1
2
3
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 }