1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2015 Chen-Yu Tsai
5 * Chen-Yu Tsai <wens@csie.org>
7 * Allwinner A80 CPUS clock driver
11 #include <linux/clk.h>
12 #include <linux/clk-provider.h>
14 #include <linux/slab.h>
15 #include <linux/spinlock.h>
17 #include <linux/of_address.h>
19 static DEFINE_SPINLOCK(sun9i_a80_cpus_lock
);
22 #define SUN9I_CPUS_MAX_PARENTS 4
23 #define SUN9I_CPUS_MUX_PARENT_PLL4 3
24 #define SUN9I_CPUS_MUX_SHIFT 16
25 #define SUN9I_CPUS_MUX_MASK GENMASK(17, 16)
26 #define SUN9I_CPUS_MUX_GET_PARENT(reg) ((reg & SUN9I_CPUS_MUX_MASK) >> \
29 #define SUN9I_CPUS_DIV_SHIFT 4
30 #define SUN9I_CPUS_DIV_MASK GENMASK(5, 4)
31 #define SUN9I_CPUS_DIV_GET(reg) ((reg & SUN9I_CPUS_DIV_MASK) >> \
33 #define SUN9I_CPUS_DIV_SET(reg, div) ((reg & ~SUN9I_CPUS_DIV_MASK) | \
34 (div << SUN9I_CPUS_DIV_SHIFT))
35 #define SUN9I_CPUS_PLL4_DIV_SHIFT 8
36 #define SUN9I_CPUS_PLL4_DIV_MASK GENMASK(12, 8)
37 #define SUN9I_CPUS_PLL4_DIV_GET(reg) ((reg & SUN9I_CPUS_PLL4_DIV_MASK) >> \
38 SUN9I_CPUS_PLL4_DIV_SHIFT)
39 #define SUN9I_CPUS_PLL4_DIV_SET(reg, div) ((reg & ~SUN9I_CPUS_PLL4_DIV_MASK) | \
40 (div << SUN9I_CPUS_PLL4_DIV_SHIFT))
42 struct sun9i_a80_cpus_clk
{
47 #define to_sun9i_a80_cpus_clk(_hw) container_of(_hw, struct sun9i_a80_cpus_clk, hw)
49 static unsigned long sun9i_a80_cpus_clk_recalc_rate(struct clk_hw
*hw
,
50 unsigned long parent_rate
)
52 struct sun9i_a80_cpus_clk
*cpus
= to_sun9i_a80_cpus_clk(hw
);
56 /* Fetch the register value */
57 reg
= readl(cpus
->reg
);
59 /* apply pre-divider first if parent is pll4 */
60 if (SUN9I_CPUS_MUX_GET_PARENT(reg
) == SUN9I_CPUS_MUX_PARENT_PLL4
)
61 parent_rate
/= SUN9I_CPUS_PLL4_DIV_GET(reg
) + 1;
64 rate
= parent_rate
/ (SUN9I_CPUS_DIV_GET(reg
) + 1);
69 static long sun9i_a80_cpus_clk_round(unsigned long rate
, u8
*divp
, u8
*pre_divp
,
70 u8 parent
, unsigned long parent_rate
)
75 * clock can only divide, so we will never be able to achieve
76 * frequencies higher than the parent frequency
78 if (parent_rate
&& rate
> parent_rate
)
81 div
= DIV_ROUND_UP(parent_rate
, rate
);
83 /* calculate pre-divider if parent is pll4 */
84 if (parent
== SUN9I_CPUS_MUX_PARENT_PLL4
&& div
> 4) {
85 /* pre-divider is 1 ~ 32 */
89 } else if (div
< 64) {
90 pre_div
= DIV_ROUND_UP(div
, 2);
92 } else if (div
< 96) {
93 pre_div
= DIV_ROUND_UP(div
, 3);
96 pre_div
= DIV_ROUND_UP(div
, 4);
101 /* we were asked to pass back divider values */
104 *pre_divp
= pre_div
- 1;
107 return parent_rate
/ pre_div
/ div
;
110 static int sun9i_a80_cpus_clk_determine_rate(struct clk_hw
*clk
,
111 struct clk_rate_request
*req
)
113 struct clk_hw
*parent
, *best_parent
= NULL
;
115 unsigned long parent_rate
, best
= 0, child_rate
, best_child_rate
= 0;
116 unsigned long rate
= req
->rate
;
118 /* find the parent that can help provide the fastest rate <= rate */
119 num_parents
= clk_hw_get_num_parents(clk
);
120 for (i
= 0; i
< num_parents
; i
++) {
121 parent
= clk_hw_get_parent_by_index(clk
, i
);
124 if (clk_hw_get_flags(clk
) & CLK_SET_RATE_PARENT
)
125 parent_rate
= clk_hw_round_rate(parent
, rate
);
127 parent_rate
= clk_hw_get_rate(parent
);
129 child_rate
= sun9i_a80_cpus_clk_round(rate
, NULL
, NULL
, i
,
132 if (child_rate
<= rate
&& child_rate
> best_child_rate
) {
133 best_parent
= parent
;
135 best_child_rate
= child_rate
;
142 req
->best_parent_hw
= best_parent
;
143 req
->best_parent_rate
= best
;
144 req
->rate
= best_child_rate
;
149 static int sun9i_a80_cpus_clk_set_rate(struct clk_hw
*hw
, unsigned long rate
,
150 unsigned long parent_rate
)
152 struct sun9i_a80_cpus_clk
*cpus
= to_sun9i_a80_cpus_clk(hw
);
154 u8 div
, pre_div
, parent
;
157 spin_lock_irqsave(&sun9i_a80_cpus_lock
, flags
);
159 reg
= readl(cpus
->reg
);
161 /* need to know which parent is used to apply pre-divider */
162 parent
= SUN9I_CPUS_MUX_GET_PARENT(reg
);
163 sun9i_a80_cpus_clk_round(rate
, &div
, &pre_div
, parent
, parent_rate
);
165 reg
= SUN9I_CPUS_DIV_SET(reg
, div
);
166 reg
= SUN9I_CPUS_PLL4_DIV_SET(reg
, pre_div
);
167 writel(reg
, cpus
->reg
);
169 spin_unlock_irqrestore(&sun9i_a80_cpus_lock
, flags
);
174 static const struct clk_ops sun9i_a80_cpus_clk_ops
= {
175 .determine_rate
= sun9i_a80_cpus_clk_determine_rate
,
176 .recalc_rate
= sun9i_a80_cpus_clk_recalc_rate
,
177 .set_rate
= sun9i_a80_cpus_clk_set_rate
,
181 * sun9i_a80_cpus_setup() - Setup function for a80 cpus composite clk
182 * @node: &struct device_node for the clock
184 static void sun9i_a80_cpus_setup(struct device_node
*node
)
186 const char *clk_name
= node
->name
;
187 const char *parents
[SUN9I_CPUS_MAX_PARENTS
];
189 struct sun9i_a80_cpus_clk
*cpus
;
194 cpus
= kzalloc(sizeof(*cpus
), GFP_KERNEL
);
198 cpus
->reg
= of_io_request_and_map(node
, 0, of_node_full_name(node
));
199 if (IS_ERR(cpus
->reg
))
202 of_property_read_string(node
, "clock-output-names", &clk_name
);
204 /* we have a mux, we will have >1 parents */
205 ret
= of_clk_parent_fill(node
, parents
, SUN9I_CPUS_MAX_PARENTS
);
207 mux
= kzalloc(sizeof(*mux
), GFP_KERNEL
);
211 /* set up clock properties */
212 mux
->reg
= cpus
->reg
;
213 mux
->shift
= SUN9I_CPUS_MUX_SHIFT
;
214 /* un-shifted mask is what mux_clk expects */
215 mux
->mask
= SUN9I_CPUS_MUX_MASK
>> SUN9I_CPUS_MUX_SHIFT
;
216 mux
->lock
= &sun9i_a80_cpus_lock
;
218 clk
= clk_register_composite(NULL
, clk_name
, parents
, ret
,
219 &mux
->hw
, &clk_mux_ops
,
220 &cpus
->hw
, &sun9i_a80_cpus_clk_ops
,
225 ret
= of_clk_add_provider(node
, of_clk_src_simple_get
, clk
);
237 of_address_to_resource(node
, 0, &res
);
238 release_mem_region(res
.start
, resource_size(&res
));
242 CLK_OF_DECLARE(sun9i_a80_cpus
, "allwinner,sun9i-a80-cpus-clk",
243 sun9i_a80_cpus_setup
);