aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuanXuetao <gxt@mprc.pku.edu.cn>2011-02-26 07:23:59 -0500
committerGuanXuetao <gxt@mprc.pku.edu.cn>2011-03-16 21:19:11 -0400
commit64909882862e9bb88aa6177e3f92056f5601b3e3 (patch)
tree0fade894b903f6185ab97d930004c0cf56eb412d
parentf864d2f8304e6be90b4c4e4ac615edc6fcefd4c1 (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>
-rw-r--r--arch/unicore32/include/asm/suspend.h30
-rw-r--r--arch/unicore32/include/mach/pm.h43
-rw-r--r--arch/unicore32/kernel/clock.c388
-rw-r--r--arch/unicore32/kernel/cpu-ucv2.c93
-rw-r--r--arch/unicore32/kernel/hibernate.c160
-rw-r--r--arch/unicore32/kernel/hibernate_asm.S117
-rw-r--r--arch/unicore32/kernel/pm.c123
-rw-r--r--arch/unicore32/kernel/sleep.S202
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__
17static inline int arch_prepare_suspend(void) { return 0; }
18
19#include <asm/ptrace.h>
20
21struct 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
17struct 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
27extern struct puv3_cpu_pm_fns *puv3_cpu_pm_fns;
28
29/* sleep.S */
30extern void puv3_cpu_suspend(unsigned int);
31
32extern void puv3_cpu_resume(void);
33
34extern int puv3_pm_enter(suspend_state_t state);
35
36/* Defined in hibernate_asm.S */
37extern int restore_image(pgd_t *resume_pg_dir, struct pbe *restore_pblist);
38
39/* References to section boundaries */
40extern const void __nosave_begin, __nosave_end;
41
42extern 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 */
29struct clk {
30 struct list_head node;
31 unsigned long rate;
32 const char *name;
33};
34
35static struct clk clk_ost_clk = {
36 .name = "OST_CLK",
37 .rate = CLOCK_TICK_RATE,
38};
39
40static struct clk clk_mclk_clk = {
41 .name = "MAIN_CLK",
42};
43
44static struct clk clk_bclk32_clk = {
45 .name = "BUS32_CLK",
46};
47
48static struct clk clk_ddr_clk = {
49 .name = "DDR_CLK",
50};
51
52static struct clk clk_vga_clk = {
53 .name = "VGA_CLK",
54};
55
56static LIST_HEAD(clocks);
57static DEFINE_MUTEX(clocks_mutex);
58
59struct 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}
74EXPORT_SYMBOL(clk_get);
75
76void clk_put(struct clk *clk)
77{
78}
79EXPORT_SYMBOL(clk_put);
80
81int clk_enable(struct clk *clk)
82{
83 return 0;
84}
85EXPORT_SYMBOL(clk_enable);
86
87void clk_disable(struct clk *clk)
88{
89}
90EXPORT_SYMBOL(clk_disable);
91
92unsigned long clk_get_rate(struct clk *clk)
93{
94 return clk->rate;
95}
96EXPORT_SYMBOL(clk_get_rate);
97
98struct {
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
116struct {
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
135int 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}
213EXPORT_SYMBOL(clk_set_rate);
214
215int 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}
224EXPORT_SYMBOL(clk_register);
225
226void clk_unregister(struct clk *clk)
227{
228 mutex_lock(&clocks_mutex);
229 list_del(&clk->node);
230 mutex_unlock(&clocks_mutex);
231}
232EXPORT_SYMBOL(clk_unregister);
233
234struct {
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
302struct {
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
323static 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}
388core_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
22static 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 */
27int 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
38static 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
47static 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
68static 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
79static 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
88static int __init ucv2_cpufreq_init(void)
89{
90 return cpufreq_register_driver(&ucv2_driver);
91}
92
93arch_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 */
27pgd_t *resume_pg_dir;
28
29struct 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 */
36static 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 */
51static 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 */
73static 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
119static inline void resume_init_first_level_page_table(pgd_t *pg_dir)
120{
121}
122
123int 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
145int 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
153void save_processor_state(void)
154{
155}
156
157void 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@
28ENTRY(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
42101:
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
50102: 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
59109:
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
911: .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
99ENTRY(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
1171: .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
25struct puv3_cpu_pm_fns *puv3_cpu_pm_fns;
26static unsigned long *sleep_save;
27
28int 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}
67EXPORT_SYMBOL_GPL(puv3_pm_enter);
68
69unsigned long sleep_phys_sp(void *sp)
70{
71 return virt_to_phys(sp);
72}
73
74static 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
82static 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
92static 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
98static 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
105static 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
123device_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
20pkunity_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
35pkunity_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
52ENTRY(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
118pkunity_cpu_do_suspend:
119 b 101f
120 @ put DDR2 into self-refresh
121100: stw r5, [r0+], #0x24
122 @ change PLL
123 stw r6, [r1]
124 b 1f
125
126 .ltorg
127 .align 5
128101: b 102f
129 @ wait for PLL changing complete
1301: ldw r6, [r1+], #0x44
131 csub.a r6, #0x1
132 bne 1b
133 b 2f
134
135 .ltorg
136 .align 5
137102: b 100b
138 @ close PLL
1392: stw r7, [r1+], #0x4
140 @ enter sleep mode
141 stw r8, [r1]
1423: 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
161ENTRY(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
185sleep_save_sp:
186 .word 0 @ preserve stack phys ptr here
187
188 .text
189resume_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