1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun //
3*4882a593Smuzhiyun // originally in linux/arch/arm/plat-s3c24xx/pm.c
4*4882a593Smuzhiyun //
5*4882a593Smuzhiyun // Copyright (c) 2004-2008 Simtec Electronics
6*4882a593Smuzhiyun // http://armlinux.simtec.co.uk
7*4882a593Smuzhiyun // Ben Dooks <ben@simtec.co.uk>
8*4882a593Smuzhiyun //
9*4882a593Smuzhiyun // S3C Power Mangament - suspend/resume memory corruption check.
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun #include <linux/kernel.h>
12*4882a593Smuzhiyun #include <linux/suspend.h>
13*4882a593Smuzhiyun #include <linux/init.h>
14*4882a593Smuzhiyun #include <linux/crc32.h>
15*4882a593Smuzhiyun #include <linux/ioport.h>
16*4882a593Smuzhiyun #include <linux/slab.h>
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun #include <linux/soc/samsung/s3c-pm.h>
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun #if CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE < 1
21*4882a593Smuzhiyun #error CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE must be a positive non-zero value
22*4882a593Smuzhiyun #endif
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun /* suspend checking code...
25*4882a593Smuzhiyun *
26*4882a593Smuzhiyun * this next area does a set of crc checks over all the installed
27*4882a593Smuzhiyun * memory, so the system can verify if the resume was ok.
28*4882a593Smuzhiyun *
29*4882a593Smuzhiyun * CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE defines the block-size for the CRC,
30*4882a593Smuzhiyun * increasing it will mean that the area corrupted will be less easy to spot,
31*4882a593Smuzhiyun * and reducing the size will cause the CRC save area to grow
32*4882a593Smuzhiyun */
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun #define CHECK_CHUNKSIZE (CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE * 1024)
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun static u32 crc_size; /* size needed for the crc block */
37*4882a593Smuzhiyun static u32 *crcs; /* allocated over suspend/resume */
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun typedef u32 *(run_fn_t)(struct resource *ptr, u32 *arg);
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun /* s3c_pm_run_res
42*4882a593Smuzhiyun *
43*4882a593Smuzhiyun * go through the given resource list, and look for system ram
44*4882a593Smuzhiyun */
45*4882a593Smuzhiyun
s3c_pm_run_res(struct resource * ptr,run_fn_t fn,u32 * arg)46*4882a593Smuzhiyun static void s3c_pm_run_res(struct resource *ptr, run_fn_t fn, u32 *arg)
47*4882a593Smuzhiyun {
48*4882a593Smuzhiyun while (ptr != NULL) {
49*4882a593Smuzhiyun if (ptr->child != NULL)
50*4882a593Smuzhiyun s3c_pm_run_res(ptr->child, fn, arg);
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun if ((ptr->flags & IORESOURCE_SYSTEM_RAM)
53*4882a593Smuzhiyun == IORESOURCE_SYSTEM_RAM) {
54*4882a593Smuzhiyun S3C_PMDBG("Found system RAM at %08lx..%08lx\n",
55*4882a593Smuzhiyun (unsigned long)ptr->start,
56*4882a593Smuzhiyun (unsigned long)ptr->end);
57*4882a593Smuzhiyun arg = (fn)(ptr, arg);
58*4882a593Smuzhiyun }
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun ptr = ptr->sibling;
61*4882a593Smuzhiyun }
62*4882a593Smuzhiyun }
63*4882a593Smuzhiyun
s3c_pm_run_sysram(run_fn_t fn,u32 * arg)64*4882a593Smuzhiyun static void s3c_pm_run_sysram(run_fn_t fn, u32 *arg)
65*4882a593Smuzhiyun {
66*4882a593Smuzhiyun s3c_pm_run_res(&iomem_resource, fn, arg);
67*4882a593Smuzhiyun }
68*4882a593Smuzhiyun
s3c_pm_countram(struct resource * res,u32 * val)69*4882a593Smuzhiyun static u32 *s3c_pm_countram(struct resource *res, u32 *val)
70*4882a593Smuzhiyun {
71*4882a593Smuzhiyun u32 size = (u32)resource_size(res);
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun size += CHECK_CHUNKSIZE-1;
74*4882a593Smuzhiyun size /= CHECK_CHUNKSIZE;
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun S3C_PMDBG("Area %08lx..%08lx, %d blocks\n",
77*4882a593Smuzhiyun (unsigned long)res->start, (unsigned long)res->end, size);
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun *val += size * sizeof(u32);
80*4882a593Smuzhiyun return val;
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun /* s3c_pm_prepare_check
84*4882a593Smuzhiyun *
85*4882a593Smuzhiyun * prepare the necessary information for creating the CRCs. This
86*4882a593Smuzhiyun * must be done before the final save, as it will require memory
87*4882a593Smuzhiyun * allocating, and thus touching bits of the kernel we do not
88*4882a593Smuzhiyun * know about.
89*4882a593Smuzhiyun */
90*4882a593Smuzhiyun
s3c_pm_check_prepare(void)91*4882a593Smuzhiyun void s3c_pm_check_prepare(void)
92*4882a593Smuzhiyun {
93*4882a593Smuzhiyun crc_size = 0;
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun s3c_pm_run_sysram(s3c_pm_countram, &crc_size);
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun S3C_PMDBG("s3c_pm_prepare_check: %u checks needed\n", crc_size);
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun crcs = kmalloc(crc_size+4, GFP_KERNEL);
100*4882a593Smuzhiyun if (crcs == NULL)
101*4882a593Smuzhiyun printk(KERN_ERR "Cannot allocated CRC save area\n");
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun
s3c_pm_makecheck(struct resource * res,u32 * val)104*4882a593Smuzhiyun static u32 *s3c_pm_makecheck(struct resource *res, u32 *val)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun unsigned long addr, left;
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun for (addr = res->start; addr < res->end;
109*4882a593Smuzhiyun addr += CHECK_CHUNKSIZE) {
110*4882a593Smuzhiyun left = res->end - addr;
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun if (left > CHECK_CHUNKSIZE)
113*4882a593Smuzhiyun left = CHECK_CHUNKSIZE;
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun *val = crc32_le(~0, phys_to_virt(addr), left);
116*4882a593Smuzhiyun val++;
117*4882a593Smuzhiyun }
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun return val;
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun /* s3c_pm_check_store
123*4882a593Smuzhiyun *
124*4882a593Smuzhiyun * compute the CRC values for the memory blocks before the final
125*4882a593Smuzhiyun * sleep.
126*4882a593Smuzhiyun */
127*4882a593Smuzhiyun
s3c_pm_check_store(void)128*4882a593Smuzhiyun void s3c_pm_check_store(void)
129*4882a593Smuzhiyun {
130*4882a593Smuzhiyun if (crcs != NULL)
131*4882a593Smuzhiyun s3c_pm_run_sysram(s3c_pm_makecheck, crcs);
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun /* in_region
135*4882a593Smuzhiyun *
136*4882a593Smuzhiyun * return TRUE if the area defined by ptr..ptr+size contains the
137*4882a593Smuzhiyun * what..what+whatsz
138*4882a593Smuzhiyun */
139*4882a593Smuzhiyun
in_region(void * ptr,int size,void * what,size_t whatsz)140*4882a593Smuzhiyun static inline int in_region(void *ptr, int size, void *what, size_t whatsz)
141*4882a593Smuzhiyun {
142*4882a593Smuzhiyun if ((what+whatsz) < ptr)
143*4882a593Smuzhiyun return 0;
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun if (what > (ptr+size))
146*4882a593Smuzhiyun return 0;
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun return 1;
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun /**
152*4882a593Smuzhiyun * s3c_pm_runcheck() - helper to check a resource on restore.
153*4882a593Smuzhiyun * @res: The resource to check
154*4882a593Smuzhiyun * @vak: Pointer to list of CRC32 values to check.
155*4882a593Smuzhiyun *
156*4882a593Smuzhiyun * Called from the s3c_pm_check_restore() via s3c_pm_run_sysram(), this
157*4882a593Smuzhiyun * function runs the given memory resource checking it against the stored
158*4882a593Smuzhiyun * CRC to ensure that memory is restored. The function tries to skip as
159*4882a593Smuzhiyun * many of the areas used during the suspend process.
160*4882a593Smuzhiyun */
s3c_pm_runcheck(struct resource * res,u32 * val)161*4882a593Smuzhiyun static u32 *s3c_pm_runcheck(struct resource *res, u32 *val)
162*4882a593Smuzhiyun {
163*4882a593Smuzhiyun unsigned long addr;
164*4882a593Smuzhiyun unsigned long left;
165*4882a593Smuzhiyun void *stkpage;
166*4882a593Smuzhiyun void *ptr;
167*4882a593Smuzhiyun u32 calc;
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun stkpage = (void *)((u32)&calc & ~PAGE_MASK);
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun for (addr = res->start; addr < res->end;
172*4882a593Smuzhiyun addr += CHECK_CHUNKSIZE) {
173*4882a593Smuzhiyun left = res->end - addr;
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun if (left > CHECK_CHUNKSIZE)
176*4882a593Smuzhiyun left = CHECK_CHUNKSIZE;
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun ptr = phys_to_virt(addr);
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun if (in_region(ptr, left, stkpage, 4096)) {
181*4882a593Smuzhiyun S3C_PMDBG("skipping %08lx, has stack in\n", addr);
182*4882a593Smuzhiyun goto skip_check;
183*4882a593Smuzhiyun }
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun if (in_region(ptr, left, crcs, crc_size)) {
186*4882a593Smuzhiyun S3C_PMDBG("skipping %08lx, has crc block in\n", addr);
187*4882a593Smuzhiyun goto skip_check;
188*4882a593Smuzhiyun }
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun /* calculate and check the checksum */
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun calc = crc32_le(~0, ptr, left);
193*4882a593Smuzhiyun if (calc != *val) {
194*4882a593Smuzhiyun printk(KERN_ERR "Restore CRC error at "
195*4882a593Smuzhiyun "%08lx (%08x vs %08x)\n", addr, calc, *val);
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun S3C_PMDBG("Restore CRC error at %08lx (%08x vs %08x)\n",
198*4882a593Smuzhiyun addr, calc, *val);
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun skip_check:
202*4882a593Smuzhiyun val++;
203*4882a593Smuzhiyun }
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun return val;
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun /**
209*4882a593Smuzhiyun * s3c_pm_check_restore() - memory check called on resume
210*4882a593Smuzhiyun *
211*4882a593Smuzhiyun * check the CRCs after the restore event and free the memory used
212*4882a593Smuzhiyun * to hold them
213*4882a593Smuzhiyun */
s3c_pm_check_restore(void)214*4882a593Smuzhiyun void s3c_pm_check_restore(void)
215*4882a593Smuzhiyun {
216*4882a593Smuzhiyun if (crcs != NULL)
217*4882a593Smuzhiyun s3c_pm_run_sysram(s3c_pm_runcheck, crcs);
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun /**
221*4882a593Smuzhiyun * s3c_pm_check_cleanup() - free memory resources
222*4882a593Smuzhiyun *
223*4882a593Smuzhiyun * Free the resources that where allocated by the suspend
224*4882a593Smuzhiyun * memory check code. We do this separately from the
225*4882a593Smuzhiyun * s3c_pm_check_restore() function as we cannot call any
226*4882a593Smuzhiyun * functions that might sleep during that resume.
227*4882a593Smuzhiyun */
s3c_pm_check_cleanup(void)228*4882a593Smuzhiyun void s3c_pm_check_cleanup(void)
229*4882a593Smuzhiyun {
230*4882a593Smuzhiyun kfree(crcs);
231*4882a593Smuzhiyun crcs = NULL;
232*4882a593Smuzhiyun }
233*4882a593Smuzhiyun
234