2008-12-12 00:24:07 +00:00
/* linux/arch/arm/plat-s3c/pm-check.c
* originally in linux / arch / arm / plat - s3c24xx / pm . c
*
* Copyright ( c ) 2004 , 2006 , 2008 Simtec Electronics
* http : //armlinux.simtec.co.uk
* Ben Dooks < ben @ simtec . co . uk >
*
* S3C Power Mangament - suspend / resume memory corruptiuon check .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/kernel.h>
# include <linux/suspend.h>
# include <linux/init.h>
# include <linux/crc32.h>
# include <linux/ioport.h>
# include <plat/pm.h>
# if CONFIG_S3C2410_PM_CHECK_CHUNKSIZE < 1
# error CONFIG_S3C2410_PM_CHECK_CHUNKSIZE must be a positive non-zero value
# endif
/* suspend checking code...
*
* this next area does a set of crc checks over all the installed
* memory , so the system can verify if the resume was ok .
*
* CONFIG_S3C2410_PM_CHECK_CHUNKSIZE defines the block - size for the CRC ,
* increasing it will mean that the area corrupted will be less easy to spot ,
* and reducing the size will cause the CRC save area to grow
*/
# define CHECK_CHUNKSIZE (CONFIG_S3C2410_PM_CHECK_CHUNKSIZE * 1024)
static u32 crc_size ; /* size needed for the crc block */
static u32 * crcs ; /* allocated over suspend/resume */
typedef u32 * ( run_fn_t ) ( struct resource * ptr , u32 * arg ) ;
/* s3c_pm_run_res
*
* go through the given resource list , and look for system ram
*/
static void s3c_pm_run_res ( struct resource * ptr , run_fn_t fn , u32 * arg )
{
while ( ptr ! = NULL ) {
if ( ptr - > child ! = NULL )
s3c_pm_run_res ( ptr - > child , fn , arg ) ;
if ( ( ptr - > flags & IORESOURCE_MEM ) & &
strcmp ( ptr - > name , " System RAM " ) = = 0 ) {
S3C_PMDBG ( " Found system RAM at %08lx..%08lx \n " ,
2008-12-12 00:24:09 +00:00
( unsigned long ) ptr - > start ,
( unsigned long ) ptr - > end ) ;
2008-12-12 00:24:07 +00:00
arg = ( fn ) ( ptr , arg ) ;
}
ptr = ptr - > sibling ;
}
}
static void s3c_pm_run_sysram ( run_fn_t fn , u32 * arg )
{
s3c_pm_run_res ( & iomem_resource , fn , arg ) ;
}
static u32 * s3c_pm_countram ( struct resource * res , u32 * val )
{
u32 size = ( u32 ) ( res - > end - res - > start ) + 1 ;
size + = CHECK_CHUNKSIZE - 1 ;
size / = CHECK_CHUNKSIZE ;
2008-12-12 00:24:09 +00:00
S3C_PMDBG ( " Area %08lx..%08lx, %d blocks \n " ,
( unsigned long ) res - > start , ( unsigned long ) res - > end , size ) ;
2008-12-12 00:24:07 +00:00
* val + = size * sizeof ( u32 ) ;
return val ;
}
/* s3c_pm_prepare_check
*
* prepare the necessary information for creating the CRCs . This
* must be done before the final save , as it will require memory
* allocating , and thus touching bits of the kernel we do not
* know about .
*/
void s3c_pm_check_prepare ( void )
{
crc_size = 0 ;
s3c_pm_run_sysram ( s3c_pm_countram , & crc_size ) ;
S3C_PMDBG ( " s3c_pm_prepare_check: %u checks needed \n " , crc_size ) ;
crcs = kmalloc ( crc_size + 4 , GFP_KERNEL ) ;
if ( crcs = = NULL )
printk ( KERN_ERR " Cannot allocated CRC save area \n " ) ;
}
static u32 * s3c_pm_makecheck ( struct resource * res , u32 * val )
{
unsigned long addr , left ;
for ( addr = res - > start ; addr < res - > end ;
addr + = CHECK_CHUNKSIZE ) {
left = res - > end - addr ;
if ( left > CHECK_CHUNKSIZE )
left = CHECK_CHUNKSIZE ;
* val = crc32_le ( ~ 0 , phys_to_virt ( addr ) , left ) ;
val + + ;
}
return val ;
}
/* s3c_pm_check_store
*
* compute the CRC values for the memory blocks before the final
* sleep .
*/
void s3c_pm_check_store ( void )
{
if ( crcs ! = NULL )
s3c_pm_run_sysram ( s3c_pm_makecheck , crcs ) ;
}
/* in_region
*
* return TRUE if the area defined by ptr . . ptr + size contains the
* what . . what + whatsz
*/
static inline int in_region ( void * ptr , int size , void * what , size_t whatsz )
{
if ( ( what + whatsz ) < ptr )
return 0 ;
if ( what > ( ptr + size ) )
return 0 ;
return 1 ;
}
/**
2008-12-12 00:24:36 +00:00
* s3c_pm_runcheck ( ) - helper to check a resource on restore .
2008-12-12 00:24:07 +00:00
* @ res : The resource to check
* @ vak : Pointer to list of CRC32 values to check .
*
* Called from the s3c_pm_check_restore ( ) via s3c_pm_run_sysram ( ) , this
* function runs the given memory resource checking it against the stored
* CRC to ensure that memory is restored . The function tries to skip as
* many of the areas used during the suspend process .
*/
static u32 * s3c_pm_runcheck ( struct resource * res , u32 * val )
{
void * save_at = phys_to_virt ( s3c_sleep_save_phys ) ;
unsigned long addr ;
unsigned long left ;
2008-12-12 00:24:36 +00:00
void * stkpage ;
2008-12-12 00:24:07 +00:00
void * ptr ;
u32 calc ;
2008-12-12 00:24:36 +00:00
stkpage = ( void * ) ( ( u32 ) & calc & ~ PAGE_MASK ) ;
2008-12-12 00:24:07 +00:00
for ( addr = res - > start ; addr < res - > end ;
addr + = CHECK_CHUNKSIZE ) {
left = res - > end - addr ;
if ( left > CHECK_CHUNKSIZE )
left = CHECK_CHUNKSIZE ;
ptr = phys_to_virt ( addr ) ;
2008-12-12 00:24:36 +00:00
if ( in_region ( ptr , left , stkpage , 4096 ) ) {
S3C_PMDBG ( " skipping %08lx, has stack in \n " , addr ) ;
goto skip_check ;
}
2008-12-12 00:24:07 +00:00
if ( in_region ( ptr , left , crcs , crc_size ) ) {
S3C_PMDBG ( " skipping %08lx, has crc block in \n " , addr ) ;
goto skip_check ;
}
if ( in_region ( ptr , left , save_at , 32 * 4 ) ) {
S3C_PMDBG ( " skipping %08lx, has save block in \n " , addr ) ;
goto skip_check ;
}
/* calculate and check the checksum */
calc = crc32_le ( ~ 0 , ptr , left ) ;
if ( calc ! = * val ) {
printk ( KERN_ERR " Restore CRC error at "
" %08lx (%08x vs %08x) \n " , addr , calc , * val ) ;
S3C_PMDBG ( " Restore CRC error at %08lx (%08x vs %08x) \n " ,
addr , calc , * val ) ;
}
skip_check :
val + + ;
}
return val ;
}
/**
* s3c_pm_check_restore ( ) - memory check called on resume
*
* check the CRCs after the restore event and free the memory used
* to hold them
*/
void s3c_pm_check_restore ( void )
{
2008-12-12 00:24:34 +00:00
if ( crcs ! = NULL )
2008-12-12 00:24:07 +00:00
s3c_pm_run_sysram ( s3c_pm_runcheck , crcs ) ;
}
2008-12-12 00:24:34 +00:00
/**
* s3c_pm_check_cleanup ( ) - free memory resources
*
* Free the resources that where allocated by the suspend
* memory check code . We do this separately from the
* s3c_pm_check_restore ( ) function as we cannot call any
* functions that might sleep during that resume .
*/
void s3c_pm_check_cleanup ( void )
{
kfree ( crcs ) ;
crcs = NULL ;
}