2019-05-27 08:55:08 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2011-09-06 15:08:40 +08:00
/*
2014-01-17 11:39:05 +08:00
* Copyright 2011 - 2014 Freescale Semiconductor , Inc .
2011-09-06 15:08:40 +08:00
* Copyright 2011 Linaro Ltd .
*/
2021-01-26 12:45:39 +00:00
# include <linux/clk/imx.h>
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>
2014-01-17 11:39:05 +08:00
# include <linux/genalloc.h>
2021-09-28 15:49:40 +02:00
# include <linux/irqchip/arm-gic.h>
2013-10-16 19:52:00 +08:00
# 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>
2014-01-17 11:39:05 +08:00
# include <linux/of_platform.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>
2014-01-17 11:39:05 +08:00
# include <asm/fncpy.h>
2011-09-06 15:08:40 +08:00
# include <asm/proc-fns.h>
# include <asm/suspend.h>
2014-01-17 11:39:05 +08:00
# include <asm/tlb.h>
2011-09-06 15:08:40 +08:00
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
2014-01-07 08:00:40 -02:00
# define BM_CGPR_INT_MEM_CLK_LPM (0x1 << 17)
2013-09-25 23:09:36 +08:00
2014-01-17 11:39:05 +08:00
# define MX6Q_SUSPEND_OCRAM_SIZE 0x1000
# define MX6_MAX_MMDC_IO_NUM 33
2013-09-25 23:09:36 +08:00
static void __iomem * ccm_base ;
2014-01-17 11:39:05 +08:00
static void __iomem * suspend_ocram_base ;
static void ( * imx6_suspend_in_ocram_fn ) ( void __iomem * ocram_vbase ) ;
/*
* suspend ocram space layout :
* = = = = = = = = = = = = = = = = = = = = = = = = high address = = = = = = = = = = = = = = = = = = = = = =
* .
* .
* .
* ^
* ^
* ^
* imx6_suspend code
* PM_INFO structure ( imx6_cpu_pm_info )
* = = = = = = = = = = = = = = = = = = = = = = = = low address = = = = = = = = = = = = = = = = = = = = = = =
*/
struct imx6_pm_base {
phys_addr_t pbase ;
void __iomem * vbase ;
} ;
struct imx6_pm_socdata {
2014-09-17 11:11:45 +08:00
u32 ddr_type ;
2014-01-17 11:39:05 +08:00
const char * mmdc_compat ;
const char * src_compat ;
const char * iomuxc_compat ;
const char * gpc_compat ;
2015-08-05 01:48:37 +08:00
const char * pl310_compat ;
2014-01-17 11:39:05 +08:00
const u32 mmdc_io_num ;
const u32 * mmdc_io_offset ;
} ;
static const u32 imx6q_mmdc_io_offset [ ] __initconst = {
0x5ac , 0x5b4 , 0x528 , 0x520 , /* DQM0 ~ DQM3 */
0x514 , 0x510 , 0x5bc , 0x5c4 , /* DQM4 ~ DQM7 */
0x56c , 0x578 , 0x588 , 0x594 , /* CAS, RAS, SDCLK_0, SDCLK_1 */
0x5a8 , 0x5b0 , 0x524 , 0x51c , /* SDQS0 ~ SDQS3 */
0x518 , 0x50c , 0x5b8 , 0x5c0 , /* SDQS4 ~ SDQS7 */
0x784 , 0x788 , 0x794 , 0x79c , /* GPR_B0DS ~ GPR_B3DS */
0x7a0 , 0x7a4 , 0x7a8 , 0x748 , /* GPR_B4DS ~ GPR_B7DS */
0x59c , 0x5a0 , 0x750 , 0x774 , /* SODT0, SODT1, MODE_CTL, MODE */
0x74c , /* GPR_ADDS */
} ;
2014-01-17 11:39:06 +08:00
static const u32 imx6dl_mmdc_io_offset [ ] __initconst = {
0x470 , 0x474 , 0x478 , 0x47c , /* DQM0 ~ DQM3 */
0x480 , 0x484 , 0x488 , 0x48c , /* DQM4 ~ DQM7 */
0x464 , 0x490 , 0x4ac , 0x4b0 , /* CAS, RAS, SDCLK_0, SDCLK_1 */
0x4bc , 0x4c0 , 0x4c4 , 0x4c8 , /* DRAM_SDQS0 ~ DRAM_SDQS3 */
0x4cc , 0x4d0 , 0x4d4 , 0x4d8 , /* DRAM_SDQS4 ~ DRAM_SDQS7 */
0x764 , 0x770 , 0x778 , 0x77c , /* GPR_B0DS ~ GPR_B3DS */
0x780 , 0x784 , 0x78c , 0x748 , /* GPR_B4DS ~ GPR_B7DS */
0x4b4 , 0x4b8 , 0x750 , 0x760 , /* SODT0, SODT1, MODE_CTL, MODE */
0x74c , /* GPR_ADDS */
} ;
2014-01-17 11:39:07 +08:00
static const u32 imx6sl_mmdc_io_offset [ ] __initconst = {
0x30c , 0x310 , 0x314 , 0x318 , /* DQM0 ~ DQM3 */
0x5c4 , 0x5cc , 0x5d4 , 0x5d8 , /* GPR_B0DS ~ GPR_B3DS */
0x300 , 0x31c , 0x338 , 0x5ac , /* CAS, RAS, SDCLK_0, GPR_ADDS */
0x33c , 0x340 , 0x5b0 , 0x5c0 , /* SODT0, SODT1, MODE_CTL, MODE */
0x330 , 0x334 , 0x320 , /* SDCKE0, SDCKE1, RESET */
} ;
2018-05-31 10:06:09 +08:00
static const u32 imx6sll_mmdc_io_offset [ ] __initconst = {
0x294 , 0x298 , 0x29c , 0x2a0 , /* DQM0 ~ DQM3 */
0x544 , 0x54c , 0x554 , 0x558 , /* GPR_B0DS ~ GPR_B3DS */
0x530 , 0x540 , 0x2ac , 0x52c , /* MODE_CTL, MODE, SDCLK_0, GPR_ADDDS */
0x2a4 , 0x2a8 , /* SDCKE0, SDCKE1*/
} ;
2014-06-20 13:20:54 +08:00
static const u32 imx6sx_mmdc_io_offset [ ] __initconst = {
0x2ec , 0x2f0 , 0x2f4 , 0x2f8 , /* DQM0 ~ DQM3 */
0x60c , 0x610 , 0x61c , 0x620 , /* GPR_B0DS ~ GPR_B3DS */
0x300 , 0x2fc , 0x32c , 0x5f4 , /* CAS, RAS, SDCLK_0, GPR_ADDS */
0x310 , 0x314 , 0x5f8 , 0x608 , /* SODT0, SODT1, MODE_CTL, MODE */
0x330 , 0x334 , 0x338 , 0x33c , /* SDQS0 ~ SDQS3 */
} ;
2015-08-05 01:48:37 +08:00
static const u32 imx6ul_mmdc_io_offset [ ] __initconst = {
0x244 , 0x248 , 0x24c , 0x250 , /* DQM0, DQM1, RAS, CAS */
0x27c , 0x498 , 0x4a4 , 0x490 , /* SDCLK0, GPR_B0DS-B1DS, GPR_ADDS */
0x280 , 0x284 , 0x260 , 0x264 , /* SDQS0~1, SODT0, SODT1 */
0x494 , 0x4b0 , /* MODE_CTL, MODE, */
} ;
2014-01-17 11:39:05 +08:00
static const struct imx6_pm_socdata imx6q_pm_data __initconst = {
. mmdc_compat = " fsl,imx6q-mmdc " ,
. src_compat = " fsl,imx6q-src " ,
. iomuxc_compat = " fsl,imx6q-iomuxc " ,
. gpc_compat = " fsl,imx6q-gpc " ,
2015-08-05 01:48:37 +08:00
. pl310_compat = " arm,pl310-cache " ,
2014-01-17 11:39:05 +08:00
. mmdc_io_num = ARRAY_SIZE ( imx6q_mmdc_io_offset ) ,
. mmdc_io_offset = imx6q_mmdc_io_offset ,
} ;
2014-01-17 11:39:06 +08:00
static const struct imx6_pm_socdata imx6dl_pm_data __initconst = {
. mmdc_compat = " fsl,imx6q-mmdc " ,
. src_compat = " fsl,imx6q-src " ,
. iomuxc_compat = " fsl,imx6dl-iomuxc " ,
. gpc_compat = " fsl,imx6q-gpc " ,
2015-08-05 01:48:37 +08:00
. pl310_compat = " arm,pl310-cache " ,
2014-01-17 11:39:06 +08:00
. mmdc_io_num = ARRAY_SIZE ( imx6dl_mmdc_io_offset ) ,
. mmdc_io_offset = imx6dl_mmdc_io_offset ,
} ;
2014-01-17 11:39:07 +08:00
static const struct imx6_pm_socdata imx6sl_pm_data __initconst = {
. mmdc_compat = " fsl,imx6sl-mmdc " ,
. src_compat = " fsl,imx6sl-src " ,
. iomuxc_compat = " fsl,imx6sl-iomuxc " ,
. gpc_compat = " fsl,imx6sl-gpc " ,
2015-08-05 01:48:37 +08:00
. pl310_compat = " arm,pl310-cache " ,
2014-01-17 11:39:07 +08:00
. mmdc_io_num = ARRAY_SIZE ( imx6sl_mmdc_io_offset ) ,
. mmdc_io_offset = imx6sl_mmdc_io_offset ,
} ;
2018-05-31 10:06:09 +08:00
static const struct imx6_pm_socdata imx6sll_pm_data __initconst = {
. mmdc_compat = " fsl,imx6sll-mmdc " ,
. src_compat = " fsl,imx6sll-src " ,
. iomuxc_compat = " fsl,imx6sll-iomuxc " ,
. gpc_compat = " fsl,imx6sll-gpc " ,
. pl310_compat = " arm,pl310-cache " ,
. mmdc_io_num = ARRAY_SIZE ( imx6sll_mmdc_io_offset ) ,
. mmdc_io_offset = imx6sll_mmdc_io_offset ,
} ;
2014-06-20 13:20:54 +08:00
static const struct imx6_pm_socdata imx6sx_pm_data __initconst = {
. mmdc_compat = " fsl,imx6sx-mmdc " ,
. src_compat = " fsl,imx6sx-src " ,
. iomuxc_compat = " fsl,imx6sx-iomuxc " ,
. gpc_compat = " fsl,imx6sx-gpc " ,
2015-08-05 01:48:37 +08:00
. pl310_compat = " arm,pl310-cache " ,
2014-06-20 13:20:54 +08:00
. mmdc_io_num = ARRAY_SIZE ( imx6sx_mmdc_io_offset ) ,
. mmdc_io_offset = imx6sx_mmdc_io_offset ,
} ;
2015-08-05 01:48:37 +08:00
static const struct imx6_pm_socdata imx6ul_pm_data __initconst = {
. mmdc_compat = " fsl,imx6ul-mmdc " ,
. src_compat = " fsl,imx6ul-src " ,
. iomuxc_compat = " fsl,imx6ul-iomuxc " ,
. gpc_compat = " fsl,imx6ul-gpc " ,
. pl310_compat = NULL ,
. mmdc_io_num = ARRAY_SIZE ( imx6ul_mmdc_io_offset ) ,
. mmdc_io_offset = imx6ul_mmdc_io_offset ,
} ;
2014-01-17 11:39:05 +08:00
/*
* This structure is for passing necessary data for low level ocram
* suspend code ( arch / arm / mach - imx / suspend - imx6 . S ) , if this struct
* definition is changed , the offset definition in
* arch / arm / mach - imx / suspend - imx6 . S must be also changed accordingly ,
* otherwise , the suspend to ocram function will be broken !
*/
struct imx6_cpu_pm_info {
phys_addr_t pbase ; /* The physical address of pm_info. */
phys_addr_t resume_addr ; /* The physical resume address for asm code */
2014-09-17 11:11:45 +08:00
u32 ddr_type ;
2014-01-17 11:39:05 +08:00
u32 pm_info_size ; /* Size of pm_info. */
struct imx6_pm_base mmdc_base ;
struct imx6_pm_base src_base ;
struct imx6_pm_base iomuxc_base ;
struct imx6_pm_base ccm_base ;
struct imx6_pm_base gpc_base ;
struct imx6_pm_base l2_base ;
u32 mmdc_io_num ; /* Number of MMDC IOs which need saved/restored. */
u32 mmdc_io_val [ MX6_MAX_MMDC_IO_NUM ] [ 2 ] ; /* To save offset and value */
} __aligned ( 8 ) ;
2013-09-25 23:09:36 +08:00
2016-08-29 21:49:56 +08:00
void imx6_set_int_mem_clk_lpm ( bool enable )
2013-09-25 23:09:36 +08:00
{
u32 val = readl_relaxed ( ccm_base + CGPR ) ;
2014-06-23 16:42:43 +08:00
val & = ~ BM_CGPR_INT_MEM_CLK_LPM ;
if ( enable )
val | = BM_CGPR_INT_MEM_CLK_LPM ;
2013-09-25 23:09:36 +08:00
writel_relaxed ( val , ccm_base + CGPR ) ;
}
2014-12-17 12:24:12 +08:00
void imx6_enable_rbc ( bool enable )
2013-09-25 23:09:36 +08:00
{
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 ) ;
}
2015-04-25 22:59:19 +08:00
int imx6_set_lpm ( enum mxc_cpu_pwr_mode mode )
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 ;
2014-06-23 16:42:44 +08:00
val & = ~ BM_CLPCR_VSTBY ;
val & = ~ BM_CLPCR_SBYOS ;
if ( cpu_is_imx6sl ( ) )
val | = BM_CLPCR_BYPASS_PMIC_READY ;
2017-06-06 20:50:43 +03:00
if ( cpu_is_imx6sl ( ) | | cpu_is_imx6sx ( ) | | cpu_is_imx6ul ( ) | |
2018-09-30 11:32:26 +08:00
cpu_is_imx6ull ( ) | | cpu_is_imx6sll ( ) | | cpu_is_imx6ulz ( ) )
2014-06-23 16:42:44 +08:00
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 ;
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 ;
2016-08-22 23:53:25 +08:00
if ( cpu_is_imx6sl ( ) | | cpu_is_imx6sx ( ) )
2013-10-17 10:07:09 +08:00
val | = BM_CLPCR_BYPASS_PMIC_READY ;
2017-06-06 20:50:43 +03:00
if ( cpu_is_imx6sl ( ) | | cpu_is_imx6sx ( ) | | cpu_is_imx6ul ( ) | |
2018-09-30 11:32:26 +08:00
cpu_is_imx6ull ( ) | | cpu_is_imx6sll ( ) | | cpu_is_imx6ulz ( ) )
2013-10-17 10:07:09 +08:00
val | = BM_CLPCR_BYP_MMDC_CH0_LPM_HS ;
2014-06-20 13:20:54 +08:00
else
2013-10-17 10:07:09 +08:00
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
/*
2013-12-24 17:19:21 -05:00
* ERR007265 : CCM : When improper low - power sequence is used ,
* the SoC enters low power mode before the ARM core executes WFI .
*
* Software workaround :
* 1 ) Software should trigger IRQ # 32 ( IOMUX ) to be always pending
* by setting IOMUX_GPR1_GINT .
* 2 ) Software should then unmask IRQ # 32 in GPC before setting CCM
* Low - Power mode .
* 3 ) Software should mask IRQ # 32 right after CCM Low - Power mode
* is set ( set bits 0 - 1 of CCM_CLPCR ) .
2015-02-23 17:45:18 +00:00
*
* Note that IRQ # 32 is GIC SPI # 0.
2013-10-16 19:52:00 +08:00
*/
2019-03-06 13:30:42 +09:00
if ( mode ! = WAIT_CLOCKED )
imx_gpc_hwirq_unmask ( 0 ) ;
2013-09-25 23:09:36 +08:00
writel_relaxed ( val , ccm_base + CLPCR ) ;
2019-03-06 13:30:42 +09:00
if ( mode ! = WAIT_CLOCKED )
imx_gpc_hwirq_mask ( 0 ) ;
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 )
{
2014-01-17 11:39:05 +08:00
if ( ! imx6_suspend_in_ocram_fn ) {
cpu_do_idle ( ) ;
} else {
/*
* call low level suspend function in ocram ,
* as we need to float DDR IO .
*/
local_flush_tlb_all ( ) ;
2015-08-05 01:48:37 +08:00
/* check if need to flush internal L2 cache */
if ( ! ( ( struct imx6_cpu_pm_info * )
suspend_ocram_base ) - > l2_base . vbase )
flush_cache_all ( ) ;
2014-01-17 11:39:05 +08:00
imx6_suspend_in_ocram_fn ( suspend_ocram_base ) ;
}
2011-09-06 15:08:40 +08:00
return 0 ;
}
static int imx6q_pm_enter ( suspend_state_t state )
{
switch ( state ) {
2014-06-23 16:42:44 +08:00
case PM_SUSPEND_STANDBY :
2015-04-25 22:59:19 +08:00
imx6_set_lpm ( STOP_POWER_ON ) ;
2016-08-29 21:49:56 +08:00
imx6_set_int_mem_clk_lpm ( true ) ;
2014-06-23 16:42:44 +08:00
imx_gpc_pre_suspend ( false ) ;
if ( cpu_is_imx6sl ( ) )
imx6sl_set_wait_clk ( true ) ;
/* Zzz ... */
cpu_do_idle ( ) ;
if ( cpu_is_imx6sl ( ) )
imx6sl_set_wait_clk ( false ) ;
imx_gpc_post_resume ( ) ;
2015-04-25 22:59:19 +08:00
imx6_set_lpm ( WAIT_CLOCKED ) ;
2014-06-23 16:42:44 +08:00
break ;
2011-09-06 15:08:40 +08:00
case PM_SUSPEND_MEM :
2015-04-25 22:59:19 +08:00
imx6_set_lpm ( STOP_POWER_OFF ) ;
2016-08-29 21:49:56 +08:00
imx6_set_int_mem_clk_lpm ( false ) ;
2013-10-09 20:31:28 +08:00
imx6q_enable_wb ( true ) ;
2014-01-17 11:39:05 +08:00
/*
* For suspend into ocram , asm code already take care of
* RBC setting , so we do NOT need to do that here .
*/
if ( ! imx6_suspend_in_ocram_fn )
2014-12-17 12:24:12 +08:00
imx6_enable_rbc ( true ) ;
2014-06-23 16:42:44 +08:00
imx_gpc_pre_suspend ( true ) ;
2013-03-20 19:39:42 -04:00
imx_anatop_pre_suspend ( ) ;
2011-09-06 15:08:40 +08:00
/* 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 ( ) ;
2014-12-17 12:24:12 +08:00
imx6_enable_rbc ( false ) ;
2013-10-09 20:31:28 +08:00
imx6q_enable_wb ( false ) ;
2016-08-29 21:49:56 +08:00
imx6_set_int_mem_clk_lpm ( true ) ;
2015-04-25 22:59:19 +08:00
imx6_set_lpm ( WAIT_CLOCKED ) ;
2011-09-06 15:08:40 +08:00
break ;
default :
return - EINVAL ;
}
return 0 ;
}
2014-06-23 16:42:44 +08:00
static int imx6q_pm_valid ( suspend_state_t state )
{
return ( state = = PM_SUSPEND_STANDBY | | state = = PM_SUSPEND_MEM ) ;
}
2011-09-06 15:08:40 +08:00
static const struct platform_suspend_ops imx6q_pm_ops = {
. enter = imx6q_pm_enter ,
2014-06-23 16:42:44 +08:00
. valid = imx6q_pm_valid ,
2011-09-06 15:08:40 +08:00
} ;
2014-01-17 11:39:05 +08:00
static int __init imx6_pm_get_base ( struct imx6_pm_base * base ,
const char * compat )
{
struct device_node * node ;
struct resource res ;
int ret = 0 ;
node = of_find_compatible_node ( NULL , NULL , compat ) ;
2018-02-23 22:42:04 -03:00
if ( ! node )
return - ENODEV ;
2014-01-17 11:39:05 +08:00
ret = of_address_to_resource ( node , 0 , & res ) ;
if ( ret )
goto put_node ;
base - > pbase = res . start ;
base - > vbase = ioremap ( res . start , resource_size ( & res ) ) ;
if ( ! base - > vbase )
ret = - ENOMEM ;
put_node :
of_node_put ( node ) ;
return ret ;
}
2014-02-26 21:28:18 +08:00
static int __init imx6q_suspend_init ( const struct imx6_pm_socdata * socdata )
2014-01-17 11:39:05 +08:00
{
phys_addr_t ocram_pbase ;
struct device_node * node ;
struct platform_device * pdev ;
struct imx6_cpu_pm_info * pm_info ;
struct gen_pool * ocram_pool ;
unsigned long ocram_base ;
int i , ret = 0 ;
const u32 * mmdc_offset_array ;
2014-02-26 21:28:18 +08:00
suspend_set_ops ( & imx6q_pm_ops ) ;
2014-01-17 11:39:05 +08:00
if ( ! socdata ) {
pr_warn ( " %s: invalid argument! \n " , __func__ ) ;
return - EINVAL ;
}
node = of_find_compatible_node ( NULL , NULL , " mmio-sram " ) ;
if ( ! node ) {
pr_warn ( " %s: failed to find ocram node! \n " , __func__ ) ;
return - ENODEV ;
}
pdev = of_find_device_by_node ( node ) ;
if ( ! pdev ) {
pr_warn ( " %s: failed to find ocram device! \n " , __func__ ) ;
ret = - ENODEV ;
goto put_node ;
}
2015-09-04 15:47:43 -07:00
ocram_pool = gen_pool_get ( & pdev - > dev , NULL ) ;
2014-01-17 11:39:05 +08:00
if ( ! ocram_pool ) {
pr_warn ( " %s: ocram pool unavailable! \n " , __func__ ) ;
ret = - ENODEV ;
2020-06-04 20:54:49 +08:00
goto put_device ;
2014-01-17 11:39:05 +08:00
}
ocram_base = gen_pool_alloc ( ocram_pool , MX6Q_SUSPEND_OCRAM_SIZE ) ;
if ( ! ocram_base ) {
pr_warn ( " %s: unable to alloc ocram! \n " , __func__ ) ;
ret = - ENOMEM ;
2020-06-04 20:54:49 +08:00
goto put_device ;
2014-01-17 11:39:05 +08:00
}
ocram_pbase = gen_pool_virt_to_phys ( ocram_pool , ocram_base ) ;
suspend_ocram_base = __arm_ioremap_exec ( ocram_pbase ,
MX6Q_SUSPEND_OCRAM_SIZE , false ) ;
2015-08-05 01:48:37 +08:00
memset ( suspend_ocram_base , 0 , sizeof ( * pm_info ) ) ;
2014-01-17 11:39:05 +08:00
pm_info = suspend_ocram_base ;
pm_info - > pbase = ocram_pbase ;
2017-01-15 03:59:29 +01:00
pm_info - > resume_addr = __pa_symbol ( v7_cpu_resume ) ;
2014-01-17 11:39:05 +08:00
pm_info - > pm_info_size = sizeof ( * pm_info ) ;
/*
* ccm physical address is not used by asm code currently ,
2015-04-25 23:37:12 +08:00
* so get ccm virtual address directly .
2014-01-17 11:39:05 +08:00
*/
pm_info - > ccm_base . vbase = ccm_base ;
ret = imx6_pm_get_base ( & pm_info - > mmdc_base , socdata - > mmdc_compat ) ;
if ( ret ) {
pr_warn ( " %s: failed to get mmdc base %d! \n " , __func__ , ret ) ;
2020-06-04 20:54:49 +08:00
goto put_device ;
2014-01-17 11:39:05 +08:00
}
ret = imx6_pm_get_base ( & pm_info - > src_base , socdata - > src_compat ) ;
if ( ret ) {
pr_warn ( " %s: failed to get src base %d! \n " , __func__ , ret ) ;
goto src_map_failed ;
}
ret = imx6_pm_get_base ( & pm_info - > iomuxc_base , socdata - > iomuxc_compat ) ;
if ( ret ) {
pr_warn ( " %s: failed to get iomuxc base %d! \n " , __func__ , ret ) ;
goto iomuxc_map_failed ;
}
ret = imx6_pm_get_base ( & pm_info - > gpc_base , socdata - > gpc_compat ) ;
if ( ret ) {
pr_warn ( " %s: failed to get gpc base %d! \n " , __func__ , ret ) ;
goto gpc_map_failed ;
}
2015-08-05 01:48:37 +08:00
if ( socdata - > pl310_compat ) {
ret = imx6_pm_get_base ( & pm_info - > l2_base , socdata - > pl310_compat ) ;
if ( ret ) {
pr_warn ( " %s: failed to get pl310-cache base %d! \n " ,
__func__ , ret ) ;
goto pl310_cache_map_failed ;
}
2014-01-17 11:39:05 +08:00
}
2014-09-17 11:11:45 +08:00
pm_info - > ddr_type = imx_mmdc_get_ddr_type ( ) ;
2014-01-17 11:39:05 +08:00
pm_info - > mmdc_io_num = socdata - > mmdc_io_num ;
mmdc_offset_array = socdata - > mmdc_io_offset ;
for ( i = 0 ; i < pm_info - > mmdc_io_num ; i + + ) {
pm_info - > mmdc_io_val [ i ] [ 0 ] =
mmdc_offset_array [ i ] ;
pm_info - > mmdc_io_val [ i ] [ 1 ] =
readl_relaxed ( pm_info - > iomuxc_base . vbase +
mmdc_offset_array [ i ] ) ;
}
imx6_suspend_in_ocram_fn = fncpy (
suspend_ocram_base + sizeof ( * pm_info ) ,
& imx6_suspend ,
MX6Q_SUSPEND_OCRAM_SIZE - sizeof ( * pm_info ) ) ;
2021-09-21 12:59:59 +01:00
__arm_iomem_set_ro ( suspend_ocram_base , MX6Q_SUSPEND_OCRAM_SIZE ) ;
2020-06-04 20:54:49 +08:00
goto put_device ;
2014-01-17 11:39:05 +08:00
pl310_cache_map_failed :
2015-12-26 23:16:08 +01:00
iounmap ( pm_info - > gpc_base . vbase ) ;
2014-01-17 11:39:05 +08:00
gpc_map_failed :
2015-12-26 23:16:08 +01:00
iounmap ( pm_info - > iomuxc_base . vbase ) ;
2014-01-17 11:39:05 +08:00
iomuxc_map_failed :
2015-12-26 23:16:08 +01:00
iounmap ( pm_info - > src_base . vbase ) ;
2014-01-17 11:39:05 +08:00
src_map_failed :
2015-12-26 23:16:08 +01:00
iounmap ( pm_info - > mmdc_base . vbase ) ;
2020-06-04 20:54:49 +08:00
put_device :
put_device ( & pdev - > dev ) ;
2014-01-17 11:39:05 +08:00
put_node :
of_node_put ( node ) ;
return ret ;
}
static void __init imx6_pm_common_init ( const struct imx6_pm_socdata
* socdata )
2011-09-06 15:08:40 +08:00
{
2013-10-16 19:52:00 +08:00
struct regmap * gpr ;
2014-01-17 11:39:05 +08:00
int ret ;
2013-10-16 19:52:00 +08:00
2013-09-25 23:09:36 +08:00
WARN_ON ( ! ccm_base ) ;
2014-02-26 21:40:32 +08:00
if ( IS_ENABLED ( CONFIG_SUSPEND ) ) {
ret = imx6q_suspend_init ( socdata ) ;
if ( ret )
pr_warn ( " %s: No DDR LPM support with suspend %d! \n " ,
__func__ , ret ) ;
}
2014-01-17 11:39:05 +08:00
2013-10-16 19:52:00 +08:00
/*
2013-12-24 17:19:21 -05:00
* This is for SW workaround step # 1 of ERR007265 , see comments
2015-04-25 22:59:19 +08:00
* in imx6_set_lpm for details of this errata .
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 ) ;
2011-09-06 15:08:40 +08:00
}
2014-01-17 11:39:05 +08:00
2018-08-02 12:34:21 +02:00
static void imx6_pm_stby_poweroff ( void )
{
2021-09-28 15:49:40 +02:00
gic_cpu_if_down ( 0 ) ;
2018-08-02 12:34:21 +02:00
imx6_set_lpm ( STOP_POWER_OFF ) ;
imx6q_suspend_finish ( 0 ) ;
mdelay ( 1000 ) ;
pr_emerg ( " Unable to poweroff system \n " ) ;
}
static int imx6_pm_stby_poweroff_probe ( void )
{
if ( pm_power_off ) {
2019-03-25 21:32:28 +02:00
pr_warn ( " %s: pm_power_off already claimed %p %ps! \n " ,
2018-08-02 12:34:21 +02:00
__func__ , pm_power_off , pm_power_off ) ;
return - EBUSY ;
}
pm_power_off = imx6_pm_stby_poweroff ;
return 0 ;
}
2015-04-29 13:07:03 +08:00
void __init imx6_pm_ccm_init ( const char * ccm_compat )
{
struct device_node * np ;
u32 val ;
np = of_find_compatible_node ( NULL , NULL , ccm_compat ) ;
ccm_base = of_iomap ( np , 0 ) ;
BUG_ON ( ! ccm_base ) ;
/*
* Initialize CCM_CLPCR_LPM into RUN mode to avoid ARM core
* clock being shut down unexpectedly by WAIT mode .
*/
val = readl_relaxed ( ccm_base + CLPCR ) ;
val & = ~ BM_CLPCR_LPM ;
writel_relaxed ( val , ccm_base + CLPCR ) ;
2018-08-02 12:34:21 +02:00
if ( of_property_read_bool ( np , " fsl,pmic-stby-poweroff " ) )
imx6_pm_stby_poweroff_probe ( ) ;
2020-02-13 16:35:33 +08:00
of_node_put ( np ) ;
2015-04-29 13:07:03 +08:00
}
2014-01-17 11:39:05 +08:00
void __init imx6q_pm_init ( void )
{
imx6_pm_common_init ( & imx6q_pm_data ) ;
}
void __init imx6dl_pm_init ( void )
{
2014-01-17 11:39:06 +08:00
imx6_pm_common_init ( & imx6dl_pm_data ) ;
2014-01-17 11:39:05 +08:00
}
void __init imx6sl_pm_init ( void )
{
2018-06-22 13:32:48 +08:00
struct regmap * gpr ;
if ( cpu_is_imx6sl ( ) ) {
2018-05-31 10:06:09 +08:00
imx6_pm_common_init ( & imx6sl_pm_data ) ;
2018-06-22 13:32:48 +08:00
} else {
2018-05-31 10:06:09 +08:00
imx6_pm_common_init ( & imx6sll_pm_data ) ;
2018-06-22 13:32:48 +08:00
gpr = syscon_regmap_lookup_by_compatible ( " fsl,imx6q-iomuxc-gpr " ) ;
if ( ! IS_ERR ( gpr ) )
regmap_update_bits ( gpr , IOMUXC_GPR5 ,
IMX6SLL_GPR5_AFCG_X_BYPASS_MASK , 0 ) ;
}
2014-01-17 11:39:05 +08:00
}
2014-06-20 13:20:54 +08:00
void __init imx6sx_pm_init ( void )
{
imx6_pm_common_init ( & imx6sx_pm_data ) ;
}
2015-08-05 01:48:37 +08:00
void __init imx6ul_pm_init ( void )
{
imx6_pm_common_init ( & imx6ul_pm_data ) ;
}