diff options
author | Stephen Boyd <sboyd@codeaurora.org> | 2014-01-15 13:47:25 -0500 |
---|---|---|
committer | Mike Turquette <mturquette@linaro.org> | 2014-01-16 15:01:00 -0500 |
commit | bcd61c0f535a04a2aaa3f3ba27e60fae681fc88f (patch) | |
tree | 81fbb7830130ed91c207a3f088a9b5683a9f089e /drivers/clk/qcom/clk-rcg2.c | |
parent | 9e2631313c463c11645db046beb9bdecaf28b62f (diff) |
clk: qcom: Add support for root clock generators (RCGs)
Add support for the root clock generators on Qualcomm devices.
RCGs are highly customizable mux/divider/counter clocks that can
be used to generate almost any rate desired given some input
source that is faster than the desired rate.
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Mike Turquette <mturquette@linaro.org>
Diffstat (limited to 'drivers/clk/qcom/clk-rcg2.c')
-rw-r--r-- | drivers/clk/qcom/clk-rcg2.c | 291 |
1 files changed, 291 insertions, 0 deletions
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c new file mode 100644 index 000000000000..00f878a04d3f --- /dev/null +++ b/drivers/clk/qcom/clk-rcg2.c | |||
@@ -0,0 +1,291 @@ | |||
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> | ||
22 | |||
23 | #include <asm/div64.h> | ||
24 | |||
25 | #include "clk-rcg.h" | ||
26 | |||
27 | #define CMD_REG 0x0 | ||
28 | #define CMD_UPDATE BIT(0) | ||
29 | #define CMD_ROOT_EN BIT(1) | ||
30 | #define CMD_DIRTY_CFG BIT(4) | ||
31 | #define CMD_DIRTY_N BIT(5) | ||
32 | #define CMD_DIRTY_M BIT(6) | ||
33 | #define CMD_DIRTY_D BIT(7) | ||
34 | #define CMD_ROOT_OFF BIT(31) | ||
35 | |||
36 | #define CFG_REG 0x4 | ||
37 | #define CFG_SRC_DIV_SHIFT 0 | ||
38 | #define CFG_SRC_SEL_SHIFT 8 | ||
39 | #define CFG_SRC_SEL_MASK (0x7 << CFG_SRC_SEL_SHIFT) | ||
40 | #define CFG_MODE_SHIFT 12 | ||
41 | #define CFG_MODE_MASK (0x3 << CFG_MODE_SHIFT) | ||
42 | #define CFG_MODE_DUAL_EDGE (0x2 << CFG_MODE_SHIFT) | ||
43 | |||
44 | #define M_REG 0x8 | ||
45 | #define N_REG 0xc | ||
46 | #define D_REG 0x10 | ||
47 | |||
48 | static int clk_rcg2_is_enabled(struct clk_hw *hw) | ||
49 | { | ||
50 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); | ||
51 | u32 cmd; | ||
52 | int ret; | ||
53 | |||
54 | ret = regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, &cmd); | ||
55 | if (ret) | ||
56 | return ret; | ||
57 | |||
58 | return (cmd & CMD_ROOT_OFF) != 0; | ||
59 | } | ||
60 | |||
61 | static u8 clk_rcg2_get_parent(struct clk_hw *hw) | ||
62 | { | ||
63 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); | ||
64 | int num_parents = __clk_get_num_parents(hw->clk); | ||
65 | u32 cfg; | ||
66 | int i, ret; | ||
67 | |||
68 | ret = regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg); | ||
69 | if (ret) | ||
70 | return ret; | ||
71 | |||
72 | cfg &= CFG_SRC_SEL_MASK; | ||
73 | cfg >>= CFG_SRC_SEL_SHIFT; | ||
74 | |||
75 | for (i = 0; i < num_parents; i++) | ||
76 | if (cfg == rcg->parent_map[i]) | ||
77 | return i; | ||
78 | |||
79 | return -EINVAL; | ||
80 | } | ||
81 | |||
82 | static int update_config(struct clk_rcg2 *rcg) | ||
83 | { | ||
84 | int count, ret; | ||
85 | u32 cmd; | ||
86 | struct clk_hw *hw = &rcg->clkr.hw; | ||
87 | const char *name = __clk_get_name(hw->clk); | ||
88 | |||
89 | ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, | ||
90 | CMD_UPDATE, CMD_UPDATE); | ||
91 | if (ret) | ||
92 | return ret; | ||
93 | |||
94 | /* Wait for update to take effect */ | ||
95 | for (count = 500; count > 0; count--) { | ||
96 | ret = regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, &cmd); | ||
97 | if (ret) | ||
98 | return ret; | ||
99 | if (!(cmd & CMD_UPDATE)) | ||
100 | return 0; | ||
101 | udelay(1); | ||
102 | } | ||
103 | |||
104 | WARN(1, "%s: rcg didn't update its configuration.", name); | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | static int clk_rcg2_set_parent(struct clk_hw *hw, u8 index) | ||
109 | { | ||
110 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); | ||
111 | int ret; | ||
112 | |||
113 | ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, | ||
114 | CFG_SRC_SEL_MASK, | ||
115 | rcg->parent_map[index] << CFG_SRC_SEL_SHIFT); | ||
116 | if (ret) | ||
117 | return ret; | ||
118 | |||
119 | return update_config(rcg); | ||
120 | } | ||
121 | |||
122 | /* | ||
123 | * Calculate m/n:d rate | ||
124 | * | ||
125 | * parent_rate m | ||
126 | * rate = ----------- x --- | ||
127 | * hid_div n | ||
128 | */ | ||
129 | static unsigned long | ||
130 | calc_rate(unsigned long rate, u32 m, u32 n, u32 mode, u32 hid_div) | ||
131 | { | ||
132 | if (hid_div) { | ||
133 | rate *= 2; | ||
134 | rate /= hid_div + 1; | ||
135 | } | ||
136 | |||
137 | if (mode) { | ||
138 | u64 tmp = rate; | ||
139 | tmp *= m; | ||
140 | do_div(tmp, n); | ||
141 | rate = tmp; | ||
142 | } | ||
143 | |||
144 | return rate; | ||
145 | } | ||
146 | |||
147 | static unsigned long | ||
148 | clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) | ||
149 | { | ||
150 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); | ||
151 | u32 cfg, hid_div, m = 0, n = 0, mode = 0, mask; | ||
152 | |||
153 | regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg); | ||
154 | |||
155 | if (rcg->mnd_width) { | ||
156 | mask = BIT(rcg->mnd_width) - 1; | ||
157 | regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + M_REG, &m); | ||
158 | m &= mask; | ||
159 | regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + N_REG, &n); | ||
160 | n = ~n; | ||
161 | n &= mask; | ||
162 | n += m; | ||
163 | mode = cfg & CFG_MODE_MASK; | ||
164 | mode >>= CFG_MODE_SHIFT; | ||
165 | } | ||
166 | |||
167 | mask = BIT(rcg->hid_width) - 1; | ||
168 | hid_div = cfg >> CFG_SRC_DIV_SHIFT; | ||
169 | hid_div &= mask; | ||
170 | |||
171 | return calc_rate(parent_rate, m, n, mode, hid_div); | ||
172 | } | ||
173 | |||
174 | static const | ||
175 | struct freq_tbl *find_freq(const struct freq_tbl *f, unsigned long rate) | ||
176 | { | ||
177 | if (!f) | ||
178 | return NULL; | ||
179 | |||
180 | for (; f->freq; f++) | ||
181 | if (rate <= f->freq) | ||
182 | return f; | ||
183 | |||
184 | return NULL; | ||
185 | } | ||
186 | |||
187 | static long _freq_tbl_determine_rate(struct clk_hw *hw, | ||
188 | const struct freq_tbl *f, unsigned long rate, | ||
189 | unsigned long *p_rate, struct clk **p) | ||
190 | { | ||
191 | unsigned long clk_flags; | ||
192 | |||
193 | f = find_freq(f, rate); | ||
194 | if (!f) | ||
195 | return -EINVAL; | ||
196 | |||
197 | clk_flags = __clk_get_flags(hw->clk); | ||
198 | *p = clk_get_parent_by_index(hw->clk, f->src); | ||
199 | if (clk_flags & CLK_SET_RATE_PARENT) { | ||
200 | if (f->pre_div) { | ||
201 | rate /= 2; | ||
202 | rate *= f->pre_div + 1; | ||
203 | } | ||
204 | |||
205 | if (f->n) { | ||
206 | u64 tmp = rate; | ||
207 | tmp = tmp * f->n; | ||
208 | do_div(tmp, f->m); | ||
209 | rate = tmp; | ||
210 | } | ||
211 | } else { | ||
212 | rate = __clk_get_rate(*p); | ||
213 | } | ||
214 | *p_rate = rate; | ||
215 | |||
216 | return f->freq; | ||
217 | } | ||
218 | |||
219 | static long clk_rcg2_determine_rate(struct clk_hw *hw, unsigned long rate, | ||
220 | unsigned long *p_rate, struct clk **p) | ||
221 | { | ||
222 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); | ||
223 | |||
224 | return _freq_tbl_determine_rate(hw, rcg->freq_tbl, rate, p_rate, p); | ||
225 | } | ||
226 | |||
227 | static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate) | ||
228 | { | ||
229 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); | ||
230 | const struct freq_tbl *f; | ||
231 | u32 cfg, mask; | ||
232 | int ret; | ||
233 | |||
234 | f = find_freq(rcg->freq_tbl, rate); | ||
235 | if (!f) | ||
236 | return -EINVAL; | ||
237 | |||
238 | if (rcg->mnd_width && f->n) { | ||
239 | mask = BIT(rcg->mnd_width) - 1; | ||
240 | ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + M_REG, | ||
241 | mask, f->m); | ||
242 | if (ret) | ||
243 | return ret; | ||
244 | |||
245 | ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + N_REG, | ||
246 | mask, ~(f->n - f->m)); | ||
247 | if (ret) | ||
248 | return ret; | ||
249 | |||
250 | ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + D_REG, | ||
251 | mask, ~f->n); | ||
252 | if (ret) | ||
253 | return ret; | ||
254 | } | ||
255 | |||
256 | mask = BIT(rcg->hid_width) - 1; | ||
257 | mask |= CFG_SRC_SEL_MASK | CFG_MODE_MASK; | ||
258 | cfg = f->pre_div << CFG_SRC_DIV_SHIFT; | ||
259 | cfg |= rcg->parent_map[f->src] << CFG_SRC_SEL_SHIFT; | ||
260 | if (rcg->mnd_width && f->n) | ||
261 | cfg |= CFG_MODE_DUAL_EDGE; | ||
262 | ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, mask, | ||
263 | cfg); | ||
264 | if (ret) | ||
265 | return ret; | ||
266 | |||
267 | return update_config(rcg); | ||
268 | } | ||
269 | |||
270 | static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate, | ||
271 | unsigned long parent_rate) | ||
272 | { | ||
273 | return __clk_rcg2_set_rate(hw, rate); | ||
274 | } | ||
275 | |||
276 | static int clk_rcg2_set_rate_and_parent(struct clk_hw *hw, | ||
277 | unsigned long rate, unsigned long parent_rate, u8 index) | ||
278 | { | ||
279 | return __clk_rcg2_set_rate(hw, rate); | ||
280 | } | ||
281 | |||
282 | const struct clk_ops clk_rcg2_ops = { | ||
283 | .is_enabled = clk_rcg2_is_enabled, | ||
284 | .get_parent = clk_rcg2_get_parent, | ||
285 | .set_parent = clk_rcg2_set_parent, | ||
286 | .recalc_rate = clk_rcg2_recalc_rate, | ||
287 | .determine_rate = clk_rcg2_determine_rate, | ||
288 | .set_rate = clk_rcg2_set_rate, | ||
289 | .set_rate_and_parent = clk_rcg2_set_rate_and_parent, | ||
290 | }; | ||
291 | EXPORT_SYMBOL_GPL(clk_rcg2_ops); | ||