1 // SPDX-License-Identifier: GPL-2.0-or-later
5 * Copyright (C) 2014 Atmel
7 * Alexandre Belloni <alexandre.belloni@free-electrons.com>
10 #include <linux/clk-provider.h>
11 #include <linux/clkdev.h>
12 #include <linux/clk/at91_pmc.h>
14 #include <linux/regmap.h>
15 #include <linux/mfd/syscon.h>
19 #define H32MX_MAX_FREQ 90000000
21 struct clk_sama5d4_h32mx
{
23 struct regmap
*regmap
;
26 #define to_clk_sama5d4_h32mx(hw) container_of(hw, struct clk_sama5d4_h32mx, hw)
28 static unsigned long clk_sama5d4_h32mx_recalc_rate(struct clk_hw
*hw
,
29 unsigned long parent_rate
)
31 struct clk_sama5d4_h32mx
*h32mxclk
= to_clk_sama5d4_h32mx(hw
);
34 regmap_read(h32mxclk
->regmap
, AT91_PMC_MCKR
, &mckr
);
35 if (mckr
& AT91_PMC_H32MXDIV
)
36 return parent_rate
/ 2;
38 if (parent_rate
> H32MX_MAX_FREQ
)
39 pr_warn("H32MX clock is too fast\n");
43 static long clk_sama5d4_h32mx_round_rate(struct clk_hw
*hw
, unsigned long rate
,
44 unsigned long *parent_rate
)
48 if (rate
> *parent_rate
)
50 div
= *parent_rate
/ 2;
54 if (rate
- div
< *parent_rate
- rate
)
60 static int clk_sama5d4_h32mx_set_rate(struct clk_hw
*hw
, unsigned long rate
,
61 unsigned long parent_rate
)
63 struct clk_sama5d4_h32mx
*h32mxclk
= to_clk_sama5d4_h32mx(hw
);
66 if (parent_rate
!= rate
&& (parent_rate
/ 2) != rate
)
69 if ((parent_rate
/ 2) == rate
)
70 mckr
= AT91_PMC_H32MXDIV
;
72 regmap_update_bits(h32mxclk
->regmap
, AT91_PMC_MCKR
,
73 AT91_PMC_H32MXDIV
, mckr
);
78 static const struct clk_ops h32mx_ops
= {
79 .recalc_rate
= clk_sama5d4_h32mx_recalc_rate
,
80 .round_rate
= clk_sama5d4_h32mx_round_rate
,
81 .set_rate
= clk_sama5d4_h32mx_set_rate
,
84 struct clk_hw
* __init
85 at91_clk_register_h32mx(struct regmap
*regmap
, const char *name
,
86 const char *parent_name
)
88 struct clk_sama5d4_h32mx
*h32mxclk
;
89 struct clk_init_data init
;
92 h32mxclk
= kzalloc(sizeof(*h32mxclk
), GFP_KERNEL
);
94 return ERR_PTR(-ENOMEM
);
97 init
.ops
= &h32mx_ops
;
98 init
.parent_names
= parent_name
? &parent_name
: NULL
;
99 init
.num_parents
= parent_name
? 1 : 0;
100 init
.flags
= CLK_SET_RATE_GATE
;
102 h32mxclk
->hw
.init
= &init
;
103 h32mxclk
->regmap
= regmap
;
105 ret
= clk_hw_register(NULL
, &h32mxclk
->hw
);
111 return &h32mxclk
->hw
;