2 * Copyright (C) 2015 Chen-Yu Tsai
4 * Chen-Yu Tsai <wens@csie.org>
6 * Allwinner A80 CPUS clock driver
10 #include <linux/clk.h>
11 #include <linux/clk-provider.h>
12 #include <linux/slab.h>
13 #include <linux/spinlock.h>
15 #include <linux/of_address.h>
17 static DEFINE_SPINLOCK(sun9i_a80_cpus_lock
);
20 * sun9i_a80_cpus_clk_setup() - Setup function for a80 cpus composite clk
23 #define SUN9I_CPUS_MAX_PARENTS 4
24 #define SUN9I_CPUS_MUX_PARENT_PLL4 3
25 #define SUN9I_CPUS_MUX_SHIFT 16
26 #define SUN9I_CPUS_MUX_MASK GENMASK(17, 16)
27 #define SUN9I_CPUS_MUX_GET_PARENT(reg) ((reg & SUN9I_CPUS_MUX_MASK) >> \
30 #define SUN9I_CPUS_DIV_SHIFT 4
31 #define SUN9I_CPUS_DIV_MASK GENMASK(5, 4)
32 #define SUN9I_CPUS_DIV_GET(reg) ((reg & SUN9I_CPUS_DIV_MASK) >> \
34 #define SUN9I_CPUS_DIV_SET(reg, div) ((reg & ~SUN9I_CPUS_DIV_MASK) | \
35 (div << SUN9I_CPUS_DIV_SHIFT))
36 #define SUN9I_CPUS_PLL4_DIV_SHIFT 8
37 #define SUN9I_CPUS_PLL4_DIV_MASK GENMASK(12, 8)
38 #define SUN9I_CPUS_PLL4_DIV_GET(reg) ((reg & SUN9I_CPUS_PLL4_DIV_MASK) >> \
39 SUN9I_CPUS_PLL4_DIV_SHIFT)
40 #define SUN9I_CPUS_PLL4_DIV_SET(reg, div) ((reg & ~SUN9I_CPUS_PLL4_DIV_MASK) | \
41 (div << SUN9I_CPUS_PLL4_DIV_SHIFT))
43 struct sun9i_a80_cpus_clk
{
48 #define to_sun9i_a80_cpus_clk(_hw) container_of(_hw, struct sun9i_a80_cpus_clk, hw)
50 static unsigned long sun9i_a80_cpus_clk_recalc_rate(struct clk_hw
*hw
,
51 unsigned long parent_rate
)
53 struct sun9i_a80_cpus_clk
*cpus
= to_sun9i_a80_cpus_clk(hw
);
57 /* Fetch the register value */
58 reg
= readl(cpus
->reg
);
60 /* apply pre-divider first if parent is pll4 */
61 if (SUN9I_CPUS_MUX_GET_PARENT(reg
) == SUN9I_CPUS_MUX_PARENT_PLL4
)
62 parent_rate
/= SUN9I_CPUS_PLL4_DIV_GET(reg
) + 1;
65 rate
= parent_rate
/ (SUN9I_CPUS_DIV_GET(reg
) + 1);
70 static long sun9i_a80_cpus_clk_round(unsigned long rate
, u8
*divp
, u8
*pre_divp
,
71 u8 parent
, unsigned long parent_rate
)
76 * clock can only divide, so we will never be able to achieve
77 * frequencies higher than the parent frequency
79 if (parent_rate
&& rate
> parent_rate
)
82 div
= DIV_ROUND_UP(parent_rate
, rate
);
84 /* calculate pre-divider if parent is pll4 */
85 if (parent
== SUN9I_CPUS_MUX_PARENT_PLL4
&& div
> 4) {
86 /* pre-divider is 1 ~ 32 */
90 } else if (div
< 64) {
91 pre_div
= DIV_ROUND_UP(div
, 2);
93 } else if (div
< 96) {
94 pre_div
= DIV_ROUND_UP(div
, 3);
97 pre_div
= DIV_ROUND_UP(div
, 4);
102 /* we were asked to pass back divider values */
105 *pre_divp
= pre_div
- 1;
108 return parent_rate
/ pre_div
/ div
;
111 static int sun9i_a80_cpus_clk_determine_rate(struct clk_hw
*clk
,
112 struct clk_rate_request
*req
)
114 struct clk_hw
*parent
, *best_parent
= NULL
;
116 unsigned long parent_rate
, best
= 0, child_rate
, best_child_rate
= 0;
117 unsigned long rate
= req
->rate
;
119 /* find the parent that can help provide the fastest rate <= rate */
120 num_parents
= clk_hw_get_num_parents(clk
);
121 for (i
= 0; i
< num_parents
; i
++) {
122 parent
= clk_hw_get_parent_by_index(clk
, i
);
125 if (clk_hw_get_flags(clk
) & CLK_SET_RATE_PARENT
)
126 parent_rate
= clk_hw_round_rate(parent
, rate
);
128 parent_rate
= clk_hw_get_rate(parent
);
130 child_rate
= sun9i_a80_cpus_clk_round(rate
, NULL
, NULL
, i
,
133 if (child_rate
<= rate
&& child_rate
> best_child_rate
) {
134 best_parent
= parent
;
136 best_child_rate
= child_rate
;
143 req
->best_parent_hw
= best_parent
;
144 req
->best_parent_rate
= best
;
145 req
->rate
= best_child_rate
;
150 static int sun9i_a80_cpus_clk_set_rate(struct clk_hw
*hw
, unsigned long rate
,
151 unsigned long parent_rate
)
153 struct sun9i_a80_cpus_clk
*cpus
= to_sun9i_a80_cpus_clk(hw
);
155 u8 div
, pre_div
, parent
;
158 spin_lock_irqsave(&sun9i_a80_cpus_lock
, flags
);
160 reg
= readl(cpus
->reg
);
162 /* need to know which parent is used to apply pre-divider */
163 parent
= SUN9I_CPUS_MUX_GET_PARENT(reg
);
164 sun9i_a80_cpus_clk_round(rate
, &div
, &pre_div
, parent
, parent_rate
);
166 reg
= SUN9I_CPUS_DIV_SET(reg
, div
);
167 reg
= SUN9I_CPUS_PLL4_DIV_SET(reg
, pre_div
);
168 writel(reg
, cpus
->reg
);
170 spin_unlock_irqrestore(&sun9i_a80_cpus_lock
, flags
);
175 static const struct clk_ops sun9i_a80_cpus_clk_ops
= {
176 .determine_rate
= sun9i_a80_cpus_clk_determine_rate
,
177 .recalc_rate
= sun9i_a80_cpus_clk_recalc_rate
,
178 .set_rate
= sun9i_a80_cpus_clk_set_rate
,
181 static void sun9i_a80_cpus_setup(struct device_node
*node
)
183 const char *clk_name
= node
->name
;
184 const char *parents
[SUN9I_CPUS_MAX_PARENTS
];
186 struct sun9i_a80_cpus_clk
*cpus
;
191 cpus
= kzalloc(sizeof(*cpus
), GFP_KERNEL
);
195 cpus
->reg
= of_io_request_and_map(node
, 0, of_node_full_name(node
));
196 if (IS_ERR(cpus
->reg
))
199 of_property_read_string(node
, "clock-output-names", &clk_name
);
201 /* we have a mux, we will have >1 parents */
202 ret
= of_clk_parent_fill(node
, parents
, SUN9I_CPUS_MAX_PARENTS
);
204 mux
= kzalloc(sizeof(*mux
), GFP_KERNEL
);
208 /* set up clock properties */
209 mux
->reg
= cpus
->reg
;
210 mux
->shift
= SUN9I_CPUS_MUX_SHIFT
;
211 /* un-shifted mask is what mux_clk expects */
212 mux
->mask
= SUN9I_CPUS_MUX_MASK
>> SUN9I_CPUS_MUX_SHIFT
;
213 mux
->lock
= &sun9i_a80_cpus_lock
;
215 clk
= clk_register_composite(NULL
, clk_name
, parents
, ret
,
216 &mux
->hw
, &clk_mux_ops
,
217 &cpus
->hw
, &sun9i_a80_cpus_clk_ops
,
222 ret
= of_clk_add_provider(node
, of_clk_src_simple_get
, clk
);
234 of_address_to_resource(node
, 0, &res
);
235 release_mem_region(res
.start
, resource_size(&res
));
239 CLK_OF_DECLARE(sun9i_a80_cpus
, "allwinner,sun9i-a80-cpus-clk",
240 sun9i_a80_cpus_setup
);