2011-09-06 15:08:40 +08:00
/*
2013-03-20 19:39:42 -04:00
* Copyright 2011 - 2013 Freescale Semiconductor , Inc .
2011-09-06 15:08:40 +08:00
* Copyright 2011 Linaro Ltd .
*
* The code contained herein is licensed under the GNU General Public
* License . You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations :
*
* http : //www.opensource.org/licenses/gpl-license.html
* http : //www.gnu.org/copyleft/gpl.html
*/
2013-09-25 23:09:36 +08:00
# include <linux/delay.h>
2011-09-06 15:08:40 +08:00
# include <linux/init.h>
# include <linux/io.h>
2013-10-16 19:52:00 +08:00
# include <linux/irq.h>
# include <linux/mfd/syscon.h>
# include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
2011-09-06 15:08:40 +08:00
# include <linux/of.h>
2013-09-25 23:09:36 +08:00
# include <linux/of_address.h>
2013-10-16 19:52:00 +08:00
# include <linux/regmap.h>
2011-09-06 15:08:40 +08:00
# include <linux/suspend.h>
# include <asm/cacheflush.h>
# include <asm/proc-fns.h>
# include <asm/suspend.h>
# include <asm/hardware/cache-l2x0.h>
2012-09-13 21:01:00 +08:00
# include "common.h"
2012-09-14 14:14:45 +08:00
# include "hardware.h"
2012-09-13 21:01:00 +08:00
2013-09-25 23:09:36 +08:00
# define CCR 0x0
# define BM_CCR_WB_COUNT (0x7 << 16)
# define BM_CCR_RBC_BYPASS_COUNT (0x3f << 21)
# define BM_CCR_RBC_EN (0x1 << 27)
# define CLPCR 0x54
# define BP_CLPCR_LPM 0
# define BM_CLPCR_LPM (0x3 << 0)
# define BM_CLPCR_BYPASS_PMIC_READY (0x1 << 2)
# define BM_CLPCR_ARM_CLK_DIS_ON_LPM (0x1 << 5)
# define BM_CLPCR_SBYOS (0x1 << 6)
# define BM_CLPCR_DIS_REF_OSC (0x1 << 7)
# define BM_CLPCR_VSTBY (0x1 << 8)
# define BP_CLPCR_STBY_COUNT 9
# define BM_CLPCR_STBY_COUNT (0x3 << 9)
# define BM_CLPCR_COSC_PWRDOWN (0x1 << 11)
# define BM_CLPCR_WB_PER_AT_LPM (0x1 << 16)
# define BM_CLPCR_WB_CORE_AT_LPM (0x1 << 17)
# define BM_CLPCR_BYP_MMDC_CH0_LPM_HS (0x1 << 19)
# define BM_CLPCR_BYP_MMDC_CH1_LPM_HS (0x1 << 21)
# define BM_CLPCR_MASK_CORE0_WFI (0x1 << 22)
# define BM_CLPCR_MASK_CORE1_WFI (0x1 << 23)
# define BM_CLPCR_MASK_CORE2_WFI (0x1 << 24)
# define BM_CLPCR_MASK_CORE3_WFI (0x1 << 25)
# define BM_CLPCR_MASK_SCU_IDLE (0x1 << 26)
# define BM_CLPCR_MASK_L2CC_IDLE (0x1 << 27)
# define CGPR 0x64
# define BM_CGPR_CHICKEN_BIT (0x1 << 17)
static void __iomem * ccm_base ;
void imx6q_set_chicken_bit ( void )
{
u32 val = readl_relaxed ( ccm_base + CGPR ) ;
val | = BM_CGPR_CHICKEN_BIT ;
writel_relaxed ( val , ccm_base + CGPR ) ;
}
static void imx6q_enable_rbc ( bool enable )
{
u32 val ;
/*
* need to mask all interrupts in GPC before
* operating RBC configurations
*/
imx_gpc_mask_all ( ) ;
/* configure RBC enable bit */
val = readl_relaxed ( ccm_base + CCR ) ;
val & = ~ BM_CCR_RBC_EN ;
val | = enable ? BM_CCR_RBC_EN : 0 ;
writel_relaxed ( val , ccm_base + CCR ) ;
/* configure RBC count */
val = readl_relaxed ( ccm_base + CCR ) ;
val & = ~ BM_CCR_RBC_BYPASS_COUNT ;
val | = enable ? BM_CCR_RBC_BYPASS_COUNT : 0 ;
writel ( val , ccm_base + CCR ) ;
/*
* need to delay at least 2 cycles of CKIL ( 32 K )
* due to hardware design requirement , which is
* ~ 61u s , here we use 65u s for safe
*/
udelay ( 65 ) ;
/* restore GPC interrupt mask settings */
imx_gpc_restore_all ( ) ;
}
static void imx6q_enable_wb ( bool enable )
{
u32 val ;
/* configure well bias enable bit */
val = readl_relaxed ( ccm_base + CLPCR ) ;
val & = ~ BM_CLPCR_WB_PER_AT_LPM ;
val | = enable ? BM_CLPCR_WB_PER_AT_LPM : 0 ;
writel_relaxed ( val , ccm_base + CLPCR ) ;
/* configure well bias count */
val = readl_relaxed ( ccm_base + CCR ) ;
val & = ~ BM_CCR_WB_COUNT ;
val | = enable ? BM_CCR_WB_COUNT : 0 ;
writel_relaxed ( val , ccm_base + CCR ) ;
}
int imx6q_set_lpm ( enum mxc_cpu_pwr_mode mode )
{
2013-10-16 19:52:00 +08:00
struct irq_desc * iomuxc_irq_desc ;
2013-09-25 23:09:36 +08:00
u32 val = readl_relaxed ( ccm_base + CLPCR ) ;
val & = ~ BM_CLPCR_LPM ;
switch ( mode ) {
case WAIT_CLOCKED :
break ;
case WAIT_UNCLOCKED :
val | = 0x1 < < BP_CLPCR_LPM ;
val | = BM_CLPCR_ARM_CLK_DIS_ON_LPM ;
break ;
case STOP_POWER_ON :
val | = 0x2 < < BP_CLPCR_LPM ;
break ;
case WAIT_UNCLOCKED_POWER_OFF :
val | = 0x1 < < BP_CLPCR_LPM ;
val & = ~ BM_CLPCR_VSTBY ;
val & = ~ BM_CLPCR_SBYOS ;
break ;
case STOP_POWER_OFF :
val | = 0x2 < < BP_CLPCR_LPM ;
val | = 0x3 < < BP_CLPCR_STBY_COUNT ;
val | = BM_CLPCR_VSTBY ;
val | = BM_CLPCR_SBYOS ;
2013-10-17 10:07:09 +08:00
if ( cpu_is_imx6sl ( ) ) {
val | = BM_CLPCR_BYPASS_PMIC_READY ;
val | = BM_CLPCR_BYP_MMDC_CH0_LPM_HS ;
} else {
val | = BM_CLPCR_BYP_MMDC_CH1_LPM_HS ;
}
2013-09-25 23:09:36 +08:00
break ;
default :
return - EINVAL ;
}
2013-10-16 19:52:00 +08:00
/*
* Unmask the always pending IOMUXC interrupt # 32 as wakeup source to
* deassert dsm_request signal , so that we can ensure dsm_request
* is not asserted when we ' re going to write CLPCR register to set LPM .
* After setting up LPM bits , we need to mask this wakeup source .
*/
iomuxc_irq_desc = irq_to_desc ( 32 ) ;
imx_gpc_irq_unmask ( & iomuxc_irq_desc - > irq_data ) ;
2013-09-25 23:09:36 +08:00
writel_relaxed ( val , ccm_base + CLPCR ) ;
2013-10-16 19:52:00 +08:00
imx_gpc_irq_mask ( & iomuxc_irq_desc - > irq_data ) ;
2013-09-25 23:09:36 +08:00
return 0 ;
}
2011-09-06 15:08:40 +08:00
static int imx6q_suspend_finish ( unsigned long val )
{
cpu_do_idle ( ) ;
return 0 ;
}
static int imx6q_pm_enter ( suspend_state_t state )
{
switch ( state ) {
case PM_SUSPEND_MEM :
imx6q_set_lpm ( STOP_POWER_OFF ) ;
2013-10-09 20:31:28 +08:00
imx6q_enable_wb ( true ) ;
imx6q_enable_rbc ( true ) ;
2011-09-06 15:08:40 +08:00
imx_gpc_pre_suspend ( ) ;
2013-03-20 19:39:42 -04:00
imx_anatop_pre_suspend ( ) ;
2011-09-06 15:08:40 +08:00
imx_set_cpu_jump ( 0 , v7_cpu_resume ) ;
/* Zzz ... */
cpu_suspend ( 0 , imx6q_suspend_finish ) ;
2013-10-17 10:07:09 +08:00
if ( cpu_is_imx6q ( ) | | cpu_is_imx6dl ( ) )
imx_smp_prepare ( ) ;
2013-03-20 19:39:42 -04:00
imx_anatop_post_resume ( ) ;
2011-09-06 15:08:40 +08:00
imx_gpc_post_resume ( ) ;
2013-10-09 20:31:28 +08:00
imx6q_enable_rbc ( false ) ;
imx6q_enable_wb ( false ) ;
2013-01-14 21:11:10 +08:00
imx6q_set_lpm ( WAIT_CLOCKED ) ;
2011-09-06 15:08:40 +08:00
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static const struct platform_suspend_ops imx6q_pm_ops = {
. enter = imx6q_pm_enter ,
. valid = suspend_valid_only_mem ,
} ;
2013-09-25 23:09:36 +08:00
void __init imx6q_pm_set_ccm_base ( void __iomem * base )
{
ccm_base = base ;
}
2011-09-06 15:08:40 +08:00
void __init imx6q_pm_init ( void )
{
2013-10-16 19:52:00 +08:00
struct regmap * gpr ;
2013-09-25 23:09:36 +08:00
WARN_ON ( ! ccm_base ) ;
2013-10-16 19:52:00 +08:00
/*
* Force IOMUXC irq pending , so that the interrupt to GPC can be
* used to deassert dsm_request signal when the signal gets
* asserted unexpectedly .
*/
gpr = syscon_regmap_lookup_by_compatible ( " fsl,imx6q-iomuxc-gpr " ) ;
if ( ! IS_ERR ( gpr ) )
regmap_update_bits ( gpr , IOMUXC_GPR1 , IMX6Q_GPR1_GINT ,
IMX6Q_GPR1_GINT ) ;
2013-09-25 23:09:36 +08:00
/* Set initial power mode */
imx6q_set_lpm ( WAIT_CLOCKED ) ;
2011-09-06 15:08:40 +08:00
suspend_set_ops ( & imx6q_pm_ops ) ;
}