2006-04-02 17:46:25 +01:00
/*
* linux / arch / arm / mach - omap2 / pm . c
*
* OMAP2 Power Management Routines
*
* Copyright ( C ) 2006 Nokia Corporation
* Tony Lindgren < tony @ atomide . com >
*
* Copyright ( C ) 2005 Texas Instruments , Inc .
* Richard Woodruff < r - woodruff2 @ ti . com >
*
* Based on pm . c for omap1
*
* 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/pm.h>
# include <linux/sched.h>
# include <linux/proc_fs.h>
# include <linux/pm.h>
# include <linux/interrupt.h>
# include <linux/sysfs.h>
# include <linux/module.h>
2006-06-26 16:16:18 -07:00
# include <linux/delay.h>
2006-04-02 17:46:25 +01:00
# include <asm/io.h>
# include <asm/irq.h>
# include <asm/atomic.h>
# include <asm/mach/time.h>
# include <asm/mach/irq.h>
# include <asm/mach-types.h>
# include <asm/arch/irqs.h>
# include <asm/arch/clock.h>
# include <asm/arch/sram.h>
# include <asm/arch/pm.h>
2006-06-26 16:16:18 -07:00
# include "prcm-regs.h"
2006-04-02 17:46:25 +01:00
static struct clk * vclk ;
static void ( * omap2_sram_idle ) ( void ) ;
static void ( * omap2_sram_suspend ) ( int dllctrl , int cpu_rev ) ;
static void ( * saved_idle ) ( void ) ;
2006-06-26 16:16:18 -07:00
extern void __init pmdomain_init ( void ) ;
extern void pmdomain_set_autoidle ( void ) ;
static unsigned int omap24xx_sleep_save [ OMAP24XX_SLEEP_SAVE_SIZE ] ;
2006-04-02 17:46:25 +01:00
void omap2_pm_idle ( void )
{
local_irq_disable ( ) ;
local_fiq_disable ( ) ;
if ( need_resched ( ) ) {
local_fiq_enable ( ) ;
local_irq_enable ( ) ;
return ;
}
/*
* Since an interrupt may set up a timer , we don ' t want to
* reprogram the hardware timer with interrupts enabled .
* Re - enable interrupts only after returning from idle .
*/
timer_dyn_reprogram ( ) ;
omap2_sram_idle ( ) ;
local_fiq_enable ( ) ;
local_irq_enable ( ) ;
}
static int omap2_pm_prepare ( suspend_state_t state )
{
int error = 0 ;
/* We cannot sleep in idle until we have resumed */
saved_idle = pm_idle ;
pm_idle = NULL ;
switch ( state )
{
case PM_SUSPEND_STANDBY :
case PM_SUSPEND_MEM :
break ;
case PM_SUSPEND_DISK :
return - ENOTSUPP ;
default :
return - EINVAL ;
}
return error ;
}
2006-06-26 16:16:18 -07:00
# define INT0_WAKE_MASK (OMAP_IRQ_BIT(INT_24XX_GPIO_BANK1) | \
OMAP_IRQ_BIT ( INT_24XX_GPIO_BANK2 ) | \
OMAP_IRQ_BIT ( INT_24XX_GPIO_BANK3 ) )
# define INT1_WAKE_MASK (OMAP_IRQ_BIT(INT_24XX_GPIO_BANK4))
# define INT2_WAKE_MASK (OMAP_IRQ_BIT(INT_24XX_UART1_IRQ) | \
OMAP_IRQ_BIT ( INT_24XX_UART2_IRQ ) | \
OMAP_IRQ_BIT ( INT_24XX_UART3_IRQ ) )
# define preg(reg) printk("%s\t(0x%p):\t0x%08x\n", #reg, ®, reg);
static void omap2_pm_debug ( char * desc )
{
printk ( " %s: \n " , desc ) ;
preg ( CM_CLKSTCTRL_MPU ) ;
preg ( CM_CLKSTCTRL_CORE ) ;
preg ( CM_CLKSTCTRL_GFX ) ;
preg ( CM_CLKSTCTRL_DSP ) ;
preg ( CM_CLKSTCTRL_MDM ) ;
preg ( PM_PWSTCTRL_MPU ) ;
preg ( PM_PWSTCTRL_CORE ) ;
preg ( PM_PWSTCTRL_GFX ) ;
preg ( PM_PWSTCTRL_DSP ) ;
preg ( PM_PWSTCTRL_MDM ) ;
preg ( PM_PWSTST_MPU ) ;
preg ( PM_PWSTST_CORE ) ;
preg ( PM_PWSTST_GFX ) ;
preg ( PM_PWSTST_DSP ) ;
preg ( PM_PWSTST_MDM ) ;
preg ( CM_AUTOIDLE1_CORE ) ;
preg ( CM_AUTOIDLE2_CORE ) ;
preg ( CM_AUTOIDLE3_CORE ) ;
preg ( CM_AUTOIDLE4_CORE ) ;
preg ( CM_AUTOIDLE_WKUP ) ;
preg ( CM_AUTOIDLE_PLL ) ;
preg ( CM_AUTOIDLE_DSP ) ;
preg ( CM_AUTOIDLE_MDM ) ;
preg ( CM_ICLKEN1_CORE ) ;
preg ( CM_ICLKEN2_CORE ) ;
preg ( CM_ICLKEN3_CORE ) ;
preg ( CM_ICLKEN4_CORE ) ;
preg ( CM_ICLKEN_GFX ) ;
preg ( CM_ICLKEN_WKUP ) ;
preg ( CM_ICLKEN_DSP ) ;
preg ( CM_ICLKEN_MDM ) ;
preg ( CM_IDLEST1_CORE ) ;
preg ( CM_IDLEST2_CORE ) ;
preg ( CM_IDLEST3_CORE ) ;
preg ( CM_IDLEST4_CORE ) ;
preg ( CM_IDLEST_GFX ) ;
preg ( CM_IDLEST_WKUP ) ;
preg ( CM_IDLEST_CKGEN ) ;
preg ( CM_IDLEST_DSP ) ;
preg ( CM_IDLEST_MDM ) ;
preg ( RM_RSTST_MPU ) ;
preg ( RM_RSTST_GFX ) ;
preg ( RM_RSTST_WKUP ) ;
preg ( RM_RSTST_DSP ) ;
preg ( RM_RSTST_MDM ) ;
preg ( PM_WKDEP_MPU ) ;
preg ( PM_WKDEP_CORE ) ;
preg ( PM_WKDEP_GFX ) ;
preg ( PM_WKDEP_DSP ) ;
preg ( PM_WKDEP_MDM ) ;
preg ( CM_FCLKEN_WKUP ) ;
preg ( CM_ICLKEN_WKUP ) ;
preg ( CM_IDLEST_WKUP ) ;
preg ( CM_AUTOIDLE_WKUP ) ;
preg ( CM_CLKSEL_WKUP ) ;
preg ( PM_WKEN_WKUP ) ;
preg ( PM_WKST_WKUP ) ;
}
static inline void omap2_pm_save_registers ( void )
{
/* Save interrupt registers */
OMAP24XX_SAVE ( INTC_MIR0 ) ;
OMAP24XX_SAVE ( INTC_MIR1 ) ;
OMAP24XX_SAVE ( INTC_MIR2 ) ;
/* Save power control registers */
OMAP24XX_SAVE ( CM_CLKSTCTRL_MPU ) ;
OMAP24XX_SAVE ( CM_CLKSTCTRL_CORE ) ;
OMAP24XX_SAVE ( CM_CLKSTCTRL_GFX ) ;
OMAP24XX_SAVE ( CM_CLKSTCTRL_DSP ) ;
OMAP24XX_SAVE ( CM_CLKSTCTRL_MDM ) ;
/* Save power state registers */
OMAP24XX_SAVE ( PM_PWSTCTRL_MPU ) ;
OMAP24XX_SAVE ( PM_PWSTCTRL_CORE ) ;
OMAP24XX_SAVE ( PM_PWSTCTRL_GFX ) ;
OMAP24XX_SAVE ( PM_PWSTCTRL_DSP ) ;
OMAP24XX_SAVE ( PM_PWSTCTRL_MDM ) ;
/* Save autoidle registers */
OMAP24XX_SAVE ( CM_AUTOIDLE1_CORE ) ;
OMAP24XX_SAVE ( CM_AUTOIDLE2_CORE ) ;
OMAP24XX_SAVE ( CM_AUTOIDLE3_CORE ) ;
OMAP24XX_SAVE ( CM_AUTOIDLE4_CORE ) ;
OMAP24XX_SAVE ( CM_AUTOIDLE_WKUP ) ;
OMAP24XX_SAVE ( CM_AUTOIDLE_PLL ) ;
OMAP24XX_SAVE ( CM_AUTOIDLE_DSP ) ;
OMAP24XX_SAVE ( CM_AUTOIDLE_MDM ) ;
/* Save idle state registers */
OMAP24XX_SAVE ( CM_IDLEST1_CORE ) ;
OMAP24XX_SAVE ( CM_IDLEST2_CORE ) ;
OMAP24XX_SAVE ( CM_IDLEST3_CORE ) ;
OMAP24XX_SAVE ( CM_IDLEST4_CORE ) ;
OMAP24XX_SAVE ( CM_IDLEST_GFX ) ;
OMAP24XX_SAVE ( CM_IDLEST_WKUP ) ;
OMAP24XX_SAVE ( CM_IDLEST_CKGEN ) ;
OMAP24XX_SAVE ( CM_IDLEST_DSP ) ;
OMAP24XX_SAVE ( CM_IDLEST_MDM ) ;
/* Save clock registers */
OMAP24XX_SAVE ( CM_FCLKEN1_CORE ) ;
OMAP24XX_SAVE ( CM_FCLKEN2_CORE ) ;
OMAP24XX_SAVE ( CM_ICLKEN1_CORE ) ;
OMAP24XX_SAVE ( CM_ICLKEN2_CORE ) ;
OMAP24XX_SAVE ( CM_ICLKEN3_CORE ) ;
OMAP24XX_SAVE ( CM_ICLKEN4_CORE ) ;
}
static inline void omap2_pm_restore_registers ( void )
{
/* Restore clock state registers */
OMAP24XX_RESTORE ( CM_CLKSTCTRL_MPU ) ;
OMAP24XX_RESTORE ( CM_CLKSTCTRL_CORE ) ;
OMAP24XX_RESTORE ( CM_CLKSTCTRL_GFX ) ;
OMAP24XX_RESTORE ( CM_CLKSTCTRL_DSP ) ;
OMAP24XX_RESTORE ( CM_CLKSTCTRL_MDM ) ;
/* Restore power state registers */
OMAP24XX_RESTORE ( PM_PWSTCTRL_MPU ) ;
OMAP24XX_RESTORE ( PM_PWSTCTRL_CORE ) ;
OMAP24XX_RESTORE ( PM_PWSTCTRL_GFX ) ;
OMAP24XX_RESTORE ( PM_PWSTCTRL_DSP ) ;
OMAP24XX_RESTORE ( PM_PWSTCTRL_MDM ) ;
/* Restore idle state registers */
OMAP24XX_RESTORE ( CM_IDLEST1_CORE ) ;
OMAP24XX_RESTORE ( CM_IDLEST2_CORE ) ;
OMAP24XX_RESTORE ( CM_IDLEST3_CORE ) ;
OMAP24XX_RESTORE ( CM_IDLEST4_CORE ) ;
OMAP24XX_RESTORE ( CM_IDLEST_GFX ) ;
OMAP24XX_RESTORE ( CM_IDLEST_WKUP ) ;
OMAP24XX_RESTORE ( CM_IDLEST_CKGEN ) ;
OMAP24XX_RESTORE ( CM_IDLEST_DSP ) ;
OMAP24XX_RESTORE ( CM_IDLEST_MDM ) ;
/* Restore autoidle registers */
OMAP24XX_RESTORE ( CM_AUTOIDLE1_CORE ) ;
OMAP24XX_RESTORE ( CM_AUTOIDLE2_CORE ) ;
OMAP24XX_RESTORE ( CM_AUTOIDLE3_CORE ) ;
OMAP24XX_RESTORE ( CM_AUTOIDLE4_CORE ) ;
OMAP24XX_RESTORE ( CM_AUTOIDLE_WKUP ) ;
OMAP24XX_RESTORE ( CM_AUTOIDLE_PLL ) ;
OMAP24XX_RESTORE ( CM_AUTOIDLE_DSP ) ;
OMAP24XX_RESTORE ( CM_AUTOIDLE_MDM ) ;
/* Restore clock registers */
OMAP24XX_RESTORE ( CM_FCLKEN1_CORE ) ;
OMAP24XX_RESTORE ( CM_FCLKEN2_CORE ) ;
OMAP24XX_RESTORE ( CM_ICLKEN1_CORE ) ;
OMAP24XX_RESTORE ( CM_ICLKEN2_CORE ) ;
OMAP24XX_RESTORE ( CM_ICLKEN3_CORE ) ;
OMAP24XX_RESTORE ( CM_ICLKEN4_CORE ) ;
/* REVISIT: Clear interrupts here */
/* Restore interrupt registers */
OMAP24XX_RESTORE ( INTC_MIR0 ) ;
OMAP24XX_RESTORE ( INTC_MIR1 ) ;
OMAP24XX_RESTORE ( INTC_MIR2 ) ;
}
static int omap2_pm_suspend ( void )
{
int processor_type = 0 ;
/* REVISIT: 0x21 or 0x26? */
if ( cpu_is_omap2420 ( ) )
processor_type = 0x21 ;
if ( ! processor_type )
return - ENOTSUPP ;
local_irq_disable ( ) ;
local_fiq_disable ( ) ;
omap2_pm_save_registers ( ) ;
/* Disable interrupts except for the wake events */
INTC_MIR_SET0 = 0xffffffff & ~ INT0_WAKE_MASK ;
INTC_MIR_SET1 = 0xffffffff & ~ INT1_WAKE_MASK ;
INTC_MIR_SET2 = 0xffffffff & ~ INT2_WAKE_MASK ;
pmdomain_set_autoidle ( ) ;
/* Clear old wake-up events */
PM_WKST1_CORE = 0 ;
PM_WKST2_CORE = 0 ;
PM_WKST_WKUP = 0 ;
/* Enable wake-up events */
PM_WKEN1_CORE = ( 1 < < 22 ) | ( 1 < < 21 ) ; /* UART1 & 2 */
PM_WKEN2_CORE = ( 1 < < 2 ) ; /* UART3 */
PM_WKEN_WKUP = ( 1 < < 2 ) | ( 1 < < 0 ) ; /* GPIO & GPT1 */
/* Disable clocks except for CM_ICLKEN2_CORE. It gets disabled
* in the SRAM suspend code */
CM_FCLKEN1_CORE = 0 ;
CM_FCLKEN2_CORE = 0 ;
CM_ICLKEN1_CORE = 0 ;
CM_ICLKEN3_CORE = 0 ;
CM_ICLKEN4_CORE = 0 ;
omap2_pm_debug ( " Status before suspend " ) ;
/* Must wait for serial buffers to clear */
mdelay ( 200 ) ;
/* Jump to SRAM suspend code
* REVISIT : When is this SDRC_DLLB_CTRL ?
*/
omap2_sram_suspend ( SDRC_DLLA_CTRL , processor_type ) ;
/* Back from sleep */
omap2_pm_restore_registers ( ) ;
local_fiq_enable ( ) ;
local_irq_enable ( ) ;
return 0 ;
}
2006-04-02 17:46:25 +01:00
static int omap2_pm_enter ( suspend_state_t state )
{
2006-06-26 16:16:18 -07:00
int ret = 0 ;
2006-04-02 17:46:25 +01:00
switch ( state )
{
case PM_SUSPEND_STANDBY :
case PM_SUSPEND_MEM :
2006-06-26 16:16:18 -07:00
ret = omap2_pm_suspend ( ) ;
2006-04-02 17:46:25 +01:00
break ;
case PM_SUSPEND_DISK :
2006-06-26 16:16:18 -07:00
ret = - ENOTSUPP ;
break ;
2006-04-02 17:46:25 +01:00
default :
2006-06-26 16:16:18 -07:00
ret = - EINVAL ;
2006-04-02 17:46:25 +01:00
}
2006-06-26 16:16:18 -07:00
return ret ;
2006-04-02 17:46:25 +01:00
}
static int omap2_pm_finish ( suspend_state_t state )
{
pm_idle = saved_idle ;
return 0 ;
}
static struct pm_ops omap_pm_ops = {
. pm_disk_mode = 0 ,
. prepare = omap2_pm_prepare ,
. enter = omap2_pm_enter ,
. finish = omap2_pm_finish ,
} ;
int __init omap2_pm_init ( void )
{
printk ( " Power Management for TI OMAP. \n " ) ;
vclk = clk_get ( NULL , " virt_prcm_set " ) ;
if ( IS_ERR ( vclk ) ) {
printk ( KERN_ERR " Could not get PM vclk \n " ) ;
return - ENODEV ;
}
/*
* We copy the assembler sleep / wakeup routines to SRAM .
* These routines need to be in SRAM as that ' s the only
* memory the MPU can see when it wakes up .
*/
omap2_sram_idle = omap_sram_push ( omap24xx_idle_loop_suspend ,
omap24xx_idle_loop_suspend_sz ) ;
omap2_sram_suspend = omap_sram_push ( omap24xx_cpu_suspend ,
omap24xx_cpu_suspend_sz ) ;
pm_set_ops ( & omap_pm_ops ) ;
pm_idle = omap2_pm_idle ;
2006-06-26 16:16:18 -07:00
pmdomain_init ( ) ;
2006-04-02 17:46:25 +01:00
return 0 ;
}
__initcall ( omap2_pm_init ) ;