7107134a8e7e56de6f684af95577d04b8e19c945
[firefly-linux-kernel-4.4.55.git] / drivers / clk / rockchip / clk-ops.c
1 #include <linux/clk.h>
2 #include <linux/clkdev.h>
3 #include <linux/io.h>
4 #include <linux/clk-provider.h>
5 #include <linux/of.h>
6 #include <linux/of_address.h>
7 #include <linux/clk-private.h>
8 #include <linux/delay.h>
9 #include <linux/rockchip/common.h>
10
11 #include "clk-ops.h"
12
13 /* mux_ops */
14 struct clk_ops_table rk_clk_mux_ops_table[] = {
15         {.index = CLKOPS_TABLE_END},
16 };
17
18
19 /* rate_ops */
20 #define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw)
21 #define div_mask(d)     ((1 << ((d)->width)) - 1)
22
23 static u32 clk_gcd(u32 numerator, u32 denominator)
24 {
25         u32 a, b;
26
27         if (!numerator || !denominator)
28                 return 0;
29         if (numerator > denominator) {
30                 a = numerator;
31                 b = denominator;
32         } else {
33                 a = denominator;
34                 b = numerator;
35         }
36         while (b != 0) {
37                 int r = b;
38                 b = a % b;
39                 a = r;
40         }
41
42         return a;
43 }
44
45 static int clk_fracdiv_get_config(unsigned long rate_out, unsigned long rate,
46                 u32 *numerator, u32 *denominator)
47 {
48         u32 gcd_val;
49         gcd_val = clk_gcd(rate, rate_out);
50         clk_debug("%s: frac_get_seting rate=%lu, parent=%lu, gcd=%d\n",
51                         __func__, rate_out, rate, gcd_val);
52
53         if (!gcd_val) {
54                 clk_err("gcd=0, frac div is not be supported\n");
55                 return -EINVAL;
56         }
57
58         *numerator = rate_out / gcd_val;
59         *denominator = rate / gcd_val;
60
61         clk_debug("%s: frac_get_seting numerator=%d, denominator=%d, times=%d\n",
62                         __func__, *numerator, *denominator,
63                         *denominator / *numerator);
64
65         if (*numerator > 0xffff || *denominator > 0xffff ||
66                         (*denominator / (*numerator)) < 20) {
67                 clk_err("can't get a available nume and deno\n");
68                 return -EINVAL;
69         }
70
71         return 0;
72
73 }
74
75 static int clk_fracdiv_set_rate(struct clk_hw *hw, unsigned long rate,
76                 unsigned long parent_rate)
77 {
78         u32 numerator, denominator;
79         struct clk_divider *div = to_clk_divider(hw);
80
81
82         if(clk_fracdiv_get_config(rate, parent_rate,
83                                 &numerator, &denominator) == 0) {
84                 writel(numerator << 16 | denominator, div->reg);
85                 clk_debug("%s set rate=%lu,is ok\n", hw->clk->name, rate);
86         } else {
87                 clk_err("clk_frac_div name=%s can't get rate=%lu\n",
88                                 hw->clk->name, rate);
89                 return -EINVAL;
90         }
91
92         return 0;
93 }
94
95 static unsigned long clk_fracdiv_recalc(struct clk_hw *hw,
96                 unsigned long parent_rate)
97 {
98         unsigned long rate;
99         u64 rate64;
100         struct clk_divider *div = to_clk_divider(hw);
101         u32 numerator, denominator, reg_val;
102
103         reg_val = readl(div->reg);
104         if (reg_val == 0)
105                 return parent_rate;
106
107         numerator = reg_val >> 16;
108         denominator = reg_val & 0xFFFF;
109         rate64 = (u64)parent_rate * numerator;
110         do_div(rate64, denominator);
111         rate = rate64;
112         clk_debug("%s: %s new clock rate is %lu, prate %lu (frac %u/%u)\n",
113                         __func__, hw->clk->name, rate, parent_rate,
114                         numerator, denominator);
115         return rate;
116 }
117
118 static long clk_fracdiv_round_rate(struct clk_hw *hw, unsigned long rate,
119                 unsigned long *prate)
120 {
121         struct clk *clk = hw->clk;
122         struct clk *parent = clk->parent;
123         long rate_out;
124
125         //FIXME: now just simply return rate
126         /*
127          *frac_div request a big input rate, and its parent is always a div,
128          *so we set parent->parent->rate as best_parent_rate.
129          */
130         rate_out = rate;
131         *prate = parent->parent->rate;
132
133         return rate_out;
134 }
135
136 static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
137                 unsigned long parent_rate)
138 {
139         return clk_divider_ops.recalc_rate(hw, parent_rate);
140 }
141
142 static long clk_divider_round_rate(struct clk_hw *hw,
143                 unsigned long rate, unsigned long *prate)
144 {
145         return clk_divider_ops.round_rate(hw, rate, prate);
146 }
147
148 static int clk_divider_set_rate(struct clk_hw *hw,
149                 unsigned long rate, unsigned long parent_rate)
150 {
151         return clk_divider_ops.set_rate(hw, rate, parent_rate);
152 }
153
154 static long clk_mux_with_div_determine_rate(struct clk_hw *div_hw, unsigned long rate,
155                 unsigned long *best_parent_rate,
156                 struct clk **best_parent_p)
157 {
158         struct clk *clk = div_hw->clk, *parent = NULL, *best_parent = NULL;
159         int i, num_parents;
160         unsigned long parent_rate = 0, best_prate = 0, best = 0, now = 0;
161
162
163         parent = __clk_get_parent(clk);
164         if(!parent){
165                 best = __clk_get_rate(clk);
166                 goto out;
167         }
168
169         /* if NO_REPARENT flag set, pass through to current parent */
170         if (clk->flags & CLK_SET_RATE_NO_REPARENT) {
171                 best_prate = __clk_get_rate(parent);
172                 best = clk_divider_ops.round_rate(div_hw, rate, &best_prate);
173                 goto out;
174         }
175
176         /* find the parent that can provide the fastest rate <= rate */
177         num_parents = clk->num_parents;
178         for (i = 0; i < num_parents; i++) {
179                 parent = clk_get_parent_by_index(clk, i);
180                 if (!parent)
181                         continue;
182
183                 parent_rate = __clk_get_rate(parent);
184                 now = clk_divider_ops.round_rate(div_hw, rate, &parent_rate);
185
186                 if (now <= rate && now > best) {
187                         best_parent = parent;
188                         best_prate = parent_rate;
189                         best = now;
190                 }
191         }
192
193 out:
194         if(best_prate)
195                 *best_parent_rate = best_prate;
196
197         if (best_parent)
198                 *best_parent_p = best_parent;
199
200         clk_debug("clk name = %s, determine rate = %lu, best = %lu\n"
201                         "\tbest_parent name = %s, best_prate = %lu\n",
202                         clk->name, rate, best,
203                         __clk_get_name(*best_parent_p), *best_parent_rate);
204
205         return best;
206 }
207
208 const struct clk_ops clkops_rate_auto_parent = {
209         .recalc_rate    = clk_divider_recalc_rate,
210         .round_rate     = clk_divider_round_rate,
211         .set_rate       = clk_divider_set_rate,
212         .determine_rate = clk_mux_with_div_determine_rate,
213 };
214
215 static long clk_div_round_rate_even(struct clk_hw *hw, unsigned long rate,
216                 unsigned long *prate)
217 {
218         int i = 0;
219         struct clk_divider *divider =to_clk_divider(hw);
220         int max_div = 1 << divider->width;
221
222         for (i = 1; i <= max_div; i++) {
223                 if (i > 1 && (i % 2 != 0))
224                         continue;
225                 if (rate >= (*prate / i))
226                         return *prate / i;
227         }
228
229         return (*prate / max_div);
230 }
231
232 const struct clk_ops clkops_rate_evendiv = {
233         .recalc_rate    = clk_divider_recalc_rate,
234         .round_rate     = clk_div_round_rate_even,
235         .set_rate       = clk_divider_set_rate,
236 };
237
238 static long clk_mux_with_evendiv_determine_rate(struct clk_hw *div_hw, unsigned long rate,
239                 unsigned long *best_parent_rate,
240                 struct clk **best_parent_p)
241 {
242         struct clk *clk = div_hw->clk, *parent = NULL, *best_parent = NULL;
243         int i, num_parents;
244         unsigned long parent_rate = 0, best_prate = 0, best = 0, now = 0;
245
246
247         parent = __clk_get_parent(clk);
248         if(!parent){
249                 best = __clk_get_rate(clk);
250                 goto out;
251         }
252
253         /* if NO_REPARENT flag set, pass through to current parent */
254         if (clk->flags & CLK_SET_RATE_NO_REPARENT) {
255                 best_prate = __clk_get_rate(parent);
256                 best = clk_div_round_rate_even(div_hw, rate, &best_prate);
257                 goto out;
258         }
259
260         /* find the parent that can provide the fastest rate <= rate */
261         num_parents = clk->num_parents;
262         for (i = 0; i < num_parents; i++) {
263                 parent = clk_get_parent_by_index(clk, i);
264                 if (!parent)
265                         continue;
266
267                 parent_rate = __clk_get_rate(parent);
268                 now = clk_div_round_rate_even(div_hw, rate, &parent_rate);
269
270                 if (now <= rate && now > best) {
271                         best_parent = parent;
272                         best_prate = parent_rate;
273                         best = now;
274                 }
275         }
276
277 out:
278         if(best_prate)
279                 *best_parent_rate = best_prate;
280
281         if (best_parent)
282                 *best_parent_p = best_parent;
283
284         clk_debug("clk name = %s, determine rate = %lu, best = %lu\n"
285                         "\tbest_parent name = %s, best_prate = %lu\n",
286                         clk->name, rate, best,
287                         __clk_get_name(*best_parent_p), *best_parent_rate);
288
289         return best;
290 }
291
292 static long clk_mux_with_evendiv_round_rate(struct clk_hw *hw, unsigned long rate,
293                 unsigned long *prate)
294 {
295         return clk_div_round_rate_even(hw, rate, prate);
296 }
297
298 const struct clk_ops clkops_rate_mux_with_evendiv = {
299         .recalc_rate    = clk_divider_recalc_rate,
300         .set_rate       = clk_divider_set_rate,
301         .round_rate     = clk_mux_with_evendiv_round_rate,
302         .determine_rate = clk_mux_with_evendiv_determine_rate,
303 };
304
305 static int clk_i2s_fracdiv_set_rate(struct clk_hw *hw, unsigned long rate,
306                 unsigned long parent_rate)
307 {
308         u32 numerator, denominator;
309         struct clk_divider *div = to_clk_divider(hw);
310         int i = 10;
311
312
313         if(clk_fracdiv_get_config(rate, parent_rate,
314                                 &numerator, &denominator) == 0) {
315                 while (i--) {
316                         writel((numerator - 1) << 16 | denominator, div->reg);
317                         mdelay(1);
318                         writel(numerator << 16 | denominator, div->reg);
319                         mdelay(1);
320                 }
321                 clk_debug("%s set rate=%lu,is ok\n", hw->clk->name, rate);
322         } else {
323                 clk_err("clk_frac_div name=%s can't get rate=%lu\n",
324                                 hw->clk->name, rate);
325                 return -EINVAL;
326         }
327
328         return 0;
329 }
330
331 const struct clk_ops clkops_rate_frac = {
332         .recalc_rate    = clk_fracdiv_recalc,
333         .round_rate     = clk_fracdiv_round_rate,
334         .set_rate       = clk_fracdiv_set_rate,
335 };
336
337 const struct clk_ops clkops_rate_i2s_frac = {
338         .recalc_rate    = clk_fracdiv_recalc,
339         .round_rate     = clk_fracdiv_round_rate,
340         .set_rate       = clk_i2s_fracdiv_set_rate,
341 };
342
343 static unsigned long clk_core_recalc_rate(struct clk_hw *hw,
344                 unsigned long parent_rate)
345 {
346         /* As parent rate could be changed in clk_core.set_rate
347          * ops, the passing_in parent_rate may not be the newest
348          * and we should use the parent->rate instead. As a side
349          * effect, we should NOT directly set clk_core's parent
350          * (apll) rate, otherwise we will get a wrong recalc rate
351          * with clk_core_recalc_rate.
352          */
353         struct clk *parent = __clk_get_parent(hw->clk);
354
355         return clk_divider_recalc_rate(hw, __clk_get_rate(parent));
356 }
357
358 static long clk_core_determine_rate(struct clk_hw *hw, unsigned long rate,
359                 unsigned long *best_parent_rate,
360                 struct clk **best_parent_p)
361 {
362         struct clk *parent = __clk_get_parent(hw->clk);
363
364         if (IS_ERR_OR_NULL(parent)) {
365                 clk_err("fail to get parent!\n");
366                 return 0;
367         }
368
369         return clk_round_rate(parent, rate);
370 }
371
372 static long clk_core_round_rate(struct clk_hw *hw, unsigned long rate,
373                 unsigned long *prate)
374 {
375         return clk_core_determine_rate(hw, rate, prate, NULL);
376 }
377
378 static int clk_core_set_rate(struct clk_hw *hw, unsigned long rate,
379                 unsigned long parent_rate)
380 {
381         struct clk *parent = __clk_get_parent(hw->clk);
382         struct clk *grand_p = __clk_get_parent(parent);
383         int ret;
384
385         if (IS_ERR_OR_NULL(parent) || IS_ERR_OR_NULL(grand_p)) {
386                 clk_err("fail to get parent or grand_parent!\n");
387                 return -EINVAL;
388         }
389
390         ret = parent->ops->set_rate(parent->hw, rate, __clk_get_rate(grand_p));
391         parent->rate = parent->ops->recalc_rate(parent->hw,
392                         __clk_get_rate(grand_p));
393
394         return ret;
395 }
396
397 const struct clk_ops clkops_rate_core = {
398         .recalc_rate    = clk_core_recalc_rate,
399         .round_rate     = clk_core_round_rate,
400         .set_rate       = clk_core_set_rate,
401         .determine_rate = clk_core_determine_rate,
402 };
403
404 /* Clk_ops for the child clk of clk_core, for example core_periph in rk3188 */
405 const struct clk_ops clkops_rate_core_peri = {
406         .recalc_rate    = clk_divider_recalc_rate,
407         .round_rate     = clk_divider_round_rate,
408         .set_rate       = NULL,
409 };
410
411 static unsigned long clk_ddr_recalc_rate(struct clk_hw *hw,
412                 unsigned long parent_rate)
413 {
414         /* Same as clk_core, we should NOT set clk_ddr's parent
415          * (dpll) rate directly as a side effect.
416          */
417         return clk_core_recalc_rate(hw, parent_rate);
418 }
419
420 static long clk_ddr_determine_rate(struct clk_hw *hw, unsigned long rate,
421                 unsigned long *best_parent_rate,
422                 struct clk **best_parent_p)
423 {
424         long best = 0;
425
426         if (!ddr_round_rate) {
427                 /* Do nothing before ddr init */
428                 best = rate;//__clk_get_rate(hw->clk);
429         } else {
430                 /* Func provided by ddr driver */
431                 best = ddr_round_rate(rate/MHZ) * MHZ;
432         }
433
434         clk_debug("%s: from %lu to %lu\n", __func__, rate, best);
435
436         return best;
437 }
438
439 static long clk_ddr_round_rate(struct clk_hw *hw, unsigned long rate,
440                 unsigned long *prate)
441 {
442         return clk_ddr_determine_rate(hw, rate, prate, NULL);
443 }
444
445 static int clk_ddr_set_rate(struct clk_hw *hw, unsigned long rate,
446                 unsigned long parent_rate)
447 {
448         struct clk *parent = __clk_get_parent(hw->clk);
449         struct clk *grand_p = __clk_get_parent(parent);
450
451
452         /* Do nothing before ddr init */
453         if (!ddr_change_freq)
454                 return 0;
455
456         if (IS_ERR_OR_NULL(parent) || IS_ERR_OR_NULL(grand_p)) {
457                 clk_err("fail to get parent or grand_parent!\n");
458                 return -EINVAL;
459         }
460
461         clk_debug("%s: will set rate = %lu\n", __func__, rate);
462
463         /* Func provided by ddr driver */
464         ddr_change_freq(rate/MHZ);
465
466         parent->rate = parent->ops->recalc_rate(parent->hw,
467                         __clk_get_rate(grand_p));
468
469         return 0;
470 }
471
472 const struct clk_ops clkops_rate_ddr = {
473         .recalc_rate    = clk_ddr_recalc_rate,
474         .round_rate     = clk_ddr_round_rate,
475         .set_rate       = clk_ddr_set_rate,
476         .determine_rate = clk_ddr_determine_rate,
477 };
478
479 static unsigned long clk_3288_i2s_recalc_rate(struct clk_hw *hw,
480                 unsigned long parent_rate)
481 {
482         return parent_rate;
483 }
484
485 static long clk_3288_i2s_round_rate(struct clk_hw *hw, unsigned long rate,
486                 unsigned long *prate)
487 {
488         return rate;
489 }
490
491 static int clk_3288_i2s_set_rate(struct clk_hw *hw, unsigned long rate,
492                 unsigned long parent_rate)
493 {
494         struct clk *parent = __clk_get_parent(hw->clk);
495         struct clk *grand_p = __clk_get_parent(parent);
496
497
498         if (IS_ERR_OR_NULL(parent) || IS_ERR_OR_NULL(grand_p)) {
499                 return 0;
500         }
501
502         if (parent->ops->set_rate) {
503                 parent->ops->set_rate(parent->hw, rate/2, __clk_get_rate(grand_p));
504                 parent->ops->set_rate(parent->hw, rate, __clk_get_rate(grand_p));
505         }
506
507         return 0;
508 }
509
510 const struct clk_ops clkops_rate_3288_i2s = {
511         .recalc_rate    = clk_3288_i2s_recalc_rate,
512         .round_rate     = clk_3288_i2s_round_rate,
513         .set_rate       = clk_3288_i2s_set_rate,
514 };
515
516 static bool usb480m_state = false;
517
518 static long clk_3288_usb480m_determine_rate(struct clk_hw *hw, unsigned long rate,
519                 unsigned long *best_parent_rate,
520                 struct clk **best_parent_p)
521 {
522         if(rate == 0)
523                 return 0;
524         else
525                 return 480*MHZ;
526 }
527
528 static long clk_3288_usb480m_round_rate(struct clk_hw *hw, unsigned long rate,
529                 unsigned long *prate)
530 {
531         return clk_3288_usb480m_determine_rate(hw, rate, prate, NULL);
532 }
533
534 static int clk_3288_usb480m_set_rate(struct clk_hw *hw, unsigned long rate,
535                 unsigned long parent_rate)
536 {
537         if(rate == 0)
538                 usb480m_state = false;
539         else
540                 usb480m_state = true;
541
542         return 0;
543 }
544
545 static unsigned long clk_3288_usb480m_recalc_rate(struct clk_hw *hw,
546                 unsigned long parent_rate)
547 {
548         if(usb480m_state)
549                 return 480*MHZ;
550         else
551                 return 0;
552 }
553
554 const struct clk_ops clkops_rate_3288_usb480m = {
555         .determine_rate = clk_3288_usb480m_determine_rate,
556         .set_rate       = clk_3288_usb480m_set_rate,
557         .round_rate     = clk_3288_usb480m_round_rate,
558         .recalc_rate    = clk_3288_usb480m_recalc_rate,
559 };
560
561 #define RK3288_LIMIT_PLL_VIO0 (410*MHZ)
562
563 static long clk_3288_dclk_lcdc0_determine_rate(struct clk_hw *hw, unsigned long rate,
564                 unsigned long *best_parent_rate,
565                 struct clk **best_parent_p)
566 {
567         struct clk *gpll = clk_get(NULL, "clk_gpll");
568         struct clk *cpll = clk_get(NULL, "clk_cpll");
569         unsigned long best, div, prate;
570
571
572         if((rate <= (297*MHZ)) && ((297*MHZ)%rate == 0)) {
573                 *best_parent_p = gpll;
574                 best = rate;
575                 *best_parent_rate = 297*MHZ;
576         } else {
577                 *best_parent_p = cpll;
578                 div = RK3288_LIMIT_PLL_VIO0/rate;
579                 prate = div * rate;
580                 *best_parent_rate = clk_round_rate(cpll, prate);
581                 best = (*best_parent_rate)/div; 
582         }
583
584         return best;
585 }
586
587 static long clk_3288_dclk_lcdc0_round_rate(struct clk_hw *hw, unsigned long rate,
588                 unsigned long *prate)
589 {
590         return clk_3288_dclk_lcdc0_determine_rate(hw, rate, prate, NULL);
591 }
592
593 static int clk_3288_dclk_lcdc0_set_rate(struct clk_hw *hw, unsigned long rate,
594                 unsigned long parent_rate)
595 {
596         struct clk* aclk_vio0 = clk_get(NULL, "aclk_vio0");
597         struct clk* hclk_vio = clk_get(NULL, "hclk_vio");
598         struct clk* parent;
599
600         clk_divider_ops.set_rate(hw, rate, parent_rate);
601
602         /* set aclk_vio */
603         if(parent_rate  == 297*MHZ)
604                 parent = clk_get(NULL, "clk_gpll");
605         else
606                 parent = clk_get(NULL, "clk_cpll");
607
608         clk_set_parent(aclk_vio0, parent);
609         clk_set_rate(aclk_vio0, __clk_get_rate(parent));
610         clk_set_rate(hclk_vio, 100*MHZ);
611
612         return 0;
613 }
614
615 const struct clk_ops clkops_rate_3288_dclk_lcdc0 = {
616         .determine_rate = clk_3288_dclk_lcdc0_determine_rate,
617         .set_rate       = clk_3288_dclk_lcdc0_set_rate,
618         .round_rate     = clk_3288_dclk_lcdc0_round_rate,
619         .recalc_rate    = clk_divider_recalc_rate,
620 };
621
622 #define RK3288_LIMIT_PLL_VIO1 (350*MHZ)
623
624 static long clk_3288_dclk_lcdc1_determine_rate(struct clk_hw *hw, unsigned long rate,
625                 unsigned long *best_parent_rate,
626                 struct clk **best_parent_p)
627 {
628         struct clk *gpll = clk_get(NULL, "clk_gpll");
629         struct clk *cpll = clk_get(NULL, "clk_cpll");
630         unsigned long best, div, prate;
631
632
633         if((rate <= (297*MHZ)) && ((297*MHZ)%rate == 0)) {
634                 *best_parent_p = gpll;
635                 best = rate;
636                 *best_parent_rate = 297*MHZ;
637         } else {
638                 *best_parent_p = cpll;
639                 div = RK3288_LIMIT_PLL_VIO1/rate;
640                 prate = div * rate;
641                 *best_parent_rate = clk_round_rate(cpll, prate);
642                 best = (*best_parent_rate)/div; 
643         }
644
645         return best;
646 }
647
648 static long clk_3288_dclk_lcdc1_round_rate(struct clk_hw *hw, unsigned long rate,
649                 unsigned long *prate)
650 {
651         return clk_3288_dclk_lcdc1_determine_rate(hw, rate, prate, NULL);
652 }
653
654 static int clk_3288_dclk_lcdc1_set_rate(struct clk_hw *hw, unsigned long rate,
655                 unsigned long parent_rate)
656 {
657         struct clk* aclk_vio1 = clk_get(NULL, "aclk_vio1");
658         struct clk* parent;
659
660         clk_divider_ops.set_rate(hw, rate, parent_rate);
661
662         /* set aclk_vio */
663         if(parent_rate  == 297*MHZ)
664                 parent = clk_get(NULL, "clk_gpll");
665         else
666                 parent = clk_get(NULL, "clk_cpll");
667
668         clk_set_parent(aclk_vio1, parent);
669         clk_set_rate(aclk_vio1, __clk_get_rate(parent));
670
671         return 0;
672 }
673
674 const struct clk_ops clkops_rate_3288_dclk_lcdc1 = {
675         .determine_rate = clk_3288_dclk_lcdc1_determine_rate,
676         .set_rate       = clk_3288_dclk_lcdc1_set_rate,
677         .round_rate     = clk_3288_dclk_lcdc1_round_rate,
678         .recalc_rate    = clk_divider_recalc_rate,
679 };
680
681
682 struct clk_ops_table rk_clkops_rate_table[] = {
683         {.index = CLKOPS_RATE_MUX_DIV,          .clk_ops = &clkops_rate_auto_parent},
684         {.index = CLKOPS_RATE_EVENDIV,          .clk_ops = &clkops_rate_evendiv},
685         {.index = CLKOPS_RATE_MUX_EVENDIV,      .clk_ops = &clkops_rate_mux_with_evendiv},
686         {.index = CLKOPS_RATE_I2S_FRAC,         .clk_ops = &clkops_rate_i2s_frac},
687         {.index = CLKOPS_RATE_FRAC,             .clk_ops = &clkops_rate_frac},
688         {.index = CLKOPS_RATE_CORE,             .clk_ops = &clkops_rate_core},
689         {.index = CLKOPS_RATE_CORE_CHILD,       .clk_ops = &clkops_rate_core_peri},
690         {.index = CLKOPS_RATE_DDR,              .clk_ops = &clkops_rate_ddr},
691         {.index = CLKOPS_RATE_RK3288_I2S,       .clk_ops = &clkops_rate_3288_i2s},
692         {.index = CLKOPS_RATE_RK3288_USB480M,   .clk_ops = &clkops_rate_3288_usb480m},
693         {.index = CLKOPS_RATE_RK3288_DCLK_LCDC0,.clk_ops = &clkops_rate_3288_dclk_lcdc0},
694         {.index = CLKOPS_RATE_RK3288_DCLK_LCDC1,.clk_ops = &clkops_rate_3288_dclk_lcdc1},
695         {.index = CLKOPS_RATE_I2S,              .clk_ops = NULL},
696         {.index = CLKOPS_RATE_CIFOUT,           .clk_ops = NULL},
697         {.index = CLKOPS_RATE_UART,             .clk_ops = NULL},
698         {.index = CLKOPS_RATE_HSADC,            .clk_ops = NULL},
699         {.index = CLKOPS_RATE_MAC_REF,          .clk_ops = NULL},
700         {.index = CLKOPS_TABLE_END,             .clk_ops = NULL},
701 };
702
703 const struct clk_ops *rk_get_clkops(unsigned int idx)
704 {
705         int i = 0;
706         unsigned int now_idx;
707
708         while(1){
709                 now_idx = rk_clkops_rate_table[i].index;
710
711                 if ((now_idx == idx) || (now_idx == CLKOPS_TABLE_END))
712                         return rk_clkops_rate_table[i].clk_ops;
713
714                 i++;
715         }
716 }
717 EXPORT_SYMBOL_GPL(rk_get_clkops);