1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
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
;
43 #define to_clk_sam9x5_peripheral(hw) \
44 container_of(hw, struct clk_sam9x5_peripheral, hw)
46 static int clk_peripheral_enable(struct clk_hw
*hw
)
48 struct clk_peripheral
*periph
= to_clk_peripheral(hw
);
49 int offset
= AT91_PMC_PCER
;
52 if (id
< PERIPHERAL_ID_MIN
)
54 if (id
> PERIPHERAL_ID_MAX
)
55 offset
= AT91_PMC_PCER1
;
56 regmap_write(periph
->regmap
, offset
, PERIPHERAL_MASK(id
));
61 static void clk_peripheral_disable(struct clk_hw
*hw
)
63 struct clk_peripheral
*periph
= to_clk_peripheral(hw
);
64 int offset
= AT91_PMC_PCDR
;
67 if (id
< PERIPHERAL_ID_MIN
)
69 if (id
> PERIPHERAL_ID_MAX
)
70 offset
= AT91_PMC_PCDR1
;
71 regmap_write(periph
->regmap
, offset
, PERIPHERAL_MASK(id
));
74 static int clk_peripheral_is_enabled(struct clk_hw
*hw
)
76 struct clk_peripheral
*periph
= to_clk_peripheral(hw
);
77 int offset
= AT91_PMC_PCSR
;
81 if (id
< PERIPHERAL_ID_MIN
)
83 if (id
> PERIPHERAL_ID_MAX
)
84 offset
= AT91_PMC_PCSR1
;
85 regmap_read(periph
->regmap
, offset
, &status
);
87 return status
& PERIPHERAL_MASK(id
) ? 1 : 0;
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
,
96 struct clk_hw
* __init
97 at91_clk_register_peripheral(struct regmap
*regmap
, const char *name
,
98 const char *parent_name
, u32 id
)
100 struct clk_peripheral
*periph
;
101 struct clk_init_data init
;
105 if (!name
|| !parent_name
|| id
> PERIPHERAL_ID_MAX
)
106 return ERR_PTR(-EINVAL
);
108 periph
= kzalloc(sizeof(*periph
), GFP_KERNEL
);
110 return ERR_PTR(-ENOMEM
);
113 init
.ops
= &peripheral_ops
;
114 init
.parent_names
= (parent_name
? &parent_name
: NULL
);
115 init
.num_parents
= (parent_name
? 1 : 0);
119 periph
->hw
.init
= &init
;
120 periph
->regmap
= regmap
;
123 ret
= clk_hw_register(NULL
, &periph
->hw
);
132 static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral
*periph
)
134 struct clk_hw
*parent
;
135 unsigned long parent_rate
;
138 if (!periph
->auto_div
)
141 if (periph
->range
.max
) {
142 parent
= clk_hw_get_parent_by_index(&periph
->hw
, 0);
143 parent_rate
= clk_hw_get_rate(parent
);
147 for (; shift
< PERIPHERAL_MAX_SHIFT
; shift
++) {
148 if (parent_rate
>> shift
<= periph
->range
.max
)
153 periph
->auto_div
= false;
157 static int clk_sam9x5_peripheral_enable(struct clk_hw
*hw
)
159 struct clk_sam9x5_peripheral
*periph
= to_clk_sam9x5_peripheral(hw
);
162 if (periph
->id
< PERIPHERAL_ID_MIN
)
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
|
171 field_prep(periph
->layout
->div_mask
, periph
->div
) |
172 periph
->layout
->cmd
|
174 spin_unlock_irqrestore(periph
->lock
, flags
);
179 static void clk_sam9x5_peripheral_disable(struct clk_hw
*hw
)
181 struct clk_sam9x5_peripheral
*periph
= to_clk_sam9x5_peripheral(hw
);
184 if (periph
->id
< PERIPHERAL_ID_MIN
)
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
);
196 static int clk_sam9x5_peripheral_is_enabled(struct clk_hw
*hw
)
198 struct clk_sam9x5_peripheral
*periph
= to_clk_sam9x5_peripheral(hw
);
202 if (periph
->id
< PERIPHERAL_ID_MIN
)
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
);
211 return status
& AT91_PMC_PCR_EN
? 1 : 0;
215 clk_sam9x5_peripheral_recalc_rate(struct clk_hw
*hw
,
216 unsigned long parent_rate
)
218 struct clk_sam9x5_peripheral
*periph
= to_clk_sam9x5_peripheral(hw
);
222 if (periph
->id
< PERIPHERAL_ID_MIN
)
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
);
231 if (status
& AT91_PMC_PCR_EN
) {
232 periph
->div
= field_get(periph
->layout
->div_mask
, status
);
233 periph
->auto_div
= false;
235 clk_sam9x5_peripheral_autodiv(periph
);
238 return parent_rate
>> periph
->div
;
241 static long clk_sam9x5_peripheral_round_rate(struct clk_hw
*hw
,
243 unsigned long *parent_rate
)
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
);
252 if (periph
->id
< PERIPHERAL_ID_MIN
|| !periph
->range
.max
)
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
)
263 if (rate
>= cur_rate
)
266 best_diff
= cur_rate
- rate
;
267 best_rate
= cur_rate
;
268 for (; shift
<= PERIPHERAL_MAX_SHIFT
; shift
++) {
269 cur_rate
= *parent_rate
>> shift
;
271 cur_diff
= rate
- cur_rate
;
273 cur_diff
= cur_rate
- rate
;
275 if (cur_diff
< best_diff
) {
276 best_diff
= cur_diff
;
277 best_rate
= cur_rate
;
280 if (!best_diff
|| cur_rate
< rate
)
287 static int clk_sam9x5_peripheral_set_rate(struct clk_hw
*hw
,
289 unsigned long parent_rate
)
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
)
300 if (periph
->range
.max
&& rate
> periph
->range
.max
)
303 for (shift
= 0; shift
<= PERIPHERAL_MAX_SHIFT
; shift
++) {
304 if (parent_rate
>> shift
== rate
) {
305 periph
->auto_div
= false;
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
,
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
)
329 struct clk_sam9x5_peripheral
*periph
;
330 struct clk_init_data init
;
334 if (!name
|| !parent_name
)
335 return ERR_PTR(-EINVAL
);
337 periph
= kzalloc(sizeof(*periph
), GFP_KERNEL
);
339 return ERR_PTR(-ENOMEM
);
342 init
.ops
= &sam9x5_peripheral_ops
;
343 init
.parent_names
= (parent_name
? &parent_name
: NULL
);
344 init
.num_parents
= (parent_name
? 1 : 0);
348 periph
->hw
.init
= &init
;
350 periph
->regmap
= regmap
;
352 if (layout
->div_mask
)
353 periph
->auto_div
= true;
354 periph
->layout
= layout
;
355 periph
->range
= *range
;
358 ret
= clk_hw_register(NULL
, &periph
->hw
);
363 clk_sam9x5_peripheral_autodiv(periph
);