Commit | Line | Data |
---|---|---|
bcd61c0f SB |
1 | /* |
2 | * Copyright (c) 2013, The Linux Foundation. All rights reserved. | |
3 | * | |
4 | * This software is licensed under the terms of the GNU General Public | |
5 | * License version 2, as published by the Free Software Foundation, and | |
6 | * may be copied, distributed, and modified under those terms. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | * GNU General Public License for more details. | |
12 | */ | |
13 | ||
14 | #include <linux/kernel.h> | |
15 | #include <linux/bitops.h> | |
16 | #include <linux/err.h> | |
17 | #include <linux/bug.h> | |
18 | #include <linux/export.h> | |
19 | #include <linux/clk-provider.h> | |
20 | #include <linux/delay.h> | |
21 | #include <linux/regmap.h> | |
99cbd064 | 22 | #include <linux/math64.h> |
bcd61c0f SB |
23 | |
24 | #include <asm/div64.h> | |
25 | ||
26 | #include "clk-rcg.h" | |
50c6a503 | 27 | #include "common.h" |
bcd61c0f SB |
28 | |
29 | #define CMD_REG 0x0 | |
30 | #define CMD_UPDATE BIT(0) | |
31 | #define CMD_ROOT_EN BIT(1) | |
32 | #define CMD_DIRTY_CFG BIT(4) | |
33 | #define CMD_DIRTY_N BIT(5) | |
34 | #define CMD_DIRTY_M BIT(6) | |
35 | #define CMD_DIRTY_D BIT(7) | |
36 | #define CMD_ROOT_OFF BIT(31) | |
37 | ||
38 | #define CFG_REG 0x4 | |
39 | #define CFG_SRC_DIV_SHIFT 0 | |
40 | #define CFG_SRC_SEL_SHIFT 8 | |
41 | #define CFG_SRC_SEL_MASK (0x7 << CFG_SRC_SEL_SHIFT) | |
42 | #define CFG_MODE_SHIFT 12 | |
43 | #define CFG_MODE_MASK (0x3 << CFG_MODE_SHIFT) | |
44 | #define CFG_MODE_DUAL_EDGE (0x2 << CFG_MODE_SHIFT) | |
45 | ||
46 | #define M_REG 0x8 | |
47 | #define N_REG 0xc | |
48 | #define D_REG 0x10 | |
49 | ||
081ba802 RN |
50 | enum freq_policy { |
51 | FLOOR, | |
52 | CEIL, | |
53 | }; | |
54 | ||
bcd61c0f SB |
55 | static int clk_rcg2_is_enabled(struct clk_hw *hw) |
56 | { | |
57 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); | |
58 | u32 cmd; | |
59 | int ret; | |
60 | ||
61 | ret = regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, &cmd); | |
62 | if (ret) | |
63 | return ret; | |
64 | ||
aa014149 | 65 | return (cmd & CMD_ROOT_OFF) == 0; |
bcd61c0f SB |
66 | } |
67 | ||
68 | static u8 clk_rcg2_get_parent(struct clk_hw *hw) | |
69 | { | |
70 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); | |
497295af | 71 | int num_parents = clk_hw_get_num_parents(hw); |
bcd61c0f SB |
72 | u32 cfg; |
73 | int i, ret; | |
74 | ||
75 | ret = regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg); | |
76 | if (ret) | |
7f218978 | 77 | goto err; |
bcd61c0f SB |
78 | |
79 | cfg &= CFG_SRC_SEL_MASK; | |
80 | cfg >>= CFG_SRC_SEL_SHIFT; | |
81 | ||
82 | for (i = 0; i < num_parents; i++) | |
293d2e97 | 83 | if (cfg == rcg->parent_map[i].cfg) |
bcd61c0f SB |
84 | return i; |
85 | ||
7f218978 GD |
86 | err: |
87 | pr_debug("%s: Clock %s has invalid parent, using default.\n", | |
ac269395 | 88 | __func__, clk_hw_get_name(hw)); |
7f218978 | 89 | return 0; |
bcd61c0f SB |
90 | } |
91 | ||
92 | static int update_config(struct clk_rcg2 *rcg) | |
93 | { | |
94 | int count, ret; | |
95 | u32 cmd; | |
96 | struct clk_hw *hw = &rcg->clkr.hw; | |
ac269395 | 97 | const char *name = clk_hw_get_name(hw); |
bcd61c0f SB |
98 | |
99 | ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, | |
100 | CMD_UPDATE, CMD_UPDATE); | |
101 | if (ret) | |
102 | return ret; | |
103 | ||
104 | /* Wait for update to take effect */ | |
105 | for (count = 500; count > 0; count--) { | |
106 | ret = regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, &cmd); | |
107 | if (ret) | |
108 | return ret; | |
109 | if (!(cmd & CMD_UPDATE)) | |
110 | return 0; | |
111 | udelay(1); | |
112 | } | |
113 | ||
114 | WARN(1, "%s: rcg didn't update its configuration.", name); | |
115 | return 0; | |
116 | } | |
117 | ||
118 | static int clk_rcg2_set_parent(struct clk_hw *hw, u8 index) | |
119 | { | |
120 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); | |
121 | int ret; | |
293d2e97 | 122 | u32 cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT; |
bcd61c0f SB |
123 | |
124 | ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, | |
293d2e97 | 125 | CFG_SRC_SEL_MASK, cfg); |
bcd61c0f SB |
126 | if (ret) |
127 | return ret; | |
128 | ||
129 | return update_config(rcg); | |
130 | } | |
131 | ||
132 | /* | |
133 | * Calculate m/n:d rate | |
134 | * | |
135 | * parent_rate m | |
136 | * rate = ----------- x --- | |
137 | * hid_div n | |
138 | */ | |
139 | static unsigned long | |
140 | calc_rate(unsigned long rate, u32 m, u32 n, u32 mode, u32 hid_div) | |
141 | { | |
142 | if (hid_div) { | |
143 | rate *= 2; | |
144 | rate /= hid_div + 1; | |
145 | } | |
146 | ||
147 | if (mode) { | |
148 | u64 tmp = rate; | |
149 | tmp *= m; | |
150 | do_div(tmp, n); | |
151 | rate = tmp; | |
152 | } | |
153 | ||
154 | return rate; | |
155 | } | |
156 | ||
157 | static unsigned long | |
158 | clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) | |
159 | { | |
160 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); | |
161 | u32 cfg, hid_div, m = 0, n = 0, mode = 0, mask; | |
162 | ||
163 | regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg); | |
164 | ||
165 | if (rcg->mnd_width) { | |
166 | mask = BIT(rcg->mnd_width) - 1; | |
167 | regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + M_REG, &m); | |
168 | m &= mask; | |
169 | regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + N_REG, &n); | |
170 | n = ~n; | |
171 | n &= mask; | |
172 | n += m; | |
173 | mode = cfg & CFG_MODE_MASK; | |
174 | mode >>= CFG_MODE_SHIFT; | |
175 | } | |
176 | ||
177 | mask = BIT(rcg->hid_width) - 1; | |
178 | hid_div = cfg >> CFG_SRC_DIV_SHIFT; | |
179 | hid_div &= mask; | |
180 | ||
181 | return calc_rate(parent_rate, m, n, mode, hid_div); | |
182 | } | |
183 | ||
081ba802 RN |
184 | static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f, |
185 | struct clk_rate_request *req, | |
186 | enum freq_policy policy) | |
bcd61c0f | 187 | { |
0817b62c | 188 | unsigned long clk_flags, rate = req->rate; |
ac269395 | 189 | struct clk_hw *p; |
2f272e7b GD |
190 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); |
191 | int index; | |
bcd61c0f | 192 | |
081ba802 RN |
193 | switch (policy) { |
194 | case FLOOR: | |
195 | f = qcom_find_freq_floor(f, rate); | |
196 | break; | |
197 | case CEIL: | |
198 | f = qcom_find_freq(f, rate); | |
199 | break; | |
200 | default: | |
201 | return -EINVAL; | |
202 | }; | |
203 | ||
bcd61c0f SB |
204 | if (!f) |
205 | return -EINVAL; | |
206 | ||
2f272e7b GD |
207 | index = qcom_find_src_index(hw, rcg->parent_map, f->src); |
208 | if (index < 0) | |
209 | return index; | |
210 | ||
98d8a60e | 211 | clk_flags = clk_hw_get_flags(hw); |
ac269395 | 212 | p = clk_hw_get_parent_by_index(hw, index); |
bcd61c0f SB |
213 | if (clk_flags & CLK_SET_RATE_PARENT) { |
214 | if (f->pre_div) { | |
215 | rate /= 2; | |
216 | rate *= f->pre_div + 1; | |
217 | } | |
218 | ||
219 | if (f->n) { | |
220 | u64 tmp = rate; | |
221 | tmp = tmp * f->n; | |
222 | do_div(tmp, f->m); | |
223 | rate = tmp; | |
224 | } | |
225 | } else { | |
ac269395 | 226 | rate = clk_hw_get_rate(p); |
bcd61c0f | 227 | } |
ac269395 | 228 | req->best_parent_hw = p; |
0817b62c BB |
229 | req->best_parent_rate = rate; |
230 | req->rate = f->freq; | |
bcd61c0f | 231 | |
0817b62c | 232 | return 0; |
bcd61c0f SB |
233 | } |
234 | ||
0817b62c BB |
235 | static int clk_rcg2_determine_rate(struct clk_hw *hw, |
236 | struct clk_rate_request *req) | |
bcd61c0f SB |
237 | { |
238 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); | |
239 | ||
081ba802 RN |
240 | return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req, CEIL); |
241 | } | |
242 | ||
243 | static int clk_rcg2_determine_floor_rate(struct clk_hw *hw, | |
244 | struct clk_rate_request *req) | |
245 | { | |
246 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); | |
247 | ||
248 | return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req, FLOOR); | |
bcd61c0f SB |
249 | } |
250 | ||
99cbd064 | 251 | static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f) |
bcd61c0f | 252 | { |
bcd61c0f | 253 | u32 cfg, mask; |
293d2e97 GD |
254 | struct clk_hw *hw = &rcg->clkr.hw; |
255 | int ret, index = qcom_find_src_index(hw, rcg->parent_map, f->src); | |
256 | ||
257 | if (index < 0) | |
258 | return index; | |
bcd61c0f | 259 | |
bcd61c0f SB |
260 | if (rcg->mnd_width && f->n) { |
261 | mask = BIT(rcg->mnd_width) - 1; | |
99cbd064 SB |
262 | ret = regmap_update_bits(rcg->clkr.regmap, |
263 | rcg->cmd_rcgr + M_REG, mask, f->m); | |
bcd61c0f SB |
264 | if (ret) |
265 | return ret; | |
266 | ||
99cbd064 SB |
267 | ret = regmap_update_bits(rcg->clkr.regmap, |
268 | rcg->cmd_rcgr + N_REG, mask, ~(f->n - f->m)); | |
bcd61c0f SB |
269 | if (ret) |
270 | return ret; | |
271 | ||
99cbd064 SB |
272 | ret = regmap_update_bits(rcg->clkr.regmap, |
273 | rcg->cmd_rcgr + D_REG, mask, ~f->n); | |
bcd61c0f SB |
274 | if (ret) |
275 | return ret; | |
276 | } | |
277 | ||
278 | mask = BIT(rcg->hid_width) - 1; | |
279 | mask |= CFG_SRC_SEL_MASK | CFG_MODE_MASK; | |
280 | cfg = f->pre_div << CFG_SRC_DIV_SHIFT; | |
293d2e97 | 281 | cfg |= rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT; |
0b21503d | 282 | if (rcg->mnd_width && f->n && (f->m != f->n)) |
bcd61c0f | 283 | cfg |= CFG_MODE_DUAL_EDGE; |
99cbd064 SB |
284 | ret = regmap_update_bits(rcg->clkr.regmap, |
285 | rcg->cmd_rcgr + CFG_REG, mask, cfg); | |
bcd61c0f SB |
286 | if (ret) |
287 | return ret; | |
288 | ||
289 | return update_config(rcg); | |
290 | } | |
291 | ||
081ba802 RN |
292 | static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate, |
293 | enum freq_policy policy) | |
99cbd064 SB |
294 | { |
295 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); | |
296 | const struct freq_tbl *f; | |
297 | ||
081ba802 RN |
298 | switch (policy) { |
299 | case FLOOR: | |
300 | f = qcom_find_freq_floor(rcg->freq_tbl, rate); | |
301 | break; | |
302 | case CEIL: | |
303 | f = qcom_find_freq(rcg->freq_tbl, rate); | |
304 | break; | |
305 | default: | |
306 | return -EINVAL; | |
307 | }; | |
308 | ||
99cbd064 SB |
309 | if (!f) |
310 | return -EINVAL; | |
311 | ||
312 | return clk_rcg2_configure(rcg, f); | |
313 | } | |
314 | ||
bcd61c0f SB |
315 | static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate, |
316 | unsigned long parent_rate) | |
317 | { | |
081ba802 RN |
318 | return __clk_rcg2_set_rate(hw, rate, CEIL); |
319 | } | |
320 | ||
321 | static int clk_rcg2_set_floor_rate(struct clk_hw *hw, unsigned long rate, | |
322 | unsigned long parent_rate) | |
323 | { | |
324 | return __clk_rcg2_set_rate(hw, rate, FLOOR); | |
bcd61c0f SB |
325 | } |
326 | ||
327 | static int clk_rcg2_set_rate_and_parent(struct clk_hw *hw, | |
328 | unsigned long rate, unsigned long parent_rate, u8 index) | |
329 | { | |
081ba802 RN |
330 | return __clk_rcg2_set_rate(hw, rate, CEIL); |
331 | } | |
332 | ||
333 | static int clk_rcg2_set_floor_rate_and_parent(struct clk_hw *hw, | |
334 | unsigned long rate, unsigned long parent_rate, u8 index) | |
335 | { | |
336 | return __clk_rcg2_set_rate(hw, rate, FLOOR); | |
bcd61c0f SB |
337 | } |
338 | ||
339 | const struct clk_ops clk_rcg2_ops = { | |
340 | .is_enabled = clk_rcg2_is_enabled, | |
341 | .get_parent = clk_rcg2_get_parent, | |
342 | .set_parent = clk_rcg2_set_parent, | |
343 | .recalc_rate = clk_rcg2_recalc_rate, | |
344 | .determine_rate = clk_rcg2_determine_rate, | |
345 | .set_rate = clk_rcg2_set_rate, | |
346 | .set_rate_and_parent = clk_rcg2_set_rate_and_parent, | |
347 | }; | |
348 | EXPORT_SYMBOL_GPL(clk_rcg2_ops); | |
99cbd064 | 349 | |
081ba802 RN |
350 | const struct clk_ops clk_rcg2_floor_ops = { |
351 | .is_enabled = clk_rcg2_is_enabled, | |
352 | .get_parent = clk_rcg2_get_parent, | |
353 | .set_parent = clk_rcg2_set_parent, | |
354 | .recalc_rate = clk_rcg2_recalc_rate, | |
355 | .determine_rate = clk_rcg2_determine_floor_rate, | |
356 | .set_rate = clk_rcg2_set_floor_rate, | |
357 | .set_rate_and_parent = clk_rcg2_set_floor_rate_and_parent, | |
358 | }; | |
359 | EXPORT_SYMBOL_GPL(clk_rcg2_floor_ops); | |
360 | ||
99cbd064 SB |
361 | struct frac_entry { |
362 | int num; | |
363 | int den; | |
364 | }; | |
365 | ||
366 | static const struct frac_entry frac_table_675m[] = { /* link rate of 270M */ | |
367 | { 52, 295 }, /* 119 M */ | |
368 | { 11, 57 }, /* 130.25 M */ | |
369 | { 63, 307 }, /* 138.50 M */ | |
370 | { 11, 50 }, /* 148.50 M */ | |
371 | { 47, 206 }, /* 154 M */ | |
372 | { 31, 100 }, /* 205.25 M */ | |
373 | { 107, 269 }, /* 268.50 M */ | |
374 | { }, | |
375 | }; | |
376 | ||
377 | static struct frac_entry frac_table_810m[] = { /* Link rate of 162M */ | |
378 | { 31, 211 }, /* 119 M */ | |
379 | { 32, 199 }, /* 130.25 M */ | |
380 | { 63, 307 }, /* 138.50 M */ | |
381 | { 11, 60 }, /* 148.50 M */ | |
382 | { 50, 263 }, /* 154 M */ | |
383 | { 31, 120 }, /* 205.25 M */ | |
384 | { 119, 359 }, /* 268.50 M */ | |
385 | { }, | |
386 | }; | |
387 | ||
388 | static int clk_edp_pixel_set_rate(struct clk_hw *hw, unsigned long rate, | |
389 | unsigned long parent_rate) | |
390 | { | |
391 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); | |
392 | struct freq_tbl f = *rcg->freq_tbl; | |
393 | const struct frac_entry *frac; | |
394 | int delta = 100000; | |
395 | s64 src_rate = parent_rate; | |
396 | s64 request; | |
397 | u32 mask = BIT(rcg->hid_width) - 1; | |
398 | u32 hid_div; | |
399 | ||
400 | if (src_rate == 810000000) | |
401 | frac = frac_table_810m; | |
402 | else | |
403 | frac = frac_table_675m; | |
404 | ||
405 | for (; frac->num; frac++) { | |
406 | request = rate; | |
407 | request *= frac->den; | |
408 | request = div_s64(request, frac->num); | |
409 | if ((src_rate < (request - delta)) || | |
410 | (src_rate > (request + delta))) | |
411 | continue; | |
412 | ||
413 | regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, | |
414 | &hid_div); | |
415 | f.pre_div = hid_div; | |
416 | f.pre_div >>= CFG_SRC_DIV_SHIFT; | |
417 | f.pre_div &= mask; | |
418 | f.m = frac->num; | |
419 | f.n = frac->den; | |
420 | ||
421 | return clk_rcg2_configure(rcg, &f); | |
422 | } | |
423 | ||
424 | return -EINVAL; | |
425 | } | |
426 | ||
427 | static int clk_edp_pixel_set_rate_and_parent(struct clk_hw *hw, | |
428 | unsigned long rate, unsigned long parent_rate, u8 index) | |
429 | { | |
430 | /* Parent index is set statically in frequency table */ | |
431 | return clk_edp_pixel_set_rate(hw, rate, parent_rate); | |
432 | } | |
433 | ||
0817b62c BB |
434 | static int clk_edp_pixel_determine_rate(struct clk_hw *hw, |
435 | struct clk_rate_request *req) | |
99cbd064 SB |
436 | { |
437 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); | |
438 | const struct freq_tbl *f = rcg->freq_tbl; | |
439 | const struct frac_entry *frac; | |
440 | int delta = 100000; | |
99cbd064 SB |
441 | s64 request; |
442 | u32 mask = BIT(rcg->hid_width) - 1; | |
443 | u32 hid_div; | |
2f272e7b | 444 | int index = qcom_find_src_index(hw, rcg->parent_map, f->src); |
99cbd064 SB |
445 | |
446 | /* Force the correct parent */ | |
ac269395 SB |
447 | req->best_parent_hw = clk_hw_get_parent_by_index(hw, index); |
448 | req->best_parent_rate = clk_hw_get_rate(req->best_parent_hw); | |
99cbd064 | 449 | |
0817b62c | 450 | if (req->best_parent_rate == 810000000) |
99cbd064 SB |
451 | frac = frac_table_810m; |
452 | else | |
453 | frac = frac_table_675m; | |
454 | ||
455 | for (; frac->num; frac++) { | |
0817b62c | 456 | request = req->rate; |
99cbd064 SB |
457 | request *= frac->den; |
458 | request = div_s64(request, frac->num); | |
0817b62c BB |
459 | if ((req->best_parent_rate < (request - delta)) || |
460 | (req->best_parent_rate > (request + delta))) | |
99cbd064 SB |
461 | continue; |
462 | ||
463 | regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, | |
464 | &hid_div); | |
465 | hid_div >>= CFG_SRC_DIV_SHIFT; | |
466 | hid_div &= mask; | |
467 | ||
0817b62c BB |
468 | req->rate = calc_rate(req->best_parent_rate, |
469 | frac->num, frac->den, | |
470 | !!frac->den, hid_div); | |
471 | return 0; | |
99cbd064 SB |
472 | } |
473 | ||
474 | return -EINVAL; | |
475 | } | |
476 | ||
477 | const struct clk_ops clk_edp_pixel_ops = { | |
478 | .is_enabled = clk_rcg2_is_enabled, | |
479 | .get_parent = clk_rcg2_get_parent, | |
480 | .set_parent = clk_rcg2_set_parent, | |
481 | .recalc_rate = clk_rcg2_recalc_rate, | |
482 | .set_rate = clk_edp_pixel_set_rate, | |
483 | .set_rate_and_parent = clk_edp_pixel_set_rate_and_parent, | |
484 | .determine_rate = clk_edp_pixel_determine_rate, | |
485 | }; | |
486 | EXPORT_SYMBOL_GPL(clk_edp_pixel_ops); | |
487 | ||
0817b62c BB |
488 | static int clk_byte_determine_rate(struct clk_hw *hw, |
489 | struct clk_rate_request *req) | |
99cbd064 SB |
490 | { |
491 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); | |
492 | const struct freq_tbl *f = rcg->freq_tbl; | |
2f272e7b | 493 | int index = qcom_find_src_index(hw, rcg->parent_map, f->src); |
99cbd064 SB |
494 | unsigned long parent_rate, div; |
495 | u32 mask = BIT(rcg->hid_width) - 1; | |
ac269395 | 496 | struct clk_hw *p; |
99cbd064 | 497 | |
0817b62c | 498 | if (req->rate == 0) |
99cbd064 SB |
499 | return -EINVAL; |
500 | ||
ac269395 SB |
501 | req->best_parent_hw = p = clk_hw_get_parent_by_index(hw, index); |
502 | req->best_parent_rate = parent_rate = clk_hw_round_rate(p, req->rate); | |
99cbd064 | 503 | |
0817b62c | 504 | div = DIV_ROUND_UP((2 * parent_rate), req->rate) - 1; |
99cbd064 SB |
505 | div = min_t(u32, div, mask); |
506 | ||
0817b62c BB |
507 | req->rate = calc_rate(parent_rate, 0, 0, 0, div); |
508 | ||
509 | return 0; | |
99cbd064 SB |
510 | } |
511 | ||
512 | static int clk_byte_set_rate(struct clk_hw *hw, unsigned long rate, | |
513 | unsigned long parent_rate) | |
514 | { | |
515 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); | |
516 | struct freq_tbl f = *rcg->freq_tbl; | |
517 | unsigned long div; | |
518 | u32 mask = BIT(rcg->hid_width) - 1; | |
519 | ||
520 | div = DIV_ROUND_UP((2 * parent_rate), rate) - 1; | |
521 | div = min_t(u32, div, mask); | |
522 | ||
523 | f.pre_div = div; | |
524 | ||
525 | return clk_rcg2_configure(rcg, &f); | |
526 | } | |
527 | ||
528 | static int clk_byte_set_rate_and_parent(struct clk_hw *hw, | |
529 | unsigned long rate, unsigned long parent_rate, u8 index) | |
530 | { | |
531 | /* Parent index is set statically in frequency table */ | |
532 | return clk_byte_set_rate(hw, rate, parent_rate); | |
533 | } | |
534 | ||
535 | const struct clk_ops clk_byte_ops = { | |
536 | .is_enabled = clk_rcg2_is_enabled, | |
537 | .get_parent = clk_rcg2_get_parent, | |
538 | .set_parent = clk_rcg2_set_parent, | |
539 | .recalc_rate = clk_rcg2_recalc_rate, | |
540 | .set_rate = clk_byte_set_rate, | |
541 | .set_rate_and_parent = clk_byte_set_rate_and_parent, | |
542 | .determine_rate = clk_byte_determine_rate, | |
543 | }; | |
544 | EXPORT_SYMBOL_GPL(clk_byte_ops); | |
545 | ||
8ee9c7de SB |
546 | static int clk_byte2_determine_rate(struct clk_hw *hw, |
547 | struct clk_rate_request *req) | |
548 | { | |
549 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); | |
550 | unsigned long parent_rate, div; | |
551 | u32 mask = BIT(rcg->hid_width) - 1; | |
552 | struct clk_hw *p; | |
553 | unsigned long rate = req->rate; | |
554 | ||
555 | if (rate == 0) | |
556 | return -EINVAL; | |
557 | ||
558 | p = req->best_parent_hw; | |
559 | req->best_parent_rate = parent_rate = clk_hw_round_rate(p, rate); | |
560 | ||
561 | div = DIV_ROUND_UP((2 * parent_rate), rate) - 1; | |
562 | div = min_t(u32, div, mask); | |
563 | ||
564 | req->rate = calc_rate(parent_rate, 0, 0, 0, div); | |
565 | ||
566 | return 0; | |
567 | } | |
568 | ||
569 | static int clk_byte2_set_rate(struct clk_hw *hw, unsigned long rate, | |
570 | unsigned long parent_rate) | |
571 | { | |
572 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); | |
573 | struct freq_tbl f = { 0 }; | |
574 | unsigned long div; | |
575 | int i, num_parents = clk_hw_get_num_parents(hw); | |
576 | u32 mask = BIT(rcg->hid_width) - 1; | |
577 | u32 cfg; | |
578 | ||
579 | div = DIV_ROUND_UP((2 * parent_rate), rate) - 1; | |
580 | div = min_t(u32, div, mask); | |
581 | ||
582 | f.pre_div = div; | |
583 | ||
584 | regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg); | |
585 | cfg &= CFG_SRC_SEL_MASK; | |
586 | cfg >>= CFG_SRC_SEL_SHIFT; | |
587 | ||
588 | for (i = 0; i < num_parents; i++) { | |
589 | if (cfg == rcg->parent_map[i].cfg) { | |
590 | f.src = rcg->parent_map[i].src; | |
591 | return clk_rcg2_configure(rcg, &f); | |
592 | } | |
593 | } | |
594 | ||
595 | return -EINVAL; | |
596 | } | |
597 | ||
598 | static int clk_byte2_set_rate_and_parent(struct clk_hw *hw, | |
599 | unsigned long rate, unsigned long parent_rate, u8 index) | |
600 | { | |
601 | /* Read the hardware to determine parent during set_rate */ | |
602 | return clk_byte2_set_rate(hw, rate, parent_rate); | |
603 | } | |
604 | ||
605 | const struct clk_ops clk_byte2_ops = { | |
606 | .is_enabled = clk_rcg2_is_enabled, | |
607 | .get_parent = clk_rcg2_get_parent, | |
608 | .set_parent = clk_rcg2_set_parent, | |
609 | .recalc_rate = clk_rcg2_recalc_rate, | |
610 | .set_rate = clk_byte2_set_rate, | |
611 | .set_rate_and_parent = clk_byte2_set_rate_and_parent, | |
612 | .determine_rate = clk_byte2_determine_rate, | |
613 | }; | |
614 | EXPORT_SYMBOL_GPL(clk_byte2_ops); | |
615 | ||
99cbd064 SB |
616 | static const struct frac_entry frac_table_pixel[] = { |
617 | { 3, 8 }, | |
618 | { 2, 9 }, | |
619 | { 4, 9 }, | |
620 | { 1, 1 }, | |
621 | { } | |
622 | }; | |
623 | ||
0817b62c BB |
624 | static int clk_pixel_determine_rate(struct clk_hw *hw, |
625 | struct clk_rate_request *req) | |
99cbd064 | 626 | { |
99cbd064 SB |
627 | unsigned long request, src_rate; |
628 | int delta = 100000; | |
99cbd064 | 629 | const struct frac_entry *frac = frac_table_pixel; |
99cbd064 SB |
630 | |
631 | for (; frac->num; frac++) { | |
0817b62c | 632 | request = (req->rate * frac->den) / frac->num; |
99cbd064 | 633 | |
ac269395 | 634 | src_rate = clk_hw_round_rate(req->best_parent_hw, request); |
99cbd064 SB |
635 | if ((src_rate < (request - delta)) || |
636 | (src_rate > (request + delta))) | |
637 | continue; | |
638 | ||
0817b62c BB |
639 | req->best_parent_rate = src_rate; |
640 | req->rate = (src_rate * frac->num) / frac->den; | |
641 | return 0; | |
99cbd064 SB |
642 | } |
643 | ||
644 | return -EINVAL; | |
645 | } | |
646 | ||
647 | static int clk_pixel_set_rate(struct clk_hw *hw, unsigned long rate, | |
648 | unsigned long parent_rate) | |
649 | { | |
650 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); | |
8ee9c7de | 651 | struct freq_tbl f = { 0 }; |
99cbd064 | 652 | const struct frac_entry *frac = frac_table_pixel; |
6d451367 | 653 | unsigned long request; |
99cbd064 SB |
654 | int delta = 100000; |
655 | u32 mask = BIT(rcg->hid_width) - 1; | |
8ee9c7de SB |
656 | u32 hid_div, cfg; |
657 | int i, num_parents = clk_hw_get_num_parents(hw); | |
658 | ||
659 | regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg); | |
660 | cfg &= CFG_SRC_SEL_MASK; | |
661 | cfg >>= CFG_SRC_SEL_SHIFT; | |
662 | ||
663 | for (i = 0; i < num_parents; i++) | |
664 | if (cfg == rcg->parent_map[i].cfg) { | |
665 | f.src = rcg->parent_map[i].src; | |
666 | break; | |
667 | } | |
99cbd064 SB |
668 | |
669 | for (; frac->num; frac++) { | |
670 | request = (rate * frac->den) / frac->num; | |
671 | ||
6d451367 HL |
672 | if ((parent_rate < (request - delta)) || |
673 | (parent_rate > (request + delta))) | |
99cbd064 SB |
674 | continue; |
675 | ||
676 | regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, | |
677 | &hid_div); | |
678 | f.pre_div = hid_div; | |
679 | f.pre_div >>= CFG_SRC_DIV_SHIFT; | |
680 | f.pre_div &= mask; | |
681 | f.m = frac->num; | |
682 | f.n = frac->den; | |
683 | ||
684 | return clk_rcg2_configure(rcg, &f); | |
685 | } | |
686 | return -EINVAL; | |
687 | } | |
688 | ||
689 | static int clk_pixel_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, | |
690 | unsigned long parent_rate, u8 index) | |
691 | { | |
99cbd064 SB |
692 | return clk_pixel_set_rate(hw, rate, parent_rate); |
693 | } | |
694 | ||
695 | const struct clk_ops clk_pixel_ops = { | |
696 | .is_enabled = clk_rcg2_is_enabled, | |
697 | .get_parent = clk_rcg2_get_parent, | |
698 | .set_parent = clk_rcg2_set_parent, | |
699 | .recalc_rate = clk_rcg2_recalc_rate, | |
700 | .set_rate = clk_pixel_set_rate, | |
701 | .set_rate_and_parent = clk_pixel_set_rate_and_parent, | |
702 | .determine_rate = clk_pixel_determine_rate, | |
703 | }; | |
704 | EXPORT_SYMBOL_GPL(clk_pixel_ops); | |
55213e1a SB |
705 | |
706 | static int clk_gfx3d_determine_rate(struct clk_hw *hw, | |
707 | struct clk_rate_request *req) | |
708 | { | |
709 | struct clk_rate_request parent_req = { }; | |
710 | struct clk_hw *p2, *p8, *p9, *xo; | |
711 | unsigned long p9_rate; | |
712 | int ret; | |
713 | ||
714 | xo = clk_hw_get_parent_by_index(hw, 0); | |
715 | if (req->rate == clk_hw_get_rate(xo)) { | |
716 | req->best_parent_hw = xo; | |
717 | return 0; | |
718 | } | |
719 | ||
720 | p9 = clk_hw_get_parent_by_index(hw, 2); | |
721 | p2 = clk_hw_get_parent_by_index(hw, 3); | |
722 | p8 = clk_hw_get_parent_by_index(hw, 4); | |
723 | ||
724 | /* PLL9 is a fixed rate PLL */ | |
725 | p9_rate = clk_hw_get_rate(p9); | |
726 | ||
727 | parent_req.rate = req->rate = min(req->rate, p9_rate); | |
728 | if (req->rate == p9_rate) { | |
729 | req->rate = req->best_parent_rate = p9_rate; | |
730 | req->best_parent_hw = p9; | |
731 | return 0; | |
732 | } | |
733 | ||
734 | if (req->best_parent_hw == p9) { | |
735 | /* Are we going back to a previously used rate? */ | |
736 | if (clk_hw_get_rate(p8) == req->rate) | |
737 | req->best_parent_hw = p8; | |
738 | else | |
739 | req->best_parent_hw = p2; | |
740 | } else if (req->best_parent_hw == p8) { | |
741 | req->best_parent_hw = p2; | |
742 | } else { | |
743 | req->best_parent_hw = p8; | |
744 | } | |
745 | ||
746 | ret = __clk_determine_rate(req->best_parent_hw, &parent_req); | |
747 | if (ret) | |
748 | return ret; | |
749 | ||
750 | req->rate = req->best_parent_rate = parent_req.rate; | |
751 | ||
752 | return 0; | |
753 | } | |
754 | ||
755 | static int clk_gfx3d_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, | |
756 | unsigned long parent_rate, u8 index) | |
757 | { | |
758 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); | |
759 | u32 cfg; | |
760 | int ret; | |
761 | ||
762 | /* Just mux it, we don't use the division or m/n hardware */ | |
763 | cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT; | |
764 | ret = regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, cfg); | |
765 | if (ret) | |
766 | return ret; | |
767 | ||
768 | return update_config(rcg); | |
769 | } | |
770 | ||
771 | static int clk_gfx3d_set_rate(struct clk_hw *hw, unsigned long rate, | |
772 | unsigned long parent_rate) | |
773 | { | |
774 | /* | |
775 | * We should never get here; clk_gfx3d_determine_rate() should always | |
776 | * make us use a different parent than what we're currently using, so | |
777 | * clk_gfx3d_set_rate_and_parent() should always be called. | |
778 | */ | |
779 | return 0; | |
780 | } | |
781 | ||
782 | const struct clk_ops clk_gfx3d_ops = { | |
783 | .is_enabled = clk_rcg2_is_enabled, | |
784 | .get_parent = clk_rcg2_get_parent, | |
785 | .set_parent = clk_rcg2_set_parent, | |
786 | .recalc_rate = clk_rcg2_recalc_rate, | |
787 | .set_rate = clk_gfx3d_set_rate, | |
788 | .set_rate_and_parent = clk_gfx3d_set_rate_and_parent, | |
789 | .determine_rate = clk_gfx3d_determine_rate, | |
790 | }; | |
791 | EXPORT_SYMBOL_GPL(clk_gfx3d_ops); |