diff options
author | Jun Nie <jun.nie@linaro.org> | 2015-07-23 03:02:51 -0400 |
---|---|---|
committer | Stephen Boyd <sboyd@codeaurora.org> | 2015-07-28 14:59:34 -0400 |
commit | 4599dd2c926915b5e8c27e0ca21a6172f9d6881c (patch) | |
tree | 311e7418c570e049d2abbd60bd720c9b7b537aa1 | |
parent | 7764d0cdc3dbf15010f66e0e2e5786f0f03d402a (diff) |
clk: zx: Add audio div clock method for zx296702
Add SPDIF/I2S divider clock method for zx296702
Signed-off-by: Jun Nie <jun.nie@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
-rw-r--r-- | drivers/clk/zte/Makefile | 2 | ||||
-rw-r--r-- | drivers/clk/zte/clk.c (renamed from drivers/clk/zte/clk-pll.c) | 141 | ||||
-rw-r--r-- | drivers/clk/zte/clk.h | 9 |
3 files changed, 149 insertions, 3 deletions
diff --git a/drivers/clk/zte/Makefile b/drivers/clk/zte/Makefile index 95b707c18108..74005aa322a2 100644 --- a/drivers/clk/zte/Makefile +++ b/drivers/clk/zte/Makefile | |||
@@ -1,2 +1,2 @@ | |||
1 | obj-y := clk-pll.o | 1 | obj-y := clk.o |
2 | obj-$(CONFIG_SOC_ZX296702) += clk-zx296702.o | 2 | obj-$(CONFIG_SOC_ZX296702) += clk-zx296702.o |
diff --git a/drivers/clk/zte/clk-pll.c b/drivers/clk/zte/clk.c index c3b221ae6cd7..7c73c538c43d 100644 --- a/drivers/clk/zte/clk-pll.c +++ b/drivers/clk/zte/clk.c | |||
@@ -13,10 +13,12 @@ | |||
13 | #include <linux/iopoll.h> | 13 | #include <linux/iopoll.h> |
14 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
15 | #include <linux/spinlock.h> | 15 | #include <linux/spinlock.h> |
16 | #include <asm/div64.h> | ||
16 | 17 | ||
17 | #include "clk.h" | 18 | #include "clk.h" |
18 | 19 | ||
19 | #define to_clk_zx_pll(_hw) container_of(_hw, struct clk_zx_pll, hw) | 20 | #define to_clk_zx_pll(_hw) container_of(_hw, struct clk_zx_pll, hw) |
21 | #define to_clk_zx_audio(_hw) container_of(_hw, struct clk_zx_audio, hw) | ||
20 | 22 | ||
21 | #define CFG0_CFG1_OFFSET 4 | 23 | #define CFG0_CFG1_OFFSET 4 |
22 | #define LOCK_FLAG BIT(30) | 24 | #define LOCK_FLAG BIT(30) |
@@ -141,8 +143,9 @@ static const struct clk_ops zx_pll_ops = { | |||
141 | }; | 143 | }; |
142 | 144 | ||
143 | struct clk *clk_register_zx_pll(const char *name, const char *parent_name, | 145 | struct clk *clk_register_zx_pll(const char *name, const char *parent_name, |
144 | unsigned long flags, void __iomem *reg_base, | 146 | unsigned long flags, void __iomem *reg_base, |
145 | const struct zx_pll_config *lookup_table, int count, spinlock_t *lock) | 147 | const struct zx_pll_config *lookup_table, |
148 | int count, spinlock_t *lock) | ||
146 | { | 149 | { |
147 | struct clk_zx_pll *zx_pll; | 150 | struct clk_zx_pll *zx_pll; |
148 | struct clk *clk; | 151 | struct clk *clk; |
@@ -170,3 +173,137 @@ struct clk *clk_register_zx_pll(const char *name, const char *parent_name, | |||
170 | 173 | ||
171 | return clk; | 174 | return clk; |
172 | } | 175 | } |
176 | |||
177 | #define BPAR 1000000 | ||
178 | static u32 calc_reg(u32 parent_rate, u32 rate) | ||
179 | { | ||
180 | u32 sel, integ, fra_div, tmp; | ||
181 | u64 tmp64 = (u64)parent_rate * BPAR; | ||
182 | |||
183 | do_div(tmp64, rate); | ||
184 | integ = (u32)tmp64 / BPAR; | ||
185 | integ = integ >> 1; | ||
186 | |||
187 | tmp = (u32)tmp64 % BPAR; | ||
188 | sel = tmp / BPAR; | ||
189 | |||
190 | tmp = tmp % BPAR; | ||
191 | fra_div = tmp * 0xff / BPAR; | ||
192 | tmp = (sel << 24) | (integ << 16) | (0xff << 8) | fra_div; | ||
193 | |||
194 | /* Set I2S integer divider as 1. This bit is reserved for SPDIF | ||
195 | * and do no harm. | ||
196 | */ | ||
197 | tmp |= BIT(28); | ||
198 | return tmp; | ||
199 | } | ||
200 | |||
201 | static u32 calc_rate(u32 reg, u32 parent_rate) | ||
202 | { | ||
203 | u32 sel, integ, fra_div, tmp; | ||
204 | u64 tmp64 = (u64)parent_rate * BPAR; | ||
205 | |||
206 | tmp = reg; | ||
207 | sel = (tmp >> 24) & BIT(0); | ||
208 | integ = (tmp >> 16) & 0xff; | ||
209 | fra_div = tmp & 0xff; | ||
210 | |||
211 | tmp = fra_div * BPAR; | ||
212 | tmp = tmp / 0xff; | ||
213 | tmp += sel * BPAR; | ||
214 | tmp += 2 * integ * BPAR; | ||
215 | do_div(tmp64, tmp); | ||
216 | |||
217 | return (u32)tmp64; | ||
218 | } | ||
219 | |||
220 | static unsigned long zx_audio_recalc_rate(struct clk_hw *hw, | ||
221 | unsigned long parent_rate) | ||
222 | { | ||
223 | struct clk_zx_audio *zx_audio = to_clk_zx_audio(hw); | ||
224 | u32 reg; | ||
225 | |||
226 | reg = readl_relaxed(zx_audio->reg_base); | ||
227 | return calc_rate(reg, parent_rate); | ||
228 | } | ||
229 | |||
230 | static long zx_audio_round_rate(struct clk_hw *hw, unsigned long rate, | ||
231 | unsigned long *prate) | ||
232 | { | ||
233 | u32 reg; | ||
234 | |||
235 | if (rate * 2 > *prate) | ||
236 | return -EINVAL; | ||
237 | |||
238 | reg = calc_reg(*prate, rate); | ||
239 | return calc_rate(reg, *prate); | ||
240 | } | ||
241 | |||
242 | static int zx_audio_set_rate(struct clk_hw *hw, unsigned long rate, | ||
243 | unsigned long parent_rate) | ||
244 | { | ||
245 | struct clk_zx_audio *zx_audio = to_clk_zx_audio(hw); | ||
246 | u32 reg; | ||
247 | |||
248 | reg = calc_reg(parent_rate, rate); | ||
249 | writel_relaxed(reg, zx_audio->reg_base); | ||
250 | |||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | #define ZX_AUDIO_EN BIT(25) | ||
255 | static int zx_audio_enable(struct clk_hw *hw) | ||
256 | { | ||
257 | struct clk_zx_audio *zx_audio = to_clk_zx_audio(hw); | ||
258 | u32 reg; | ||
259 | |||
260 | reg = readl_relaxed(zx_audio->reg_base); | ||
261 | writel_relaxed(reg & ~ZX_AUDIO_EN, zx_audio->reg_base); | ||
262 | return 0; | ||
263 | } | ||
264 | |||
265 | static void zx_audio_disable(struct clk_hw *hw) | ||
266 | { | ||
267 | struct clk_zx_audio *zx_audio = to_clk_zx_audio(hw); | ||
268 | u32 reg; | ||
269 | |||
270 | reg = readl_relaxed(zx_audio->reg_base); | ||
271 | writel_relaxed(reg | ZX_AUDIO_EN, zx_audio->reg_base); | ||
272 | } | ||
273 | |||
274 | static const struct clk_ops zx_audio_ops = { | ||
275 | .recalc_rate = zx_audio_recalc_rate, | ||
276 | .round_rate = zx_audio_round_rate, | ||
277 | .set_rate = zx_audio_set_rate, | ||
278 | .enable = zx_audio_enable, | ||
279 | .disable = zx_audio_disable, | ||
280 | }; | ||
281 | |||
282 | struct clk *clk_register_zx_audio(const char *name, | ||
283 | const char * const parent_name, | ||
284 | unsigned long flags, | ||
285 | void __iomem *reg_base) | ||
286 | { | ||
287 | struct clk_zx_audio *zx_audio; | ||
288 | struct clk *clk; | ||
289 | struct clk_init_data init; | ||
290 | |||
291 | zx_audio = kzalloc(sizeof(*zx_audio), GFP_KERNEL); | ||
292 | if (!zx_audio) | ||
293 | return ERR_PTR(-ENOMEM); | ||
294 | |||
295 | init.name = name; | ||
296 | init.ops = &zx_audio_ops; | ||
297 | init.flags = flags; | ||
298 | init.parent_names = parent_name ? &parent_name : NULL; | ||
299 | init.num_parents = parent_name ? 1 : 0; | ||
300 | |||
301 | zx_audio->reg_base = reg_base; | ||
302 | zx_audio->hw.init = &init; | ||
303 | |||
304 | clk = clk_register(NULL, &zx_audio->hw); | ||
305 | if (IS_ERR(clk)) | ||
306 | kfree(zx_audio); | ||
307 | |||
308 | return clk; | ||
309 | } | ||
diff --git a/drivers/clk/zte/clk.h b/drivers/clk/zte/clk.h index 0914a82d0535..65ae08b818d3 100644 --- a/drivers/clk/zte/clk.h +++ b/drivers/clk/zte/clk.h | |||
@@ -29,4 +29,13 @@ struct clk_zx_pll { | |||
29 | struct clk *clk_register_zx_pll(const char *name, const char *parent_name, | 29 | struct clk *clk_register_zx_pll(const char *name, const char *parent_name, |
30 | unsigned long flags, void __iomem *reg_base, | 30 | unsigned long flags, void __iomem *reg_base, |
31 | const struct zx_pll_config *lookup_table, int count, spinlock_t *lock); | 31 | const struct zx_pll_config *lookup_table, int count, spinlock_t *lock); |
32 | |||
33 | struct clk_zx_audio { | ||
34 | struct clk_hw hw; | ||
35 | void __iomem *reg_base; | ||
36 | }; | ||
37 | |||
38 | struct clk *clk_register_zx_audio(const char *name, | ||
39 | const char * const parent_name, | ||
40 | unsigned long flags, void __iomem *reg_base); | ||
32 | #endif | 41 | #endif |