MLK-11365-02 ARM: imx: add M/F mix support on imx6ul

Add M/F mix support on i.MX6UL.

Signed-off-by: Bai Ping <b51503@freescale.com>
Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>
This commit is contained in:
Bai Ping
2015-08-13 18:52:08 +08:00
committed by Jason Liu
parent 71d040fd40
commit 86f0cd3bda
3 changed files with 263 additions and 31 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2011-2013 Freescale Semiconductor, Inc.
* Copyright 2011-2015 Freescale Semiconductor, Inc.
* Copyright 2011 Linaro Ltd.
*
* The code contained herein is licensed under the GNU General Public
@ -21,6 +21,7 @@
#include "hardware.h"
#define GPC_IMR1 0x008
#define GPC_PGC_MF_PDN 0x220
#define GPC_PGC_CPU_PDN 0x2a0
#define GPC_PGC_CPU_PUPSCR 0x2a4
#define GPC_PGC_CPU_PDNSCR 0x2a8
@ -33,6 +34,27 @@
static void __iomem *gpc_base;
static u32 gpc_wake_irqs[IMR_NUM];
static u32 gpc_saved_imrs[IMR_NUM];
static u32 gpc_mf_irqs[IMR_NUM];
static u32 gpc_mf_request_on[IMR_NUM];
unsigned int imx_gpc_is_mf_mix_off(void)
{
return readl_relaxed(gpc_base + GPC_PGC_MF_PDN);
}
static void imx_gpc_mf_mix_off(void)
{
int i;
for (i = 0; i < IMR_NUM; i++)
if (((gpc_wake_irqs[i] | gpc_mf_request_on[i]) &
gpc_mf_irqs[i]) != 0)
return;
pr_info("Turn off M/F mix!\n");
/* turn off mega/fast mix */
writel_relaxed(0x1, gpc_base + GPC_PGC_MF_PDN);
}
void imx_gpc_set_arm_power_up_timing(u32 sw2iso, u32 sw)
{
@ -56,6 +78,10 @@ void imx_gpc_pre_suspend(bool arm_power_off)
void __iomem *reg_imr1 = gpc_base + GPC_IMR1;
int i;
/* power down the mega-fast power domain */
if (cpu_is_imx6ul() && arm_power_off)
imx_gpc_mf_mix_off();
/* Tell GPC to power off ARM core when suspend */
if (arm_power_off)
imx_gpc_set_arm_power_in_lpm(arm_power_off);
@ -250,6 +276,21 @@ static int __init imx_gpc_init(struct device_node *node,
for (i = 0; i < IMR_NUM; i++)
writel_relaxed(~0, gpc_base + GPC_IMR1 + i * 4);
/* Read supported wakeup source in M/F domain */
if (cpu_is_imx6ul()) {
of_property_read_u32_index(node, "fsl,mf-mix-wakeup-irq", 0,
&gpc_mf_irqs[0]);
of_property_read_u32_index(node, "fsl,mf-mix-wakeup-irq", 1,
&gpc_mf_irqs[1]);
of_property_read_u32_index(node, "fsl,mf-mix-wakeup-irq", 2,
&gpc_mf_irqs[2]);
of_property_read_u32_index(node, "fsl,mf-mix-wakeup-irq", 3,
&gpc_mf_irqs[3]);
if (!(gpc_mf_irqs[0] | gpc_mf_irqs[1] |
gpc_mf_irqs[2] | gpc_mf_irqs[3]))
pr_info("No wakeup source in Mega/Fast domain found!\n");
}
/*
* Clear the OF_POPULATED flag set in of_irq_init so that
* later the GPC power domain driver will not be skipped.

View File

@ -65,6 +65,7 @@
#define MX6Q_SUSPEND_OCRAM_SIZE 0x1000
#define MX6_MAX_MMDC_IO_NUM 33
#define MX6_MAX_MMDC_NUM 34
extern unsigned long iram_tlb_base_addr;
extern unsigned long iram_tlb_phys_addr;
@ -101,6 +102,8 @@ struct imx6_pm_socdata {
const char *pl310_compat;
const u32 mmdc_io_num;
const u32 *mmdc_io_offset;
const u32 mmdc_num;
const u32 *mmdc_offset;
};
static const u32 imx6q_mmdc_io_offset[] __initconst = {
@ -150,6 +153,16 @@ static const u32 imx6ul_mmdc_io_offset[] __initconst = {
0x494, 0x4b0, /* MODE_CTL, MODE, */
};
static const u32 imx6ul_mmdc_offset[] __initconst = {
0x01c, 0x800, 0x80c, 0x83c,
0x848, 0x850, 0x81c, 0x820,
0x82c, 0x830, 0x8c0, 0x8b8,
0x004, 0x008, 0x00c, 0x010,
0x014, 0x018, 0x01c, 0x02c,
0x030, 0x040, 0x000, 0x01c,
0x020, 0x818, 0x01c,
};
static const struct imx6_pm_socdata imx6q_pm_data __initconst = {
.mmdc_compat = "fsl,imx6q-mmdc",
.src_compat = "fsl,imx6q-src",
@ -158,6 +171,8 @@ static const struct imx6_pm_socdata imx6q_pm_data __initconst = {
.pl310_compat = "arm,pl310-cache",
.mmdc_io_num = ARRAY_SIZE(imx6q_mmdc_io_offset),
.mmdc_io_offset = imx6q_mmdc_io_offset,
.mmdc_num = 0,
.mmdc_offset = NULL,
};
static const struct imx6_pm_socdata imx6dl_pm_data __initconst = {
@ -168,6 +183,8 @@ static const struct imx6_pm_socdata imx6dl_pm_data __initconst = {
.pl310_compat = "arm,pl310-cache",
.mmdc_io_num = ARRAY_SIZE(imx6dl_mmdc_io_offset),
.mmdc_io_offset = imx6dl_mmdc_io_offset,
.mmdc_num = 0,
.mmdc_offset = NULL,
};
static const struct imx6_pm_socdata imx6sl_pm_data __initconst = {
@ -178,6 +195,8 @@ static const struct imx6_pm_socdata imx6sl_pm_data __initconst = {
.pl310_compat = "arm,pl310-cache",
.mmdc_io_num = ARRAY_SIZE(imx6sl_mmdc_io_offset),
.mmdc_io_offset = imx6sl_mmdc_io_offset,
.mmdc_num = 0,
.mmdc_offset = NULL,
};
static const struct imx6_pm_socdata imx6sx_pm_data __initconst = {
@ -188,6 +207,8 @@ static const struct imx6_pm_socdata imx6sx_pm_data __initconst = {
.pl310_compat = "arm,pl310-cache",
.mmdc_io_num = ARRAY_SIZE(imx6sx_mmdc_io_offset),
.mmdc_io_offset = imx6sx_mmdc_io_offset,
.mmdc_num = 0,
.mmdc_offset = NULL,
};
static const struct imx6_pm_socdata imx6ul_pm_data __initconst = {
@ -198,6 +219,8 @@ static const struct imx6_pm_socdata imx6ul_pm_data __initconst = {
.pl310_compat = NULL,
.mmdc_io_num = ARRAY_SIZE(imx6ul_mmdc_io_offset),
.mmdc_io_offset = imx6ul_mmdc_io_offset,
.mmdc_num = ARRAY_SIZE(imx6ul_mmdc_offset),
.mmdc_offset = imx6ul_mmdc_offset,
};
static struct map_desc iram_tlb_io_desc __initdata = {
@ -253,6 +276,8 @@ struct imx6_cpu_pm_info {
u32 ttbr1; /* Store TTBR1 */
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 */
u32 mmdc_num; /* Number of MMDC registers which need saved/restored. */
u32 mmdc_val[MX6_MAX_MMDC_NUM][2];
} __aligned(8);
unsigned long save_ttbr1(void)
@ -581,6 +606,7 @@ static int __init imx6q_suspend_init(const struct imx6_pm_socdata *socdata)
unsigned long iram_paddr;
int i, ret = 0;
const u32 *mmdc_offset_array;
const u32 *mmdc_io_offset_array;
suspend_set_ops(&imx6q_pm_ops);
@ -644,16 +670,34 @@ static int __init imx6q_suspend_init(const struct imx6_pm_socdata *socdata)
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;
mmdc_io_offset_array = socdata->mmdc_io_offset;
pm_info->mmdc_num = socdata->mmdc_num;
mmdc_offset_array = socdata->mmdc_offset;
for (i = 0; i < pm_info->mmdc_io_num; i++) {
pm_info->mmdc_io_val[i][0] =
mmdc_offset_array[i];
mmdc_io_offset_array[i];
pm_info->mmdc_io_val[i][1] =
readl_relaxed(pm_info->iomuxc_base.vbase +
mmdc_io_offset_array[i]);
}
/* initialize MMDC settings */
for (i = 0; i < pm_info->mmdc_num; i++) {
pm_info->mmdc_val[i][0] =
mmdc_offset_array[i];
pm_info->mmdc_val[i][1] =
readl_relaxed(pm_info->mmdc_base.vbase +
mmdc_offset_array[i]);
}
/* need to overwrite the value for some mmdc registers */
if (cpu_is_imx6ul()) {
pm_info->mmdc_val[20][1] = (pm_info->mmdc_val[20][1]
& 0xffff0000) | 0x0202;
pm_info->mmdc_val[23][1] = 0x8033;
}
imx6_suspend_in_ocram_fn = fncpy(
suspend_ocram_base + sizeof(*pm_info),
&imx6_suspend,

View File

@ -64,6 +64,9 @@
#define PM_INFO_MX6Q_TTBR1_V_OFFSET 0x48
#define PM_INFO_MMDC_IO_NUM_OFFSET 0x4c
#define PM_INFO_MMDC_IO_VAL_OFFSET 0x50
/* below offsets depends on MX6_MAX_MMDC_IO_NUM(33) definition */
#define PM_INFO_MMDC_NUM_OFFSET 0x158
#define PM_INFO_MMDC_VAL_OFFSET 0x15c
#define MX6Q_SRC_GPR1 0x20
#define MX6Q_SRC_GPR2 0x24
@ -134,29 +137,8 @@
.endm
.macro resume_mmdc
/* restore MMDC IO */
cmp r5, #0x0
ldreq r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
ldrne r11, [r0, #PM_INFO_MX6Q_IOMUXC_P_OFFSET]
ldr r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET
add r7, r7, r0
1:
ldr r8, [r7], #0x4
ldr r9, [r7], #0x4
str r9, [r11, r8]
subs r6, r6, #0x1
bne 1b
cmp r5, #0x0
ldreq r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
ldrne r11, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET]
cmp r3, #IMX_DDR_TYPE_LPDDR2
bne 4f
/* r11 must be MMDC base address */
.macro reset_read_fifo
/* reset read FIFO, RST_RD_FIFO */
ldr r7, =MX6Q_MMDC_MPDGCTRL0
@ -176,15 +158,20 @@
ldr r6, [r11, r7]
ands r6, r6, #(1 << 31)
bne 3b
4:
.endm
/* r11 must be MMDC base address */
.macro mmdc_out_and_auto_self_refresh
/* let DDR out of self-refresh */
ldr r7, [r11, #MX6Q_MMDC_MAPSR]
bic r7, r7, #(1 << 21)
str r7, [r11, #MX6Q_MMDC_MAPSR]
5:
4:
ldr r7, [r11, #MX6Q_MMDC_MAPSR]
ands r7, r7, #(1 << 25)
bne 5b
bne 4b
/* enable DDR auto power saving */
ldr r7, [r11, #MX6Q_MMDC_MAPSR]
@ -193,6 +180,126 @@
.endm
/* r10 must be iomuxc base address */
.macro resume_iomuxc_gpr
add r10, r10, #0x4000
/* IOMUXC GPR DRAM_RESET_BYPASS */
ldr r4, [r10, #0x8]
bic r4, r4, #(0x1 << 27)
str r4, [r10, #0x8]
/* IOMUXC GPR DRAM_CKE_BYPASS */
ldr r4, [r10, #0x8]
bic r4, r4, #(0x1 << 31)
str r4, [r10, #0x8]
.endm
.macro resume_io
/* restore MMDC IO */
cmp r5, #0x0
ldreq r10, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
ldrne r10, [r0, #PM_INFO_MX6Q_IOMUXC_P_OFFSET]
ldr r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET
add r7, r7, r0
5:
ldr r8, [r7], #0x4
ldr r9, [r7], #0x4
str r9, [r10, r8]
subs r6, r6, #0x1
bne 5b
cmp r5, #0x0
/* Here only MMDC0 is set */
ldreq r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
ldrne r11, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET]
cmp r3, #IMX_DDR_TYPE_LPDDR2
bne 6f
reset_read_fifo
6:
mmdc_out_and_auto_self_refresh
.endm
.macro resume_mmdc_io
cmp r5, #0x0
ldreq r10, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
ldrne r10, [r0, #PM_INFO_MX6Q_IOMUXC_P_OFFSET]
ldreq r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
ldrne r11, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET]
/* resume mmdc iomuxc settings */
ldr r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET
add r7, r7, r0
7:
ldr r8, [r7], #0x4
ldr r9, [r7], #0x4
str r9, [r10, r8]
subs r6, r6, #0x1
bne 7b
/* check whether we need to restore MMDC */
cmp r5, #0x0
beq 8f
/* check whether last suspend is with M/F mix off */
ldr r9, [r0, #PM_INFO_MX6Q_GPC_P_OFFSET]
ldr r6, [r9, #0x220]
cmp r6, #0x0
bne 9f
8:
resume_iomuxc_gpr
b 13f
9:
/* restore MMDC settings */
ldr r6, [r0, #PM_INFO_MMDC_NUM_OFFSET]
ldr r7, =PM_INFO_MMDC_VAL_OFFSET
add r7, r7, r0
10:
ldr r8, [r7], #0x4
ldr r9, [r7], #0x4
str r9, [r11, r8]
subs r6, r6, #0x1
bne 10b
/* let DDR enter self-refresh */
ldr r7, [r11, #MX6Q_MMDC_MAPSR]
orr r7, r7, #(1 << 20)
str r7, [r11, #MX6Q_MMDC_MAPSR]
11:
ldr r7, [r11, #MX6Q_MMDC_MAPSR]
ands r7, r7, #(1 << 24)
beq 11b
resume_iomuxc_gpr
reset_read_fifo
/* let DDR out of self-refresh */
ldr r7, [r11, #MX6Q_MMDC_MAPSR]
bic r7, r7, #(1 << 20)
str r7, [r11, #MX6Q_MMDC_MAPSR]
12:
ldr r7, [r11, #MX6Q_MMDC_MAPSR]
ands r7, r7, #(1 << 24)
bne 12b
/* kick off MMDC */
ldr r4, =0x0
str r4, [r11, #0x1c]
13:
mmdc_out_and_auto_self_refresh
.endm
.macro store_ttbr1
/* Store TTBR1 to pm_info->ttbr1 */
@ -394,6 +501,28 @@ set_mmdc_io_lpm:
str r6, [r11, r9]
set_mmdc_io_lpm_done:
/* check whether it supports Mega/Fast off */
ldr r6, [r0, #PM_INFO_MMDC_NUM_OFFSET]
cmp r6, #0x0
beq set_mmdc_lpm_done
/* IOMUXC GPR DRAM_RESET */
add r11, r11, #0x4000
ldr r6, [r11, #0x8]
orr r6, r6, #(0x1 << 28)
str r6, [r11, #0x8]
/* IOMUXC GPR DRAM_RESET_BYPASS */
ldr r6, [r11, #0x8]
orr r6, r6, #(0x1 << 27)
str r6, [r11, #0x8]
/* IOMUXC GPR DRAM_CKE_BYPASS */
ldr r6, [r11, #0x8]
orr r6, r6, #(0x1 << 31)
str r6, [r11, #0x8]
set_mmdc_lpm_done:
/*
* mask all GPC interrupts before
* enabling the RBC counters to
@ -465,7 +594,16 @@ rbc_loop:
* resume, we need to restore MMDC IO first
*/
mov r5, #0x0
resume_mmdc
/* check whether it supports Mega/Fast off */
ldr r6, [r0, #PM_INFO_MMDC_NUM_OFFSET]
cmp r6, #0x0
beq only_resume_io
resume_mmdc_io
b resume_mmdc_done
only_resume_io:
resume_io
resume_mmdc_done:
restore_ttbr1
/* return to suspend finish */
@ -491,7 +629,16 @@ resume:
ldr r3, [r0, #PM_INFO_DDR_TYPE_OFFSET]
mov r5, #0x1
resume_mmdc
/* check whether it supports Mega/Fast off */
ldr r6, [r0, #PM_INFO_MMDC_NUM_OFFSET]
cmp r6, #0x0
beq dsm_only_resume_io
resume_mmdc_io
b dsm_resume_mmdc_done
dsm_only_resume_io:
ldr r3, [r0, #PM_INFO_DDR_TYPE_OFFSET]
resume_io
dsm_resume_mmdc_done:
ret lr
ENDPROC(imx6_suspend)