Commit | Line | Data |
---|---|---|
64909882 G |
1 | /* |
2 | * linux/arch/unicore32/kernel/clock.c | |
3 | * | |
4 | * Code specific to PKUnity SoC and UniCore ISA | |
5 | * | |
6 | * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn> | |
7 | * Copyright (C) 2001-2010 Guan Xuetao | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License version 2 as | |
11 | * published by the Free Software Foundation. | |
12 | */ | |
13 | #include <linux/module.h> | |
14 | #include <linux/kernel.h> | |
15 | #include <linux/device.h> | |
16 | #include <linux/list.h> | |
17 | #include <linux/errno.h> | |
18 | #include <linux/err.h> | |
19 | #include <linux/string.h> | |
20 | #include <linux/clk.h> | |
21 | #include <linux/mutex.h> | |
22 | #include <linux/delay.h> | |
e5abf78b | 23 | #include <linux/io.h> |
64909882 G |
24 | |
25 | #include <mach/hardware.h> | |
26 | ||
27 | /* | |
28 | * Very simple clock implementation | |
29 | */ | |
30 | struct clk { | |
31 | struct list_head node; | |
32 | unsigned long rate; | |
33 | const char *name; | |
34 | }; | |
35 | ||
36 | static struct clk clk_ost_clk = { | |
37 | .name = "OST_CLK", | |
38 | .rate = CLOCK_TICK_RATE, | |
39 | }; | |
40 | ||
41 | static struct clk clk_mclk_clk = { | |
42 | .name = "MAIN_CLK", | |
43 | }; | |
44 | ||
45 | static struct clk clk_bclk32_clk = { | |
46 | .name = "BUS32_CLK", | |
47 | }; | |
48 | ||
49 | static struct clk clk_ddr_clk = { | |
50 | .name = "DDR_CLK", | |
51 | }; | |
52 | ||
53 | static struct clk clk_vga_clk = { | |
54 | .name = "VGA_CLK", | |
55 | }; | |
56 | ||
57 | static LIST_HEAD(clocks); | |
58 | static DEFINE_MUTEX(clocks_mutex); | |
59 | ||
60 | struct clk *clk_get(struct device *dev, const char *id) | |
61 | { | |
62 | struct clk *p, *clk = ERR_PTR(-ENOENT); | |
63 | ||
64 | mutex_lock(&clocks_mutex); | |
65 | list_for_each_entry(p, &clocks, node) { | |
66 | if (strcmp(id, p->name) == 0) { | |
67 | clk = p; | |
68 | break; | |
69 | } | |
70 | } | |
71 | mutex_unlock(&clocks_mutex); | |
72 | ||
73 | return clk; | |
74 | } | |
75 | EXPORT_SYMBOL(clk_get); | |
76 | ||
77 | void clk_put(struct clk *clk) | |
78 | { | |
79 | } | |
80 | EXPORT_SYMBOL(clk_put); | |
81 | ||
82 | int clk_enable(struct clk *clk) | |
83 | { | |
84 | return 0; | |
85 | } | |
86 | EXPORT_SYMBOL(clk_enable); | |
87 | ||
88 | void clk_disable(struct clk *clk) | |
89 | { | |
90 | } | |
91 | EXPORT_SYMBOL(clk_disable); | |
92 | ||
93 | unsigned long clk_get_rate(struct clk *clk) | |
94 | { | |
95 | return clk->rate; | |
96 | } | |
97 | EXPORT_SYMBOL(clk_get_rate); | |
98 | ||
99 | struct { | |
100 | unsigned long rate; | |
101 | unsigned long cfg; | |
102 | unsigned long div; | |
103 | } vga_clk_table[] = { | |
104 | {.rate = 25175000, .cfg = 0x00002001, .div = 0x9}, | |
105 | {.rate = 31500000, .cfg = 0x00002001, .div = 0x7}, | |
106 | {.rate = 40000000, .cfg = 0x00003801, .div = 0x9}, | |
107 | {.rate = 49500000, .cfg = 0x00003801, .div = 0x7}, | |
108 | {.rate = 65000000, .cfg = 0x00002c01, .div = 0x4}, | |
109 | {.rate = 78750000, .cfg = 0x00002400, .div = 0x7}, | |
110 | {.rate = 108000000, .cfg = 0x00002c01, .div = 0x2}, | |
111 | {.rate = 106500000, .cfg = 0x00003c01, .div = 0x3}, | |
112 | {.rate = 50650000, .cfg = 0x00106400, .div = 0x9}, | |
113 | {.rate = 61500000, .cfg = 0x00106400, .div = 0xa}, | |
114 | {.rate = 85500000, .cfg = 0x00002800, .div = 0x6}, | |
115 | }; | |
116 | ||
117 | struct { | |
118 | unsigned long mrate; | |
119 | unsigned long prate; | |
120 | } mclk_clk_table[] = { | |
121 | {.mrate = 500000000, .prate = 0x00109801}, | |
122 | {.mrate = 525000000, .prate = 0x00104C00}, | |
123 | {.mrate = 550000000, .prate = 0x00105000}, | |
124 | {.mrate = 575000000, .prate = 0x00105400}, | |
125 | {.mrate = 600000000, .prate = 0x00105800}, | |
126 | {.mrate = 625000000, .prate = 0x00105C00}, | |
127 | {.mrate = 650000000, .prate = 0x00106000}, | |
128 | {.mrate = 675000000, .prate = 0x00106400}, | |
129 | {.mrate = 700000000, .prate = 0x00106800}, | |
130 | {.mrate = 725000000, .prate = 0x00106C00}, | |
131 | {.mrate = 750000000, .prate = 0x00107000}, | |
132 | {.mrate = 775000000, .prate = 0x00107400}, | |
133 | {.mrate = 800000000, .prate = 0x00107800}, | |
134 | }; | |
135 | ||
136 | int clk_set_rate(struct clk *clk, unsigned long rate) | |
137 | { | |
138 | if (clk == &clk_vga_clk) { | |
139 | unsigned long pll_vgacfg, pll_vgadiv; | |
140 | int ret, i; | |
141 | ||
142 | /* lookup vga_clk_table */ | |
143 | ret = -EINVAL; | |
144 | for (i = 0; i < ARRAY_SIZE(vga_clk_table); i++) { | |
145 | if (rate == vga_clk_table[i].rate) { | |
146 | pll_vgacfg = vga_clk_table[i].cfg; | |
147 | pll_vgadiv = vga_clk_table[i].div; | |
148 | ret = 0; | |
149 | break; | |
150 | } | |
151 | } | |
152 | ||
153 | if (ret) | |
154 | return ret; | |
155 | ||
e5abf78b | 156 | if (readl(PM_PLLVGACFG) == pll_vgacfg) |
64909882 G |
157 | return 0; |
158 | ||
159 | /* set pll vga cfg reg. */ | |
e5abf78b | 160 | writel(pll_vgacfg, PM_PLLVGACFG); |
64909882 | 161 | |
e5abf78b G |
162 | writel(PM_PMCR_CFBVGA, PM_PMCR); |
163 | while ((readl(PM_PLLDFCDONE) & PM_PLLDFCDONE_VGADFC) | |
64909882 G |
164 | != PM_PLLDFCDONE_VGADFC) |
165 | udelay(100); /* about 1ms */ | |
166 | ||
167 | /* set div cfg reg. */ | |
e5abf78b | 168 | writel(readl(PM_PCGR) | PM_PCGR_VGACLK, PM_PCGR); |
64909882 | 169 | |
e5abf78b G |
170 | writel((readl(PM_DIVCFG) & ~PM_DIVCFG_VGACLK_MASK) |
171 | | PM_DIVCFG_VGACLK(pll_vgadiv), PM_DIVCFG); | |
64909882 | 172 | |
e5abf78b G |
173 | writel(readl(PM_SWRESET) | PM_SWRESET_VGADIV, PM_SWRESET); |
174 | while ((readl(PM_SWRESET) & PM_SWRESET_VGADIV) | |
175 | == PM_SWRESET_VGADIV) | |
64909882 G |
176 | udelay(100); /* 65536 bclk32, about 320us */ |
177 | ||
e5abf78b | 178 | writel(readl(PM_PCGR) & ~PM_PCGR_VGACLK, PM_PCGR); |
64909882 G |
179 | } |
180 | #ifdef CONFIG_CPU_FREQ | |
181 | if (clk == &clk_mclk_clk) { | |
db7ef289 | 182 | u32 pll_rate, divstatus = readl(PM_DIVSTATUS); |
64909882 G |
183 | int ret, i; |
184 | ||
185 | /* lookup mclk_clk_table */ | |
186 | ret = -EINVAL; | |
187 | for (i = 0; i < ARRAY_SIZE(mclk_clk_table); i++) { | |
188 | if (rate == mclk_clk_table[i].mrate) { | |
189 | pll_rate = mclk_clk_table[i].prate; | |
190 | clk_mclk_clk.rate = mclk_clk_table[i].mrate; | |
191 | ret = 0; | |
192 | break; | |
193 | } | |
194 | } | |
195 | ||
196 | if (ret) | |
197 | return ret; | |
198 | ||
199 | if (clk_mclk_clk.rate) | |
200 | clk_bclk32_clk.rate = clk_mclk_clk.rate | |
201 | / (((divstatus & 0x0000f000) >> 12) + 1); | |
202 | ||
203 | /* set pll sys cfg reg. */ | |
db7ef289 | 204 | writel(pll_rate, PM_PLLSYSCFG); |
64909882 | 205 | |
db7ef289 CG |
206 | writel(PM_PMCR_CFBSYS, PM_PMCR); |
207 | while ((readl(PM_PLLDFCDONE) & PM_PLLDFCDONE_SYSDFC) | |
64909882 G |
208 | != PM_PLLDFCDONE_SYSDFC) |
209 | udelay(100); | |
210 | /* about 1ms */ | |
211 | } | |
212 | #endif | |
213 | return 0; | |
214 | } | |
215 | EXPORT_SYMBOL(clk_set_rate); | |
216 | ||
217 | int clk_register(struct clk *clk) | |
218 | { | |
219 | mutex_lock(&clocks_mutex); | |
220 | list_add(&clk->node, &clocks); | |
221 | mutex_unlock(&clocks_mutex); | |
222 | printk(KERN_DEFAULT "PKUnity PM: %s %lu.%02luM\n", clk->name, | |
223 | (clk->rate)/1000000, (clk->rate)/10000 % 100); | |
224 | return 0; | |
225 | } | |
226 | EXPORT_SYMBOL(clk_register); | |
227 | ||
228 | void clk_unregister(struct clk *clk) | |
229 | { | |
230 | mutex_lock(&clocks_mutex); | |
231 | list_del(&clk->node); | |
232 | mutex_unlock(&clocks_mutex); | |
233 | } | |
234 | EXPORT_SYMBOL(clk_unregister); | |
235 | ||
236 | struct { | |
237 | unsigned long prate; | |
238 | unsigned long rate; | |
239 | } pllrate_table[] = { | |
240 | {.prate = 0x00002001, .rate = 250000000}, | |
241 | {.prate = 0x00104801, .rate = 250000000}, | |
242 | {.prate = 0x00104C01, .rate = 262500000}, | |
243 | {.prate = 0x00002401, .rate = 275000000}, | |
244 | {.prate = 0x00105001, .rate = 275000000}, | |
245 | {.prate = 0x00105401, .rate = 287500000}, | |
246 | {.prate = 0x00002801, .rate = 300000000}, | |
247 | {.prate = 0x00105801, .rate = 300000000}, | |
248 | {.prate = 0x00105C01, .rate = 312500000}, | |
249 | {.prate = 0x00002C01, .rate = 325000000}, | |
250 | {.prate = 0x00106001, .rate = 325000000}, | |
251 | {.prate = 0x00106401, .rate = 337500000}, | |
252 | {.prate = 0x00003001, .rate = 350000000}, | |
253 | {.prate = 0x00106801, .rate = 350000000}, | |
254 | {.prate = 0x00106C01, .rate = 362500000}, | |
255 | {.prate = 0x00003401, .rate = 375000000}, | |
256 | {.prate = 0x00107001, .rate = 375000000}, | |
257 | {.prate = 0x00107401, .rate = 387500000}, | |
258 | {.prate = 0x00003801, .rate = 400000000}, | |
259 | {.prate = 0x00107801, .rate = 400000000}, | |
260 | {.prate = 0x00107C01, .rate = 412500000}, | |
261 | {.prate = 0x00003C01, .rate = 425000000}, | |
262 | {.prate = 0x00108001, .rate = 425000000}, | |
263 | {.prate = 0x00108401, .rate = 437500000}, | |
264 | {.prate = 0x00004001, .rate = 450000000}, | |
265 | {.prate = 0x00108801, .rate = 450000000}, | |
266 | {.prate = 0x00108C01, .rate = 462500000}, | |
267 | {.prate = 0x00004401, .rate = 475000000}, | |
268 | {.prate = 0x00109001, .rate = 475000000}, | |
269 | {.prate = 0x00109401, .rate = 487500000}, | |
270 | {.prate = 0x00004801, .rate = 500000000}, | |
271 | {.prate = 0x00109801, .rate = 500000000}, | |
272 | {.prate = 0x00104C00, .rate = 525000000}, | |
273 | {.prate = 0x00002400, .rate = 550000000}, | |
274 | {.prate = 0x00105000, .rate = 550000000}, | |
275 | {.prate = 0x00105400, .rate = 575000000}, | |
276 | {.prate = 0x00002800, .rate = 600000000}, | |
277 | {.prate = 0x00105800, .rate = 600000000}, | |
278 | {.prate = 0x00105C00, .rate = 625000000}, | |
279 | {.prate = 0x00002C00, .rate = 650000000}, | |
280 | {.prate = 0x00106000, .rate = 650000000}, | |
281 | {.prate = 0x00106400, .rate = 675000000}, | |
282 | {.prate = 0x00003000, .rate = 700000000}, | |
283 | {.prate = 0x00106800, .rate = 700000000}, | |
284 | {.prate = 0x00106C00, .rate = 725000000}, | |
285 | {.prate = 0x00003400, .rate = 750000000}, | |
286 | {.prate = 0x00107000, .rate = 750000000}, | |
287 | {.prate = 0x00107400, .rate = 775000000}, | |
288 | {.prate = 0x00003800, .rate = 800000000}, | |
289 | {.prate = 0x00107800, .rate = 800000000}, | |
290 | {.prate = 0x00107C00, .rate = 825000000}, | |
291 | {.prate = 0x00003C00, .rate = 850000000}, | |
292 | {.prate = 0x00108000, .rate = 850000000}, | |
293 | {.prate = 0x00108400, .rate = 875000000}, | |
294 | {.prate = 0x00004000, .rate = 900000000}, | |
295 | {.prate = 0x00108800, .rate = 900000000}, | |
296 | {.prate = 0x00108C00, .rate = 925000000}, | |
297 | {.prate = 0x00004400, .rate = 950000000}, | |
298 | {.prate = 0x00109000, .rate = 950000000}, | |
299 | {.prate = 0x00109400, .rate = 975000000}, | |
300 | {.prate = 0x00004800, .rate = 1000000000}, | |
301 | {.prate = 0x00109800, .rate = 1000000000}, | |
302 | }; | |
303 | ||
304 | struct { | |
305 | unsigned long prate; | |
306 | unsigned long drate; | |
307 | } pddr_table[] = { | |
308 | {.prate = 0x00100800, .drate = 44236800}, | |
309 | {.prate = 0x00100C00, .drate = 66355200}, | |
310 | {.prate = 0x00101000, .drate = 88473600}, | |
311 | {.prate = 0x00101400, .drate = 110592000}, | |
312 | {.prate = 0x00101800, .drate = 132710400}, | |
313 | {.prate = 0x00101C01, .drate = 154828800}, | |
314 | {.prate = 0x00102001, .drate = 176947200}, | |
315 | {.prate = 0x00102401, .drate = 199065600}, | |
316 | {.prate = 0x00102801, .drate = 221184000}, | |
317 | {.prate = 0x00102C01, .drate = 243302400}, | |
318 | {.prate = 0x00103001, .drate = 265420800}, | |
319 | {.prate = 0x00103401, .drate = 287539200}, | |
320 | {.prate = 0x00103801, .drate = 309657600}, | |
321 | {.prate = 0x00103C01, .drate = 331776000}, | |
322 | {.prate = 0x00104001, .drate = 353894400}, | |
323 | }; | |
324 | ||
325 | static int __init clk_init(void) | |
326 | { | |
327 | #ifdef CONFIG_PUV3_PM | |
e5abf78b G |
328 | u32 pllrate, divstatus = readl(PM_DIVSTATUS); |
329 | u32 pcgr_val = readl(PM_PCGR); | |
64909882 G |
330 | int i; |
331 | ||
332 | pcgr_val |= PM_PCGR_BCLKMME | PM_PCGR_BCLKH264E | PM_PCGR_BCLKH264D | |
333 | | PM_PCGR_HECLK | PM_PCGR_HDCLK; | |
e5abf78b | 334 | writel(pcgr_val, PM_PCGR); |
64909882 | 335 | |
e5abf78b | 336 | pllrate = readl(PM_PLLSYSSTATUS); |
64909882 G |
337 | |
338 | /* lookup pmclk_table */ | |
339 | clk_mclk_clk.rate = 0; | |
340 | for (i = 0; i < ARRAY_SIZE(pllrate_table); i++) { | |
341 | if (pllrate == pllrate_table[i].prate) { | |
342 | clk_mclk_clk.rate = pllrate_table[i].rate; | |
343 | break; | |
344 | } | |
345 | } | |
346 | ||
347 | if (clk_mclk_clk.rate) | |
348 | clk_bclk32_clk.rate = clk_mclk_clk.rate / | |
349 | (((divstatus & 0x0000f000) >> 12) + 1); | |
350 | ||
e5abf78b | 351 | pllrate = readl(PM_PLLDDRSTATUS); |
64909882 G |
352 | |
353 | /* lookup pddr_table */ | |
354 | clk_ddr_clk.rate = 0; | |
355 | for (i = 0; i < ARRAY_SIZE(pddr_table); i++) { | |
356 | if (pllrate == pddr_table[i].prate) { | |
357 | clk_ddr_clk.rate = pddr_table[i].drate; | |
358 | break; | |
359 | } | |
360 | } | |
361 | ||
e5abf78b | 362 | pllrate = readl(PM_PLLVGASTATUS); |
64909882 G |
363 | |
364 | /* lookup pvga_table */ | |
365 | clk_vga_clk.rate = 0; | |
366 | for (i = 0; i < ARRAY_SIZE(pllrate_table); i++) { | |
367 | if (pllrate == pllrate_table[i].prate) { | |
368 | clk_vga_clk.rate = pllrate_table[i].rate; | |
369 | break; | |
370 | } | |
371 | } | |
372 | ||
373 | if (clk_vga_clk.rate) | |
374 | clk_vga_clk.rate = clk_vga_clk.rate / | |
375 | (((divstatus & 0x00f00000) >> 20) + 1); | |
376 | ||
377 | clk_register(&clk_vga_clk); | |
378 | #endif | |
379 | #ifdef CONFIG_ARCH_FPGA | |
380 | clk_ddr_clk.rate = 33000000; | |
381 | clk_mclk_clk.rate = 33000000; | |
382 | clk_bclk32_clk.rate = 33000000; | |
383 | #endif | |
384 | clk_register(&clk_ddr_clk); | |
385 | clk_register(&clk_mclk_clk); | |
386 | clk_register(&clk_bclk32_clk); | |
387 | clk_register(&clk_ost_clk); | |
388 | return 0; | |
389 | } | |
390 | core_initcall(clk_init); |