1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
6 #include <linux/clk-provider.h>
7 #include <linux/clkdev.h>
8 #include <linux/clk/at91_pmc.h>
10 #include <linux/mfd/syscon.h>
11 #include <linux/regmap.h>
15 #define to_clk_plldiv(hw) container_of(hw, struct clk_plldiv, hw)
19 struct regmap
*regmap
;
22 static unsigned long clk_plldiv_recalc_rate(struct clk_hw
*hw
,
23 unsigned long parent_rate
)
25 struct clk_plldiv
*plldiv
= to_clk_plldiv(hw
);
28 regmap_read(plldiv
->regmap
, AT91_PMC_MCKR
, &mckr
);
30 if (mckr
& AT91_PMC_PLLADIV2
)
31 return parent_rate
/ 2;
36 static long clk_plldiv_round_rate(struct clk_hw
*hw
, unsigned long rate
,
37 unsigned long *parent_rate
)
41 if (rate
> *parent_rate
)
43 div
= *parent_rate
/ 2;
47 if (rate
- div
< *parent_rate
- rate
)
53 static int clk_plldiv_set_rate(struct clk_hw
*hw
, unsigned long rate
,
54 unsigned long parent_rate
)
56 struct clk_plldiv
*plldiv
= to_clk_plldiv(hw
);
58 if ((parent_rate
!= rate
) && (parent_rate
/ 2 != rate
))
61 regmap_update_bits(plldiv
->regmap
, AT91_PMC_MCKR
, AT91_PMC_PLLADIV2
,
62 parent_rate
!= rate
? AT91_PMC_PLLADIV2
: 0);
67 static const struct clk_ops plldiv_ops
= {
68 .recalc_rate
= clk_plldiv_recalc_rate
,
69 .round_rate
= clk_plldiv_round_rate
,
70 .set_rate
= clk_plldiv_set_rate
,
73 struct clk_hw
* __init
74 at91_clk_register_plldiv(struct regmap
*regmap
, const char *name
,
75 const char *parent_name
)
77 struct clk_plldiv
*plldiv
;
79 struct clk_init_data init
;
82 plldiv
= kzalloc(sizeof(*plldiv
), GFP_KERNEL
);
84 return ERR_PTR(-ENOMEM
);
87 init
.ops
= &plldiv_ops
;
88 init
.parent_names
= parent_name
? &parent_name
: NULL
;
89 init
.num_parents
= parent_name
? 1 : 0;
90 init
.flags
= CLK_SET_RATE_GATE
;
92 plldiv
->hw
.init
= &init
;
93 plldiv
->regmap
= regmap
;
96 ret
= clk_hw_register(NULL
, &plldiv
->hw
);