Commit | Line | Data |
---|---|---|
5a465808 JN |
1 | /* |
2 | * Copyright 2014 Linaro Ltd. | |
3 | * Copyright (C) 2014 ZTE Corporation. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License version 2 as | |
7 | * published by the Free Software Foundation. | |
8 | */ | |
9 | ||
10 | #include <linux/clk-provider.h> | |
11 | #include <linux/err.h> | |
12 | #include <linux/io.h> | |
13 | #include <linux/iopoll.h> | |
14 | #include <linux/slab.h> | |
15 | #include <linux/spinlock.h> | |
4599dd2c | 16 | #include <asm/div64.h> |
5a465808 JN |
17 | |
18 | #include "clk.h" | |
19 | ||
20 | #define to_clk_zx_pll(_hw) container_of(_hw, struct clk_zx_pll, hw) | |
4599dd2c | 21 | #define to_clk_zx_audio(_hw) container_of(_hw, struct clk_zx_audio, hw) |
5a465808 JN |
22 | |
23 | #define CFG0_CFG1_OFFSET 4 | |
8d9a0860 JN |
24 | #define LOCK_FLAG 30 |
25 | #define POWER_DOWN 31 | |
5a465808 JN |
26 | |
27 | static int rate_to_idx(struct clk_zx_pll *zx_pll, unsigned long rate) | |
28 | { | |
29 | const struct zx_pll_config *config = zx_pll->lookup_table; | |
30 | int i; | |
31 | ||
32 | for (i = 0; i < zx_pll->count; i++) { | |
33 | if (config[i].rate > rate) | |
34 | return i > 0 ? i - 1 : 0; | |
35 | ||
36 | if (config[i].rate == rate) | |
37 | return i; | |
38 | } | |
39 | ||
40 | return i - 1; | |
41 | } | |
42 | ||
43 | static int hw_to_idx(struct clk_zx_pll *zx_pll) | |
44 | { | |
45 | const struct zx_pll_config *config = zx_pll->lookup_table; | |
46 | u32 hw_cfg0, hw_cfg1; | |
47 | int i; | |
48 | ||
49 | hw_cfg0 = readl_relaxed(zx_pll->reg_base); | |
50 | hw_cfg1 = readl_relaxed(zx_pll->reg_base + CFG0_CFG1_OFFSET); | |
51 | ||
52 | /* For matching the value in lookup table */ | |
8d9a0860 JN |
53 | hw_cfg0 &= ~BIT(zx_pll->lock_bit); |
54 | hw_cfg0 |= BIT(zx_pll->pd_bit); | |
5a465808 JN |
55 | |
56 | for (i = 0; i < zx_pll->count; i++) { | |
57 | if (hw_cfg0 == config[i].cfg0 && hw_cfg1 == config[i].cfg1) | |
58 | return i; | |
59 | } | |
60 | ||
61 | return -EINVAL; | |
62 | } | |
63 | ||
64 | static unsigned long zx_pll_recalc_rate(struct clk_hw *hw, | |
65 | unsigned long parent_rate) | |
66 | { | |
67 | struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw); | |
68 | int idx; | |
69 | ||
70 | idx = hw_to_idx(zx_pll); | |
71 | if (unlikely(idx == -EINVAL)) | |
72 | return 0; | |
73 | ||
74 | return zx_pll->lookup_table[idx].rate; | |
75 | } | |
76 | ||
77 | static long zx_pll_round_rate(struct clk_hw *hw, unsigned long rate, | |
78 | unsigned long *prate) | |
79 | { | |
80 | struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw); | |
81 | int idx; | |
82 | ||
83 | idx = rate_to_idx(zx_pll, rate); | |
84 | ||
85 | return zx_pll->lookup_table[idx].rate; | |
86 | } | |
87 | ||
88 | static int zx_pll_set_rate(struct clk_hw *hw, unsigned long rate, | |
89 | unsigned long parent_rate) | |
90 | { | |
91 | /* Assume current cpu is not running on current PLL */ | |
92 | struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw); | |
93 | const struct zx_pll_config *config; | |
94 | int idx; | |
95 | ||
96 | idx = rate_to_idx(zx_pll, rate); | |
97 | config = &zx_pll->lookup_table[idx]; | |
98 | ||
99 | writel_relaxed(config->cfg0, zx_pll->reg_base); | |
100 | writel_relaxed(config->cfg1, zx_pll->reg_base + CFG0_CFG1_OFFSET); | |
101 | ||
102 | return 0; | |
103 | } | |
104 | ||
105 | static int zx_pll_enable(struct clk_hw *hw) | |
106 | { | |
107 | struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw); | |
108 | u32 reg; | |
109 | ||
110 | reg = readl_relaxed(zx_pll->reg_base); | |
8d9a0860 | 111 | writel_relaxed(reg & ~BIT(zx_pll->pd_bit), zx_pll->reg_base); |
5a465808 JN |
112 | |
113 | return readl_relaxed_poll_timeout(zx_pll->reg_base, reg, | |
8d9a0860 | 114 | reg & BIT(zx_pll->lock_bit), 0, 100); |
5a465808 JN |
115 | } |
116 | ||
117 | static void zx_pll_disable(struct clk_hw *hw) | |
118 | { | |
119 | struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw); | |
120 | u32 reg; | |
121 | ||
122 | reg = readl_relaxed(zx_pll->reg_base); | |
8d9a0860 | 123 | writel_relaxed(reg | BIT(zx_pll->pd_bit), zx_pll->reg_base); |
5a465808 JN |
124 | } |
125 | ||
126 | static int zx_pll_is_enabled(struct clk_hw *hw) | |
127 | { | |
128 | struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw); | |
129 | u32 reg; | |
130 | ||
131 | reg = readl_relaxed(zx_pll->reg_base); | |
132 | ||
8d9a0860 | 133 | return !(reg & BIT(zx_pll->pd_bit)); |
5a465808 JN |
134 | } |
135 | ||
8d9a0860 | 136 | const struct clk_ops zx_pll_ops = { |
5a465808 JN |
137 | .recalc_rate = zx_pll_recalc_rate, |
138 | .round_rate = zx_pll_round_rate, | |
139 | .set_rate = zx_pll_set_rate, | |
140 | .enable = zx_pll_enable, | |
141 | .disable = zx_pll_disable, | |
142 | .is_enabled = zx_pll_is_enabled, | |
143 | }; | |
8d9a0860 | 144 | EXPORT_SYMBOL(zx_pll_ops); |
5a465808 JN |
145 | |
146 | struct clk *clk_register_zx_pll(const char *name, const char *parent_name, | |
4599dd2c JN |
147 | unsigned long flags, void __iomem *reg_base, |
148 | const struct zx_pll_config *lookup_table, | |
149 | int count, spinlock_t *lock) | |
5a465808 JN |
150 | { |
151 | struct clk_zx_pll *zx_pll; | |
152 | struct clk *clk; | |
153 | struct clk_init_data init; | |
154 | ||
155 | zx_pll = kzalloc(sizeof(*zx_pll), GFP_KERNEL); | |
156 | if (!zx_pll) | |
157 | return ERR_PTR(-ENOMEM); | |
158 | ||
159 | init.name = name; | |
160 | init.ops = &zx_pll_ops; | |
161 | init.flags = flags; | |
162 | init.parent_names = parent_name ? &parent_name : NULL; | |
163 | init.num_parents = parent_name ? 1 : 0; | |
164 | ||
165 | zx_pll->reg_base = reg_base; | |
166 | zx_pll->lookup_table = lookup_table; | |
167 | zx_pll->count = count; | |
8d9a0860 JN |
168 | zx_pll->lock_bit = LOCK_FLAG; |
169 | zx_pll->pd_bit = POWER_DOWN; | |
5a465808 JN |
170 | zx_pll->lock = lock; |
171 | zx_pll->hw.init = &init; | |
172 | ||
173 | clk = clk_register(NULL, &zx_pll->hw); | |
174 | if (IS_ERR(clk)) | |
175 | kfree(zx_pll); | |
176 | ||
177 | return clk; | |
178 | } | |
4599dd2c JN |
179 | |
180 | #define BPAR 1000000 | |
181 | static u32 calc_reg(u32 parent_rate, u32 rate) | |
182 | { | |
183 | u32 sel, integ, fra_div, tmp; | |
184 | u64 tmp64 = (u64)parent_rate * BPAR; | |
185 | ||
186 | do_div(tmp64, rate); | |
187 | integ = (u32)tmp64 / BPAR; | |
188 | integ = integ >> 1; | |
189 | ||
190 | tmp = (u32)tmp64 % BPAR; | |
191 | sel = tmp / BPAR; | |
192 | ||
193 | tmp = tmp % BPAR; | |
194 | fra_div = tmp * 0xff / BPAR; | |
195 | tmp = (sel << 24) | (integ << 16) | (0xff << 8) | fra_div; | |
196 | ||
197 | /* Set I2S integer divider as 1. This bit is reserved for SPDIF | |
198 | * and do no harm. | |
199 | */ | |
200 | tmp |= BIT(28); | |
201 | return tmp; | |
202 | } | |
203 | ||
204 | static u32 calc_rate(u32 reg, u32 parent_rate) | |
205 | { | |
206 | u32 sel, integ, fra_div, tmp; | |
207 | u64 tmp64 = (u64)parent_rate * BPAR; | |
208 | ||
209 | tmp = reg; | |
210 | sel = (tmp >> 24) & BIT(0); | |
211 | integ = (tmp >> 16) & 0xff; | |
212 | fra_div = tmp & 0xff; | |
213 | ||
214 | tmp = fra_div * BPAR; | |
215 | tmp = tmp / 0xff; | |
216 | tmp += sel * BPAR; | |
217 | tmp += 2 * integ * BPAR; | |
218 | do_div(tmp64, tmp); | |
219 | ||
220 | return (u32)tmp64; | |
221 | } | |
222 | ||
223 | static unsigned long zx_audio_recalc_rate(struct clk_hw *hw, | |
224 | unsigned long parent_rate) | |
225 | { | |
226 | struct clk_zx_audio *zx_audio = to_clk_zx_audio(hw); | |
227 | u32 reg; | |
228 | ||
229 | reg = readl_relaxed(zx_audio->reg_base); | |
230 | return calc_rate(reg, parent_rate); | |
231 | } | |
232 | ||
233 | static long zx_audio_round_rate(struct clk_hw *hw, unsigned long rate, | |
234 | unsigned long *prate) | |
235 | { | |
236 | u32 reg; | |
237 | ||
238 | if (rate * 2 > *prate) | |
239 | return -EINVAL; | |
240 | ||
241 | reg = calc_reg(*prate, rate); | |
242 | return calc_rate(reg, *prate); | |
243 | } | |
244 | ||
245 | static int zx_audio_set_rate(struct clk_hw *hw, unsigned long rate, | |
246 | unsigned long parent_rate) | |
247 | { | |
248 | struct clk_zx_audio *zx_audio = to_clk_zx_audio(hw); | |
249 | u32 reg; | |
250 | ||
251 | reg = calc_reg(parent_rate, rate); | |
252 | writel_relaxed(reg, zx_audio->reg_base); | |
253 | ||
254 | return 0; | |
255 | } | |
256 | ||
257 | #define ZX_AUDIO_EN BIT(25) | |
258 | static int zx_audio_enable(struct clk_hw *hw) | |
259 | { | |
260 | struct clk_zx_audio *zx_audio = to_clk_zx_audio(hw); | |
261 | u32 reg; | |
262 | ||
263 | reg = readl_relaxed(zx_audio->reg_base); | |
264 | writel_relaxed(reg & ~ZX_AUDIO_EN, zx_audio->reg_base); | |
265 | return 0; | |
266 | } | |
267 | ||
268 | static void zx_audio_disable(struct clk_hw *hw) | |
269 | { | |
270 | struct clk_zx_audio *zx_audio = to_clk_zx_audio(hw); | |
271 | u32 reg; | |
272 | ||
273 | reg = readl_relaxed(zx_audio->reg_base); | |
274 | writel_relaxed(reg | ZX_AUDIO_EN, zx_audio->reg_base); | |
275 | } | |
276 | ||
277 | static const struct clk_ops zx_audio_ops = { | |
278 | .recalc_rate = zx_audio_recalc_rate, | |
279 | .round_rate = zx_audio_round_rate, | |
280 | .set_rate = zx_audio_set_rate, | |
281 | .enable = zx_audio_enable, | |
282 | .disable = zx_audio_disable, | |
283 | }; | |
284 | ||
285 | struct clk *clk_register_zx_audio(const char *name, | |
286 | const char * const parent_name, | |
287 | unsigned long flags, | |
288 | void __iomem *reg_base) | |
289 | { | |
290 | struct clk_zx_audio *zx_audio; | |
291 | struct clk *clk; | |
292 | struct clk_init_data init; | |
293 | ||
294 | zx_audio = kzalloc(sizeof(*zx_audio), GFP_KERNEL); | |
295 | if (!zx_audio) | |
296 | return ERR_PTR(-ENOMEM); | |
297 | ||
298 | init.name = name; | |
299 | init.ops = &zx_audio_ops; | |
300 | init.flags = flags; | |
301 | init.parent_names = parent_name ? &parent_name : NULL; | |
302 | init.num_parents = parent_name ? 1 : 0; | |
303 | ||
304 | zx_audio->reg_base = reg_base; | |
305 | zx_audio->hw.init = &init; | |
306 | ||
307 | clk = clk_register(NULL, &zx_audio->hw); | |
308 | if (IS_ERR(clk)) | |
309 | kfree(zx_audio); | |
310 | ||
311 | return clk; | |
312 | } |