2007-07-18 00:32:31 +04:00
# include <linux/init.h>
2007-10-18 14:04:41 +04:00
# include <linux/suspend.h>
2007-07-18 00:32:31 +04:00
# include <asm/io.h>
# include <asm/time.h>
# include <asm/mpc52xx.h>
# include "mpc52xx_pic.h"
/* defined in lite5200_sleep.S and only used here */
extern void lite5200_low_power ( void __iomem * sram , void __iomem * mbar ) ;
static struct mpc52xx_cdm __iomem * cdm ;
static struct mpc52xx_intr __iomem * pic ;
static struct mpc52xx_sdma __iomem * bes ;
static struct mpc52xx_xlb __iomem * xlb ;
static struct mpc52xx_gpio __iomem * gps ;
static struct mpc52xx_gpio_wkup __iomem * gpw ;
2008-06-17 12:33:14 +04:00
static void __iomem * pci ;
2007-07-18 00:32:31 +04:00
static void __iomem * sram ;
static const int sram_size = 0x4000 ; /* 16 kBytes */
static void __iomem * mbar ;
2007-10-18 14:04:41 +04:00
static suspend_state_t lite5200_pm_target_state ;
2007-07-18 00:32:31 +04:00
static int lite5200_pm_valid ( suspend_state_t state )
{
switch ( state ) {
case PM_SUSPEND_STANDBY :
case PM_SUSPEND_MEM :
return 1 ;
default :
return 0 ;
}
}
2008-01-08 02:04:17 +03:00
static int lite5200_pm_begin ( suspend_state_t state )
2007-10-18 14:04:41 +04:00
{
if ( lite5200_pm_valid ( state ) ) {
lite5200_pm_target_state = state ;
return 0 ;
}
return - EINVAL ;
}
static int lite5200_pm_prepare ( void )
2007-07-18 00:32:31 +04:00
{
2008-01-18 19:30:37 +03:00
struct device_node * np ;
2008-01-25 08:25:31 +03:00
const struct of_device_id immr_ids [ ] = {
{ . compatible = " fsl,mpc5200-immr " , } ,
{ . compatible = " fsl,mpc5200b-immr " , } ,
{ . type = " soc " , . compatible = " mpc5200 " , } , /* lite5200 */
{ . type = " builtin " , . compatible = " mpc5200 " , } , /* efika */
{ }
} ;
2008-06-17 12:33:14 +04:00
u64 regaddr64 = 0 ;
const u32 * regaddr_p ;
2008-01-18 19:30:37 +03:00
2007-07-18 00:32:31 +04:00
/* deep sleep? let mpc52xx code handle that */
2007-10-18 14:04:41 +04:00
if ( lite5200_pm_target_state = = PM_SUSPEND_STANDBY )
return mpc52xx_pm_prepare ( ) ;
2007-07-18 00:32:31 +04:00
2007-10-18 14:04:41 +04:00
if ( lite5200_pm_target_state ! = PM_SUSPEND_MEM )
2007-07-18 00:32:31 +04:00
return - EINVAL ;
/* map registers */
2008-01-25 08:25:31 +03:00
np = of_find_matching_node ( NULL , immr_ids ) ;
2008-06-17 12:33:14 +04:00
regaddr_p = of_get_address ( np , 0 , NULL , NULL ) ;
if ( regaddr_p )
regaddr64 = of_translate_address ( np , regaddr_p ) ;
2008-01-18 19:30:37 +03:00
of_node_put ( np ) ;
2008-06-17 12:33:14 +04:00
mbar = ioremap ( ( u32 ) regaddr64 , 0xC000 ) ;
2007-07-18 00:32:31 +04:00
if ( ! mbar ) {
printk ( KERN_ERR " %s:%i Error mapping registers \n " , __func__ , __LINE__ ) ;
return - ENOSYS ;
}
cdm = mbar + 0x200 ;
pic = mbar + 0x500 ;
gps = mbar + 0xb00 ;
gpw = mbar + 0xc00 ;
2008-06-17 12:33:14 +04:00
pci = mbar + 0xd00 ;
2007-07-18 00:32:31 +04:00
bes = mbar + 0x1200 ;
xlb = mbar + 0x1f00 ;
sram = mbar + 0x8000 ;
return 0 ;
}
/* save and restore registers not bound to any real devices */
static struct mpc52xx_cdm scdm ;
static struct mpc52xx_intr spic ;
static struct mpc52xx_sdma sbes ;
static struct mpc52xx_xlb sxlb ;
static struct mpc52xx_gpio sgps ;
static struct mpc52xx_gpio_wkup sgpw ;
2008-06-17 12:33:14 +04:00
static char spci [ 0x200 ] ;
2007-07-18 00:32:31 +04:00
static void lite5200_save_regs ( void )
{
_memcpy_fromio ( & spic , pic , sizeof ( * pic ) ) ;
_memcpy_fromio ( & sbes , bes , sizeof ( * bes ) ) ;
_memcpy_fromio ( & scdm , cdm , sizeof ( * cdm ) ) ;
_memcpy_fromio ( & sxlb , xlb , sizeof ( * xlb ) ) ;
_memcpy_fromio ( & sgps , gps , sizeof ( * gps ) ) ;
_memcpy_fromio ( & sgpw , gpw , sizeof ( * gpw ) ) ;
2008-06-17 12:33:14 +04:00
_memcpy_fromio ( spci , pci , 0x200 ) ;
2007-07-18 00:32:31 +04:00
_memcpy_fromio ( saved_sram , sram , sram_size ) ;
}
static void lite5200_restore_regs ( void )
{
int i ;
_memcpy_toio ( sram , saved_sram , sram_size ) ;
2008-06-17 12:33:14 +04:00
/* PCI Configuration */
_memcpy_toio ( pci , spci , 0x200 ) ;
2007-07-18 00:32:31 +04:00
/*
* GPIOs . Interrupt Master Enable has higher address then other
* registers , so just memcpy is ok .
*/
_memcpy_toio ( gpw , & sgpw , sizeof ( * gpw ) ) ;
_memcpy_toio ( gps , & sgps , sizeof ( * gps ) ) ;
/* XLB Arbitrer */
out_be32 ( & xlb - > snoop_window , sxlb . snoop_window ) ;
out_be32 ( & xlb - > master_priority , sxlb . master_priority ) ;
out_be32 ( & xlb - > master_pri_enable , sxlb . master_pri_enable ) ;
/* enable */
out_be32 ( & xlb - > int_enable , sxlb . int_enable ) ;
out_be32 ( & xlb - > config , sxlb . config ) ;
/* CDM - Clock Distribution Module */
out_8 ( & cdm - > ipb_clk_sel , scdm . ipb_clk_sel ) ;
out_8 ( & cdm - > pci_clk_sel , scdm . pci_clk_sel ) ;
out_8 ( & cdm - > ext_48mhz_en , scdm . ext_48mhz_en ) ;
out_8 ( & cdm - > fd_enable , scdm . fd_enable ) ;
out_be16 ( & cdm - > fd_counters , scdm . fd_counters ) ;
out_be32 ( & cdm - > clk_enables , scdm . clk_enables ) ;
out_8 ( & cdm - > osc_disable , scdm . osc_disable ) ;
out_be16 ( & cdm - > mclken_div_psc1 , scdm . mclken_div_psc1 ) ;
out_be16 ( & cdm - > mclken_div_psc2 , scdm . mclken_div_psc2 ) ;
out_be16 ( & cdm - > mclken_div_psc3 , scdm . mclken_div_psc3 ) ;
out_be16 ( & cdm - > mclken_div_psc6 , scdm . mclken_div_psc6 ) ;
/* BESTCOMM */
out_be32 ( & bes - > taskBar , sbes . taskBar ) ;
out_be32 ( & bes - > currentPointer , sbes . currentPointer ) ;
out_be32 ( & bes - > endPointer , sbes . endPointer ) ;
out_be32 ( & bes - > variablePointer , sbes . variablePointer ) ;
out_8 ( & bes - > IntVect1 , sbes . IntVect1 ) ;
out_8 ( & bes - > IntVect2 , sbes . IntVect2 ) ;
out_be16 ( & bes - > PtdCntrl , sbes . PtdCntrl ) ;
for ( i = 0 ; i < 32 ; i + + )
out_8 ( & bes - > ipr [ i ] , sbes . ipr [ i ] ) ;
out_be32 ( & bes - > cReqSelect , sbes . cReqSelect ) ;
out_be32 ( & bes - > task_size0 , sbes . task_size0 ) ;
out_be32 ( & bes - > task_size1 , sbes . task_size1 ) ;
out_be32 ( & bes - > MDEDebug , sbes . MDEDebug ) ;
out_be32 ( & bes - > ADSDebug , sbes . ADSDebug ) ;
out_be32 ( & bes - > Value1 , sbes . Value1 ) ;
out_be32 ( & bes - > Value2 , sbes . Value2 ) ;
out_be32 ( & bes - > Control , sbes . Control ) ;
out_be32 ( & bes - > Status , sbes . Status ) ;
out_be32 ( & bes - > PTDDebug , sbes . PTDDebug ) ;
/* restore tasks */
for ( i = 0 ; i < 16 ; i + + )
out_be16 ( & bes - > tcr [ i ] , sbes . tcr [ i ] ) ;
/* enable interrupts */
out_be32 ( & bes - > IntPend , sbes . IntPend ) ;
out_be32 ( & bes - > IntMask , sbes . IntMask ) ;
/* PIC */
out_be32 ( & pic - > per_pri1 , spic . per_pri1 ) ;
out_be32 ( & pic - > per_pri2 , spic . per_pri2 ) ;
out_be32 ( & pic - > per_pri3 , spic . per_pri3 ) ;
out_be32 ( & pic - > main_pri1 , spic . main_pri1 ) ;
out_be32 ( & pic - > main_pri2 , spic . main_pri2 ) ;
out_be32 ( & pic - > enc_status , spic . enc_status ) ;
/* unmask and enable interrupts */
out_be32 ( & pic - > per_mask , spic . per_mask ) ;
out_be32 ( & pic - > main_mask , spic . main_mask ) ;
out_be32 ( & pic - > ctrl , spic . ctrl ) ;
}
static int lite5200_pm_enter ( suspend_state_t state )
{
/* deep sleep? let mpc52xx code handle that */
if ( state = = PM_SUSPEND_STANDBY ) {
return mpc52xx_pm_enter ( state ) ;
}
lite5200_save_regs ( ) ;
/* effectively save FP regs */
enable_kernel_fp ( ) ;
lite5200_low_power ( sram , mbar ) ;
lite5200_restore_regs ( ) ;
/* restart jiffies */
wakeup_decrementer ( ) ;
iounmap ( mbar ) ;
return 0 ;
}
2007-10-18 14:04:41 +04:00
static void lite5200_pm_finish ( void )
2007-07-18 00:32:31 +04:00
{
/* deep sleep? let mpc52xx code handle that */
2007-10-18 14:04:41 +04:00
if ( lite5200_pm_target_state = = PM_SUSPEND_STANDBY )
mpc52xx_pm_finish ( ) ;
2007-07-18 00:32:31 +04:00
}
2008-01-08 02:04:17 +03:00
static void lite5200_pm_end ( void )
{
lite5200_pm_target_state = PM_SUSPEND_ON ;
}
2007-10-18 14:04:41 +04:00
static struct platform_suspend_ops lite5200_pm_ops = {
2007-07-18 00:32:31 +04:00
. valid = lite5200_pm_valid ,
2008-01-08 02:04:17 +03:00
. begin = lite5200_pm_begin ,
2007-07-18 00:32:31 +04:00
. prepare = lite5200_pm_prepare ,
. enter = lite5200_pm_enter ,
. finish = lite5200_pm_finish ,
2008-01-08 02:04:17 +03:00
. end = lite5200_pm_end ,
2007-07-18 00:32:31 +04:00
} ;
int __init lite5200_pm_init ( void )
{
2007-10-18 14:04:41 +04:00
suspend_set_ops ( & lite5200_pm_ops ) ;
2007-07-18 00:32:31 +04:00
return 0 ;
}