ipq: more v4.9 fixes
[openwrt/staging/blogic.git] / target / linux / ipq806x / patches-4.9 / 0041-clk-qcom-Add-support-for-Krait-clocks.patch
1 From b9747125a8e7633ed2701a70e32dbb0442193774 Mon Sep 17 00:00:00 2001
2 From: Stephen Boyd <sboyd@codeaurora.org>
3 Date: Fri, 20 Mar 2015 23:45:28 -0700
4 Subject: [PATCH 41/69] clk: qcom: Add support for Krait clocks
5
6 The Krait clocks are made up of a series of muxes and a divider
7 that choose between a fixed rate clock and dedicated HFPLLs for
8 each CPU. Instead of using mmio accesses to remux parents, the
9 Krait implementation exposes the remux control via cp15
10 registers. Support these clocks.
11
12 Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
13 ---
14 drivers/clk/qcom/Kconfig | 4 ++
15 drivers/clk/qcom/Makefile | 1 +
16 drivers/clk/qcom/clk-krait.c | 167 +++++++++++++++++++++++++++++++++++++++++++
17 drivers/clk/qcom/clk-krait.h | 49 +++++++++++++
18 4 files changed, 221 insertions(+)
19 create mode 100644 drivers/clk/qcom/clk-krait.c
20 create mode 100644 drivers/clk/qcom/clk-krait.h
21
22 diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
23 index e7ccc7fd7c47..503b0de3ed87 100644
24 --- a/drivers/clk/qcom/Kconfig
25 +++ b/drivers/clk/qcom/Kconfig
26 @@ -187,3 +187,7 @@ config QCOM_HFPLL
27 Support for the high-frequency PLLs present on Qualcomm devices.
28 Say Y if you want to support CPU frequency scaling on devices
29 such as MSM8974, APQ8084, etc.
30 +
31 +config KRAIT_CLOCKS
32 + bool
33 + select KRAIT_L2_ACCESSORS
34 diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
35 index 52a8777657aa..3565478472ae 100644
36 --- a/drivers/clk/qcom/Makefile
37 +++ b/drivers/clk/qcom/Makefile
38 @@ -9,6 +9,7 @@ clk-qcom-y += clk-rcg2.o
39 clk-qcom-y += clk-branch.o
40 clk-qcom-y += clk-regmap-divider.o
41 clk-qcom-y += clk-regmap-mux.o
42 +clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
43 clk-qcom-y += clk-hfpll.o
44 clk-qcom-y += reset.o
45 clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o
46 diff --git a/drivers/clk/qcom/clk-krait.c b/drivers/clk/qcom/clk-krait.c
47 new file mode 100644
48 index 000000000000..84277741a9c8
49 --- /dev/null
50 +++ b/drivers/clk/qcom/clk-krait.c
51 @@ -0,0 +1,167 @@
52 +/*
53 + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
54 + *
55 + * This program is free software; you can redistribute it and/or modify
56 + * it under the terms of the GNU General Public License version 2 and
57 + * only version 2 as published by the Free Software Foundation.
58 + *
59 + * This program is distributed in the hope that it will be useful,
60 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
61 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
62 + * GNU General Public License for more details.
63 + */
64 +
65 +#include <linux/kernel.h>
66 +#include <linux/module.h>
67 +#include <linux/init.h>
68 +#include <linux/io.h>
69 +#include <linux/delay.h>
70 +#include <linux/err.h>
71 +#include <linux/clk-provider.h>
72 +#include <linux/spinlock.h>
73 +
74 +#include <asm/krait-l2-accessors.h>
75 +
76 +#include "clk-krait.h"
77 +
78 +/* Secondary and primary muxes share the same cp15 register */
79 +static DEFINE_SPINLOCK(krait_clock_reg_lock);
80 +
81 +#define LPL_SHIFT 8
82 +static void __krait_mux_set_sel(struct krait_mux_clk *mux, int sel)
83 +{
84 + unsigned long flags;
85 + u32 regval;
86 +
87 + spin_lock_irqsave(&krait_clock_reg_lock, flags);
88 + regval = krait_get_l2_indirect_reg(mux->offset);
89 + regval &= ~(mux->mask << mux->shift);
90 + regval |= (sel & mux->mask) << mux->shift;
91 + if (mux->lpl) {
92 + regval &= ~(mux->mask << (mux->shift + LPL_SHIFT));
93 + regval |= (sel & mux->mask) << (mux->shift + LPL_SHIFT);
94 + }
95 + krait_set_l2_indirect_reg(mux->offset, regval);
96 + spin_unlock_irqrestore(&krait_clock_reg_lock, flags);
97 +
98 + /* Wait for switch to complete. */
99 + mb();
100 + udelay(1);
101 +}
102 +
103 +static int krait_mux_set_parent(struct clk_hw *hw, u8 index)
104 +{
105 + struct krait_mux_clk *mux = to_krait_mux_clk(hw);
106 + u32 sel;
107 +
108 + sel = clk_mux_reindex(index, mux->parent_map, 0);
109 + mux->en_mask = sel;
110 + /* Don't touch mux if CPU is off as it won't work */
111 + if (__clk_is_enabled(hw->clk))
112 + __krait_mux_set_sel(mux, sel);
113 + return 0;
114 +}
115 +
116 +static u8 krait_mux_get_parent(struct clk_hw *hw)
117 +{
118 + struct krait_mux_clk *mux = to_krait_mux_clk(hw);
119 + u32 sel;
120 +
121 + sel = krait_get_l2_indirect_reg(mux->offset);
122 + sel >>= mux->shift;
123 + sel &= mux->mask;
124 + mux->en_mask = sel;
125 +
126 + return clk_mux_get_parent(hw, sel, mux->parent_map, 0);
127 +}
128 +
129 +static struct clk_hw *krait_mux_get_safe_parent(struct clk_hw *hw,
130 + unsigned long *safe_freq)
131 +{
132 + int i;
133 + struct krait_mux_clk *mux = to_krait_mux_clk(hw);
134 + int num_parents = clk_hw_get_num_parents(hw);
135 +
136 + i = mux->safe_sel;
137 + for (i = 0; i < num_parents; i++)
138 + if (mux->safe_sel == mux->parent_map[i])
139 + break;
140 +
141 + return clk_hw_get_parent_by_index(hw, i);
142 +}
143 +
144 +static int krait_mux_enable(struct clk_hw *hw)
145 +{
146 + struct krait_mux_clk *mux = to_krait_mux_clk(hw);
147 +
148 + __krait_mux_set_sel(mux, mux->en_mask);
149 +
150 + return 0;
151 +}
152 +
153 +static void krait_mux_disable(struct clk_hw *hw)
154 +{
155 + struct krait_mux_clk *mux = to_krait_mux_clk(hw);
156 +
157 + __krait_mux_set_sel(mux, mux->safe_sel);
158 +}
159 +
160 +const struct clk_ops krait_mux_clk_ops = {
161 + .enable = krait_mux_enable,
162 + .disable = krait_mux_disable,
163 + .set_parent = krait_mux_set_parent,
164 + .get_parent = krait_mux_get_parent,
165 + .determine_rate = __clk_mux_determine_rate_closest,
166 + .get_safe_parent = krait_mux_get_safe_parent,
167 +};
168 +EXPORT_SYMBOL_GPL(krait_mux_clk_ops);
169 +
170 +/* The divider can divide by 2, 4, 6 and 8. But we only really need div-2. */
171 +static long krait_div2_round_rate(struct clk_hw *hw, unsigned long rate,
172 + unsigned long *parent_rate)
173 +{
174 + *parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), rate * 2);
175 + return DIV_ROUND_UP(*parent_rate, 2);
176 +}
177 +
178 +static int krait_div2_set_rate(struct clk_hw *hw, unsigned long rate,
179 + unsigned long parent_rate)
180 +{
181 + struct krait_div2_clk *d = to_krait_div2_clk(hw);
182 + unsigned long flags;
183 + u32 val;
184 + u32 mask = BIT(d->width) - 1;
185 +
186 + if (d->lpl)
187 + mask = mask << (d->shift + LPL_SHIFT) | mask << d->shift;
188 +
189 + spin_lock_irqsave(&krait_clock_reg_lock, flags);
190 + val = krait_get_l2_indirect_reg(d->offset);
191 + val &= ~mask;
192 + krait_set_l2_indirect_reg(d->offset, val);
193 + spin_unlock_irqrestore(&krait_clock_reg_lock, flags);
194 +
195 + return 0;
196 +}
197 +
198 +static unsigned long
199 +krait_div2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
200 +{
201 + struct krait_div2_clk *d = to_krait_div2_clk(hw);
202 + u32 mask = BIT(d->width) - 1;
203 + u32 div;
204 +
205 + div = krait_get_l2_indirect_reg(d->offset);
206 + div >>= d->shift;
207 + div &= mask;
208 + div = (div + 1) * 2;
209 +
210 + return DIV_ROUND_UP(parent_rate, div);
211 +}
212 +
213 +const struct clk_ops krait_div2_clk_ops = {
214 + .round_rate = krait_div2_round_rate,
215 + .set_rate = krait_div2_set_rate,
216 + .recalc_rate = krait_div2_recalc_rate,
217 +};
218 +EXPORT_SYMBOL_GPL(krait_div2_clk_ops);
219 diff --git a/drivers/clk/qcom/clk-krait.h b/drivers/clk/qcom/clk-krait.h
220 new file mode 100644
221 index 000000000000..5d0063538e5d
222 --- /dev/null
223 +++ b/drivers/clk/qcom/clk-krait.h
224 @@ -0,0 +1,49 @@
225 +/*
226 + * Copyright (c) 2013, The Linux Foundation. All rights reserved.
227 + *
228 + * This program is free software; you can redistribute it and/or modify
229 + * it under the terms of the GNU General Public License version 2 and
230 + * only version 2 as published by the Free Software Foundation.
231 + *
232 + * This program is distributed in the hope that it will be useful,
233 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
234 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
235 + * GNU General Public License for more details.
236 + */
237 +
238 +#ifndef __QCOM_CLK_KRAIT_H
239 +#define __QCOM_CLK_KRAIT_H
240 +
241 +#include <linux/clk-provider.h>
242 +
243 +struct krait_mux_clk {
244 + unsigned int *parent_map;
245 + bool has_safe_parent;
246 + u8 safe_sel;
247 + u32 offset;
248 + u32 mask;
249 + u32 shift;
250 + u32 en_mask;
251 + bool lpl;
252 +
253 + struct clk_hw hw;
254 +};
255 +
256 +#define to_krait_mux_clk(_hw) container_of(_hw, struct krait_mux_clk, hw)
257 +
258 +extern const struct clk_ops krait_mux_clk_ops;
259 +
260 +struct krait_div2_clk {
261 + u32 offset;
262 + u8 width;
263 + u32 shift;
264 + bool lpl;
265 +
266 + struct clk_hw hw;
267 +};
268 +
269 +#define to_krait_div2_clk(_hw) container_of(_hw, struct krait_div2_clk, hw)
270 +
271 +extern const struct clk_ops krait_div2_clk_ops;
272 +
273 +#endif
274 --
275 2.11.0
276