2010-07-27 08:35:56 -07:00
/*
* arch / arm / mach - lpc32xx / pm . c
*
* Original authors : Vitaly Wool , Dmitry Chigirev < source @ mvista . com >
* Modified by Kevin Wells < kevin . wells @ nxp . com >
*
* 2005 ( c ) MontaVista Software , Inc . This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed " as is " without any warranty of any kind , whether express
* or implied .
*/
/*
* LPC32XX CPU and system power management
*
2012-02-27 17:31:24 +01:00
* The LPC32XX has three CPU modes for controlling system power : run ,
2010-07-27 08:35:56 -07:00
* direct - run , and halt modes . When switching between halt and run modes ,
* the CPU transistions through direct - run mode . For Linux , direct - run
* mode is not used in normal operation . Halt mode is used when the
* system is fully suspended .
*
* Run mode :
* The ARM CPU clock ( HCLK_PLL ) , HCLK bus clock , and PCLK bus clocks are
* derived from the HCLK PLL . The HCLK and PCLK bus rates are divided from
* the HCLK_PLL rate . Linux runs in this mode .
*
* Direct - run mode :
* The ARM CPU clock , HCLK bus clock , and PCLK bus clocks are driven from
* SYSCLK . SYSCLK is usually around 13 MHz , but may vary based on SYSCLK
* source or the frequency of the main oscillator . In this mode , the
* HCLK_PLL can be safely enabled , changed , or disabled .
*
* Halt mode :
* SYSCLK is gated off and the CPU and system clocks are halted .
* Peripherals based on the 32 KHz oscillator clock ( ie , RTC , touch ,
* key scanner , etc . ) still operate if enabled . In this state , an enabled
* system event ( ie , GPIO state change , RTC match , key press , etc . ) will
* wake the system up back into direct - run mode .
*
* DRAM refresh
* DRAM clocking and refresh are slightly different for systems with DDR
* DRAM or regular SDRAM devices . If SDRAM is used in the system , the
* SDRAM will still be accessible in direct - run mode . In DDR based systems ,
2011-03-30 22:57:33 -03:00
* a transition to direct - run mode will stop all DDR accesses ( no clocks ) .
2010-07-27 08:35:56 -07:00
* Because of this , the code to switch power modes and the code to enter
* and exit DRAM self - refresh modes must not be executed in DRAM . A small
* section of IRAM is used instead for this .
*
* Suspend is handled with the following logic :
* Backup a small area of IRAM used for the suspend code
* Copy suspend code to IRAM
* Transfer control to code in IRAM
* Places DRAMs in self - refresh mode
* Enter direct - run mode
* Save state of HCLK_PLL PLL
* Disable HCLK_PLL PLL
* Enter halt mode - CPU and buses will stop
* System enters direct - run mode when an enabled event occurs
* HCLK PLL state is restored
* Run mode is entered
* DRAMS are placed back into normal mode
* Code execution returns from IRAM
* IRAM code are used for suspend is restored
* Suspend mode is exited
*/
# include <linux/suspend.h>
# include <linux/io.h>
# include <linux/slab.h>
# include <asm/cacheflush.h>
# include <mach/hardware.h>
# include <mach/platform.h>
# include "common.h"
# define TEMP_IRAM_AREA IO_ADDRESS(LPC32XX_IRAM_BASE)
/*
* Both STANDBY and MEM suspend states are handled the same with no
* loss of CPU or memory state
*/
static int lpc32xx_pm_enter ( suspend_state_t state )
{
int ( * lpc32xx_suspend_ptr ) ( void ) ;
void * iram_swap_area ;
/* Allocate some space for temporary IRAM storage */
2018-08-10 10:40:51 +08:00
iram_swap_area = kmemdup ( ( void * ) TEMP_IRAM_AREA ,
lpc32xx_sys_suspend_sz , GFP_KERNEL ) ;
2017-06-01 19:50:32 +02:00
if ( ! iram_swap_area )
2010-07-27 08:35:56 -07:00
return - ENOMEM ;
/*
* Copy code to suspend system into IRAM . The suspend code
* needs to run from IRAM as DRAM may no longer be available
* when the PLL is stopped .
*/
memcpy ( ( void * ) TEMP_IRAM_AREA , & lpc32xx_sys_suspend ,
lpc32xx_sys_suspend_sz ) ;
flush_icache_range ( ( unsigned long ) TEMP_IRAM_AREA ,
( unsigned long ) ( TEMP_IRAM_AREA ) + lpc32xx_sys_suspend_sz ) ;
/* Transfer to suspend code in IRAM */
lpc32xx_suspend_ptr = ( void * ) TEMP_IRAM_AREA ;
flush_cache_all ( ) ;
( void ) lpc32xx_suspend_ptr ( ) ;
/* Restore original IRAM contents */
memcpy ( ( void * ) TEMP_IRAM_AREA , iram_swap_area ,
lpc32xx_sys_suspend_sz ) ;
kfree ( iram_swap_area ) ;
return 0 ;
}
2010-11-16 14:14:02 +01:00
static const struct platform_suspend_ops lpc32xx_pm_ops = {
2010-07-27 08:35:56 -07:00
. valid = suspend_valid_only_mem ,
. enter = lpc32xx_pm_enter ,
} ;
# define EMC_DYN_MEM_CTRL_OFS 0x20
# define EMC_SRMMC (1 << 3)
# define EMC_CTRL_REG io_p2v(LPC32XX_EMC_BASE + EMC_DYN_MEM_CTRL_OFS)
static int __init lpc32xx_pm_init ( void )
{
/*
* Setup SDRAM self - refresh clock to automatically disable o
* start of self - refresh . This only needs to be done once .
*/
__raw_writel ( __raw_readl ( EMC_CTRL_REG ) | EMC_SRMMC , EMC_CTRL_REG ) ;
suspend_set_ops ( & lpc32xx_pm_ops ) ;
return 0 ;
}
arch_initcall ( lpc32xx_pm_init ) ;