diff options
author | GuanXuetao <gxt@mprc.pku.edu.cn> | 2011-02-26 07:23:59 -0500 |
---|---|---|
committer | GuanXuetao <gxt@mprc.pku.edu.cn> | 2011-03-16 21:19:11 -0400 |
commit | 64909882862e9bb88aa6177e3f92056f5601b3e3 (patch) | |
tree | 0fade894b903f6185ab97d930004c0cf56eb412d /arch | |
parent | f864d2f8304e6be90b4c4e4ac615edc6fcefd4c1 (diff) |
unicore32 additional architecture files: pm related files
This patch adds pm related files, including hibernate and sleep supports.
Signed-off-by: Guan Xuetao <gxt@mprc.pku.edu.cn>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/unicore32/include/asm/suspend.h | 30 | ||||
-rw-r--r-- | arch/unicore32/include/mach/pm.h | 43 | ||||
-rw-r--r-- | arch/unicore32/kernel/clock.c | 388 | ||||
-rw-r--r-- | arch/unicore32/kernel/cpu-ucv2.c | 93 | ||||
-rw-r--r-- | arch/unicore32/kernel/hibernate.c | 160 | ||||
-rw-r--r-- | arch/unicore32/kernel/hibernate_asm.S | 117 | ||||
-rw-r--r-- | arch/unicore32/kernel/pm.c | 123 | ||||
-rw-r--r-- | arch/unicore32/kernel/sleep.S | 202 |
8 files changed, 1156 insertions, 0 deletions
diff --git a/arch/unicore32/include/asm/suspend.h b/arch/unicore32/include/asm/suspend.h new file mode 100644 index 00000000000..88a9c0f32b2 --- /dev/null +++ b/arch/unicore32/include/asm/suspend.h | |||
@@ -0,0 +1,30 @@ | |||
1 | /* | ||
2 | * linux/arch/unicore32/include/asm/suspend.h | ||
3 | * | ||
4 | * Code specific to PKUnity SoC and UniCore ISA | ||
5 | * | ||
6 | * Copyright (C) 2001-2010 GUAN Xue-tao | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #ifndef __UNICORE_SUSPEND_H__ | ||
14 | #define __UNICORE_SUSPEND_H__ | ||
15 | |||
16 | #ifndef __ASSEMBLY__ | ||
17 | static inline int arch_prepare_suspend(void) { return 0; } | ||
18 | |||
19 | #include <asm/ptrace.h> | ||
20 | |||
21 | struct swsusp_arch_regs { | ||
22 | struct cpu_context_save cpu_context; /* cpu context */ | ||
23 | #ifdef CONFIG_UNICORE_FPU_F64 | ||
24 | struct fp_state fpstate __attribute__((aligned(8))); | ||
25 | #endif | ||
26 | }; | ||
27 | #endif | ||
28 | |||
29 | #endif /* __UNICORE_SUSPEND_H__ */ | ||
30 | |||
diff --git a/arch/unicore32/include/mach/pm.h b/arch/unicore32/include/mach/pm.h new file mode 100644 index 00000000000..4dcd34ae194 --- /dev/null +++ b/arch/unicore32/include/mach/pm.h | |||
@@ -0,0 +1,43 @@ | |||
1 | /* | ||
2 | * linux/arch/unicore/include/mach/pm.h | ||
3 | * | ||
4 | * Code specific to PKUnity SoC and UniCore ISA | ||
5 | * | ||
6 | * Copyright (C) 2001-2010 GUAN Xue-tao | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | #ifndef __PUV3_PM_H__ | ||
13 | #define __PUV3_PM_H__ | ||
14 | |||
15 | #include <linux/suspend.h> | ||
16 | |||
17 | struct puv3_cpu_pm_fns { | ||
18 | int save_count; | ||
19 | void (*save)(unsigned long *); | ||
20 | void (*restore)(unsigned long *); | ||
21 | int (*valid)(suspend_state_t state); | ||
22 | void (*enter)(suspend_state_t state); | ||
23 | int (*prepare)(void); | ||
24 | void (*finish)(void); | ||
25 | }; | ||
26 | |||
27 | extern struct puv3_cpu_pm_fns *puv3_cpu_pm_fns; | ||
28 | |||
29 | /* sleep.S */ | ||
30 | extern void puv3_cpu_suspend(unsigned int); | ||
31 | |||
32 | extern void puv3_cpu_resume(void); | ||
33 | |||
34 | extern int puv3_pm_enter(suspend_state_t state); | ||
35 | |||
36 | /* Defined in hibernate_asm.S */ | ||
37 | extern int restore_image(pgd_t *resume_pg_dir, struct pbe *restore_pblist); | ||
38 | |||
39 | /* References to section boundaries */ | ||
40 | extern const void __nosave_begin, __nosave_end; | ||
41 | |||
42 | extern struct pbe *restore_pblist; | ||
43 | #endif | ||
diff --git a/arch/unicore32/kernel/clock.c b/arch/unicore32/kernel/clock.c new file mode 100644 index 00000000000..80323db581f --- /dev/null +++ b/arch/unicore32/kernel/clock.c | |||
@@ -0,0 +1,388 @@ | |||
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> | ||
23 | |||
24 | #include <mach/hardware.h> | ||
25 | |||
26 | /* | ||
27 | * Very simple clock implementation | ||
28 | */ | ||
29 | struct clk { | ||
30 | struct list_head node; | ||
31 | unsigned long rate; | ||
32 | const char *name; | ||
33 | }; | ||
34 | |||
35 | static struct clk clk_ost_clk = { | ||
36 | .name = "OST_CLK", | ||
37 | .rate = CLOCK_TICK_RATE, | ||
38 | }; | ||
39 | |||
40 | static struct clk clk_mclk_clk = { | ||
41 | .name = "MAIN_CLK", | ||
42 | }; | ||
43 | |||
44 | static struct clk clk_bclk32_clk = { | ||
45 | .name = "BUS32_CLK", | ||
46 | }; | ||
47 | |||
48 | static struct clk clk_ddr_clk = { | ||
49 | .name = "DDR_CLK", | ||
50 | }; | ||
51 | |||
52 | static struct clk clk_vga_clk = { | ||
53 | .name = "VGA_CLK", | ||
54 | }; | ||
55 | |||
56 | static LIST_HEAD(clocks); | ||
57 | static DEFINE_MUTEX(clocks_mutex); | ||
58 | |||
59 | struct clk *clk_get(struct device *dev, const char *id) | ||
60 | { | ||
61 | struct clk *p, *clk = ERR_PTR(-ENOENT); | ||
62 | |||
63 | mutex_lock(&clocks_mutex); | ||
64 | list_for_each_entry(p, &clocks, node) { | ||
65 | if (strcmp(id, p->name) == 0) { | ||
66 | clk = p; | ||
67 | break; | ||
68 | } | ||
69 | } | ||
70 | mutex_unlock(&clocks_mutex); | ||
71 | |||
72 | return clk; | ||
73 | } | ||
74 | EXPORT_SYMBOL(clk_get); | ||
75 | |||
76 | void clk_put(struct clk *clk) | ||
77 | { | ||
78 | } | ||
79 | EXPORT_SYMBOL(clk_put); | ||
80 | |||
81 | int clk_enable(struct clk *clk) | ||
82 | { | ||
83 | return 0; | ||
84 | } | ||
85 | EXPORT_SYMBOL(clk_enable); | ||
86 | |||
87 | void clk_disable(struct clk *clk) | ||
88 | { | ||
89 | } | ||
90 | EXPORT_SYMBOL(clk_disable); | ||
91 | |||
92 | unsigned long clk_get_rate(struct clk *clk) | ||
93 | { | ||
94 | return clk->rate; | ||
95 | } | ||
96 | EXPORT_SYMBOL(clk_get_rate); | ||
97 | |||
98 | struct { | ||
99 | unsigned long rate; | ||
100 | unsigned long cfg; | ||
101 | unsigned long div; | ||
102 | } vga_clk_table[] = { | ||
103 | {.rate = 25175000, .cfg = 0x00002001, .div = 0x9}, | ||
104 | {.rate = 31500000, .cfg = 0x00002001, .div = 0x7}, | ||
105 | {.rate = 40000000, .cfg = 0x00003801, .div = 0x9}, | ||
106 | {.rate = 49500000, .cfg = 0x00003801, .div = 0x7}, | ||
107 | {.rate = 65000000, .cfg = 0x00002c01, .div = 0x4}, | ||
108 | {.rate = 78750000, .cfg = 0x00002400, .div = 0x7}, | ||
109 | {.rate = 108000000, .cfg = 0x00002c01, .div = 0x2}, | ||
110 | {.rate = 106500000, .cfg = 0x00003c01, .div = 0x3}, | ||
111 | {.rate = 50650000, .cfg = 0x00106400, .div = 0x9}, | ||
112 | {.rate = 61500000, .cfg = 0x00106400, .div = 0xa}, | ||
113 | {.rate = 85500000, .cfg = 0x00002800, .div = 0x6}, | ||
114 | }; | ||
115 | |||
116 | struct { | ||
117 | unsigned long mrate; | ||
118 | unsigned long prate; | ||
119 | } mclk_clk_table[] = { | ||
120 | {.mrate = 500000000, .prate = 0x00109801}, | ||
121 | {.mrate = 525000000, .prate = 0x00104C00}, | ||
122 | {.mrate = 550000000, .prate = 0x00105000}, | ||
123 | {.mrate = 575000000, .prate = 0x00105400}, | ||
124 | {.mrate = 600000000, .prate = 0x00105800}, | ||
125 | {.mrate = 625000000, .prate = 0x00105C00}, | ||
126 | {.mrate = 650000000, .prate = 0x00106000}, | ||
127 | {.mrate = 675000000, .prate = 0x00106400}, | ||
128 | {.mrate = 700000000, .prate = 0x00106800}, | ||
129 | {.mrate = 725000000, .prate = 0x00106C00}, | ||
130 | {.mrate = 750000000, .prate = 0x00107000}, | ||
131 | {.mrate = 775000000, .prate = 0x00107400}, | ||
132 | {.mrate = 800000000, .prate = 0x00107800}, | ||
133 | }; | ||
134 | |||
135 | int clk_set_rate(struct clk *clk, unsigned long rate) | ||
136 | { | ||
137 | if (clk == &clk_vga_clk) { | ||
138 | unsigned long pll_vgacfg, pll_vgadiv; | ||
139 | int ret, i; | ||
140 | |||
141 | /* lookup vga_clk_table */ | ||
142 | ret = -EINVAL; | ||
143 | for (i = 0; i < ARRAY_SIZE(vga_clk_table); i++) { | ||
144 | if (rate == vga_clk_table[i].rate) { | ||
145 | pll_vgacfg = vga_clk_table[i].cfg; | ||
146 | pll_vgadiv = vga_clk_table[i].div; | ||
147 | ret = 0; | ||
148 | break; | ||
149 | } | ||
150 | } | ||
151 | |||
152 | if (ret) | ||
153 | return ret; | ||
154 | |||
155 | if (PM_PLLVGACFG == pll_vgacfg) | ||
156 | return 0; | ||
157 | |||
158 | /* set pll vga cfg reg. */ | ||
159 | PM_PLLVGACFG = pll_vgacfg; | ||
160 | |||
161 | PM_PMCR = PM_PMCR_CFBVGA; | ||
162 | while ((PM_PLLDFCDONE & PM_PLLDFCDONE_VGADFC) | ||
163 | != PM_PLLDFCDONE_VGADFC) | ||
164 | udelay(100); /* about 1ms */ | ||
165 | |||
166 | /* set div cfg reg. */ | ||
167 | PM_PCGR |= PM_PCGR_VGACLK; | ||
168 | |||
169 | PM_DIVCFG = (PM_DIVCFG & ~PM_DIVCFG_VGACLK_MASK) | ||
170 | | PM_DIVCFG_VGACLK(pll_vgadiv); | ||
171 | |||
172 | PM_SWRESET |= PM_SWRESET_VGADIV; | ||
173 | while ((PM_SWRESET & PM_SWRESET_VGADIV) == PM_SWRESET_VGADIV) | ||
174 | udelay(100); /* 65536 bclk32, about 320us */ | ||
175 | |||
176 | PM_PCGR &= ~PM_PCGR_VGACLK; | ||
177 | } | ||
178 | #ifdef CONFIG_CPU_FREQ | ||
179 | if (clk == &clk_mclk_clk) { | ||
180 | u32 pll_rate, divstatus = PM_DIVSTATUS; | ||
181 | int ret, i; | ||
182 | |||
183 | /* lookup mclk_clk_table */ | ||
184 | ret = -EINVAL; | ||
185 | for (i = 0; i < ARRAY_SIZE(mclk_clk_table); i++) { | ||
186 | if (rate == mclk_clk_table[i].mrate) { | ||
187 | pll_rate = mclk_clk_table[i].prate; | ||
188 | clk_mclk_clk.rate = mclk_clk_table[i].mrate; | ||
189 | ret = 0; | ||
190 | break; | ||
191 | } | ||
192 | } | ||
193 | |||
194 | if (ret) | ||
195 | return ret; | ||
196 | |||
197 | if (clk_mclk_clk.rate) | ||
198 | clk_bclk32_clk.rate = clk_mclk_clk.rate | ||
199 | / (((divstatus & 0x0000f000) >> 12) + 1); | ||
200 | |||
201 | /* set pll sys cfg reg. */ | ||
202 | PM_PLLSYSCFG = pll_rate; | ||
203 | |||
204 | PM_PMCR = PM_PMCR_CFBSYS; | ||
205 | while ((PM_PLLDFCDONE & PM_PLLDFCDONE_SYSDFC) | ||
206 | != PM_PLLDFCDONE_SYSDFC) | ||
207 | udelay(100); | ||
208 | /* about 1ms */ | ||
209 | } | ||
210 | #endif | ||
211 | return 0; | ||
212 | } | ||
213 | EXPORT_SYMBOL(clk_set_rate); | ||
214 | |||
215 | int clk_register(struct clk *clk) | ||
216 | { | ||
217 | mutex_lock(&clocks_mutex); | ||
218 | list_add(&clk->node, &clocks); | ||
219 | mutex_unlock(&clocks_mutex); | ||
220 | printk(KERN_DEFAULT "PKUnity PM: %s %lu.%02luM\n", clk->name, | ||
221 | (clk->rate)/1000000, (clk->rate)/10000 % 100); | ||
222 | return 0; | ||
223 | } | ||
224 | EXPORT_SYMBOL(clk_register); | ||
225 | |||
226 | void clk_unregister(struct clk *clk) | ||
227 | { | ||
228 | mutex_lock(&clocks_mutex); | ||
229 | list_del(&clk->node); | ||
230 | mutex_unlock(&clocks_mutex); | ||
231 | } | ||
232 | EXPORT_SYMBOL(clk_unregister); | ||
233 | |||
234 | struct { | ||
235 | unsigned long prate; | ||
236 | unsigned long rate; | ||
237 | } pllrate_table[] = { | ||
238 | {.prate = 0x00002001, .rate = 250000000}, | ||
239 | {.prate = 0x00104801, .rate = 250000000}, | ||
240 | {.prate = 0x00104C01, .rate = 262500000}, | ||
241 | {.prate = 0x00002401, .rate = 275000000}, | ||
242 | {.prate = 0x00105001, .rate = 275000000}, | ||
243 | {.prate = 0x00105401, .rate = 287500000}, | ||
244 | {.prate = 0x00002801, .rate = 300000000}, | ||
245 | {.prate = 0x00105801, .rate = 300000000}, | ||
246 | {.prate = 0x00105C01, .rate = 312500000}, | ||
247 | {.prate = 0x00002C01, .rate = 325000000}, | ||
248 | {.prate = 0x00106001, .rate = 325000000}, | ||
249 | {.prate = 0x00106401, .rate = 337500000}, | ||
250 | {.prate = 0x00003001, .rate = 350000000}, | ||
251 | {.prate = 0x00106801, .rate = 350000000}, | ||
252 | {.prate = 0x00106C01, .rate = 362500000}, | ||
253 | {.prate = 0x00003401, .rate = 375000000}, | ||
254 | {.prate = 0x00107001, .rate = 375000000}, | ||
255 | {.prate = 0x00107401, .rate = 387500000}, | ||
256 | {.prate = 0x00003801, .rate = 400000000}, | ||
257 | {.prate = 0x00107801, .rate = 400000000}, | ||
258 | {.prate = 0x00107C01, .rate = 412500000}, | ||
259 | {.prate = 0x00003C01, .rate = 425000000}, | ||
260 | {.prate = 0x00108001, .rate = 425000000}, | ||
261 | {.prate = 0x00108401, .rate = 437500000}, | ||
262 | {.prate = 0x00004001, .rate = 450000000}, | ||
263 | {.prate = 0x00108801, .rate = 450000000}, | ||
264 | {.prate = 0x00108C01, .rate = 462500000}, | ||
265 | {.prate = 0x00004401, .rate = 475000000}, | ||
266 | {.prate = 0x00109001, .rate = 475000000}, | ||
267 | {.prate = 0x00109401, .rate = 487500000}, | ||
268 | {.prate = 0x00004801, .rate = 500000000}, | ||
269 | {.prate = 0x00109801, .rate = 500000000}, | ||
270 | {.prate = 0x00104C00, .rate = 525000000}, | ||
271 | {.prate = 0x00002400, .rate = 550000000}, | ||
272 | {.prate = 0x00105000, .rate = 550000000}, | ||
273 | {.prate = 0x00105400, .rate = 575000000}, | ||
274 | {.prate = 0x00002800, .rate = 600000000}, | ||
275 | {.prate = 0x00105800, .rate = 600000000}, | ||
276 | {.prate = 0x00105C00, .rate = 625000000}, | ||
277 | {.prate = 0x00002C00, .rate = 650000000}, | ||
278 | {.prate = 0x00106000, .rate = 650000000}, | ||
279 | {.prate = 0x00106400, .rate = 675000000}, | ||
280 | {.prate = 0x00003000, .rate = 700000000}, | ||
281 | {.prate = 0x00106800, .rate = 700000000}, | ||
282 | {.prate = 0x00106C00, .rate = 725000000}, | ||
283 | {.prate = 0x00003400, .rate = 750000000}, | ||
284 | {.prate = 0x00107000, .rate = 750000000}, | ||
285 | {.prate = 0x00107400, .rate = 775000000}, | ||
286 | {.prate = 0x00003800, .rate = 800000000}, | ||
287 | {.prate = 0x00107800, .rate = 800000000}, | ||
288 | {.prate = 0x00107C00, .rate = 825000000}, | ||
289 | {.prate = 0x00003C00, .rate = 850000000}, | ||
290 | {.prate = 0x00108000, .rate = 850000000}, | ||
291 | {.prate = 0x00108400, .rate = 875000000}, | ||
292 | {.prate = 0x00004000, .rate = 900000000}, | ||
293 | {.prate = 0x00108800, .rate = 900000000}, | ||
294 | {.prate = 0x00108C00, .rate = 925000000}, | ||
295 | {.prate = 0x00004400, .rate = 950000000}, | ||
296 | {.prate = 0x00109000, .rate = 950000000}, | ||
297 | {.prate = 0x00109400, .rate = 975000000}, | ||
298 | {.prate = 0x00004800, .rate = 1000000000}, | ||
299 | {.prate = 0x00109800, .rate = 1000000000}, | ||
300 | }; | ||
301 | |||
302 | struct { | ||
303 | unsigned long prate; | ||
304 | unsigned long drate; | ||
305 | } pddr_table[] = { | ||
306 | {.prate = 0x00100800, .drate = 44236800}, | ||
307 | {.prate = 0x00100C00, .drate = 66355200}, | ||
308 | {.prate = 0x00101000, .drate = 88473600}, | ||
309 | {.prate = 0x00101400, .drate = 110592000}, | ||
310 | {.prate = 0x00101800, .drate = 132710400}, | ||
311 | {.prate = 0x00101C01, .drate = 154828800}, | ||
312 | {.prate = 0x00102001, .drate = 176947200}, | ||
313 | {.prate = 0x00102401, .drate = 199065600}, | ||
314 | {.prate = 0x00102801, .drate = 221184000}, | ||
315 | {.prate = 0x00102C01, .drate = 243302400}, | ||
316 | {.prate = 0x00103001, .drate = 265420800}, | ||
317 | {.prate = 0x00103401, .drate = 287539200}, | ||
318 | {.prate = 0x00103801, .drate = 309657600}, | ||
319 | {.prate = 0x00103C01, .drate = 331776000}, | ||
320 | {.prate = 0x00104001, .drate = 353894400}, | ||
321 | }; | ||
322 | |||
323 | static int __init clk_init(void) | ||
324 | { | ||
325 | #ifdef CONFIG_PUV3_PM | ||
326 | u32 pllrate, divstatus = PM_DIVSTATUS; | ||
327 | u32 pcgr_val = PM_PCGR; | ||
328 | int i; | ||
329 | |||
330 | pcgr_val |= PM_PCGR_BCLKMME | PM_PCGR_BCLKH264E | PM_PCGR_BCLKH264D | ||
331 | | PM_PCGR_HECLK | PM_PCGR_HDCLK; | ||
332 | PM_PCGR = pcgr_val; | ||
333 | |||
334 | pllrate = PM_PLLSYSSTATUS; | ||
335 | |||
336 | /* lookup pmclk_table */ | ||
337 | clk_mclk_clk.rate = 0; | ||
338 | for (i = 0; i < ARRAY_SIZE(pllrate_table); i++) { | ||
339 | if (pllrate == pllrate_table[i].prate) { | ||
340 | clk_mclk_clk.rate = pllrate_table[i].rate; | ||
341 | break; | ||
342 | } | ||
343 | } | ||
344 | |||
345 | if (clk_mclk_clk.rate) | ||
346 | clk_bclk32_clk.rate = clk_mclk_clk.rate / | ||
347 | (((divstatus & 0x0000f000) >> 12) + 1); | ||
348 | |||
349 | pllrate = PM_PLLDDRSTATUS; | ||
350 | |||
351 | /* lookup pddr_table */ | ||
352 | clk_ddr_clk.rate = 0; | ||
353 | for (i = 0; i < ARRAY_SIZE(pddr_table); i++) { | ||
354 | if (pllrate == pddr_table[i].prate) { | ||
355 | clk_ddr_clk.rate = pddr_table[i].drate; | ||
356 | break; | ||
357 | } | ||
358 | } | ||
359 | |||
360 | pllrate = PM_PLLVGASTATUS; | ||
361 | |||
362 | /* lookup pvga_table */ | ||
363 | clk_vga_clk.rate = 0; | ||
364 | for (i = 0; i < ARRAY_SIZE(pllrate_table); i++) { | ||
365 | if (pllrate == pllrate_table[i].prate) { | ||
366 | clk_vga_clk.rate = pllrate_table[i].rate; | ||
367 | break; | ||
368 | } | ||
369 | } | ||
370 | |||
371 | if (clk_vga_clk.rate) | ||
372 | clk_vga_clk.rate = clk_vga_clk.rate / | ||
373 | (((divstatus & 0x00f00000) >> 20) + 1); | ||
374 | |||
375 | clk_register(&clk_vga_clk); | ||
376 | #endif | ||
377 | #ifdef CONFIG_ARCH_FPGA | ||
378 | clk_ddr_clk.rate = 33000000; | ||
379 | clk_mclk_clk.rate = 33000000; | ||
380 | clk_bclk32_clk.rate = 33000000; | ||
381 | #endif | ||
382 | clk_register(&clk_ddr_clk); | ||
383 | clk_register(&clk_mclk_clk); | ||
384 | clk_register(&clk_bclk32_clk); | ||
385 | clk_register(&clk_ost_clk); | ||
386 | return 0; | ||
387 | } | ||
388 | core_initcall(clk_init); | ||
diff --git a/arch/unicore32/kernel/cpu-ucv2.c b/arch/unicore32/kernel/cpu-ucv2.c new file mode 100644 index 00000000000..4a99f62584c --- /dev/null +++ b/arch/unicore32/kernel/cpu-ucv2.c | |||
@@ -0,0 +1,93 @@ | |||
1 | /* | ||
2 | * linux/arch/unicore32/kernel/cpu-ucv2.c: clock scaling for the UniCore-II | ||
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 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/types.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/clk.h> | ||
18 | #include <linux/cpufreq.h> | ||
19 | |||
20 | #include <mach/hardware.h> | ||
21 | |||
22 | static struct cpufreq_driver ucv2_driver; | ||
23 | |||
24 | /* make sure that only the "userspace" governor is run | ||
25 | * -- anything else wouldn't make sense on this platform, anyway. | ||
26 | */ | ||
27 | int ucv2_verify_speed(struct cpufreq_policy *policy) | ||
28 | { | ||
29 | if (policy->cpu) | ||
30 | return -EINVAL; | ||
31 | |||
32 | cpufreq_verify_within_limits(policy, | ||
33 | policy->cpuinfo.min_freq, policy->cpuinfo.max_freq); | ||
34 | |||
35 | return 0; | ||
36 | } | ||
37 | |||
38 | static unsigned int ucv2_getspeed(unsigned int cpu) | ||
39 | { | ||
40 | struct clk *mclk = clk_get(NULL, "MAIN_CLK"); | ||
41 | |||
42 | if (cpu) | ||
43 | return 0; | ||
44 | return clk_get_rate(mclk)/1000; | ||
45 | } | ||
46 | |||
47 | static int ucv2_target(struct cpufreq_policy *policy, | ||
48 | unsigned int target_freq, | ||
49 | unsigned int relation) | ||
50 | { | ||
51 | unsigned int cur = ucv2_getspeed(0); | ||
52 | struct cpufreq_freqs freqs; | ||
53 | struct clk *mclk = clk_get(NULL, "MAIN_CLK"); | ||
54 | |||
55 | cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); | ||
56 | |||
57 | if (!clk_set_rate(mclk, target_freq * 1000)) { | ||
58 | freqs.old = cur; | ||
59 | freqs.new = target_freq; | ||
60 | freqs.cpu = 0; | ||
61 | } | ||
62 | |||
63 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); | ||
64 | |||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | static int __init ucv2_cpu_init(struct cpufreq_policy *policy) | ||
69 | { | ||
70 | if (policy->cpu != 0) | ||
71 | return -EINVAL; | ||
72 | policy->cur = ucv2_getspeed(0); | ||
73 | policy->min = policy->cpuinfo.min_freq = 250000; | ||
74 | policy->max = policy->cpuinfo.max_freq = 1000000; | ||
75 | policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; | ||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | static struct cpufreq_driver ucv2_driver = { | ||
80 | .flags = CPUFREQ_STICKY, | ||
81 | .verify = ucv2_verify_speed, | ||
82 | .target = ucv2_target, | ||
83 | .get = ucv2_getspeed, | ||
84 | .init = ucv2_cpu_init, | ||
85 | .name = "UniCore-II", | ||
86 | }; | ||
87 | |||
88 | static int __init ucv2_cpufreq_init(void) | ||
89 | { | ||
90 | return cpufreq_register_driver(&ucv2_driver); | ||
91 | } | ||
92 | |||
93 | arch_initcall(ucv2_cpufreq_init); | ||
diff --git a/arch/unicore32/kernel/hibernate.c b/arch/unicore32/kernel/hibernate.c new file mode 100644 index 00000000000..7d0f0b7983a --- /dev/null +++ b/arch/unicore32/kernel/hibernate.c | |||
@@ -0,0 +1,160 @@ | |||
1 | /* | ||
2 | * linux/arch/unicore32/kernel/hibernate.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 | |||
14 | #include <linux/gfp.h> | ||
15 | #include <linux/suspend.h> | ||
16 | #include <linux/bootmem.h> | ||
17 | |||
18 | #include <asm/system.h> | ||
19 | #include <asm/page.h> | ||
20 | #include <asm/pgtable.h> | ||
21 | #include <asm/pgalloc.h> | ||
22 | #include <asm/suspend.h> | ||
23 | |||
24 | #include "mach/pm.h" | ||
25 | |||
26 | /* Pointer to the temporary resume page tables */ | ||
27 | pgd_t *resume_pg_dir; | ||
28 | |||
29 | struct swsusp_arch_regs swsusp_arch_regs_cpu0; | ||
30 | |||
31 | /* | ||
32 | * Create a middle page table on a resume-safe page and put a pointer to it in | ||
33 | * the given global directory entry. This only returns the gd entry | ||
34 | * in non-PAE compilation mode, since the middle layer is folded. | ||
35 | */ | ||
36 | static pmd_t *resume_one_md_table_init(pgd_t *pgd) | ||
37 | { | ||
38 | pud_t *pud; | ||
39 | pmd_t *pmd_table; | ||
40 | |||
41 | pud = pud_offset(pgd, 0); | ||
42 | pmd_table = pmd_offset(pud, 0); | ||
43 | |||
44 | return pmd_table; | ||
45 | } | ||
46 | |||
47 | /* | ||
48 | * Create a page table on a resume-safe page and place a pointer to it in | ||
49 | * a middle page directory entry. | ||
50 | */ | ||
51 | static pte_t *resume_one_page_table_init(pmd_t *pmd) | ||
52 | { | ||
53 | if (pmd_none(*pmd)) { | ||
54 | pte_t *page_table = (pte_t *)get_safe_page(GFP_ATOMIC); | ||
55 | if (!page_table) | ||
56 | return NULL; | ||
57 | |||
58 | set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_KERNEL_TABLE)); | ||
59 | |||
60 | BUG_ON(page_table != pte_offset_kernel(pmd, 0)); | ||
61 | |||
62 | return page_table; | ||
63 | } | ||
64 | |||
65 | return pte_offset_kernel(pmd, 0); | ||
66 | } | ||
67 | |||
68 | /* | ||
69 | * This maps the physical memory to kernel virtual address space, a total | ||
70 | * of max_low_pfn pages, by creating page tables starting from address | ||
71 | * PAGE_OFFSET. The page tables are allocated out of resume-safe pages. | ||
72 | */ | ||
73 | static int resume_physical_mapping_init(pgd_t *pgd_base) | ||
74 | { | ||
75 | unsigned long pfn; | ||
76 | pgd_t *pgd; | ||
77 | pmd_t *pmd; | ||
78 | pte_t *pte; | ||
79 | int pgd_idx, pmd_idx; | ||
80 | |||
81 | pgd_idx = pgd_index(PAGE_OFFSET); | ||
82 | pgd = pgd_base + pgd_idx; | ||
83 | pfn = 0; | ||
84 | |||
85 | for (; pgd_idx < PTRS_PER_PGD; pgd++, pgd_idx++) { | ||
86 | pmd = resume_one_md_table_init(pgd); | ||
87 | if (!pmd) | ||
88 | return -ENOMEM; | ||
89 | |||
90 | if (pfn >= max_low_pfn) | ||
91 | continue; | ||
92 | |||
93 | for (pmd_idx = 0; pmd_idx < PTRS_PER_PMD; pmd++, pmd_idx++) { | ||
94 | pte_t *max_pte; | ||
95 | |||
96 | if (pfn >= max_low_pfn) | ||
97 | break; | ||
98 | |||
99 | /* Map with normal page tables. | ||
100 | * NOTE: We can mark everything as executable here | ||
101 | */ | ||
102 | pte = resume_one_page_table_init(pmd); | ||
103 | if (!pte) | ||
104 | return -ENOMEM; | ||
105 | |||
106 | max_pte = pte + PTRS_PER_PTE; | ||
107 | for (; pte < max_pte; pte++, pfn++) { | ||
108 | if (pfn >= max_low_pfn) | ||
109 | break; | ||
110 | |||
111 | set_pte(pte, pfn_pte(pfn, PAGE_KERNEL_EXEC)); | ||
112 | } | ||
113 | } | ||
114 | } | ||
115 | |||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | static inline void resume_init_first_level_page_table(pgd_t *pg_dir) | ||
120 | { | ||
121 | } | ||
122 | |||
123 | int swsusp_arch_resume(void) | ||
124 | { | ||
125 | int error; | ||
126 | |||
127 | resume_pg_dir = (pgd_t *)get_safe_page(GFP_ATOMIC); | ||
128 | if (!resume_pg_dir) | ||
129 | return -ENOMEM; | ||
130 | |||
131 | resume_init_first_level_page_table(resume_pg_dir); | ||
132 | error = resume_physical_mapping_init(resume_pg_dir); | ||
133 | if (error) | ||
134 | return error; | ||
135 | |||
136 | /* We have got enough memory and from now on we cannot recover */ | ||
137 | restore_image(resume_pg_dir, restore_pblist); | ||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | /* | ||
142 | * pfn_is_nosave - check if given pfn is in the 'nosave' section | ||
143 | */ | ||
144 | |||
145 | int pfn_is_nosave(unsigned long pfn) | ||
146 | { | ||
147 | unsigned long begin_pfn = __pa(&__nosave_begin) >> PAGE_SHIFT; | ||
148 | unsigned long end_pfn = PAGE_ALIGN(__pa(&__nosave_end)) >> PAGE_SHIFT; | ||
149 | |||
150 | return (pfn >= begin_pfn) && (pfn < end_pfn); | ||
151 | } | ||
152 | |||
153 | void save_processor_state(void) | ||
154 | { | ||
155 | } | ||
156 | |||
157 | void restore_processor_state(void) | ||
158 | { | ||
159 | local_flush_tlb_all(); | ||
160 | } | ||
diff --git a/arch/unicore32/kernel/hibernate_asm.S b/arch/unicore32/kernel/hibernate_asm.S new file mode 100644 index 00000000000..cc3c65253c8 --- /dev/null +++ b/arch/unicore32/kernel/hibernate_asm.S | |||
@@ -0,0 +1,117 @@ | |||
1 | /* | ||
2 | * linux/arch/unicore32/kernel/hibernate_asm.S | ||
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 | |||
14 | #include <linux/sys.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/linkage.h> | ||
17 | #include <generated/asm-offsets.h> | ||
18 | #include <asm/page.h> | ||
19 | #include <asm/pgtable.h> | ||
20 | #include <asm/assembler.h> | ||
21 | |||
22 | @ restore_image(pgd_t *resume_pg_dir, struct pbe *restore_pblist) | ||
23 | @ r0: resume_pg_dir | ||
24 | @ r1: restore_pblist | ||
25 | @ copy restore_pblist pages | ||
26 | @ restore registers from swsusp_arch_regs_cpu0 | ||
27 | @ | ||
28 | ENTRY(restore_image) | ||
29 | sub r0, r0, #PAGE_OFFSET | ||
30 | mov r5, #0 | ||
31 | movc p0.c6, r5, #6 @invalidate ITLB & DTLB | ||
32 | movc p0.c2, r0, #0 | ||
33 | nop | ||
34 | nop | ||
35 | nop | ||
36 | nop | ||
37 | nop | ||
38 | nop | ||
39 | nop | ||
40 | |||
41 | .p2align 4,,7 | ||
42 | 101: | ||
43 | csub.a r1, #0 | ||
44 | beq 109f | ||
45 | |||
46 | ldw r6, [r1+], #PBE_ADDRESS | ||
47 | ldw r7, [r1+], #PBE_ORIN_ADDRESS | ||
48 | |||
49 | movl ip, #128 | ||
50 | 102: ldm.w (r8 - r15), [r6]+ | ||
51 | stm.w (r8 - r15), [r7]+ | ||
52 | sub.a ip, ip, #1 | ||
53 | bne 102b | ||
54 | |||
55 | ldw r1, [r1+], #PBE_NEXT | ||
56 | b 101b | ||
57 | |||
58 | .p2align 4,,7 | ||
59 | 109: | ||
60 | /* go back to the original page tables */ | ||
61 | ldw r0, =swapper_pg_dir | ||
62 | sub r0, r0, #PAGE_OFFSET | ||
63 | mov r5, #0 | ||
64 | movc p0.c6, r5, #6 | ||
65 | movc p0.c2, r0, #0 | ||
66 | nop | ||
67 | nop | ||
68 | nop | ||
69 | nop | ||
70 | nop | ||
71 | nop | ||
72 | nop | ||
73 | |||
74 | #ifdef CONFIG_UNICORE_FPU_F64 | ||
75 | ldw ip, 1f | ||
76 | add ip, ip, #SWSUSP_FPSTATE | ||
77 | lfm.w (f0 - f7 ), [ip]+ | ||
78 | lfm.w (f8 - f15), [ip]+ | ||
79 | lfm.w (f16 - f23), [ip]+ | ||
80 | lfm.w (f24 - f31), [ip]+ | ||
81 | ldw r4, [ip] | ||
82 | ctf r4, s31 | ||
83 | #endif | ||
84 | mov r0, #0x0 | ||
85 | ldw ip, 1f | ||
86 | add ip, ip, #SWSUSP_CPU | ||
87 | ldm.w (r4 - r15), [ip]+ | ||
88 | ldm (r16 - r27, sp, pc), [ip]+ @ Load all regs saved previously | ||
89 | |||
90 | .align 2 | ||
91 | 1: .long swsusp_arch_regs_cpu0 | ||
92 | |||
93 | |||
94 | @ swsusp_arch_suspend() | ||
95 | @ - prepare pc for resume, return from function without swsusp_save on resume | ||
96 | @ - save registers in swsusp_arch_regs_cpu0 | ||
97 | @ - call swsusp_save write suspend image | ||
98 | |||
99 | ENTRY(swsusp_arch_suspend) | ||
100 | ldw ip, 1f | ||
101 | add ip, ip, #SWSUSP_CPU | ||
102 | stm.w (r4 - r15), [ip]+ | ||
103 | stm.w (r16 - r27, sp, lr), [ip]+ | ||
104 | |||
105 | #ifdef CONFIG_UNICORE_FPU_F64 | ||
106 | ldw ip, 1f | ||
107 | add ip, ip, #SWSUSP_FPSTATE | ||
108 | sfm.w (f0 - f7 ), [ip]+ | ||
109 | sfm.w (f8 - f15), [ip]+ | ||
110 | sfm.w (f16 - f23), [ip]+ | ||
111 | sfm.w (f24 - f31), [ip]+ | ||
112 | cff r4, s31 | ||
113 | stw r4, [ip] | ||
114 | #endif | ||
115 | b swsusp_save @ no return | ||
116 | |||
117 | 1: .long swsusp_arch_regs_cpu0 | ||
diff --git a/arch/unicore32/kernel/pm.c b/arch/unicore32/kernel/pm.c new file mode 100644 index 00000000000..784bc2db3b2 --- /dev/null +++ b/arch/unicore32/kernel/pm.c | |||
@@ -0,0 +1,123 @@ | |||
1 | /* | ||
2 | * linux/arch/unicore32/kernel/pm.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/init.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/suspend.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/io.h> | ||
19 | |||
20 | #include <mach/hardware.h> | ||
21 | #include <mach/pm.h> | ||
22 | |||
23 | #include "setup.h" | ||
24 | |||
25 | struct puv3_cpu_pm_fns *puv3_cpu_pm_fns; | ||
26 | static unsigned long *sleep_save; | ||
27 | |||
28 | int puv3_pm_enter(suspend_state_t state) | ||
29 | { | ||
30 | unsigned long sleep_save_checksum = 0, checksum = 0; | ||
31 | int i; | ||
32 | |||
33 | /* skip registers saving for standby */ | ||
34 | if (state != PM_SUSPEND_STANDBY) { | ||
35 | puv3_cpu_pm_fns->save(sleep_save); | ||
36 | /* before sleeping, calculate and save a checksum */ | ||
37 | for (i = 0; i < puv3_cpu_pm_fns->save_count - 1; i++) | ||
38 | sleep_save_checksum += sleep_save[i]; | ||
39 | } | ||
40 | |||
41 | /* *** go zzz *** */ | ||
42 | puv3_cpu_pm_fns->enter(state); | ||
43 | cpu_init(); | ||
44 | #ifdef CONFIG_INPUT_KEYBOARD | ||
45 | puv3_ps2_init(); | ||
46 | #endif | ||
47 | #ifdef CONFIG_PCI | ||
48 | pci_puv3_preinit(); | ||
49 | #endif | ||
50 | if (state != PM_SUSPEND_STANDBY) { | ||
51 | /* after sleeping, validate the checksum */ | ||
52 | for (i = 0; i < puv3_cpu_pm_fns->save_count - 1; i++) | ||
53 | checksum += sleep_save[i]; | ||
54 | |||
55 | /* if invalid, display message and wait for a hardware reset */ | ||
56 | if (checksum != sleep_save_checksum) { | ||
57 | while (1) | ||
58 | puv3_cpu_pm_fns->enter(state); | ||
59 | } | ||
60 | puv3_cpu_pm_fns->restore(sleep_save); | ||
61 | } | ||
62 | |||
63 | pr_debug("*** made it back from resume\n"); | ||
64 | |||
65 | return 0; | ||
66 | } | ||
67 | EXPORT_SYMBOL_GPL(puv3_pm_enter); | ||
68 | |||
69 | unsigned long sleep_phys_sp(void *sp) | ||
70 | { | ||
71 | return virt_to_phys(sp); | ||
72 | } | ||
73 | |||
74 | static int puv3_pm_valid(suspend_state_t state) | ||
75 | { | ||
76 | if (puv3_cpu_pm_fns) | ||
77 | return puv3_cpu_pm_fns->valid(state); | ||
78 | |||
79 | return -EINVAL; | ||
80 | } | ||
81 | |||
82 | static int puv3_pm_prepare(void) | ||
83 | { | ||
84 | int ret = 0; | ||
85 | |||
86 | if (puv3_cpu_pm_fns && puv3_cpu_pm_fns->prepare) | ||
87 | ret = puv3_cpu_pm_fns->prepare(); | ||
88 | |||
89 | return ret; | ||
90 | } | ||
91 | |||
92 | static void puv3_pm_finish(void) | ||
93 | { | ||
94 | if (puv3_cpu_pm_fns && puv3_cpu_pm_fns->finish) | ||
95 | puv3_cpu_pm_fns->finish(); | ||
96 | } | ||
97 | |||
98 | static struct platform_suspend_ops puv3_pm_ops = { | ||
99 | .valid = puv3_pm_valid, | ||
100 | .enter = puv3_pm_enter, | ||
101 | .prepare = puv3_pm_prepare, | ||
102 | .finish = puv3_pm_finish, | ||
103 | }; | ||
104 | |||
105 | static int __init puv3_pm_init(void) | ||
106 | { | ||
107 | if (!puv3_cpu_pm_fns) { | ||
108 | printk(KERN_ERR "no valid puv3_cpu_pm_fns defined\n"); | ||
109 | return -EINVAL; | ||
110 | } | ||
111 | |||
112 | sleep_save = kmalloc(puv3_cpu_pm_fns->save_count | ||
113 | * sizeof(unsigned long), GFP_KERNEL); | ||
114 | if (!sleep_save) { | ||
115 | printk(KERN_ERR "failed to alloc memory for pm save\n"); | ||
116 | return -ENOMEM; | ||
117 | } | ||
118 | |||
119 | suspend_set_ops(&puv3_pm_ops); | ||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | device_initcall(puv3_pm_init); | ||
diff --git a/arch/unicore32/kernel/sleep.S b/arch/unicore32/kernel/sleep.S new file mode 100644 index 00000000000..f7c3fc87f7f --- /dev/null +++ b/arch/unicore32/kernel/sleep.S | |||
@@ -0,0 +1,202 @@ | |||
1 | /* | ||
2 | * linux/arch/unicore32/kernel/sleep.S | ||
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 | |||
14 | #include <linux/linkage.h> | ||
15 | #include <asm/assembler.h> | ||
16 | #include <mach/hardware.h> | ||
17 | |||
18 | .text | ||
19 | |||
20 | pkunity_cpu_save_cp: | ||
21 | |||
22 | @ get coprocessor registers | ||
23 | |||
24 | movc r3, p0.c7, #0 @ PID | ||
25 | movc r4, p0.c2, #0 @ translation table base addr | ||
26 | movc r5, p0.c1, #0 @ control reg | ||
27 | |||
28 | |||
29 | @ store them plus current virtual stack ptr on stack | ||
30 | mov r6, sp | ||
31 | stm.w (r3 - r6), [sp-] | ||
32 | |||
33 | mov pc, lr | ||
34 | |||
35 | pkunity_cpu_save_sp: | ||
36 | @ preserve phys address of stack | ||
37 | mov r0, sp | ||
38 | stw.w lr, [sp+], #-4 | ||
39 | b.l sleep_phys_sp | ||
40 | ldw r1, =sleep_save_sp | ||
41 | stw r0, [r1] | ||
42 | ldw.w pc, [sp]+, #4 | ||
43 | |||
44 | /* | ||
45 | * puv3_cpu_suspend() | ||
46 | * | ||
47 | * Forces CPU into sleep state. | ||
48 | * | ||
49 | * r0 = value for PWRMODE M field for desired sleep state | ||
50 | */ | ||
51 | |||
52 | ENTRY(puv3_cpu_suspend) | ||
53 | stm.w (r16 - r27, lr), [sp-] @ save registers on stack | ||
54 | stm.w (r4 - r15), [sp-] @ save registers on stack | ||
55 | |||
56 | #ifdef CONFIG_UNICORE_FPU_F64 | ||
57 | sfm.w (f0 - f7 ), [sp-] | ||
58 | sfm.w (f8 - f15), [sp-] | ||
59 | sfm.w (f16 - f23), [sp-] | ||
60 | sfm.w (f24 - f31), [sp-] | ||
61 | cff r4, s31 | ||
62 | stm.w (r4), [sp-] | ||
63 | #endif | ||
64 | b.l pkunity_cpu_save_cp | ||
65 | |||
66 | b.l pkunity_cpu_save_sp | ||
67 | |||
68 | @ clean data cache | ||
69 | mov r1, #0 | ||
70 | movc p0.c5, r1, #14 | ||
71 | nop | ||
72 | nop | ||
73 | nop | ||
74 | nop | ||
75 | |||
76 | |||
77 | |||
78 | @ DDR2 BaseAddr | ||
79 | ldw r0, =io_p2v(PKUNITY_DDR2CTRL_BASE) | ||
80 | |||
81 | @ PM BaseAddr | ||
82 | ldw r1, =io_p2v(PKUNITY_PM_BASE) | ||
83 | |||
84 | @ set PLL_SYS_CFG reg, 275 | ||
85 | movl r6, #0x00002401 | ||
86 | stw r6, [r1+], #0x18 | ||
87 | @ set PLL_DDR_CFG reg, 66MHz | ||
88 | movl r6, #0x00100c00 | ||
89 | stw r6, [r1+], #0x1c | ||
90 | |||
91 | @ set wake up source | ||
92 | movl r8, #0x800001ff @ epip4d | ||
93 | stw r8, [r1+], #0xc | ||
94 | |||
95 | @ set PGSR | ||
96 | movl r5, #0x40000 | ||
97 | stw r5, [r1+], #0x10 | ||
98 | |||
99 | @ prepare DDR2 refresh settings | ||
100 | ldw r5, [r0+], #0x24 | ||
101 | or r5, r5, #0x00000001 | ||
102 | |||
103 | @ prepare PMCR for PLL changing | ||
104 | movl r6, #0xc | ||
105 | |||
106 | @ prepare for closing PLL | ||
107 | movl r7, #0x1 | ||
108 | |||
109 | @ prepare sleep mode | ||
110 | mov r8, #0x1 | ||
111 | |||
112 | @ movl r0, 0x11111111 | ||
113 | @ put_word_ocd r0 | ||
114 | b pkunity_cpu_do_suspend | ||
115 | |||
116 | .ltorg | ||
117 | .align 5 | ||
118 | pkunity_cpu_do_suspend: | ||
119 | b 101f | ||
120 | @ put DDR2 into self-refresh | ||
121 | 100: stw r5, [r0+], #0x24 | ||
122 | @ change PLL | ||
123 | stw r6, [r1] | ||
124 | b 1f | ||
125 | |||
126 | .ltorg | ||
127 | .align 5 | ||
128 | 101: b 102f | ||
129 | @ wait for PLL changing complete | ||
130 | 1: ldw r6, [r1+], #0x44 | ||
131 | csub.a r6, #0x1 | ||
132 | bne 1b | ||
133 | b 2f | ||
134 | |||
135 | .ltorg | ||
136 | .align 5 | ||
137 | 102: b 100b | ||
138 | @ close PLL | ||
139 | 2: stw r7, [r1+], #0x4 | ||
140 | @ enter sleep mode | ||
141 | stw r8, [r1] | ||
142 | 3: b 3b | ||
143 | |||
144 | |||
145 | |||
146 | |||
147 | /* | ||
148 | * puv3_cpu_resume() | ||
149 | * | ||
150 | * entry point from bootloader into kernel during resume | ||
151 | * | ||
152 | * Note: Yes, part of the following code is located into the .data section. | ||
153 | * This is to allow sleep_save_sp to be accessed with a relative load | ||
154 | * while we can't rely on any MMU translation. We could have put | ||
155 | * sleep_save_sp in the .text section as well, but some setups might | ||
156 | * insist on it to be truly read-only. | ||
157 | */ | ||
158 | |||
159 | .data | ||
160 | .align 5 | ||
161 | ENTRY(puv3_cpu_resume) | ||
162 | @ movl r0, 0x20202020 | ||
163 | @ put_word_ocd r0 | ||
164 | |||
165 | ldw r0, sleep_save_sp @ stack phys addr | ||
166 | ldw r2, =resume_after_mmu @ its absolute virtual address | ||
167 | ldm (r3 - r6), [r0]+ @ CP regs + virt stack ptr | ||
168 | mov sp, r6 @ CP regs + virt stack ptr | ||
169 | |||
170 | mov r1, #0 | ||
171 | movc p0.c6, r1, #6 @ invalidate I & D TLBs | ||
172 | movc p0.c5, r1, #28 @ invalidate I & D caches, BTB | ||
173 | |||
174 | movc p0.c7, r3, #0 @ PID | ||
175 | movc p0.c2, r4, #0 @ translation table base addr | ||
176 | movc p0.c1, r5, #0 @ control reg, turn on mmu | ||
177 | nop | ||
178 | jump r2 | ||
179 | nop | ||
180 | nop | ||
181 | nop | ||
182 | nop | ||
183 | nop | ||
184 | |||
185 | sleep_save_sp: | ||
186 | .word 0 @ preserve stack phys ptr here | ||
187 | |||
188 | .text | ||
189 | resume_after_mmu: | ||
190 | @ movl r0, 0x30303030 | ||
191 | @ put_word_ocd r0 | ||
192 | |||
193 | #ifdef CONFIG_UNICORE_FPU_F64 | ||
194 | lfm.w (f0 - f7 ), [sp]+ | ||
195 | lfm.w (f8 - f15), [sp]+ | ||
196 | lfm.w (f16 - f23), [sp]+ | ||
197 | lfm.w (f24 - f31), [sp]+ | ||
198 | ldm.w (r4), [sp]+ | ||
199 | ctf r4, s31 | ||
200 | #endif | ||
201 | ldm.w (r4 - r15), [sp]+ @ restore registers from stack | ||
202 | ldm.w (r16 - r27, pc), [sp]+ @ return to caller | ||