diff options
author | Steven Miao <realmz6@gmail.com> | 2012-05-16 05:49:52 -0400 |
---|---|---|
committer | Bob Liu <lliubbo@gmail.com> | 2012-05-21 02:54:13 -0400 |
commit | 969003152aa9085e50ce23822c60fab82222ecef (patch) | |
tree | ab12a8c8bf20706d624ef7ad6b540cc8776d954f /arch/blackfin/mach-bf609 | |
parent | b5affb0147cee0ea05d909396f8e389092729236 (diff) |
blackfin: bf60x: add clock support
Add clock support for bf60x.
Signed-off-by: Steven Miao <realmz6@gmail.com>
Signed-off-by: Bob Liu <lliubbo@gmail.com>
Diffstat (limited to 'arch/blackfin/mach-bf609')
-rw-r--r-- | arch/blackfin/mach-bf609/clock.c | 390 |
1 files changed, 390 insertions, 0 deletions
diff --git a/arch/blackfin/mach-bf609/clock.c b/arch/blackfin/mach-bf609/clock.c new file mode 100644 index 000000000000..b50412c8cab0 --- /dev/null +++ b/arch/blackfin/mach-bf609/clock.c | |||
@@ -0,0 +1,390 @@ | |||
1 | #include <linux/module.h> | ||
2 | #include <linux/kernel.h> | ||
3 | #include <linux/list.h> | ||
4 | #include <linux/errno.h> | ||
5 | #include <linux/err.h> | ||
6 | #include <linux/string.h> | ||
7 | #include <linux/clk.h> | ||
8 | #include <linux/mutex.h> | ||
9 | #include <linux/spinlock.h> | ||
10 | #include <linux/debugfs.h> | ||
11 | #include <linux/device.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/timer.h> | ||
14 | #include <linux/io.h> | ||
15 | #include <linux/seq_file.h> | ||
16 | #include <linux/clkdev.h> | ||
17 | |||
18 | #include <asm/clocks.h> | ||
19 | |||
20 | #define CGU0_CTL_DF (1 << 0) | ||
21 | |||
22 | #define CGU0_CTL_MSEL_SHIFT 8 | ||
23 | #define CGU0_CTL_MSEL_MASK (0x7f << 8) | ||
24 | |||
25 | #define CGU0_STAT_PLLEN (1 << 0) | ||
26 | #define CGU0_STAT_PLLBP (1 << 1) | ||
27 | #define CGU0_STAT_PLLLK (1 << 2) | ||
28 | #define CGU0_STAT_CLKSALGN (1 << 3) | ||
29 | #define CGU0_STAT_CCBF0 (1 << 4) | ||
30 | #define CGU0_STAT_CCBF1 (1 << 5) | ||
31 | #define CGU0_STAT_SCBF0 (1 << 6) | ||
32 | #define CGU0_STAT_SCBF1 (1 << 7) | ||
33 | #define CGU0_STAT_DCBF (1 << 8) | ||
34 | #define CGU0_STAT_OCBF (1 << 9) | ||
35 | #define CGU0_STAT_ADDRERR (1 << 16) | ||
36 | #define CGU0_STAT_LWERR (1 << 17) | ||
37 | #define CGU0_STAT_DIVERR (1 << 18) | ||
38 | #define CGU0_STAT_WDFMSERR (1 << 19) | ||
39 | #define CGU0_STAT_WDIVERR (1 << 20) | ||
40 | #define CGU0_STAT_PLOCKERR (1 << 21) | ||
41 | |||
42 | #define CGU0_DIV_CSEL_SHIFT 0 | ||
43 | #define CGU0_DIV_CSEL_MASK 0x0000001F | ||
44 | #define CGU0_DIV_S0SEL_SHIFT 5 | ||
45 | #define CGU0_DIV_S0SEL_MASK (0x3 << CGU0_DIV_S0SEL_SHIFT) | ||
46 | #define CGU0_DIV_SYSSEL_SHIFT 8 | ||
47 | #define CGU0_DIV_SYSSEL_MASK (0x1f << CGU0_DIV_SYSSEL_SHIFT) | ||
48 | #define CGU0_DIV_S1SEL_SHIFT 13 | ||
49 | #define CGU0_DIV_S1SEL_MASK (0x3 << CGU0_DIV_S1SEL_SHIFT) | ||
50 | #define CGU0_DIV_DSEL_SHIFT 16 | ||
51 | #define CGU0_DIV_DSEL_MASK (0x1f << CGU0_DIV_DSEL_SHIFT) | ||
52 | #define CGU0_DIV_OSEL_SHIFT 22 | ||
53 | #define CGU0_DIV_OSEL_MASK (0x7f << CGU0_DIV_OSEL_SHIFT) | ||
54 | |||
55 | #define CLK(_clk, _devname, _conname) \ | ||
56 | { \ | ||
57 | .clk = &_clk, \ | ||
58 | .dev_id = _devname, \ | ||
59 | .con_id = _conname, \ | ||
60 | } | ||
61 | |||
62 | #define NEEDS_INITIALIZATION 0x11 | ||
63 | |||
64 | static LIST_HEAD(clk_list); | ||
65 | |||
66 | static void clk_reg_write_mask(u32 reg, uint32_t val, uint32_t mask) | ||
67 | { | ||
68 | u32 val2; | ||
69 | |||
70 | val2 = bfin_read32(reg); | ||
71 | val2 &= ~mask; | ||
72 | val2 |= val; | ||
73 | bfin_write32(reg, val2); | ||
74 | } | ||
75 | |||
76 | static void clk_reg_set_bits(u32 reg, uint32_t mask) | ||
77 | { | ||
78 | u32 val; | ||
79 | |||
80 | val = bfin_read32(reg); | ||
81 | val |= mask; | ||
82 | bfin_write32(reg, val); | ||
83 | } | ||
84 | |||
85 | static void clk_reg_clear_bits(u32 reg, uint32_t mask) | ||
86 | { | ||
87 | u32 val; | ||
88 | |||
89 | val = bfin_read32(reg); | ||
90 | val &= ~mask; | ||
91 | bfin_write32(reg, val); | ||
92 | } | ||
93 | |||
94 | int wait_for_pll_align(void) | ||
95 | { | ||
96 | int i = 10000; | ||
97 | while (i-- && (bfin_read32(CGU0_STAT) & CGU0_STAT_CLKSALGN)); | ||
98 | |||
99 | if (bfin_read32(CGU0_STAT) & CGU0_STAT_CLKSALGN) { | ||
100 | printk(KERN_DEBUG "fail to align clk\n"); | ||
101 | return -1; | ||
102 | } | ||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | int clk_enable(struct clk *clk) | ||
107 | { | ||
108 | int ret = -EIO; | ||
109 | if (clk->ops && clk->ops->enable) | ||
110 | ret = clk->ops->enable(clk); | ||
111 | return ret; | ||
112 | } | ||
113 | EXPORT_SYMBOL(clk_enable); | ||
114 | |||
115 | void clk_disable(struct clk *clk) | ||
116 | { | ||
117 | if (clk->ops && clk->ops->disable) | ||
118 | clk->ops->disable(clk); | ||
119 | } | ||
120 | EXPORT_SYMBOL(clk_disable); | ||
121 | |||
122 | unsigned long clk_get_rate(struct clk *clk) | ||
123 | { | ||
124 | unsigned long ret = 0; | ||
125 | if (clk->ops && clk->ops->get_rate) | ||
126 | ret = clk->ops->get_rate(clk); | ||
127 | return ret; | ||
128 | } | ||
129 | EXPORT_SYMBOL(clk_get_rate); | ||
130 | |||
131 | long clk_round_rate(struct clk *clk, unsigned long rate) | ||
132 | { | ||
133 | long ret = -EIO; | ||
134 | if (clk->ops && clk->ops->round_rate) | ||
135 | ret = clk->ops->round_rate(clk, rate); | ||
136 | return ret; | ||
137 | } | ||
138 | EXPORT_SYMBOL(clk_round_rate); | ||
139 | |||
140 | int clk_set_rate(struct clk *clk, unsigned long rate) | ||
141 | { | ||
142 | int ret = -EIO; | ||
143 | if (clk->ops && clk->ops->set_rate) | ||
144 | ret = clk->ops->set_rate(clk, rate); | ||
145 | return ret; | ||
146 | } | ||
147 | EXPORT_SYMBOL(clk_set_rate); | ||
148 | |||
149 | unsigned long vco_get_rate(struct clk *clk) | ||
150 | { | ||
151 | return clk->rate; | ||
152 | } | ||
153 | |||
154 | unsigned long pll_get_rate(struct clk *clk) | ||
155 | { | ||
156 | u32 df; | ||
157 | u32 msel; | ||
158 | u32 ctl = bfin_read32(CGU0_CTL); | ||
159 | u32 stat = bfin_read32(CGU0_STAT); | ||
160 | if (stat & CGU0_STAT_PLLBP) | ||
161 | return 0; | ||
162 | msel = (ctl & CGU0_CTL_MSEL_MASK) >> CGU0_CTL_MSEL_SHIFT; | ||
163 | df = (ctl & CGU0_CTL_DF); | ||
164 | clk->parent->rate = clk_get_rate(clk->parent); | ||
165 | return clk->parent->rate / (df + 1) * msel * 2; | ||
166 | } | ||
167 | |||
168 | unsigned long pll_round_rate(struct clk *clk, unsigned long rate) | ||
169 | { | ||
170 | u32 div; | ||
171 | div = rate / clk->parent->rate; | ||
172 | return clk->parent->rate * div; | ||
173 | } | ||
174 | |||
175 | int pll_set_rate(struct clk *clk, unsigned long rate) | ||
176 | { | ||
177 | u32 msel; | ||
178 | u32 stat = bfin_read32(CGU0_STAT); | ||
179 | if (!(stat & CGU0_STAT_PLLEN)) | ||
180 | return -EBUSY; | ||
181 | if (!(stat & CGU0_STAT_PLLLK)) | ||
182 | return -EBUSY; | ||
183 | if (wait_for_pll_align()) | ||
184 | return -EBUSY; | ||
185 | msel = rate / clk->parent->rate / 2; | ||
186 | clk_reg_write_mask(CGU0_CTL, msel << CGU0_CTL_MSEL_SHIFT, | ||
187 | CGU0_CTL_MSEL_MASK); | ||
188 | clk->rate = rate; | ||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | unsigned long cclk_get_rate(struct clk *clk) | ||
193 | { | ||
194 | if (clk->parent) | ||
195 | return clk->parent->rate; | ||
196 | else | ||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | unsigned long sys_clk_get_rate(struct clk *clk) | ||
201 | { | ||
202 | unsigned long drate; | ||
203 | u32 msel; | ||
204 | u32 df; | ||
205 | u32 ctl = bfin_read32(CGU0_CTL); | ||
206 | u32 div = bfin_read32(CGU0_DIV); | ||
207 | div = (div & clk->mask) >> clk->shift; | ||
208 | msel = (ctl & CGU0_CTL_MSEL_MASK) >> CGU0_CTL_MSEL_SHIFT; | ||
209 | df = (ctl & CGU0_CTL_DF); | ||
210 | |||
211 | if (!strcmp(clk->parent->name, "SYS_CLKIN")) { | ||
212 | drate = clk->parent->rate / (df + 1); | ||
213 | drate *= msel; | ||
214 | drate /= div; | ||
215 | return drate; | ||
216 | } else { | ||
217 | clk->parent->rate = clk_get_rate(clk->parent); | ||
218 | return clk->parent->rate / div; | ||
219 | } | ||
220 | } | ||
221 | |||
222 | unsigned long sys_clk_round_rate(struct clk *clk, unsigned long rate) | ||
223 | { | ||
224 | unsigned long max_rate; | ||
225 | unsigned long drate; | ||
226 | int i; | ||
227 | u32 msel; | ||
228 | u32 df; | ||
229 | u32 ctl = bfin_read32(CGU0_CTL); | ||
230 | |||
231 | msel = (ctl & CGU0_CTL_MSEL_MASK) >> CGU0_CTL_MSEL_SHIFT; | ||
232 | df = (ctl & CGU0_CTL_DF); | ||
233 | max_rate = clk->parent->rate / (df + 1) * msel; | ||
234 | |||
235 | if (rate > max_rate) | ||
236 | return 0; | ||
237 | |||
238 | for (i = 1; i < clk->mask; i++) { | ||
239 | drate = max_rate / i; | ||
240 | if (rate >= drate) | ||
241 | return drate; | ||
242 | } | ||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | int sys_clk_set_rate(struct clk *clk, unsigned long rate) | ||
247 | { | ||
248 | u32 div = bfin_read32(CGU0_DIV); | ||
249 | div = (div & clk->mask) >> clk->shift; | ||
250 | |||
251 | rate = clk_round_rate(clk, rate); | ||
252 | |||
253 | if (!rate) | ||
254 | return -EINVAL; | ||
255 | |||
256 | div = (clk_get_rate(clk) * div) / rate; | ||
257 | |||
258 | if (wait_for_pll_align()) | ||
259 | return -EBUSY; | ||
260 | clk_reg_write_mask(CGU0_DIV, div << clk->shift, | ||
261 | clk->mask); | ||
262 | clk->rate = rate; | ||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | static struct clk_ops vco_ops = { | ||
267 | .get_rate = vco_get_rate, | ||
268 | }; | ||
269 | |||
270 | static struct clk_ops pll_ops = { | ||
271 | .get_rate = pll_get_rate, | ||
272 | .set_rate = pll_set_rate, | ||
273 | }; | ||
274 | |||
275 | static struct clk_ops cclk_ops = { | ||
276 | .get_rate = cclk_get_rate, | ||
277 | }; | ||
278 | |||
279 | static struct clk_ops sys_clk_ops = { | ||
280 | .get_rate = sys_clk_get_rate, | ||
281 | .set_rate = sys_clk_set_rate, | ||
282 | .round_rate = sys_clk_round_rate, | ||
283 | }; | ||
284 | |||
285 | static struct clk sys_clkin = { | ||
286 | .name = "SYS_CLKIN", | ||
287 | .rate = CONFIG_CLKIN_HZ, | ||
288 | .ops = &vco_ops, | ||
289 | }; | ||
290 | |||
291 | static struct clk pll_clk = { | ||
292 | .name = "PLLCLK", | ||
293 | .rate = 500000000, | ||
294 | .parent = &sys_clkin, | ||
295 | .ops = &pll_ops, | ||
296 | .flags = NEEDS_INITIALIZATION, | ||
297 | }; | ||
298 | |||
299 | static struct clk cclk = { | ||
300 | .name = "CCLK", | ||
301 | .rate = 500000000, | ||
302 | .mask = CGU0_DIV_CSEL_MASK, | ||
303 | .shift = CGU0_DIV_CSEL_SHIFT, | ||
304 | .parent = &sys_clkin, | ||
305 | .ops = &sys_clk_ops, | ||
306 | .flags = NEEDS_INITIALIZATION, | ||
307 | }; | ||
308 | |||
309 | static struct clk cclk0 = { | ||
310 | .name = "CCLK0", | ||
311 | .parent = &cclk, | ||
312 | .ops = &cclk_ops, | ||
313 | }; | ||
314 | |||
315 | static struct clk cclk1 = { | ||
316 | .name = "CCLK1", | ||
317 | .parent = &cclk, | ||
318 | .ops = &cclk_ops, | ||
319 | }; | ||
320 | |||
321 | static struct clk sysclk = { | ||
322 | .name = "SYSCLK", | ||
323 | .rate = 500000000, | ||
324 | .mask = CGU0_DIV_SYSSEL_MASK, | ||
325 | .shift = CGU0_DIV_SYSSEL_SHIFT, | ||
326 | .parent = &sys_clkin, | ||
327 | .ops = &sys_clk_ops, | ||
328 | .flags = NEEDS_INITIALIZATION, | ||
329 | }; | ||
330 | |||
331 | static struct clk sclk0 = { | ||
332 | .name = "SCLK0", | ||
333 | .rate = 500000000, | ||
334 | .mask = CGU0_DIV_S0SEL_MASK, | ||
335 | .shift = CGU0_DIV_S0SEL_SHIFT, | ||
336 | .parent = &sysclk, | ||
337 | .ops = &sys_clk_ops, | ||
338 | }; | ||
339 | |||
340 | static struct clk sclk1 = { | ||
341 | .name = "SCLK1", | ||
342 | .rate = 500000000, | ||
343 | .mask = CGU0_DIV_S1SEL_MASK, | ||
344 | .shift = CGU0_DIV_S1SEL_SHIFT, | ||
345 | .parent = &sysclk, | ||
346 | .ops = &sys_clk_ops, | ||
347 | }; | ||
348 | |||
349 | static struct clk dclk = { | ||
350 | .name = "DCLK", | ||
351 | .rate = 500000000, | ||
352 | .mask = CGU0_DIV_DSEL_MASK, | ||
353 | .shift = CGU0_DIV_DSEL_SHIFT, | ||
354 | .parent = &pll_clk, | ||
355 | .ops = &sys_clk_ops, | ||
356 | }; | ||
357 | |||
358 | static struct clk oclk = { | ||
359 | .name = "OCLK", | ||
360 | .rate = 500000000, | ||
361 | .mask = CGU0_DIV_OSEL_MASK, | ||
362 | .shift = CGU0_DIV_OSEL_SHIFT, | ||
363 | .parent = &pll_clk, | ||
364 | }; | ||
365 | |||
366 | static struct clk_lookup bf609_clks[] = { | ||
367 | CLK(sys_clkin, NULL, "SYS_CLKIN"), | ||
368 | CLK(pll_clk, NULL, "PLLCLK"), | ||
369 | CLK(cclk, NULL, "CCLK"), | ||
370 | CLK(cclk0, NULL, "CCLK0"), | ||
371 | CLK(cclk1, NULL, "CCLK1"), | ||
372 | CLK(sysclk, NULL, "SYSCLK"), | ||
373 | CLK(sclk0, NULL, "SCLK0"), | ||
374 | CLK(sclk1, NULL, "SCLK1"), | ||
375 | CLK(dclk, NULL, "DCLK"), | ||
376 | CLK(oclk, NULL, "OCLK"), | ||
377 | }; | ||
378 | |||
379 | int __init clk_init(void) | ||
380 | { | ||
381 | int i; | ||
382 | struct clk *clkp; | ||
383 | for (i = 0; i < ARRAY_SIZE(bf609_clks); i++) { | ||
384 | clkp = bf609_clks[i].clk; | ||
385 | if (clkp->flags & NEEDS_INITIALIZATION) | ||
386 | clk_get_rate(clkp); | ||
387 | } | ||
388 | clkdev_add_table(bf609_clks, ARRAY_SIZE(bf609_clks)); | ||
389 | return 0; | ||
390 | } | ||