2012-05-07 07:22:23 +04:00
/*
* MMP2 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 2012 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/irq.h>
# include <linux/io.h>
# include <linux/interrupt.h>
# include <asm/mach-types.h>
2014-04-15 22:38:32 +04:00
# include "cputype.h"
# include "addr-map.h"
# include "pm-mmp2.h"
# include "regs-icu.h"
# include "irqs.h"
2012-05-07 07:22:23 +04:00
int mmp2_set_wake ( struct irq_data * d , unsigned int on )
{
unsigned long data = 0 ;
2014-02-24 01:40:13 +04:00
int irq = d - > irq ;
2012-05-07 07:22:23 +04:00
/* enable wakeup sources */
switch ( irq ) {
case IRQ_MMP2_RTC :
case IRQ_MMP2_RTC_ALARM :
data = MPMU_WUCRM_PJ_WAKEUP ( 4 ) | MPMU_WUCRM_PJ_RTC_ALARM ;
break ;
case IRQ_MMP2_PMIC :
data = MPMU_WUCRM_PJ_WAKEUP ( 7 ) ;
break ;
case IRQ_MMP2_MMC2 :
/* mmc use WAKEUP2, same as GPIO wakeup source */
data = MPMU_WUCRM_PJ_WAKEUP ( 2 ) ;
break ;
}
if ( on ) {
if ( data ) {
data | = __raw_readl ( MPMU_WUCRM_PJ ) ;
__raw_writel ( data , MPMU_WUCRM_PJ ) ;
}
} else {
if ( data ) {
data = ~ data & __raw_readl ( MPMU_WUCRM_PJ ) ;
__raw_writel ( data , MPMU_WUCRM_PJ ) ;
}
}
return 0 ;
}
static void pm_scu_clk_disable ( void )
{
unsigned int val ;
/* close AXI fabric clock gate */
__raw_writel ( 0x0 , CIU_REG ( 0x64 ) ) ;
__raw_writel ( 0x0 , CIU_REG ( 0x68 ) ) ;
/* close MCB master clock gate */
val = __raw_readl ( CIU_REG ( 0x1c ) ) ;
val | = 0xf0 ;
__raw_writel ( val , CIU_REG ( 0x1c ) ) ;
return ;
}
static void pm_scu_clk_enable ( void )
{
unsigned int val ;
/* open AXI fabric clock gate */
__raw_writel ( 0x03003003 , CIU_REG ( 0x64 ) ) ;
__raw_writel ( 0x00303030 , CIU_REG ( 0x68 ) ) ;
/* open MCB master clock gate */
val = __raw_readl ( CIU_REG ( 0x1c ) ) ;
val & = ~ ( 0xf0 ) ;
__raw_writel ( val , CIU_REG ( 0x1c ) ) ;
return ;
}
static void pm_mpmu_clk_disable ( void )
{
/*
* disable clocks in MPMU_CGR_PJ register
* except clock for APMU_PLL1 , APMU_PLL1_2 and AP_26M
*/
__raw_writel ( 0x0000a010 , MPMU_CGR_PJ ) ;
}
static void pm_mpmu_clk_enable ( void )
{
unsigned int val ;
__raw_writel ( 0xdffefffe , MPMU_CGR_PJ ) ;
val = __raw_readl ( MPMU_PLL2_CTRL1 ) ;
val | = ( 1 < < 29 ) ;
__raw_writel ( val , MPMU_PLL2_CTRL1 ) ;
return ;
}
void mmp2_pm_enter_lowpower_mode ( int state )
{
uint32_t idle_cfg , apcr ;
idle_cfg = __raw_readl ( APMU_PJ_IDLE_CFG ) ;
apcr = __raw_readl ( MPMU_PCR_PJ ) ;
apcr & = ~ ( MPMU_PCR_PJ_SLPEN | MPMU_PCR_PJ_DDRCORSD | MPMU_PCR_PJ_APBSD
| MPMU_PCR_PJ_AXISD | MPMU_PCR_PJ_VCTCXOSD | ( 1 < < 13 ) ) ;
idle_cfg & = ~ APMU_PJ_IDLE_CFG_PJ_IDLE ;
switch ( state ) {
case POWER_MODE_SYS_SLEEP :
apcr | = MPMU_PCR_PJ_SLPEN ; /* set the SLPEN bit */
apcr | = MPMU_PCR_PJ_VCTCXOSD ; /* set VCTCXOSD */
/* fall through */
case POWER_MODE_CHIP_SLEEP :
apcr | = MPMU_PCR_PJ_SLPEN ;
/* fall through */
case POWER_MODE_APPS_SLEEP :
apcr | = MPMU_PCR_PJ_APBSD ; /* set APBSD */
/* fall through */
case POWER_MODE_APPS_IDLE :
apcr | = MPMU_PCR_PJ_AXISD ; /* set AXISDD bit */
apcr | = MPMU_PCR_PJ_DDRCORSD ; /* set DDRCORSD bit */
idle_cfg | = APMU_PJ_IDLE_CFG_PJ_PWRDWN ; /* PJ power down */
apcr | = MPMU_PCR_PJ_SPSD ;
/* fall through */
case POWER_MODE_CORE_EXTIDLE :
idle_cfg | = APMU_PJ_IDLE_CFG_PJ_IDLE ; /* set the IDLE bit */
idle_cfg & = ~ APMU_PJ_IDLE_CFG_ISO_MODE_CNTRL_MASK ;
idle_cfg | = APMU_PJ_IDLE_CFG_PWR_SW ( 3 )
| APMU_PJ_IDLE_CFG_L2_PWR_SW ;
break ;
case POWER_MODE_CORE_INTIDLE :
apcr & = ~ MPMU_PCR_PJ_SPSD ;
break ;
}
/* set reserve bits */
apcr | = ( 1 < < 30 ) | ( 1 < < 25 ) ;
/* finally write the registers back */
__raw_writel ( idle_cfg , APMU_PJ_IDLE_CFG ) ;
__raw_writel ( apcr , MPMU_PCR_PJ ) ; /* 0xfe086000 */
}
static int mmp2_pm_enter ( suspend_state_t state )
{
int temp ;
temp = __raw_readl ( MMP2_ICU_INT4_MASK ) ;
if ( temp & ( 1 < < 1 ) ) {
printk ( KERN_ERR " %s: PMIC interrupt is handling \n " , __func__ ) ;
return - EAGAIN ;
}
temp = __raw_readl ( APMU_SRAM_PWR_DWN ) ;
temp | = ( ( 1 < < 19 ) | ( 1 < < 18 ) ) ;
__raw_writel ( temp , APMU_SRAM_PWR_DWN ) ;
pm_mpmu_clk_disable ( ) ;
pm_scu_clk_disable ( ) ;
printk ( KERN_INFO " %s: before suspend \n " , __func__ ) ;
cpu_do_idle ( ) ;
printk ( KERN_INFO " %s: after suspend \n " , __func__ ) ;
pm_mpmu_clk_enable ( ) ; /* enable clocks in MPMU */
pm_scu_clk_enable ( ) ; /* enable clocks in SCU */
return 0 ;
}
/*
* Called after processes are frozen , but before we shut down devices .
*/
static int mmp2_pm_prepare ( void )
{
mmp2_pm_enter_lowpower_mode ( POWER_MODE_SYS_SLEEP ) ;
return 0 ;
}
/*
* Called after devices are re - setup , but before processes are thawed .
*/
static void mmp2_pm_finish ( void )
{
mmp2_pm_enter_lowpower_mode ( POWER_MODE_CORE_INTIDLE ) ;
}
static int mmp2_pm_valid ( suspend_state_t state )
{
return ( ( state = = PM_SUSPEND_STANDBY ) | | ( state = = PM_SUSPEND_MEM ) ) ;
}
/*
* Set to PM_DISK_FIRMWARE so we can quickly veto suspend - to - disk .
*/
static const struct platform_suspend_ops mmp2_pm_ops = {
. valid = mmp2_pm_valid ,
. prepare = mmp2_pm_prepare ,
. enter = mmp2_pm_enter ,
. finish = mmp2_pm_finish ,
} ;
static int __init mmp2_pm_init ( void )
{
uint32_t apcr ;
if ( ! cpu_is_mmp2 ( ) )
return - EIO ;
suspend_set_ops ( & mmp2_pm_ops ) ;
/*
* Set bit 0 , Slow clock Select 32 K clock input instead of VCXO
* VCXO is chosen by default , which would be disabled in suspend
*/
__raw_writel ( 0x5 , MPMU_SCCR ) ;
/*
* Clear bit 23 of CIU_CPU_CONF
* direct PJ4 to DDR access through Memory Controller slow queue
* fast queue has issue and cause lcd will flick
*/
__raw_writel ( __raw_readl ( CIU_REG ( 0x8 ) ) & ~ ( 0x1 < < 23 ) , CIU_REG ( 0x8 ) ) ;
/* Clear default low power control bit */
apcr = __raw_readl ( MPMU_PCR_PJ ) ;
apcr & = ~ ( MPMU_PCR_PJ_SLPEN | MPMU_PCR_PJ_DDRCORSD
| MPMU_PCR_PJ_APBSD | MPMU_PCR_PJ_AXISD | 1 < < 13 ) ;
__raw_writel ( apcr , MPMU_PCR_PJ ) ;
return 0 ;
}
late_initcall ( mmp2_pm_init ) ;