diff options
author | Ben Dooks <ben-linux@fluff.org> | 2008-12-11 19:24:07 -0500 |
---|---|---|
committer | Ben Dooks <ben-linux@fluff.org> | 2009-03-08 08:33:39 -0400 |
commit | 549c7e33aeb9bfe441ecf68639d2227bb90978e7 (patch) | |
tree | f5aa85c1c0a4f581273814cd5a62510d40e7117f /arch/arm/plat-s3c | |
parent | 6419711a164ba1304fa8fbb75ae9485455e04dcd (diff) |
[ARM] S3C: Split the resume memory check code from pm.c
Split the optional memory check code out of the pm.c file
as it is quite a big #ifdef block and as-such can be moved
out and simply compiled when the configuration is set.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Diffstat (limited to 'arch/arm/plat-s3c')
-rw-r--r-- | arch/arm/plat-s3c/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/plat-s3c/include/plat/pm.h | 12 | ||||
-rw-r--r-- | arch/arm/plat-s3c/pm-check.c | 220 |
3 files changed, 233 insertions, 0 deletions
diff --git a/arch/arm/plat-s3c/Makefile b/arch/arm/plat-s3c/Makefile index afd8d089ba16..8d7815d25a51 100644 --- a/arch/arm/plat-s3c/Makefile +++ b/arch/arm/plat-s3c/Makefile | |||
@@ -21,6 +21,7 @@ obj-y += gpio-config.o | |||
21 | # PM support | 21 | # PM support |
22 | 22 | ||
23 | obj-$(CONFIG_PM) += pm.o | 23 | obj-$(CONFIG_PM) += pm.o |
24 | obj-$(CONFIG_S3C2410_PM_CHECK) += pm-check.o | ||
24 | 25 | ||
25 | # devices | 26 | # devices |
26 | 27 | ||
diff --git a/arch/arm/plat-s3c/include/plat/pm.h b/arch/arm/plat-s3c/include/plat/pm.h index a1520997ab82..95c2612d4976 100644 --- a/arch/arm/plat-s3c/include/plat/pm.h +++ b/arch/arm/plat-s3c/include/plat/pm.h | |||
@@ -101,3 +101,15 @@ extern void s3c_pm_dbg(const char *msg, ...); | |||
101 | #else | 101 | #else |
102 | #define S3C_PMDBG(fmt...) printk(KERN_DEBUG fmt) | 102 | #define S3C_PMDBG(fmt...) printk(KERN_DEBUG fmt) |
103 | #endif | 103 | #endif |
104 | |||
105 | /* suspend memory checking */ | ||
106 | |||
107 | #ifdef CONFIG_S3C2410_PM_CHECK | ||
108 | extern void s3c_pm_check_prepare(void); | ||
109 | extern void s3c_pm_check_restore(void); | ||
110 | extern void s3c_pm_check_store(void); | ||
111 | #else | ||
112 | #define s3c_pm_check_prepare() do { } while(0) | ||
113 | #define s3c_pm_check_restore() do { } while(0) | ||
114 | #define s3c_pm_check_store() do { } while(0) | ||
115 | #endif | ||
diff --git a/arch/arm/plat-s3c/pm-check.c b/arch/arm/plat-s3c/pm-check.c new file mode 100644 index 000000000000..95ba7693b5af --- /dev/null +++ b/arch/arm/plat-s3c/pm-check.c | |||
@@ -0,0 +1,220 @@ | |||
1 | /* linux/arch/arm/plat-s3c/pm-check.c | ||
2 | * originally in linux/arch/arm/plat-s3c24xx/pm.c | ||
3 | * | ||
4 | * Copyright (c) 2004,2006,2008 Simtec Electronics | ||
5 | * http://armlinux.simtec.co.uk | ||
6 | * Ben Dooks <ben@simtec.co.uk> | ||
7 | * | ||
8 | * S3C Power Mangament - suspend/resume memory corruptiuon check. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/suspend.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/crc32.h> | ||
19 | #include <linux/ioport.h> | ||
20 | |||
21 | #include <plat/pm.h> | ||
22 | |||
23 | #if CONFIG_S3C2410_PM_CHECK_CHUNKSIZE < 1 | ||
24 | #error CONFIG_S3C2410_PM_CHECK_CHUNKSIZE must be a positive non-zero value | ||
25 | #endif | ||
26 | |||
27 | /* suspend checking code... | ||
28 | * | ||
29 | * this next area does a set of crc checks over all the installed | ||
30 | * memory, so the system can verify if the resume was ok. | ||
31 | * | ||
32 | * CONFIG_S3C2410_PM_CHECK_CHUNKSIZE defines the block-size for the CRC, | ||
33 | * increasing it will mean that the area corrupted will be less easy to spot, | ||
34 | * and reducing the size will cause the CRC save area to grow | ||
35 | */ | ||
36 | |||
37 | #define CHECK_CHUNKSIZE (CONFIG_S3C2410_PM_CHECK_CHUNKSIZE * 1024) | ||
38 | |||
39 | static u32 crc_size; /* size needed for the crc block */ | ||
40 | static u32 *crcs; /* allocated over suspend/resume */ | ||
41 | |||
42 | typedef u32 *(run_fn_t)(struct resource *ptr, u32 *arg); | ||
43 | |||
44 | /* s3c_pm_run_res | ||
45 | * | ||
46 | * go through the given resource list, and look for system ram | ||
47 | */ | ||
48 | |||
49 | static void s3c_pm_run_res(struct resource *ptr, run_fn_t fn, u32 *arg) | ||
50 | { | ||
51 | while (ptr != NULL) { | ||
52 | if (ptr->child != NULL) | ||
53 | s3c_pm_run_res(ptr->child, fn, arg); | ||
54 | |||
55 | if ((ptr->flags & IORESOURCE_MEM) && | ||
56 | strcmp(ptr->name, "System RAM") == 0) { | ||
57 | S3C_PMDBG("Found system RAM at %08lx..%08lx\n", | ||
58 | ptr->start, ptr->end); | ||
59 | arg = (fn)(ptr, arg); | ||
60 | } | ||
61 | |||
62 | ptr = ptr->sibling; | ||
63 | } | ||
64 | } | ||
65 | |||
66 | static void s3c_pm_run_sysram(run_fn_t fn, u32 *arg) | ||
67 | { | ||
68 | s3c_pm_run_res(&iomem_resource, fn, arg); | ||
69 | } | ||
70 | |||
71 | static u32 *s3c_pm_countram(struct resource *res, u32 *val) | ||
72 | { | ||
73 | u32 size = (u32)(res->end - res->start)+1; | ||
74 | |||
75 | size += CHECK_CHUNKSIZE-1; | ||
76 | size /= CHECK_CHUNKSIZE; | ||
77 | |||
78 | S3C_PMDBG("Area %08lx..%08lx, %d blocks\n", res->start, res->end, size); | ||
79 | |||
80 | *val += size * sizeof(u32); | ||
81 | return val; | ||
82 | } | ||
83 | |||
84 | /* s3c_pm_prepare_check | ||
85 | * | ||
86 | * prepare the necessary information for creating the CRCs. This | ||
87 | * must be done before the final save, as it will require memory | ||
88 | * allocating, and thus touching bits of the kernel we do not | ||
89 | * know about. | ||
90 | */ | ||
91 | |||
92 | void s3c_pm_check_prepare(void) | ||
93 | { | ||
94 | crc_size = 0; | ||
95 | |||
96 | s3c_pm_run_sysram(s3c_pm_countram, &crc_size); | ||
97 | |||
98 | S3C_PMDBG("s3c_pm_prepare_check: %u checks needed\n", crc_size); | ||
99 | |||
100 | crcs = kmalloc(crc_size+4, GFP_KERNEL); | ||
101 | if (crcs == NULL) | ||
102 | printk(KERN_ERR "Cannot allocated CRC save area\n"); | ||
103 | } | ||
104 | |||
105 | static u32 *s3c_pm_makecheck(struct resource *res, u32 *val) | ||
106 | { | ||
107 | unsigned long addr, left; | ||
108 | |||
109 | for (addr = res->start; addr < res->end; | ||
110 | addr += CHECK_CHUNKSIZE) { | ||
111 | left = res->end - addr; | ||
112 | |||
113 | if (left > CHECK_CHUNKSIZE) | ||
114 | left = CHECK_CHUNKSIZE; | ||
115 | |||
116 | *val = crc32_le(~0, phys_to_virt(addr), left); | ||
117 | val++; | ||
118 | } | ||
119 | |||
120 | return val; | ||
121 | } | ||
122 | |||
123 | /* s3c_pm_check_store | ||
124 | * | ||
125 | * compute the CRC values for the memory blocks before the final | ||
126 | * sleep. | ||
127 | */ | ||
128 | |||
129 | void s3c_pm_check_store(void) | ||
130 | { | ||
131 | if (crcs != NULL) | ||
132 | s3c_pm_run_sysram(s3c_pm_makecheck, crcs); | ||
133 | } | ||
134 | |||
135 | /* in_region | ||
136 | * | ||
137 | * return TRUE if the area defined by ptr..ptr+size contains the | ||
138 | * what..what+whatsz | ||
139 | */ | ||
140 | |||
141 | static inline int in_region(void *ptr, int size, void *what, size_t whatsz) | ||
142 | { | ||
143 | if ((what+whatsz) < ptr) | ||
144 | return 0; | ||
145 | |||
146 | if (what > (ptr+size)) | ||
147 | return 0; | ||
148 | |||
149 | return 1; | ||
150 | } | ||
151 | |||
152 | /** | ||
153 | * s3c_pm_runcheck*() - helper to check a resource on restore. | ||
154 | * @res: The resource to check | ||
155 | * @vak: Pointer to list of CRC32 values to check. | ||
156 | * | ||
157 | * Called from the s3c_pm_check_restore() via s3c_pm_run_sysram(), this | ||
158 | * function runs the given memory resource checking it against the stored | ||
159 | * CRC to ensure that memory is restored. The function tries to skip as | ||
160 | * many of the areas used during the suspend process. | ||
161 | */ | ||
162 | static u32 *s3c_pm_runcheck(struct resource *res, u32 *val) | ||
163 | { | ||
164 | void *save_at = phys_to_virt(s3c_sleep_save_phys); | ||
165 | unsigned long addr; | ||
166 | unsigned long left; | ||
167 | void *ptr; | ||
168 | u32 calc; | ||
169 | |||
170 | for (addr = res->start; addr < res->end; | ||
171 | addr += CHECK_CHUNKSIZE) { | ||
172 | left = res->end - addr; | ||
173 | |||
174 | if (left > CHECK_CHUNKSIZE) | ||
175 | left = CHECK_CHUNKSIZE; | ||
176 | |||
177 | ptr = phys_to_virt(addr); | ||
178 | |||
179 | if (in_region(ptr, left, crcs, crc_size)) { | ||
180 | S3C_PMDBG("skipping %08lx, has crc block in\n", addr); | ||
181 | goto skip_check; | ||
182 | } | ||
183 | |||
184 | if (in_region(ptr, left, save_at, 32*4 )) { | ||
185 | S3C_PMDBG("skipping %08lx, has save block in\n", addr); | ||
186 | goto skip_check; | ||
187 | } | ||
188 | |||
189 | /* calculate and check the checksum */ | ||
190 | |||
191 | calc = crc32_le(~0, ptr, left); | ||
192 | if (calc != *val) { | ||
193 | printk(KERN_ERR "Restore CRC error at " | ||
194 | "%08lx (%08x vs %08x)\n", addr, calc, *val); | ||
195 | |||
196 | S3C_PMDBG("Restore CRC error at %08lx (%08x vs %08x)\n", | ||
197 | addr, calc, *val); | ||
198 | } | ||
199 | |||
200 | skip_check: | ||
201 | val++; | ||
202 | } | ||
203 | |||
204 | return val; | ||
205 | } | ||
206 | |||
207 | /** | ||
208 | * s3c_pm_check_restore() - memory check called on resume | ||
209 | * | ||
210 | * check the CRCs after the restore event and free the memory used | ||
211 | * to hold them | ||
212 | */ | ||
213 | void s3c_pm_check_restore(void) | ||
214 | { | ||
215 | if (crcs != NULL) { | ||
216 | s3c_pm_run_sysram(s3c_pm_runcheck, crcs); | ||
217 | kfree(crcs); | ||
218 | crcs = NULL; | ||
219 | } | ||
220 | } | ||