2012-05-07 11:24:01 +08:00
/*
* PXA910 Power Management Routines
*
* This software program is licensed subject to the GNU General Public License
* ( GPL ) . Version 2 , June 1991 , available at http : //www.fsf.org/copyleft/gpl.html
*
* ( C ) Copyright 2009 Marvell International Ltd .
* All Rights Reserved
*/
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/err.h>
# include <linux/time.h>
# include <linux/delay.h>
# include <linux/suspend.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/irq.h>
# include <asm/mach-types.h>
# include <mach/hardware.h>
# include <mach/cputype.h>
# include <mach/addr-map.h>
# include <mach/pm-pxa910.h>
# include <mach/regs-icu.h>
# include <mach/irqs.h>
int pxa910_set_wake ( struct irq_data * data , unsigned int on )
{
uint32_t awucrm = 0 , apcr = 0 ;
2014-02-23 21:40:13 +00:00
int irq = data - > irq ;
2012-05-07 11:24:01 +08:00
/* setting wakeup sources */
switch ( irq ) {
/* wakeup line 2 */
case IRQ_PXA910_AP_GPIO :
awucrm = MPMU_AWUCRM_WAKEUP ( 2 ) ;
apcr | = MPMU_APCR_SLPWP2 ;
break ;
/* wakeup line 3 */
case IRQ_PXA910_KEYPAD :
awucrm = MPMU_AWUCRM_WAKEUP ( 3 ) | MPMU_AWUCRM_KEYPRESS ;
apcr | = MPMU_APCR_SLPWP3 ;
break ;
case IRQ_PXA910_ROTARY :
awucrm = MPMU_AWUCRM_WAKEUP ( 3 ) | MPMU_AWUCRM_NEWROTARY ;
apcr | = MPMU_APCR_SLPWP3 ;
break ;
case IRQ_PXA910_TRACKBALL :
awucrm = MPMU_AWUCRM_WAKEUP ( 3 ) | MPMU_AWUCRM_TRACKBALL ;
apcr | = MPMU_APCR_SLPWP3 ;
break ;
/* wakeup line 4 */
case IRQ_PXA910_AP1_TIMER1 :
awucrm = MPMU_AWUCRM_WAKEUP ( 4 ) | MPMU_AWUCRM_AP1_TIMER_1 ;
apcr | = MPMU_APCR_SLPWP4 ;
break ;
case IRQ_PXA910_AP1_TIMER2 :
awucrm = MPMU_AWUCRM_WAKEUP ( 4 ) | MPMU_AWUCRM_AP1_TIMER_2 ;
apcr | = MPMU_APCR_SLPWP4 ;
break ;
case IRQ_PXA910_AP1_TIMER3 :
awucrm = MPMU_AWUCRM_WAKEUP ( 4 ) | MPMU_AWUCRM_AP1_TIMER_3 ;
apcr | = MPMU_APCR_SLPWP4 ;
break ;
case IRQ_PXA910_AP2_TIMER1 :
awucrm = MPMU_AWUCRM_WAKEUP ( 4 ) | MPMU_AWUCRM_AP2_TIMER_1 ;
apcr | = MPMU_APCR_SLPWP4 ;
break ;
case IRQ_PXA910_AP2_TIMER2 :
awucrm = MPMU_AWUCRM_WAKEUP ( 4 ) | MPMU_AWUCRM_AP2_TIMER_2 ;
apcr | = MPMU_APCR_SLPWP4 ;
break ;
case IRQ_PXA910_AP2_TIMER3 :
awucrm = MPMU_AWUCRM_WAKEUP ( 4 ) | MPMU_AWUCRM_AP2_TIMER_3 ;
apcr | = MPMU_APCR_SLPWP4 ;
break ;
case IRQ_PXA910_RTC_ALARM :
awucrm = MPMU_AWUCRM_WAKEUP ( 4 ) | MPMU_AWUCRM_RTC_ALARM ;
apcr | = MPMU_APCR_SLPWP4 ;
break ;
/* wakeup line 5 */
case IRQ_PXA910_USB1 :
case IRQ_PXA910_USB2 :
awucrm = MPMU_AWUCRM_WAKEUP ( 5 ) ;
apcr | = MPMU_APCR_SLPWP5 ;
break ;
/* wakeup line 6 */
case IRQ_PXA910_MMC :
awucrm = MPMU_AWUCRM_WAKEUP ( 6 )
| MPMU_AWUCRM_SDH1
| MPMU_AWUCRM_SDH2 ;
apcr | = MPMU_APCR_SLPWP6 ;
break ;
/* wakeup line 7 */
case IRQ_PXA910_PMIC_INT :
awucrm = MPMU_AWUCRM_WAKEUP ( 7 ) ;
apcr | = MPMU_APCR_SLPWP7 ;
break ;
default :
if ( irq > = IRQ_GPIO_START & & irq < IRQ_BOARD_START ) {
awucrm = MPMU_AWUCRM_WAKEUP ( 2 ) ;
apcr | = MPMU_APCR_SLPWP2 ;
2014-02-23 21:40:13 +00:00
} else {
/* FIXME: This should return a proper error code ! */
2012-05-07 11:24:01 +08:00
printk ( KERN_ERR " Error: no defined wake up source irq: %d \n " ,
irq ) ;
2014-02-23 21:40:13 +00:00
}
2012-05-07 11:24:01 +08:00
}
if ( on ) {
if ( awucrm ) {
awucrm | = __raw_readl ( MPMU_AWUCRM ) ;
__raw_writel ( awucrm , MPMU_AWUCRM ) ;
}
if ( apcr ) {
apcr = ~ apcr & __raw_readl ( MPMU_APCR ) ;
__raw_writel ( apcr , MPMU_APCR ) ;
}
} else {
if ( awucrm ) {
awucrm = ~ awucrm & __raw_readl ( MPMU_AWUCRM ) ;
__raw_writel ( awucrm , MPMU_AWUCRM ) ;
}
if ( apcr ) {
apcr | = __raw_readl ( MPMU_APCR ) ;
__raw_writel ( apcr , MPMU_APCR ) ;
}
}
return 0 ;
}
void pxa910_pm_enter_lowpower_mode ( int state )
{
uint32_t idle_cfg , apcr ;
idle_cfg = __raw_readl ( APMU_MOH_IDLE_CFG ) ;
apcr = __raw_readl ( MPMU_APCR ) ;
apcr & = ~ ( MPMU_APCR_DDRCORSD | MPMU_APCR_APBSD | MPMU_APCR_AXISD
| MPMU_APCR_VCTCXOSD | MPMU_APCR_STBYEN ) ;
idle_cfg & = ~ ( APMU_MOH_IDLE_CFG_MOH_IDLE
| APMU_MOH_IDLE_CFG_MOH_PWRDWN ) ;
switch ( state ) {
case POWER_MODE_UDR :
/* only shutdown APB in UDR */
apcr | = MPMU_APCR_STBYEN | MPMU_APCR_APBSD ;
/* fall through */
case POWER_MODE_SYS_SLEEP :
apcr | = MPMU_APCR_SLPEN ; /* set the SLPEN bit */
apcr | = MPMU_APCR_VCTCXOSD ; /* set VCTCXOSD */
/* fall through */
case POWER_MODE_APPS_SLEEP :
apcr | = MPMU_APCR_DDRCORSD ; /* set DDRCORSD */
/* fall through */
case POWER_MODE_APPS_IDLE :
apcr | = MPMU_APCR_AXISD ; /* set AXISDD bit */
/* fall through */
case POWER_MODE_CORE_EXTIDLE :
idle_cfg | = APMU_MOH_IDLE_CFG_MOH_IDLE ;
idle_cfg | = APMU_MOH_IDLE_CFG_MOH_PWRDWN ;
idle_cfg | = APMU_MOH_IDLE_CFG_MOH_PWR_SW ( 3 )
| APMU_MOH_IDLE_CFG_MOH_L2_PWR_SW ( 3 ) ;
/* fall through */
case POWER_MODE_CORE_INTIDLE :
break ;
}
/* program the memory controller hardware sleep type and auto wakeup */
idle_cfg | = APMU_MOH_IDLE_CFG_MOH_DIS_MC_SW_REQ ;
idle_cfg | = APMU_MOH_IDLE_CFG_MOH_MC_WAKE_EN ;
__raw_writel ( 0x0 , APMU_MC_HW_SLP_TYPE ) ; /* auto refresh */
/* set DSPSD, DTCMSD, BBSD, MSASLPEN */
apcr | = MPMU_APCR_DSPSD | MPMU_APCR_DTCMSD | MPMU_APCR_BBSD
| MPMU_APCR_MSASLPEN ;
/*always set SLEPEN bit mainly for MSA*/
apcr | = MPMU_APCR_SLPEN ;
/* finally write the registers back */
__raw_writel ( idle_cfg , APMU_MOH_IDLE_CFG ) ;
__raw_writel ( apcr , MPMU_APCR ) ;
}
static int pxa910_pm_enter ( suspend_state_t state )
{
unsigned int idle_cfg , reg = 0 ;
/*pmic thread not completed,exit;otherwise system can't be waked up*/
reg = __raw_readl ( ICU_INT_CONF ( IRQ_PXA910_PMIC_INT ) ) ;
if ( ( reg & 0x3 ) = = 0 )
return - EAGAIN ;
idle_cfg = __raw_readl ( APMU_MOH_IDLE_CFG ) ;
idle_cfg | = APMU_MOH_IDLE_CFG_MOH_PWRDWN
| APMU_MOH_IDLE_CFG_MOH_SRAM_PWRDWN ;
__raw_writel ( idle_cfg , APMU_MOH_IDLE_CFG ) ;
/* disable L2 */
outer_disable ( ) ;
/* wait for l2 idle */
while ( ! ( readl ( CIU_REG ( 0x8 ) ) & ( 1 < < 16 ) ) )
udelay ( 1 ) ;
cpu_do_idle ( ) ;
/* enable L2 */
outer_resume ( ) ;
/* wait for l2 idle */
while ( ! ( readl ( CIU_REG ( 0x8 ) ) & ( 1 < < 16 ) ) )
udelay ( 1 ) ;
idle_cfg = __raw_readl ( APMU_MOH_IDLE_CFG ) ;
idle_cfg & = ~ ( APMU_MOH_IDLE_CFG_MOH_PWRDWN
| APMU_MOH_IDLE_CFG_MOH_SRAM_PWRDWN ) ;
__raw_writel ( idle_cfg , APMU_MOH_IDLE_CFG ) ;
return 0 ;
}
/*
* Called after processes are frozen , but before we shut down devices .
*/
static int pxa910_pm_prepare ( void )
{
pxa910_pm_enter_lowpower_mode ( POWER_MODE_UDR ) ;
return 0 ;
}
/*
* Called after devices are re - setup , but before processes are thawed .
*/
static void pxa910_pm_finish ( void )
{
pxa910_pm_enter_lowpower_mode ( POWER_MODE_CORE_INTIDLE ) ;
}
static int pxa910_pm_valid ( suspend_state_t state )
{
return ( ( state = = PM_SUSPEND_STANDBY ) | | ( state = = PM_SUSPEND_MEM ) ) ;
}
static const struct platform_suspend_ops pxa910_pm_ops = {
. valid = pxa910_pm_valid ,
. prepare = pxa910_pm_prepare ,
. enter = pxa910_pm_enter ,
. finish = pxa910_pm_finish ,
} ;
static int __init pxa910_pm_init ( void )
{
uint32_t awucrm = 0 ;
if ( ! cpu_is_pxa910 ( ) )
return - EIO ;
suspend_set_ops ( & pxa910_pm_ops ) ;
/* Set the following bits for MMP3 playback with VCTXO on */
__raw_writel ( __raw_readl ( APMU_SQU_CLK_GATE_CTRL ) | ( 1 < < 30 ) ,
APMU_SQU_CLK_GATE_CTRL ) ;
__raw_writel ( __raw_readl ( MPMU_FCCR ) | ( 1 < < 28 ) , MPMU_FCCR ) ;
awucrm | = MPMU_AWUCRM_AP_ASYNC_INT | MPMU_AWUCRM_AP_FULL_IDLE ;
__raw_writel ( awucrm , MPMU_AWUCRM ) ;
return 0 ;
}
late_initcall ( pxa910_pm_init ) ;