ipq806x: enable QCE hardware crypto inside the kernel
[oweals/openwrt.git] / target / linux / ipq806x / patches-4.9 / 0057-clk-qcom-Add-regmap-mux-div-clocks-support.patch
1 From f72c5aa18281c44945fea6181d0d816a7605505c Mon Sep 17 00:00:00 2001
2 From: Georgi Djakov <georgi.djakov@linaro.org>
3 Date: Wed, 18 Mar 2015 17:23:29 +0200
4 Subject: [PATCH 57/69] clk: qcom: Add regmap mux-div clocks support
5
6 Add support for hardware that can switch both parent clocks and divider
7 at the same time. This avoids generating intermediate frequencies from
8 either the old parent clock and new divider or new parent clock and
9 old divider combinations.
10
11 Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
12 ---
13  drivers/clk/qcom/Makefile             |   1 +
14  drivers/clk/qcom/clk-regmap-mux-div.c | 272 ++++++++++++++++++++++++++++++++++
15  drivers/clk/qcom/clk-regmap-mux-div.h |  65 ++++++++
16  3 files changed, 338 insertions(+)
17  create mode 100644 drivers/clk/qcom/clk-regmap-mux-div.c
18  create mode 100644 drivers/clk/qcom/clk-regmap-mux-div.h
19
20 --- a/drivers/clk/qcom/Makefile
21 +++ b/drivers/clk/qcom/Makefile
22 @@ -9,6 +9,7 @@ clk-qcom-y += clk-rcg2.o
23  clk-qcom-y += clk-branch.o
24  clk-qcom-y += clk-regmap-divider.o
25  clk-qcom-y += clk-regmap-mux.o
26 +clk-qcom-y += clk-regmap-mux-div.o
27  clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
28  clk-qcom-y += clk-hfpll.o
29  clk-qcom-y += reset.o
30 --- /dev/null
31 +++ b/drivers/clk/qcom/clk-regmap-mux-div.c
32 @@ -0,0 +1,272 @@
33 +/*
34 + * Copyright (c) 2015, Linaro Limited
35 + * Copyright (c) 2014, The Linux Foundation. All rights reserved.
36 + *
37 + * This software is licensed under the terms of the GNU General Public
38 + * License version 2, as published by the Free Software Foundation, and
39 + * may be copied, distributed, and modified under those terms.
40 + *
41 + * This program is distributed in the hope that it will be useful,
42 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
43 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
44 + * GNU General Public License for more details.
45 + */
46 +
47 +#include <linux/bitops.h>
48 +#include <linux/delay.h>
49 +#include <linux/export.h>
50 +#include <linux/kernel.h>
51 +#include <linux/regmap.h>
52 +
53 +#include "clk-regmap-mux-div.h"
54 +
55 +#define CMD_RCGR                       0x0
56 +#define CMD_RCGR_UPDATE                        BIT(0)
57 +#define CMD_RCGR_DIRTY_CFG             BIT(4)
58 +#define CMD_RCGR_ROOT_OFF              BIT(31)
59 +#define CFG_RCGR                       0x4
60 +
61 +#define to_clk_regmap_mux_div(_hw) \
62 +       container_of(to_clk_regmap(_hw), struct clk_regmap_mux_div, clkr)
63 +
64 +int __mux_div_set_src_div(struct clk_regmap_mux_div *md, u32 src, u32 div)
65 +{
66 +       int ret, count;
67 +       u32 val, mask;
68 +       const char *name = clk_hw_get_name(&md->clkr.hw);
69 +
70 +       val = (div << md->hid_shift) | (src << md->src_shift);
71 +       mask = ((BIT(md->hid_width) - 1) << md->hid_shift) |
72 +              ((BIT(md->src_width) - 1) << md->src_shift);
73 +
74 +       ret = regmap_update_bits(md->clkr.regmap, CFG_RCGR + md->reg_offset,
75 +                                mask, val);
76 +       if (ret)
77 +               return ret;
78 +
79 +       ret = regmap_update_bits(md->clkr.regmap, CMD_RCGR + md->reg_offset,
80 +                                CMD_RCGR_UPDATE, CMD_RCGR_UPDATE);
81 +       if (ret)
82 +               return ret;
83 +
84 +       /* Wait for update to take effect */
85 +       for (count = 500; count > 0; count--) {
86 +               ret = regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset,
87 +                                 &val);
88 +               if (ret)
89 +                       return ret;
90 +               if (!(val & CMD_RCGR_UPDATE))
91 +                       return 0;
92 +               udelay(1);
93 +       }
94 +
95 +       pr_err("%s: RCG did not update its configuration", name);
96 +       return -EBUSY;
97 +}
98 +
99 +static void __mux_div_get_src_div(struct clk_regmap_mux_div *md, u32 *src,
100 +                                 u32 *div)
101 +{
102 +       u32 val, __div, __src;
103 +       const char *name = clk_hw_get_name(&md->clkr.hw);
104 +
105 +       regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset, &val);
106 +
107 +       if (val & CMD_RCGR_DIRTY_CFG) {
108 +               pr_err("%s: RCG configuration is pending\n", name);
109 +               return;
110 +       }
111 +
112 +       regmap_read(md->clkr.regmap, CFG_RCGR + md->reg_offset, &val);
113 +       __src = (val >> md->src_shift);
114 +       __src &= BIT(md->src_width) - 1;
115 +       *src = __src;
116 +
117 +       __div = (val >> md->hid_shift);
118 +       __div &= BIT(md->hid_width) - 1;
119 +       *div = __div;
120 +}
121 +
122 +static int mux_div_enable(struct clk_hw *hw)
123 +{
124 +       struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
125 +
126 +       return __mux_div_set_src_div(md, md->src, md->div);
127 +}
128 +
129 +static inline bool is_better_rate(unsigned long req, unsigned long best,
130 +                                 unsigned long new)
131 +{
132 +       return (req <= new && new < best) || (best < req && best < new);
133 +}
134 +
135 +static int mux_div_determine_rate(struct clk_hw *hw,
136 +                                 struct clk_rate_request *req)
137 +{
138 +       struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
139 +       unsigned int i, div, max_div;
140 +       unsigned long actual_rate, best_rate = 0;
141 +       unsigned long req_rate = req->rate;
142 +
143 +       for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
144 +               struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i);
145 +               unsigned long parent_rate = clk_hw_get_rate(parent);
146 +
147 +               max_div = BIT(md->hid_width) - 1;
148 +               for (div = 1; div < max_div; div++) {
149 +                       parent_rate = mult_frac(req_rate, div, 2);
150 +                       parent_rate = clk_hw_round_rate(parent, parent_rate);
151 +                       actual_rate = mult_frac(parent_rate, 2, div);
152 +
153 +                       if (is_better_rate(req_rate, best_rate, actual_rate)) {
154 +                               best_rate = actual_rate;
155 +                               req->rate = best_rate;
156 +                               req->best_parent_rate = parent_rate;
157 +                               req->best_parent_hw = parent;
158 +                       }
159 +
160 +                       if (actual_rate < req_rate || best_rate <= req_rate)
161 +                               break;
162 +               }
163 +       }
164 +
165 +       if (!best_rate)
166 +               return -EINVAL;
167 +
168 +       return 0;
169 +}
170 +
171 +static int __mux_div_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
172 +                                        unsigned long prate, u32 src)
173 +{
174 +       struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
175 +       int ret;
176 +       u32 div, max_div, best_src = 0, best_div = 0;
177 +       unsigned int i;
178 +       unsigned long actual_rate, best_rate = 0;
179 +
180 +       for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
181 +               struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i);
182 +               unsigned long parent_rate = clk_hw_get_rate(parent);
183 +
184 +               max_div = BIT(md->hid_width) - 1;
185 +               for (div = 1; div < max_div; div++) {
186 +                       parent_rate = mult_frac(rate, div, 2);
187 +                       parent_rate = clk_hw_round_rate(parent, parent_rate);
188 +                       actual_rate = mult_frac(parent_rate, 2, div);
189 +
190 +                       if (is_better_rate(rate, best_rate, actual_rate)) {
191 +                               best_rate = actual_rate;
192 +                               best_src = md->parent_map[i].cfg;
193 +                               best_div = div - 1;
194 +                       }
195 +
196 +                       if (actual_rate < rate || best_rate <= rate)
197 +                               break;
198 +               }
199 +       }
200 +
201 +       ret = __mux_div_set_src_div(md, best_src, best_div);
202 +       if (!ret) {
203 +               md->div = best_div;
204 +               md->src = best_src;
205 +       }
206 +
207 +       return ret;
208 +}
209 +
210 +static u8 mux_div_get_parent(struct clk_hw *hw)
211 +{
212 +       struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
213 +       const char *name = clk_hw_get_name(hw);
214 +       u32 i, div, src = 0;
215 +
216 +       __mux_div_get_src_div(md, &src, &div);
217 +
218 +       for (i = 0; i < clk_hw_get_num_parents(hw); i++)
219 +               if (src == md->parent_map[i].cfg)
220 +                       return i;
221 +
222 +       pr_err("%s: Can't find parent with src %d\n", name, src);
223 +       return 0;
224 +}
225 +
226 +static int mux_div_set_parent(struct clk_hw *hw, u8 index)
227 +{
228 +       struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
229 +
230 +       return __mux_div_set_src_div(md, md->parent_map[index].cfg, md->div);
231 +}
232 +
233 +static int mux_div_set_rate(struct clk_hw *hw,
234 +                           unsigned long rate, unsigned long prate)
235 +{
236 +       struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
237 +
238 +       return __mux_div_set_rate_and_parent(hw, rate, prate, md->src);
239 +}
240 +
241 +static int mux_div_set_rate_and_parent(struct clk_hw *hw,  unsigned long rate,
242 +                                      unsigned long prate, u8 index)
243 +{
244 +       struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
245 +
246 +       return __mux_div_set_rate_and_parent(hw, rate, prate,
247 +                                            md->parent_map[index].cfg);
248 +}
249 +
250 +static unsigned long mux_div_recalc_rate(struct clk_hw *hw, unsigned long prate)
251 +{
252 +       struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
253 +       u32 div, src;
254 +       int i, num_parents = clk_hw_get_num_parents(hw);
255 +       const char *name = clk_hw_get_name(hw);
256 +
257 +       __mux_div_get_src_div(md, &src, &div);
258 +       for (i = 0; i < num_parents; i++)
259 +               if (src == md->parent_map[i].cfg) {
260 +                       struct clk_hw *p = clk_hw_get_parent_by_index(hw, i);
261 +                       unsigned long parent_rate = clk_hw_get_rate(p);
262 +
263 +                       return mult_frac(parent_rate, 2, div + 1);
264 +               }
265 +
266 +       pr_err("%s: Can't find parent %d\n", name, src);
267 +       return 0;
268 +}
269 +
270 +static struct clk_hw *mux_div_get_safe_parent(struct clk_hw *hw,
271 +                                             unsigned long *safe_freq)
272 +{
273 +       int i;
274 +       struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
275 +
276 +       if (md->safe_freq)
277 +               *safe_freq = md->safe_freq;
278 +
279 +       for (i = 0; i < clk_hw_get_num_parents(hw); i++)
280 +               if (md->safe_src == md->parent_map[i].cfg)
281 +                       break;
282 +
283 +       return clk_hw_get_parent_by_index(hw, i);
284 +}
285 +
286 +static void mux_div_disable(struct clk_hw *hw)
287 +{
288 +       struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
289 +
290 +       __mux_div_set_src_div(md, md->safe_src, md->safe_div);
291 +}
292 +
293 +const struct clk_ops clk_regmap_mux_div_ops = {
294 +       .enable = mux_div_enable,
295 +       .disable = mux_div_disable,
296 +       .get_parent = mux_div_get_parent,
297 +       .set_parent = mux_div_set_parent,
298 +       .set_rate = mux_div_set_rate,
299 +       .set_rate_and_parent = mux_div_set_rate_and_parent,
300 +       .determine_rate = mux_div_determine_rate,
301 +       .recalc_rate = mux_div_recalc_rate,
302 +       .get_safe_parent = mux_div_get_safe_parent,
303 +};
304 +EXPORT_SYMBOL_GPL(clk_regmap_mux_div_ops);
305 --- /dev/null
306 +++ b/drivers/clk/qcom/clk-regmap-mux-div.h
307 @@ -0,0 +1,65 @@
308 +/*
309 + * Copyright (c) 2015, Linaro Limited
310 + * Copyright (c) 2014, The Linux Foundation. All rights reserved.
311 + *
312 + * This software is licensed under the terms of the GNU General Public
313 + * License version 2, as published by the Free Software Foundation, and
314 + * may be copied, distributed, and modified under those terms.
315 + *
316 + * This program is distributed in the hope that it will be useful,
317 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
318 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
319 + * GNU General Public License for more details.
320 + */
321 +
322 +#ifndef __QCOM_CLK_REGMAP_MUX_DIV_H__
323 +#define __QCOM_CLK_REGMAP_MUX_DIV_H__
324 +
325 +#include <linux/clk-provider.h>
326 +#include "clk-rcg.h"
327 +#include "clk-regmap.h"
328 +
329 +/**
330 + * struct mux_div_clk - combined mux/divider clock
331 + * @reg_offset: offset of the mux/divider register
332 + * @hid_width: number of bits in half integer divider
333 + * @hid_shift: lowest bit of hid value field
334 + * @src_width: number of bits in source select
335 + * @src_shift: lowest bit of source select field
336 + * @div:       the divider raw configuration value
337 + * @src:       the mux index which will be used if the clock is enabled
338 + * @safe_src:  the safe source mux value we switch to, while the main PLL is
339 + *             reconfigured
340 + * @safe_div:  the safe divider value that we set, while the main PLL is
341 + *             reconfigured
342 + * @safe_freq: When switching rates from A to B, the mux div clock will
343 + *             instead switch from A -> safe_freq -> B. This allows the
344 + *             mux_div clock to change rates while enabled, even if this
345 + *             behavior is not supported by the parent clocks.
346 + *             If changing the rate of parent A also causes the rate of
347 + *             parent B to change, then safe_freq must be defined.
348 + *             safe_freq is expected to have a source clock which is always
349 + *             on and runs at only one rate.
350 + * @parent_map:        pointer to parent_map struct
351 + * @clkr:      handle between common and hardware-specific interfaces
352 + */
353 +
354 +struct clk_regmap_mux_div {
355 +       u32                             reg_offset;
356 +       u32                             hid_width;
357 +       u32                             hid_shift;
358 +       u32                             src_width;
359 +       u32                             src_shift;
360 +       u32                             div;
361 +       u32                             src;
362 +       u32                             safe_src;
363 +       u32                             safe_div;
364 +       unsigned long                   safe_freq;
365 +       const struct parent_map         *parent_map;
366 +       struct clk_regmap               clkr;
367 +};
368 +
369 +extern const struct clk_ops clk_regmap_mux_div_ops;
370 +int __mux_div_set_src_div(struct clk_regmap_mux_div *md, u32 src, u32 div);
371 +
372 +#endif