1 // SPDX-License-Identifier: GPL-2.0-or-later
6 #include <linux/bitops.h>
7 #include <linux/clk-provider.h>
8 #include <linux/clkdev.h>
9 #include <linux/clk/at91_pmc.h>
11 #include <linux/mfd/syscon.h>
12 #include <linux/regmap.h>
16 DEFINE_SPINLOCK(pmc_pcr_lock);
18 #define PERIPHERAL_ID_MIN 2
19 #define PERIPHERAL_ID_MAX 31
20 #define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX))
22 #define PERIPHERAL_MAX_SHIFT 3
24 struct clk_peripheral {
26 struct regmap *regmap;
30 #define to_clk_peripheral(hw) container_of(hw, struct clk_peripheral, hw)
32 struct clk_sam9x5_peripheral {
34 struct regmap *regmap;
35 struct clk_range range;
39 const struct clk_pcr_layout *layout;
44 #define to_clk_sam9x5_peripheral(hw) \
45 container_of(hw, struct clk_sam9x5_peripheral, hw)
47 static int clk_peripheral_enable(struct clk_hw *hw)
49 struct clk_peripheral *periph = to_clk_peripheral(hw);
50 int offset = AT91_PMC_PCER;
53 if (id < PERIPHERAL_ID_MIN)
55 if (id > PERIPHERAL_ID_MAX)
56 offset = AT91_PMC_PCER1;
57 regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id));
62 static void clk_peripheral_disable(struct clk_hw *hw)
64 struct clk_peripheral *periph = to_clk_peripheral(hw);
65 int offset = AT91_PMC_PCDR;
68 if (id < PERIPHERAL_ID_MIN)
70 if (id > PERIPHERAL_ID_MAX)
71 offset = AT91_PMC_PCDR1;
72 regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id));
75 static int clk_peripheral_is_enabled(struct clk_hw *hw)
77 struct clk_peripheral *periph = to_clk_peripheral(hw);
78 int offset = AT91_PMC_PCSR;
82 if (id < PERIPHERAL_ID_MIN)
84 if (id > PERIPHERAL_ID_MAX)
85 offset = AT91_PMC_PCSR1;
86 regmap_read(periph->regmap, offset, &status);
88 return status & PERIPHERAL_MASK(id) ? 1 : 0;
91 static const struct clk_ops peripheral_ops = {
92 .enable = clk_peripheral_enable,
93 .disable = clk_peripheral_disable,
94 .is_enabled = clk_peripheral_is_enabled,
97 struct clk_hw * __init
98 at91_clk_register_peripheral(struct regmap *regmap, const char *name,
99 const char *parent_name, u32 id)
101 struct clk_peripheral *periph;
102 struct clk_init_data init;
106 if (!name || !parent_name || id > PERIPHERAL_ID_MAX)
107 return ERR_PTR(-EINVAL);
109 periph = kzalloc(sizeof(*periph), GFP_KERNEL);
111 return ERR_PTR(-ENOMEM);
114 init.ops = &peripheral_ops;
115 init.parent_names = &parent_name;
116 init.num_parents = 1;
120 periph->hw.init = &init;
121 periph->regmap = regmap;
124 ret = clk_hw_register(NULL, &periph->hw);
133 static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
135 struct clk_hw *parent;
136 unsigned long parent_rate;
139 if (!periph->auto_div)
142 if (periph->range.max) {
143 parent = clk_hw_get_parent_by_index(&periph->hw, 0);
144 parent_rate = clk_hw_get_rate(parent);
148 for (; shift < PERIPHERAL_MAX_SHIFT; shift++) {
149 if (parent_rate >> shift <= periph->range.max)
154 periph->auto_div = false;
158 static int clk_sam9x5_peripheral_enable(struct clk_hw *hw)
160 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
163 if (periph->id < PERIPHERAL_ID_MIN)
166 spin_lock_irqsave(periph->lock, flags);
167 regmap_write(periph->regmap, periph->layout->offset,
168 (periph->id & periph->layout->pid_mask));
169 regmap_update_bits(periph->regmap, periph->layout->offset,
170 periph->layout->div_mask | periph->layout->cmd |
172 field_prep(periph->layout->div_mask, periph->div) |
173 periph->layout->cmd |
175 spin_unlock_irqrestore(periph->lock, flags);
180 static void clk_sam9x5_peripheral_disable(struct clk_hw *hw)
182 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
185 if (periph->id < PERIPHERAL_ID_MIN)
188 spin_lock_irqsave(periph->lock, flags);
189 regmap_write(periph->regmap, periph->layout->offset,
190 (periph->id & periph->layout->pid_mask));
191 regmap_update_bits(periph->regmap, periph->layout->offset,
192 AT91_PMC_PCR_EN | periph->layout->cmd,
193 periph->layout->cmd);
194 spin_unlock_irqrestore(periph->lock, flags);
197 static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw)
199 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
203 if (periph->id < PERIPHERAL_ID_MIN)
206 spin_lock_irqsave(periph->lock, flags);
207 regmap_write(periph->regmap, periph->layout->offset,
208 (periph->id & periph->layout->pid_mask));
209 regmap_read(periph->regmap, periph->layout->offset, &status);
210 spin_unlock_irqrestore(periph->lock, flags);
212 return !!(status & AT91_PMC_PCR_EN);
216 clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw,
217 unsigned long parent_rate)
219 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
223 if (periph->id < PERIPHERAL_ID_MIN)
226 spin_lock_irqsave(periph->lock, flags);
227 regmap_write(periph->regmap, periph->layout->offset,
228 (periph->id & periph->layout->pid_mask));
229 regmap_read(periph->regmap, periph->layout->offset, &status);
230 spin_unlock_irqrestore(periph->lock, flags);
232 if (status & AT91_PMC_PCR_EN) {
233 periph->div = field_get(periph->layout->div_mask, status);
234 periph->auto_div = false;
236 clk_sam9x5_peripheral_autodiv(periph);
239 return parent_rate >> periph->div;
242 static void clk_sam9x5_peripheral_best_diff(struct clk_rate_request *req,
243 struct clk_hw *parent,
244 unsigned long parent_rate,
245 u32 shift, long *best_diff,
248 unsigned long tmp_rate = parent_rate >> shift;
249 unsigned long tmp_diff = abs(req->rate - tmp_rate);
251 if (*best_diff < 0 || *best_diff >= tmp_diff) {
252 *best_rate = tmp_rate;
253 *best_diff = tmp_diff;
254 req->best_parent_rate = parent_rate;
255 req->best_parent_hw = parent;
259 static int clk_sam9x5_peripheral_determine_rate(struct clk_hw *hw,
260 struct clk_rate_request *req)
262 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
263 struct clk_hw *parent = clk_hw_get_parent(hw);
264 struct clk_rate_request req_parent = *req;
265 unsigned long parent_rate = clk_hw_get_rate(parent);
266 unsigned long tmp_rate;
267 long best_rate = LONG_MIN;
268 long best_diff = LONG_MIN;
271 if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max)
274 /* Fist step: check the available dividers. */
275 for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
276 tmp_rate = parent_rate >> shift;
278 if (periph->range.max && tmp_rate > periph->range.max)
281 clk_sam9x5_peripheral_best_diff(req, parent, parent_rate,
282 shift, &best_diff, &best_rate);
284 if (!best_diff || best_rate <= req->rate)
288 if (periph->chg_pid < 0)
291 /* Step two: try to request rate from parent. */
292 parent = clk_hw_get_parent_by_index(hw, periph->chg_pid);
296 for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
297 req_parent.rate = req->rate << shift;
299 if (__clk_determine_rate(parent, &req_parent))
302 clk_sam9x5_peripheral_best_diff(req, parent, req_parent.rate,
303 shift, &best_diff, &best_rate);
310 (periph->range.max && best_rate > periph->range.max))
313 pr_debug("PCK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
315 __clk_get_name((req->best_parent_hw)->clk),
316 req->best_parent_rate);
318 req->rate = best_rate;
323 static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw,
325 unsigned long *parent_rate)
328 unsigned long best_rate;
329 unsigned long best_diff;
330 unsigned long cur_rate = *parent_rate;
331 unsigned long cur_diff;
332 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
334 if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max)
337 if (periph->range.max) {
338 for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
339 cur_rate = *parent_rate >> shift;
340 if (cur_rate <= periph->range.max)
345 if (rate >= cur_rate)
348 best_diff = cur_rate - rate;
349 best_rate = cur_rate;
350 for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
351 cur_rate = *parent_rate >> shift;
353 cur_diff = rate - cur_rate;
355 cur_diff = cur_rate - rate;
357 if (cur_diff < best_diff) {
358 best_diff = cur_diff;
359 best_rate = cur_rate;
362 if (!best_diff || cur_rate < rate)
369 static int clk_sam9x5_peripheral_set_rate(struct clk_hw *hw,
371 unsigned long parent_rate)
374 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
375 if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) {
376 if (parent_rate == rate)
382 if (periph->range.max && rate > periph->range.max)
385 for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
386 if (parent_rate >> shift == rate) {
387 periph->auto_div = false;
396 static const struct clk_ops sam9x5_peripheral_ops = {
397 .enable = clk_sam9x5_peripheral_enable,
398 .disable = clk_sam9x5_peripheral_disable,
399 .is_enabled = clk_sam9x5_peripheral_is_enabled,
400 .recalc_rate = clk_sam9x5_peripheral_recalc_rate,
401 .round_rate = clk_sam9x5_peripheral_round_rate,
402 .set_rate = clk_sam9x5_peripheral_set_rate,
405 static const struct clk_ops sam9x5_peripheral_chg_ops = {
406 .enable = clk_sam9x5_peripheral_enable,
407 .disable = clk_sam9x5_peripheral_disable,
408 .is_enabled = clk_sam9x5_peripheral_is_enabled,
409 .recalc_rate = clk_sam9x5_peripheral_recalc_rate,
410 .determine_rate = clk_sam9x5_peripheral_determine_rate,
411 .set_rate = clk_sam9x5_peripheral_set_rate,
414 struct clk_hw * __init
415 at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
416 const struct clk_pcr_layout *layout,
417 const char *name, const char *parent_name,
418 u32 id, const struct clk_range *range,
421 struct clk_sam9x5_peripheral *periph;
422 struct clk_init_data init;
426 if (!name || !parent_name)
427 return ERR_PTR(-EINVAL);
429 periph = kzalloc(sizeof(*periph), GFP_KERNEL);
431 return ERR_PTR(-ENOMEM);
434 init.parent_names = &parent_name;
435 init.num_parents = 1;
438 init.ops = &sam9x5_peripheral_ops;
440 init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
442 init.ops = &sam9x5_peripheral_chg_ops;
446 periph->hw.init = &init;
448 periph->regmap = regmap;
450 if (layout->div_mask)
451 periph->auto_div = true;
452 periph->layout = layout;
453 periph->range = *range;
454 periph->chg_pid = chg_pid;
457 ret = clk_hw_register(NULL, &periph->hw);
462 clk_sam9x5_peripheral_autodiv(periph);