xref: /OK3568_Linux_fs/kernel/drivers/clk/at91/clk-plldiv.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
4*4882a593Smuzhiyun  */
5*4882a593Smuzhiyun 
6*4882a593Smuzhiyun #include <linux/clk-provider.h>
7*4882a593Smuzhiyun #include <linux/clkdev.h>
8*4882a593Smuzhiyun #include <linux/clk/at91_pmc.h>
9*4882a593Smuzhiyun #include <linux/of.h>
10*4882a593Smuzhiyun #include <linux/mfd/syscon.h>
11*4882a593Smuzhiyun #include <linux/regmap.h>
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun #include "pmc.h"
14*4882a593Smuzhiyun 
15*4882a593Smuzhiyun #define to_clk_plldiv(hw) container_of(hw, struct clk_plldiv, hw)
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun struct clk_plldiv {
18*4882a593Smuzhiyun 	struct clk_hw hw;
19*4882a593Smuzhiyun 	struct regmap *regmap;
20*4882a593Smuzhiyun };
21*4882a593Smuzhiyun 
clk_plldiv_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)22*4882a593Smuzhiyun static unsigned long clk_plldiv_recalc_rate(struct clk_hw *hw,
23*4882a593Smuzhiyun 					    unsigned long parent_rate)
24*4882a593Smuzhiyun {
25*4882a593Smuzhiyun 	struct clk_plldiv *plldiv = to_clk_plldiv(hw);
26*4882a593Smuzhiyun 	unsigned int mckr;
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun 	regmap_read(plldiv->regmap, AT91_PMC_MCKR, &mckr);
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun 	if (mckr & AT91_PMC_PLLADIV2)
31*4882a593Smuzhiyun 		return parent_rate / 2;
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun 	return parent_rate;
34*4882a593Smuzhiyun }
35*4882a593Smuzhiyun 
clk_plldiv_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * parent_rate)36*4882a593Smuzhiyun static long clk_plldiv_round_rate(struct clk_hw *hw, unsigned long rate,
37*4882a593Smuzhiyun 					unsigned long *parent_rate)
38*4882a593Smuzhiyun {
39*4882a593Smuzhiyun 	unsigned long div;
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun 	if (rate > *parent_rate)
42*4882a593Smuzhiyun 		return *parent_rate;
43*4882a593Smuzhiyun 	div = *parent_rate / 2;
44*4882a593Smuzhiyun 	if (rate < div)
45*4882a593Smuzhiyun 		return div;
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun 	if (rate - div < *parent_rate - rate)
48*4882a593Smuzhiyun 		return div;
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun 	return *parent_rate;
51*4882a593Smuzhiyun }
52*4882a593Smuzhiyun 
clk_plldiv_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)53*4882a593Smuzhiyun static int clk_plldiv_set_rate(struct clk_hw *hw, unsigned long rate,
54*4882a593Smuzhiyun 			       unsigned long parent_rate)
55*4882a593Smuzhiyun {
56*4882a593Smuzhiyun 	struct clk_plldiv *plldiv = to_clk_plldiv(hw);
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun 	if ((parent_rate != rate) && (parent_rate / 2 != rate))
59*4882a593Smuzhiyun 		return -EINVAL;
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun 	regmap_update_bits(plldiv->regmap, AT91_PMC_MCKR, AT91_PMC_PLLADIV2,
62*4882a593Smuzhiyun 			   parent_rate != rate ? AT91_PMC_PLLADIV2 : 0);
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun 	return 0;
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun static const struct clk_ops plldiv_ops = {
68*4882a593Smuzhiyun 	.recalc_rate = clk_plldiv_recalc_rate,
69*4882a593Smuzhiyun 	.round_rate = clk_plldiv_round_rate,
70*4882a593Smuzhiyun 	.set_rate = clk_plldiv_set_rate,
71*4882a593Smuzhiyun };
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun struct clk_hw * __init
at91_clk_register_plldiv(struct regmap * regmap,const char * name,const char * parent_name)74*4882a593Smuzhiyun at91_clk_register_plldiv(struct regmap *regmap, const char *name,
75*4882a593Smuzhiyun 			 const char *parent_name)
76*4882a593Smuzhiyun {
77*4882a593Smuzhiyun 	struct clk_plldiv *plldiv;
78*4882a593Smuzhiyun 	struct clk_hw *hw;
79*4882a593Smuzhiyun 	struct clk_init_data init;
80*4882a593Smuzhiyun 	int ret;
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun 	plldiv = kzalloc(sizeof(*plldiv), GFP_KERNEL);
83*4882a593Smuzhiyun 	if (!plldiv)
84*4882a593Smuzhiyun 		return ERR_PTR(-ENOMEM);
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun 	init.name = name;
87*4882a593Smuzhiyun 	init.ops = &plldiv_ops;
88*4882a593Smuzhiyun 	init.parent_names = parent_name ? &parent_name : NULL;
89*4882a593Smuzhiyun 	init.num_parents = parent_name ? 1 : 0;
90*4882a593Smuzhiyun 	init.flags = CLK_SET_RATE_GATE;
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	plldiv->hw.init = &init;
93*4882a593Smuzhiyun 	plldiv->regmap = regmap;
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	hw = &plldiv->hw;
96*4882a593Smuzhiyun 	ret = clk_hw_register(NULL, &plldiv->hw);
97*4882a593Smuzhiyun 	if (ret) {
98*4882a593Smuzhiyun 		kfree(plldiv);
99*4882a593Smuzhiyun 		hw = ERR_PTR(ret);
100*4882a593Smuzhiyun 	}
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun 	return hw;
103*4882a593Smuzhiyun }
104