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