// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2011 - 2014 Freescale Semiconductor , Inc .
* Copyright 2011 Linaro Ltd .
*/
#include <linux/clk/imx.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/genalloc.h>
#include <linux/irqchip/arm-gic.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/suspend.h>
#include <asm /cacheflush.h>
#include <asm /fncpy.h>
#include <asm /proc-fns.h>
#include <asm /suspend.h>
#include <asm /tlb.h>
#include "common.h"
#include "hardware.h"
#define CCR 0 x0
#define BM_CCR_WB_COUNT (0 x7 << 16 )
#define BM_CCR_RBC_BYPASS_COUNT (0 x3f << 21 )
#define BM_CCR_RBC_EN (0 x1 << 27 )
#define CLPCR 0 x54
#define BP_CLPCR_LPM 0
#define BM_CLPCR_LPM (0 x3 << 0 )
#define BM_CLPCR_BYPASS_PMIC_READY (0 x1 << 2 )
#define BM_CLPCR_ARM_CLK_DIS_ON_LPM (0 x1 << 5 )
#define BM_CLPCR_SBYOS (0 x1 << 6 )
#define BM_CLPCR_DIS_REF_OSC (0 x1 << 7 )
#define BM_CLPCR_VSTBY (0 x1 << 8 )
#define BP_CLPCR_STBY_COUNT 9
#define BM_CLPCR_STBY_COUNT (0 x3 << 9 )
#define BM_CLPCR_COSC_PWRDOWN (0 x1 << 11 )
#define BM_CLPCR_WB_PER_AT_LPM (0 x1 << 16 )
#define BM_CLPCR_WB_CORE_AT_LPM (0 x1 << 17 )
#define BM_CLPCR_BYP_MMDC_CH0_LPM_HS (0 x1 << 19 )
#define BM_CLPCR_BYP_MMDC_CH1_LPM_HS (0 x1 << 21 )
#define BM_CLPCR_MASK_CORE0_WFI (0 x1 << 22 )
#define BM_CLPCR_MASK_CORE1_WFI (0 x1 << 23 )
#define BM_CLPCR_MASK_CORE2_WFI (0 x1 << 24 )
#define BM_CLPCR_MASK_CORE3_WFI (0 x1 << 25 )
#define BM_CLPCR_MASK_SCU_IDLE (0 x1 << 26 )
#define BM_CLPCR_MASK_L2CC_IDLE (0 x1 << 27 )
#define CGPR 0 x64
#define BM_CGPR_INT_MEM_CLK_LPM (0 x1 << 17 )
#define MX6Q_SUSPEND_OCRAM_SIZE 0 x1000
#define MX6_MAX_MMDC_IO_NUM 33
static void __iomem *ccm_base;
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 {
u32 ddr_type;
const char *mmdc_compat;
const char *src_compat;
const char *iomuxc_compat;
const char *gpc_compat;
const char *pl310_compat;
const u32 mmdc_io_num;
const u32 *mmdc_io_offset;
};
static const u32 imx6q_mmdc_io_offset[] __initconst = {
0 x5ac, 0 x5b4, 0 x528, 0 x520, /* DQM0 ~ DQM3 */
0 x514, 0 x510, 0 x5bc, 0 x5c4, /* DQM4 ~ DQM7 */
0 x56c, 0 x578, 0 x588, 0 x594, /* CAS, RAS, SDCLK_0, SDCLK_1 */
0 x5a8, 0 x5b0, 0 x524, 0 x51c, /* SDQS0 ~ SDQS3 */
0 x518, 0 x50c, 0 x5b8, 0 x5c0, /* SDQS4 ~ SDQS7 */
0 x784, 0 x788, 0 x794, 0 x79c, /* GPR_B0DS ~ GPR_B3DS */
0 x7a0, 0 x7a4, 0 x7a8, 0 x748, /* GPR_B4DS ~ GPR_B7DS */
0 x59c, 0 x5a0, 0 x750, 0 x774, /* SODT0, SODT1, MODE_CTL, MODE */
0 x74c, /* GPR_ADDS */
};
static const u32 imx6dl_mmdc_io_offset[] __initconst = {
0 x470, 0 x474, 0 x478, 0 x47c, /* DQM0 ~ DQM3 */
0 x480, 0 x484, 0 x488, 0 x48c, /* DQM4 ~ DQM7 */
0 x464, 0 x490, 0 x4ac, 0 x4b0, /* CAS, RAS, SDCLK_0, SDCLK_1 */
0 x4bc, 0 x4c0, 0 x4c4, 0 x4c8, /* DRAM_SDQS0 ~ DRAM_SDQS3 */
0 x4cc, 0 x4d0, 0 x4d4, 0 x4d8, /* DRAM_SDQS4 ~ DRAM_SDQS7 */
0 x764, 0 x770, 0 x778, 0 x77c, /* GPR_B0DS ~ GPR_B3DS */
0 x780, 0 x784, 0 x78c, 0 x748, /* GPR_B4DS ~ GPR_B7DS */
0 x4b4, 0 x4b8, 0 x750, 0 x760, /* SODT0, SODT1, MODE_CTL, MODE */
0 x74c, /* GPR_ADDS */
};
static const u32 imx6sl_mmdc_io_offset[] __initconst = {
0 x30c, 0 x310, 0 x314, 0 x318, /* DQM0 ~ DQM3 */
0 x5c4, 0 x5cc, 0 x5d4, 0 x5d8, /* GPR_B0DS ~ GPR_B3DS */
0 x300, 0 x31c, 0 x338, 0 x5ac, /* CAS, RAS, SDCLK_0, GPR_ADDS */
0 x33c, 0 x340, 0 x5b0, 0 x5c0, /* SODT0, SODT1, MODE_CTL, MODE */
0 x330, 0 x334, 0 x320, /* SDCKE0, SDCKE1, RESET */
};
static const u32 imx6sll_mmdc_io_offset[] __initconst = {
0 x294, 0 x298, 0 x29c, 0 x2a0, /* DQM0 ~ DQM3 */
0 x544, 0 x54c, 0 x554, 0 x558, /* GPR_B0DS ~ GPR_B3DS */
0 x530, 0 x540, 0 x2ac, 0 x52c, /* MODE_CTL, MODE, SDCLK_0, GPR_ADDDS */
0 x2a4, 0 x2a8, /* SDCKE0, SDCKE1*/
};
static const u32 imx6sx_mmdc_io_offset[] __initconst = {
0 x2ec, 0 x2f0, 0 x2f4, 0 x2f8, /* DQM0 ~ DQM3 */
0 x60c, 0 x610, 0 x61c, 0 x620, /* GPR_B0DS ~ GPR_B3DS */
0 x300, 0 x2fc, 0 x32c, 0 x5f4, /* CAS, RAS, SDCLK_0, GPR_ADDS */
0 x310, 0 x314, 0 x5f8, 0 x608, /* SODT0, SODT1, MODE_CTL, MODE */
0 x330, 0 x334, 0 x338, 0 x33c, /* SDQS0 ~ SDQS3 */
};
static const u32 imx6ul_mmdc_io_offset[] __initconst = {
0 x244, 0 x248, 0 x24c, 0 x250, /* DQM0, DQM1, RAS, CAS */
0 x27c, 0 x498, 0 x4a4, 0 x490, /* SDCLK0, GPR_B0DS-B1DS, GPR_ADDS */
0 x280, 0 x284, 0 x260, 0 x264, /* SDQS0~1, SODT0, SODT1 */
0 x494, 0 x4b0, /* MODE_CTL, MODE, */
};
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" ,
.pl310_compat = "arm,pl310-cache" ,
.mmdc_io_num = ARRAY_SIZE(imx6q_mmdc_io_offset),
.mmdc_io_offset = imx6q_mmdc_io_offset,
};
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" ,
.pl310_compat = "arm,pl310-cache" ,
.mmdc_io_num = ARRAY_SIZE(imx6dl_mmdc_io_offset),
.mmdc_io_offset = imx6dl_mmdc_io_offset,
};
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" ,
.pl310_compat = "arm,pl310-cache" ,
.mmdc_io_num = ARRAY_SIZE(imx6sl_mmdc_io_offset),
.mmdc_io_offset = imx6sl_mmdc_io_offset,
};
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,
};
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" ,
.pl310_compat = "arm,pl310-cache" ,
.mmdc_io_num = ARRAY_SIZE(imx6sx_mmdc_io_offset),
.mmdc_io_offset = imx6sx_mmdc_io_offset,
};
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,
};
/*
* 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 */
u32 ddr_type;
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 );
void imx6_set_int_mem_clk_lpm(bool enable)
{
u32 val = readl_relaxed(ccm_base + CGPR);
val &= ~BM_CGPR_INT_MEM_CLK_LPM;
if (enable)
val |= BM_CGPR_INT_MEM_CLK_LPM;
writel_relaxed(val, ccm_base + CGPR);
}
void imx6_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
* ~ 61 us , here we use 65 us 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 imx6_set_lpm(enum mxc_cpu_pwr_mode mode)
{
u32 val = readl_relaxed(ccm_base + CLPCR);
val &= ~BM_CLPCR_LPM;
switch (mode) {
case WAIT_CLOCKED:
break ;
case WAIT_UNCLOCKED:
val |= 0 x1 << BP_CLPCR_LPM;
val |= BM_CLPCR_ARM_CLK_DIS_ON_LPM;
break ;
case STOP_POWER_ON:
val |= 0 x2 << BP_CLPCR_LPM;
val &= ~BM_CLPCR_VSTBY;
val &= ~BM_CLPCR_SBYOS;
if (cpu_is_imx6sl())
val |= BM_CLPCR_BYPASS_PMIC_READY;
if (cpu_is_imx6sl() || cpu_is_imx6sx() || cpu_is_imx6ul() ||
cpu_is_imx6ull() || cpu_is_imx6sll() || cpu_is_imx6ulz())
val |= BM_CLPCR_BYP_MMDC_CH0_LPM_HS;
else
val |= BM_CLPCR_BYP_MMDC_CH1_LPM_HS;
break ;
case WAIT_UNCLOCKED_POWER_OFF:
val |= 0 x1 << BP_CLPCR_LPM;
val &= ~BM_CLPCR_VSTBY;
val &= ~BM_CLPCR_SBYOS;
break ;
case STOP_POWER_OFF:
val |= 0 x2 << BP_CLPCR_LPM;
val |= 0 x3 << BP_CLPCR_STBY_COUNT;
val |= BM_CLPCR_VSTBY;
val |= BM_CLPCR_SBYOS;
if (cpu_is_imx6sl() || cpu_is_imx6sx())
val |= BM_CLPCR_BYPASS_PMIC_READY;
if (cpu_is_imx6sl() || cpu_is_imx6sx() || cpu_is_imx6ul() ||
cpu_is_imx6ull() || cpu_is_imx6sll() || cpu_is_imx6ulz())
val |= BM_CLPCR_BYP_MMDC_CH0_LPM_HS;
else
val |= BM_CLPCR_BYP_MMDC_CH1_LPM_HS;
break ;
default :
return -EINVAL;
}
/*
* 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 ) .
*
* Note that IRQ # 32 is GIC SPI # 0 .
*/
if (mode != WAIT_CLOCKED)
imx_gpc_hwirq_unmask(0 );
writel_relaxed(val, ccm_base + CLPCR);
if (mode != WAIT_CLOCKED)
imx_gpc_hwirq_mask(0 );
return 0 ;
}
static int imx6q_suspend_finish(unsigned long val)
{
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();
/* check if need to flush internal L2 cache */
if (!((struct imx6_cpu_pm_info *)
suspend_ocram_base)->l2_base.vbase)
flush_cache_all();
imx6_suspend_in_ocram_fn(suspend_ocram_base);
}
return 0 ;
}
static int imx6q_pm_enter(suspend_state_t state)
{
switch (state) {
case PM_SUSPEND_STANDBY:
imx6_set_lpm(STOP_POWER_ON);
imx6_set_int_mem_clk_lpm(true );
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();
imx6_set_lpm(WAIT_CLOCKED);
break ;
case PM_SUSPEND_MEM:
imx6_set_lpm(STOP_POWER_OFF);
imx6_set_int_mem_clk_lpm(false );
imx6q_enable_wb(true );
/*
* 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)
imx6_enable_rbc(true );
imx_gpc_pre_suspend(true );
imx_anatop_pre_suspend();
/* Zzz ... */
cpu_suspend(0 , imx6q_suspend_finish);
if (cpu_is_imx6q() || cpu_is_imx6dl())
imx_smp_prepare();
imx_anatop_post_resume();
imx_gpc_post_resume();
imx6_enable_rbc(false );
imx6q_enable_wb(false );
imx6_set_int_mem_clk_lpm(true );
imx6_set_lpm(WAIT_CLOCKED);
break ;
default :
return -EINVAL;
}
return 0 ;
}
static int imx6q_pm_valid(suspend_state_t state)
{
return (state == PM_SUSPEND_STANDBY || state == PM_SUSPEND_MEM);
}
static const struct platform_suspend_ops imx6q_pm_ops = {
.enter = imx6q_pm_enter,
.valid = imx6q_pm_valid,
};
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);
if (!node)
return -ENODEV;
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;
}
static int __init imx6q_suspend_init(const struct imx6_pm_socdata *socdata)
{
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;
suspend_set_ops(&imx6q_pm_ops);
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;
}
ocram_pool = gen_pool_get(&pdev->dev, NULL);
if (!ocram_pool) {
pr_warn("%s: ocram pool unavailable!\n" , __func__);
ret = -ENODEV;
goto put_device;
}
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;
goto put_device;
}
ocram_pbase = gen_pool_virt_to_phys(ocram_pool, ocram_base);
suspend_ocram_base = __arm_ioremap_exec(ocram_pbase,
MX6Q_SUSPEND_OCRAM_SIZE, false );
memset(suspend_ocram_base, 0 , sizeof (*pm_info));
pm_info = suspend_ocram_base;
pm_info->pbase = ocram_pbase;
pm_info->resume_addr = __pa_symbol(v7_cpu_resume);
pm_info->pm_info_size = sizeof (*pm_info);
/*
* ccm physical address is not used by asm code currently ,
* so get ccm virtual address directly .
*/
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);
goto put_device;
}
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;
}
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;
}
}
pm_info->ddr_type = imx_mmdc_get_ddr_type();
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));
__arm_iomem_set_ro(suspend_ocram_base, MX6Q_SUSPEND_OCRAM_SIZE);
goto put_device;
pl310_cache_map_failed:
iounmap(pm_info->gpc_base.vbase);
gpc_map_failed:
iounmap(pm_info->iomuxc_base.vbase);
iomuxc_map_failed:
iounmap(pm_info->src_base.vbase);
src_map_failed:
iounmap(pm_info->mmdc_base.vbase);
put_device:
put_device(&pdev->dev);
put_node:
of_node_put(node);
return ret;
}
static void __init imx6_pm_common_init(const struct imx6_pm_socdata
*socdata)
{
struct regmap *gpr;
int ret;
WARN_ON(!ccm_base);
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);
}
/*
* This is for SW workaround step # 1 of ERR007265 , see comments
* in imx6_set_lpm for details of this errata .
* 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);
}
static void imx6_pm_stby_poweroff(void )
{
gic_cpu_if_down(0 );
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 (register_platform_power_off(imx6_pm_stby_poweroff)) {
pr_warn("%s: platform power off already claimed!\n" , __func__);
return -EBUSY;
}
return 0 ;
}
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);
if (of_property_read_bool(np, "fsl,pmic-stby-poweroff" ))
imx6_pm_stby_poweroff_probe();
of_node_put(np);
}
void __init imx6q_pm_init(void )
{
imx6_pm_common_init(&imx6q_pm_data);
}
void __init imx6dl_pm_init(void )
{
imx6_pm_common_init(&imx6dl_pm_data);
}
void __init imx6sl_pm_init(void )
{
struct regmap *gpr;
if (cpu_is_imx6sl()) {
imx6_pm_common_init(&imx6sl_pm_data);
} else {
imx6_pm_common_init(&imx6sll_pm_data);
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 );
}
}
void __init imx6sx_pm_init(void )
{
imx6_pm_common_init(&imx6sx_pm_data);
}
void __init imx6ul_pm_init(void )
{
imx6_pm_common_init(&imx6ul_pm_data);
}
Messung V0.5 in Prozent C=92 H=86 G=88
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland