Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0
2 : : /*
3 : : * Copyright (C) 2015 Maxime Ripard <maxime.ripard@free-electrons.com>
4 : : */
5 : :
6 : : #include <linux/bitops.h>
7 : : #include <linux/clk-provider.h>
8 : : #include <linux/err.h>
9 : : #include <linux/export.h>
10 : : #include <linux/io.h>
11 : : #include <linux/kernel.h>
12 : : #include <linux/of.h>
13 : : #include <linux/slab.h>
14 : :
15 : : static inline u32 clk_mult_readl(struct clk_multiplier *mult)
16 : : {
17 [ # # # # ]: 0 : if (mult->flags & CLK_MULTIPLIER_BIG_ENDIAN)
18 : 0 : return ioread32be(mult->reg);
19 : :
20 : 0 : return readl(mult->reg);
21 : : }
22 : :
23 : : static inline void clk_mult_writel(struct clk_multiplier *mult, u32 val)
24 : : {
25 [ # # ]: 0 : if (mult->flags & CLK_MULTIPLIER_BIG_ENDIAN)
26 : 0 : iowrite32be(val, mult->reg);
27 : : else
28 : 0 : writel(val, mult->reg);
29 : : }
30 : :
31 : : static unsigned long __get_mult(struct clk_multiplier *mult,
32 : : unsigned long rate,
33 : : unsigned long parent_rate)
34 : : {
35 [ # # ]: 0 : if (mult->flags & CLK_MULTIPLIER_ROUND_CLOSEST)
36 : 0 : return DIV_ROUND_CLOSEST(rate, parent_rate);
37 : :
38 : 0 : return rate / parent_rate;
39 : : }
40 : :
41 : 0 : static unsigned long clk_multiplier_recalc_rate(struct clk_hw *hw,
42 : : unsigned long parent_rate)
43 : : {
44 : : struct clk_multiplier *mult = to_clk_multiplier(hw);
45 : : unsigned long val;
46 : :
47 : 0 : val = clk_mult_readl(mult) >> mult->shift;
48 : 0 : val &= GENMASK(mult->width - 1, 0);
49 : :
50 [ # # # # ]: 0 : if (!val && mult->flags & CLK_MULTIPLIER_ZERO_BYPASS)
51 : : val = 1;
52 : :
53 : 0 : return parent_rate * val;
54 : : }
55 : :
56 : : static bool __is_best_rate(unsigned long rate, unsigned long new,
57 : : unsigned long best, unsigned long flags)
58 : : {
59 [ # # ]: 0 : if (flags & CLK_MULTIPLIER_ROUND_CLOSEST)
60 : 0 : return abs(rate - new) < abs(rate - best);
61 : :
62 : 0 : return new >= rate && new < best;
63 : : }
64 : :
65 : 0 : static unsigned long __bestmult(struct clk_hw *hw, unsigned long rate,
66 : : unsigned long *best_parent_rate,
67 : : u8 width, unsigned long flags)
68 : : {
69 : : struct clk_multiplier *mult = to_clk_multiplier(hw);
70 : 0 : unsigned long orig_parent_rate = *best_parent_rate;
71 : : unsigned long parent_rate, current_rate, best_rate = ~0;
72 : : unsigned int i, bestmult = 0;
73 : 0 : unsigned int maxmult = (1 << width) - 1;
74 : :
75 [ # # ]: 0 : if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) {
76 : 0 : bestmult = rate / orig_parent_rate;
77 : :
78 : : /* Make sure we don't end up with a 0 multiplier */
79 [ # # # # ]: 0 : if ((bestmult == 0) &&
80 : 0 : !(mult->flags & CLK_MULTIPLIER_ZERO_BYPASS))
81 : : bestmult = 1;
82 : :
83 : : /* Make sure we don't overflow the multiplier */
84 [ # # ]: 0 : if (bestmult > maxmult)
85 : : bestmult = maxmult;
86 : :
87 : 0 : return bestmult;
88 : : }
89 : :
90 [ # # ]: 0 : for (i = 1; i < maxmult; i++) {
91 [ # # ]: 0 : if (rate == orig_parent_rate * i) {
92 : : /*
93 : : * This is the best case for us if we have a
94 : : * perfect match without changing the parent
95 : : * rate.
96 : : */
97 : 0 : *best_parent_rate = orig_parent_rate;
98 : 0 : return i;
99 : : }
100 : :
101 : 0 : parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw),
102 : : rate / i);
103 : 0 : current_rate = parent_rate * i;
104 : :
105 [ # # ]: 0 : if (__is_best_rate(rate, current_rate, best_rate, flags)) {
106 : : bestmult = i;
107 : : best_rate = current_rate;
108 : 0 : *best_parent_rate = parent_rate;
109 : : }
110 : : }
111 : :
112 : 0 : return bestmult;
113 : : }
114 : :
115 : 0 : static long clk_multiplier_round_rate(struct clk_hw *hw, unsigned long rate,
116 : : unsigned long *parent_rate)
117 : : {
118 : : struct clk_multiplier *mult = to_clk_multiplier(hw);
119 : 0 : unsigned long factor = __bestmult(hw, rate, parent_rate,
120 : 0 : mult->width, mult->flags);
121 : :
122 : 0 : return *parent_rate * factor;
123 : : }
124 : :
125 : 0 : static int clk_multiplier_set_rate(struct clk_hw *hw, unsigned long rate,
126 : : unsigned long parent_rate)
127 : : {
128 : : struct clk_multiplier *mult = to_clk_multiplier(hw);
129 : : unsigned long factor = __get_mult(mult, rate, parent_rate);
130 : : unsigned long flags = 0;
131 : : unsigned long val;
132 : :
133 [ # # ]: 0 : if (mult->lock)
134 : 0 : spin_lock_irqsave(mult->lock, flags);
135 : : else
136 : : __acquire(mult->lock);
137 : :
138 : : val = clk_mult_readl(mult);
139 : 0 : val &= ~GENMASK(mult->width + mult->shift - 1, mult->shift);
140 : 0 : val |= factor << mult->shift;
141 : : clk_mult_writel(mult, val);
142 : :
143 [ # # ]: 0 : if (mult->lock)
144 : : spin_unlock_irqrestore(mult->lock, flags);
145 : : else
146 : : __release(mult->lock);
147 : :
148 : 0 : return 0;
149 : : }
150 : :
151 : : const struct clk_ops clk_multiplier_ops = {
152 : : .recalc_rate = clk_multiplier_recalc_rate,
153 : : .round_rate = clk_multiplier_round_rate,
154 : : .set_rate = clk_multiplier_set_rate,
155 : : };
156 : : EXPORT_SYMBOL_GPL(clk_multiplier_ops);
|