1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Marvell Dove PMU Core PLL divider driver |
4 | * |
5 | * Cleaned up by substantially rewriting, and converted to DT by |
6 | * Russell King. Origin is not known. |
7 | */ |
8 | #include <linux/clk-provider.h> |
9 | #include <linux/delay.h> |
10 | #include <linux/io.h> |
11 | #include <linux/kernel.h> |
12 | #include <linux/of.h> |
13 | #include <linux/of_address.h> |
14 | |
15 | #include "dove-divider.h" |
16 | |
17 | struct dove_clk { |
18 | const char *name; |
19 | struct clk_hw hw; |
20 | void __iomem *base; |
21 | spinlock_t *lock; |
22 | u8 div_bit_start; |
23 | u8 div_bit_end; |
24 | u8 div_bit_load; |
25 | u8 div_bit_size; |
26 | u32 *divider_table; |
27 | }; |
28 | |
29 | enum { |
30 | DIV_CTRL0 = 0, |
31 | DIV_CTRL1 = 4, |
32 | DIV_CTRL1_N_RESET_MASK = BIT(10), |
33 | }; |
34 | |
35 | #define to_dove_clk(hw) container_of(hw, struct dove_clk, hw) |
36 | |
37 | static void dove_load_divider(void __iomem *base, u32 val, u32 mask, u32 load) |
38 | { |
39 | u32 v; |
40 | |
41 | v = readl_relaxed(base + DIV_CTRL1) | DIV_CTRL1_N_RESET_MASK; |
42 | writel_relaxed(v, base + DIV_CTRL1); |
43 | |
44 | v = (readl_relaxed(base + DIV_CTRL0) & ~(mask | load)) | val; |
45 | writel_relaxed(v, base + DIV_CTRL0); |
46 | writel_relaxed(v | load, base + DIV_CTRL0); |
47 | ndelay(250); |
48 | writel_relaxed(v, base + DIV_CTRL0); |
49 | } |
50 | |
51 | static unsigned int dove_get_divider(struct dove_clk *dc) |
52 | { |
53 | unsigned int divider; |
54 | u32 val; |
55 | |
56 | val = readl_relaxed(dc->base + DIV_CTRL0); |
57 | val >>= dc->div_bit_start; |
58 | |
59 | divider = val & ~(~0 << dc->div_bit_size); |
60 | |
61 | if (dc->divider_table) |
62 | divider = dc->divider_table[divider]; |
63 | |
64 | return divider; |
65 | } |
66 | |
67 | static int dove_calc_divider(const struct dove_clk *dc, unsigned long rate, |
68 | unsigned long parent_rate, bool set) |
69 | { |
70 | unsigned int divider, max; |
71 | |
72 | divider = DIV_ROUND_CLOSEST(parent_rate, rate); |
73 | |
74 | if (dc->divider_table) { |
75 | unsigned int i; |
76 | |
77 | for (i = 0; dc->divider_table[i]; i++) |
78 | if (divider == dc->divider_table[i]) { |
79 | divider = i; |
80 | break; |
81 | } |
82 | |
83 | if (!dc->divider_table[i]) |
84 | return -EINVAL; |
85 | } else { |
86 | max = 1 << dc->div_bit_size; |
87 | |
88 | if (set && (divider == 0 || divider >= max)) |
89 | return -EINVAL; |
90 | if (divider >= max) |
91 | divider = max - 1; |
92 | else if (divider == 0) |
93 | divider = 1; |
94 | } |
95 | |
96 | return divider; |
97 | } |
98 | |
99 | static unsigned long dove_recalc_rate(struct clk_hw *hw, unsigned long parent) |
100 | { |
101 | struct dove_clk *dc = to_dove_clk(hw); |
102 | unsigned int divider = dove_get_divider(dc); |
103 | unsigned long rate = DIV_ROUND_CLOSEST(parent, divider); |
104 | |
105 | pr_debug("%s(): %s divider=%u parent=%lu rate=%lu\n" , |
106 | __func__, dc->name, divider, parent, rate); |
107 | |
108 | return rate; |
109 | } |
110 | |
111 | static long dove_round_rate(struct clk_hw *hw, unsigned long rate, |
112 | unsigned long *parent) |
113 | { |
114 | struct dove_clk *dc = to_dove_clk(hw); |
115 | unsigned long parent_rate = *parent; |
116 | int divider; |
117 | |
118 | divider = dove_calc_divider(dc, rate, parent_rate, set: false); |
119 | if (divider < 0) |
120 | return divider; |
121 | |
122 | rate = DIV_ROUND_CLOSEST(parent_rate, divider); |
123 | |
124 | pr_debug("%s(): %s divider=%u parent=%lu rate=%lu\n" , |
125 | __func__, dc->name, divider, parent_rate, rate); |
126 | |
127 | return rate; |
128 | } |
129 | |
130 | static int dove_set_clock(struct clk_hw *hw, unsigned long rate, |
131 | unsigned long parent_rate) |
132 | { |
133 | struct dove_clk *dc = to_dove_clk(hw); |
134 | u32 mask, load, div; |
135 | int divider; |
136 | |
137 | divider = dove_calc_divider(dc, rate, parent_rate, set: true); |
138 | if (divider < 0) |
139 | return divider; |
140 | |
141 | pr_debug("%s(): %s divider=%u parent=%lu rate=%lu\n" , |
142 | __func__, dc->name, divider, parent_rate, rate); |
143 | |
144 | div = (u32)divider << dc->div_bit_start; |
145 | mask = ~(~0 << dc->div_bit_size) << dc->div_bit_start; |
146 | load = BIT(dc->div_bit_load); |
147 | |
148 | spin_lock(lock: dc->lock); |
149 | dove_load_divider(base: dc->base, val: div, mask, load); |
150 | spin_unlock(lock: dc->lock); |
151 | |
152 | return 0; |
153 | } |
154 | |
155 | static const struct clk_ops dove_divider_ops = { |
156 | .set_rate = dove_set_clock, |
157 | .round_rate = dove_round_rate, |
158 | .recalc_rate = dove_recalc_rate, |
159 | }; |
160 | |
161 | static struct clk *clk_register_dove_divider(struct device *dev, |
162 | struct dove_clk *dc, const char **parent_names, size_t num_parents, |
163 | void __iomem *base) |
164 | { |
165 | char name[32]; |
166 | struct clk_init_data init = { |
167 | .name = name, |
168 | .ops = &dove_divider_ops, |
169 | .parent_names = parent_names, |
170 | .num_parents = num_parents, |
171 | }; |
172 | |
173 | strscpy(name, dc->name, sizeof(name)); |
174 | |
175 | dc->hw.init = &init; |
176 | dc->base = base; |
177 | dc->div_bit_size = dc->div_bit_end - dc->div_bit_start + 1; |
178 | |
179 | return clk_register(dev, hw: &dc->hw); |
180 | } |
181 | |
182 | static DEFINE_SPINLOCK(dove_divider_lock); |
183 | |
184 | static u32 axi_divider[] = {-1, 2, 1, 3, 4, 6, 5, 7, 8, 10, 9, 0}; |
185 | |
186 | static struct dove_clk dove_hw_clocks[4] = { |
187 | { |
188 | .name = "axi" , |
189 | .lock = &dove_divider_lock, |
190 | .div_bit_start = 1, |
191 | .div_bit_end = 6, |
192 | .div_bit_load = 7, |
193 | .divider_table = axi_divider, |
194 | }, { |
195 | .name = "gpu" , |
196 | .lock = &dove_divider_lock, |
197 | .div_bit_start = 8, |
198 | .div_bit_end = 13, |
199 | .div_bit_load = 14, |
200 | }, { |
201 | .name = "vmeta" , |
202 | .lock = &dove_divider_lock, |
203 | .div_bit_start = 15, |
204 | .div_bit_end = 20, |
205 | .div_bit_load = 21, |
206 | }, { |
207 | .name = "lcd" , |
208 | .lock = &dove_divider_lock, |
209 | .div_bit_start = 22, |
210 | .div_bit_end = 27, |
211 | .div_bit_load = 28, |
212 | }, |
213 | }; |
214 | |
215 | static const char *core_pll[] = { |
216 | "core-pll" , |
217 | }; |
218 | |
219 | static int dove_divider_init(struct device *dev, void __iomem *base, |
220 | struct clk **clks) |
221 | { |
222 | struct clk *clk; |
223 | int i; |
224 | |
225 | /* |
226 | * Create the core PLL clock. We treat this as a fixed rate |
227 | * clock as we don't know any better, and documentation is sparse. |
228 | */ |
229 | clk = clk_register_fixed_rate(dev, name: core_pll[0], NULL, flags: 0, fixed_rate: 2000000000UL); |
230 | if (IS_ERR(ptr: clk)) |
231 | return PTR_ERR(ptr: clk); |
232 | |
233 | for (i = 0; i < ARRAY_SIZE(dove_hw_clocks); i++) |
234 | clks[i] = clk_register_dove_divider(dev, dc: &dove_hw_clocks[i], |
235 | parent_names: core_pll, |
236 | ARRAY_SIZE(core_pll), base); |
237 | |
238 | return 0; |
239 | } |
240 | |
241 | static struct clk *dove_divider_clocks[4]; |
242 | |
243 | static struct clk_onecell_data dove_divider_data = { |
244 | .clks = dove_divider_clocks, |
245 | .clk_num = ARRAY_SIZE(dove_divider_clocks), |
246 | }; |
247 | |
248 | void __init dove_divider_clk_init(struct device_node *np) |
249 | { |
250 | void __iomem *base; |
251 | |
252 | base = of_iomap(node: np, index: 0); |
253 | if (WARN_ON(!base)) |
254 | return; |
255 | |
256 | if (WARN_ON(dove_divider_init(NULL, base, dove_divider_clocks))) { |
257 | iounmap(addr: base); |
258 | return; |
259 | } |
260 | |
261 | of_clk_add_provider(np, clk_src_get: of_clk_src_onecell_get, data: &dove_divider_data); |
262 | } |
263 | |