* [PATCH V3 1/5] ARM: imx: add suspend in ocram support on i.mx6q
@ 2014-01-13 5:58 Anson Huang
[not found] ` <1389592718-9999-1-git-send-email-b20788-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
0 siblings, 1 reply; 8+ messages in thread
From: Anson Huang @ 2014-01-13 5:58 UTC (permalink / raw)
To: shawn.guo-QSEj5FYQhm4dnm+yROfE0A, kernel-bIcnvbaLZ9MEGnE8C9+IrQ
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA
When system enter suspend, we can set the DDR IO
to high-Z state to save DDR IO's power, this operation
can save many power(from ~26mA@1.5V to ~15mA@1.5V,
measured on i.MX6Q/DL SabreSD board, R25) of DDR IO. To
achieve that, we need to copy the suspend code to ocram
and run the low level hardware related code(set DDR IO
to high-Z) in ocram.
If there is no ocram space available, then system will
still do suspend in DDR, hence no DDR IO will be set
to high-Z.
The OCRAM usage layout is as below,
ocram suspend region:
======================== high address ======================
.
.
.
^
^
^
imx6_suspend code
reserved region(to make imx6_suspend aligned with 8)
PM_INFO structure(imx6_cpu_pm_info)
======================== low address =======================
Signed-off-by: Anson Huang <b20788-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
---
Changes since V2:
1. save MMDC IO info during initialization for once, no
need to do save everytime before suspend;
2. Improve ocram suspend initialization, put all of them
into a function;
3. Improve the initialization of pm_info's modules, create
a function call for them.
arch/arm/mach-imx/Makefile | 3 +-
arch/arm/mach-imx/common.h | 4 +-
arch/arm/mach-imx/hardware.h | 4 +-
arch/arm/mach-imx/pm-imx6q.c | 244 +++++++++++++++++++++++++++++-
arch/arm/mach-imx/suspend-imx6.S | 306 ++++++++++++++++++++++++++++++++++++++
5 files changed, 554 insertions(+), 7 deletions(-)
create mode 100644 arch/arm/mach-imx/suspend-imx6.S
diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile
index befcaf5..3d96a45 100644
--- a/arch/arm/mach-imx/Makefile
+++ b/arch/arm/mach-imx/Makefile
@@ -102,7 +102,8 @@ obj-$(CONFIG_SOC_IMX6Q) += clk-imx6q.o mach-imx6q.o
obj-$(CONFIG_SOC_IMX6SL) += clk-imx6sl.o mach-imx6sl.o
ifeq ($(CONFIG_PM),y)
-obj-$(CONFIG_SOC_IMX6Q) += pm-imx6q.o headsmp.o
+AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a
+obj-$(CONFIG_SOC_IMX6Q) += pm-imx6q.o headsmp.o suspend-imx6.o
# i.MX6SL reuses i.MX6Q code
obj-$(CONFIG_SOC_IMX6SL) += pm-imx6q.o headsmp.o
endif
diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h
index 4f4a95c..7c249e1 100644
--- a/arch/arm/mach-imx/common.h
+++ b/arch/arm/mach-imx/common.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2004-2014 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
@@ -145,10 +145,12 @@ void imx_cpu_die(unsigned int cpu);
int imx_cpu_kill(unsigned int cpu);
#ifdef CONFIG_PM
+void imx6_suspend(void);
void imx6q_pm_init(void);
void imx6q_pm_set_ccm_base(void __iomem *base);
void imx5_pm_init(void);
#else
+static inline void imx6_suspend(void) {}
static inline void imx6q_pm_init(void) {}
static inline void imx6q_pm_set_ccm_base(void __iomem *base) {}
static inline void imx5_pm_init(void) {}
diff --git a/arch/arm/mach-imx/hardware.h b/arch/arm/mach-imx/hardware.h
index a3b0b04..abf43bb 100644
--- a/arch/arm/mach-imx/hardware.h
+++ b/arch/arm/mach-imx/hardware.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2004-2007, 2014 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2008 Juergen Beisert, kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org
*
* This program is free software; you can redistribute it and/or
@@ -20,7 +20,9 @@
#ifndef __ASM_ARCH_MXC_HARDWARE_H__
#define __ASM_ARCH_MXC_HARDWARE_H__
+#ifndef __ASSEMBLY__
#include <asm/io.h>
+#endif
#include <asm/sizes.h>
#define addr_in_module(addr, mod) \
diff --git a/arch/arm/mach-imx/pm-imx6q.c b/arch/arm/mach-imx/pm-imx6q.c
index d45acc0..8997f58 100644
--- a/arch/arm/mach-imx/pm-imx6q.c
+++ b/arch/arm/mach-imx/pm-imx6q.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2011-2013 Freescale Semiconductor, Inc.
+ * Copyright 2011-2014 Freescale Semiconductor, Inc.
* Copyright 2011 Linaro Ltd.
*
* The code contained herein is licensed under the GNU General Public
@@ -14,16 +14,21 @@
#include <linux/init.h>
#include <linux/io.h>
#include <linux/irq.h>
+#include <linux/genalloc.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/regmap.h>
#include <linux/suspend.h>
#include <asm/cacheflush.h>
+#include <asm/fncpy.h>
+#include <asm/hardware/cache-l2x0.h>
+#include <asm/mach/map.h>
#include <asm/proc-fns.h>
#include <asm/suspend.h>
-#include <asm/hardware/cache-l2x0.h>
+#include <asm/tlb.h>
#include "common.h"
#include "hardware.h"
@@ -58,7 +63,69 @@
#define CGPR 0x64
#define BM_CGPR_INT_MEM_CLK_LPM (0x1 << 17)
+#define MX6Q_SUSPEND_OCRAM_SIZE 0x1000
+#define MX6_MAX_MMDC_IO_NUM 33
+#define MX6Q_MMDC_IO_NUM 33
+
static void __iomem *ccm_base;
+static void __iomem *suspend_ocram_base;
+static int (*imx6_suspend_in_ocram_fn)(void __iomem *ocram_vbase);
+
+/*
+ * suspend ocram space layout:
+ * ======================== high address ======================
+ * .
+ * .
+ * .
+ * ^
+ * ^
+ * ^
+ * imx6_suspend code
+ * reserved region(to make imx6_suspend aligned with 8)
+ * PM_INFO structure(imx6_cpu_pm_info)
+ * ======================== low address =======================
+ */
+
+struct imx6_pm_base {
+ phys_addr_t pbase;
+ void __iomem *vbase;
+};
+
+static u32 imx6q_mmdc_io_dsm_offset[] = {
+ 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, DDRMODE_CTL, DDRMODE */
+ 0x74c /* GPR_ADDS */
+};
+
+/*
+ * 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 fucntion 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 cpu_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;
+ /* reserve some space to make ocram function's address aligned with 8 */
+ u32 reserved_size;
+ 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 */
+};
void imx6q_set_int_mem_clk_lpm(void)
{
@@ -177,7 +244,17 @@ int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
static int imx6q_suspend_finish(unsigned long val)
{
- cpu_do_idle();
+ 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();
+ imx6_suspend_in_ocram_fn(suspend_ocram_base);
+ }
+
return 0;
}
@@ -187,7 +264,12 @@ static int imx6q_pm_enter(suspend_state_t state)
case PM_SUSPEND_MEM:
imx6q_set_lpm(STOP_POWER_OFF);
imx6q_enable_wb(true);
- imx6q_enable_rbc(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)
+ imx6q_enable_rbc(true);
imx_gpc_pre_suspend();
imx_anatop_pre_suspend();
imx_set_cpu_jump(0, v7_cpu_resume);
@@ -218,12 +300,166 @@ void __init imx6q_pm_set_ccm_base(void __iomem *base)
ccm_base = base;
}
+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) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ 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);
+out:
+ return ret;
+}
+
+static int __init imx6q_ocram_suspend_init(void)
+{
+ 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, reserved_size;
+ int i, ret = 0;
+
+ 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 = dev_get_gen_pool(&pdev->dev);
+ if (!ocram_pool) {
+ pr_warn("%s: ocram pool unavailable!\n", __func__);
+ ret = -ENODEV;
+ goto put_node;
+ }
+
+ 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_node;
+ }
+
+ ocram_pbase = gen_pool_virt_to_phys(ocram_pool, ocram_base);
+ reserved_size = 8 - ((ocram_pbase + sizeof(*pm_info)) % 8);
+
+ suspend_ocram_base = __arm_ioremap_exec(ocram_pbase,
+ MX6Q_SUSPEND_OCRAM_SIZE, false);
+
+ pm_info = suspend_ocram_base;
+ pm_info->pbase = ocram_pbase;
+ pm_info->resume_addr = virt_to_phys(v7_cpu_resume);
+ pm_info->pm_info_size = sizeof(*pm_info);
+ pm_info->reserved_size = reserved_size;
+
+ /*
+ * ccm physical address is not used by asm code currently,
+ * so get ccm virtual address directly, as we already have
+ * it from ccm driver.
+ */
+ pm_info->ccm_base.vbase = ccm_base;
+
+ ret = imx6_pm_get_base(&pm_info->mmdc_base, "fsl,imx6q-mmdc");
+ if (ret) {
+ pr_warn("%s: failed to get mmdc base %d!\n", __func__, ret);
+ goto put_node;
+ }
+
+ ret = imx6_pm_get_base(&pm_info->src_base, "fsl,imx6q-src");
+ 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, "fsl,imx6q-iomuxc");
+ 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, "fsl,imx6q-gpc");
+ if (ret) {
+ pr_warn("%s: failed to get gpc base %d!\n", __func__, ret);
+ goto gpc_map_failed;
+ }
+
+ ret = imx6_pm_get_base(&pm_info->l2_base, "arm,pl310-cache");
+ if (ret) {
+ pr_warn("%s: failed to get pl310-cache base %d!\n",
+ __func__, ret);
+ goto pl310_cache_map_failed;
+ }
+
+ if (cpu_is_imx6q()) {
+ pm_info->cpu_type = MXC_CPU_IMX6Q;
+ pm_info->mmdc_io_num = MX6Q_MMDC_IO_NUM;
+ for (i = 0; i < MX6Q_MMDC_IO_NUM; i++) {
+ pm_info->mmdc_io_val[i][0] =
+ imx6q_mmdc_io_dsm_offset[i];
+ pm_info->mmdc_io_val[i][1] =
+ readl_relaxed(*(&pm_info->iomuxc_base.vbase) +
+ imx6q_mmdc_io_dsm_offset[i]);
+ }
+ }
+
+ imx6_suspend_in_ocram_fn = (void *)fncpy(
+ suspend_ocram_base + sizeof(*pm_info) + reserved_size,
+ &imx6_suspend,
+ MX6Q_SUSPEND_OCRAM_SIZE - sizeof(*pm_info) - reserved_size);
+
+ goto put_node;
+
+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_node:
+ of_node_put(node);
+
+ return ret;
+}
+
void __init imx6q_pm_init(void)
{
struct regmap *gpr;
+ int ret;
WARN_ON(!ccm_base);
+ ret = imx6q_ocram_suspend_init();
+ if (ret)
+ pr_warn("%s: failed to initialize ocram suspend %d!\n",
+ __func__, ret);
+
/*
* This is for SW workaround step #1 of ERR007265, see comments
* in imx6q_set_lpm for details of this errata.
diff --git a/arch/arm/mach-imx/suspend-imx6.S b/arch/arm/mach-imx/suspend-imx6.S
new file mode 100644
index 0000000..da1f806
--- /dev/null
+++ b/arch/arm/mach-imx/suspend-imx6.S
@@ -0,0 +1,306 @@
+/*
+ * Copyright 2014 Freescale Semiconductor, Inc.
+ *
+ * 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
+ */
+
+#include <linux/linkage.h>
+#include <asm/hardware/cache-l2x0.h>
+#include "hardware.h"
+
+/*
+ * ==================== low level suspend ====================
+ *
+ * Better to follow below rules to use ARM registers:
+ * r0: pm_info structure address;
+ * r1 ~ r5: for saving pm_info members;
+ * r6 ~ r10: free registers;
+ * r11: io base address.
+ *
+ * suspend ocram space layout:
+ * ======================== high address ======================
+ * .
+ * .
+ * .
+ * ^
+ * ^
+ * ^
+ * imx6_suspend code
+ * reserved region(to make imx6_suspend aligned with 8)
+ * PM_INFO structure(imx6_cpu_pm_info)
+ * ======================== low address =======================
+ */
+
+/*
+ * Below offsets are based on struct imx6_cpu_pm_info
+ * which defined in arch/arm/mach-imx/pm-imx6q.c, this
+ * structure contains necessary pm info for low level
+ * suspend related code.
+ */
+#define PM_INFO_PBASE_OFFSET 0x0
+#define PM_INFO_RESUME_ADDR_OFFSET 0x4
+#define PM_INFO_CPU_TYPE_OFFSET 0x8
+#define PM_INFO_PM_INFO_SIZE_OFFSET 0xC
+#define PM_INFO_MX6Q_MMDC_P_OFFSET 0x10
+#define PM_INFO_MX6Q_MMDC_V_OFFSET 0x14
+#define PM_INFO_MX6Q_SRC_P_OFFSET 0x18
+#define PM_INFO_MX6Q_SRC_V_OFFSET 0x1C
+#define PM_INFO_MX6Q_IOMUXC_P_OFFSET 0x20
+#define PM_INFO_MX6Q_IOMUXC_V_OFFSET 0x24
+#define PM_INFO_MX6Q_CCM_P_OFFSET 0x28
+#define PM_INFO_MX6Q_CCM_V_OFFSET 0x2C
+#define PM_INFO_MX6Q_GPC_P_OFFSET 0x30
+#define PM_INFO_MX6Q_GPC_V_OFFSET 0x34
+#define PM_INFO_MX6Q_L2_P_OFFSET 0x38
+#define PM_INFO_MX6Q_L2_V_OFFSET 0x3C
+#define PM_INFO_RESERVED_SIZE_OFFSET 0x40
+#define PM_INFO_MMDC_IO_NUM_OFFSET 0x44
+#define PM_INFO_MMDC_IO_VAL_OFFSET 0x48
+
+#define MX6Q_SRC_GPR1 0x20
+#define MX6Q_SRC_GPR2 0x24
+#define MX6Q_MMDC_MAPSR 0x404
+#define MX6Q_GPC_IMR1 0x08
+#define MX6Q_GPC_IMR2 0x0c
+#define MX6Q_GPC_IMR3 0x10
+#define MX6Q_GPC_IMR4 0x14
+#define MX6Q_CCM_CCR 0x0
+
+ .align 3
+
+ .macro sync_l2_cache
+
+ /* sync L2 cache to drain L2's buffers to DRAM. */
+#ifdef CONFIG_CACHE_L2X0
+ ldr r11, [r0, #PM_INFO_MX6Q_L2_V_OFFSET]
+ mov r6, #0x0
+ str r6, [r11, #L2X0_CACHE_SYNC]
+1:
+ ldr r6, [r11, #L2X0_CACHE_SYNC]
+ ands r6, r6, #0x1
+ bne 1b
+#endif
+ .endm
+
+ENTRY(imx6_suspend)
+ ldr r1, [r0, #PM_INFO_PBASE_OFFSET]
+ ldr r2, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
+ ldr r3, [r0, #PM_INFO_CPU_TYPE_OFFSET]
+ ldr r4, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET]
+ ldr r5, [r0, #PM_INFO_RESERVED_SIZE_OFFSET]
+
+ /*
+ * counting the resume address in iram
+ * to set it in SRC register.
+ */
+ ldr r6, =imx6_suspend
+ ldr r7, =resume
+ sub r7, r7, r6
+ add r8, r1, r4
+ add r8, r8, r5
+ add r9, r8, r7
+
+ /*
+ * make sure TLB contain the addr we want,
+ * as we will access them after MMDC IO floated.
+ */
+
+ ldr r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
+ ldr r6, [r11, #0x0]
+ ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
+ ldr r6, [r11, #0x0]
+
+ /* use r11 to store the IO address */
+ ldr r11, [r0, #PM_INFO_MX6Q_SRC_V_OFFSET]
+ /* store physical resume addr and pm_info address. */
+ str r9, [r11, #MX6Q_SRC_GPR1]
+ str r1, [r11, #MX6Q_SRC_GPR2]
+
+ /* need to sync L2 cache before DSM. */
+ sync_l2_cache
+
+ ldr r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
+ /*
+ * put DDR explicitly into self-refresh and
+ * disable automatic power savings.
+ */
+ ldr r7, [r11, #MX6Q_MMDC_MAPSR]
+ orr r7, r7, #0x1
+ str r7, [r11, #MX6Q_MMDC_MAPSR]
+
+ /* make the DDR explicitly enter self-refresh. */
+ ldr r7, [r11, #MX6Q_MMDC_MAPSR]
+ orr r7, r7, #(1 << 21)
+ str r7, [r11, #MX6Q_MMDC_MAPSR]
+
+poll_dvfs_set_1:
+ ldr r7, [r11, #0x404]
+ ands r7, r7, #(1 << 25)
+ beq poll_dvfs_set_1
+
+ ldr r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
+ ldr r6, =0x0
+ ldr r7, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
+ ldr r8, =PM_INFO_MMDC_IO_VAL_OFFSET
+set_mmdc_io_lpm:
+ ldr r9, [r0, r8]
+ str r6, [r11, r9]
+ add r8, r8, #0x8
+ sub r7, r7, #0x1
+ cmp r7, #0x0
+ bne set_mmdc_io_lpm
+
+ /*
+ * mask all GPC interrupts before
+ * enabling the RBC counters to
+ * avoid the counter starting too
+ * early if an interupt is already
+ * pending.
+ */
+ ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
+ ldr r6, [r11, #MX6Q_GPC_IMR1]
+ ldr r7, [r11, #MX6Q_GPC_IMR2]
+ ldr r8, [r11, #MX6Q_GPC_IMR3]
+ ldr r9, [r11, #MX6Q_GPC_IMR4]
+
+ ldr r10, =0xffffffff
+ str r10, [r11, #MX6Q_GPC_IMR1]
+ str r10, [r11, #MX6Q_GPC_IMR2]
+ str r10, [r11, #MX6Q_GPC_IMR3]
+ str r10, [r11, #MX6Q_GPC_IMR4]
+
+ /*
+ * enable the RBC bypass counter here
+ * to hold off the interrupts. RBC counter
+ * = 32 (1ms), Minimum RBC delay should be
+ * 400us for the analog LDOs to power down.
+ */
+ ldr r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
+ ldr r10, [r11, #MX6Q_CCM_CCR]
+ bic r10, r10, #(0x3f << 21)
+ orr r10, r10, #(0x20 << 21)
+ str r10, [r11, #MX6Q_CCM_CCR]
+
+ /* enable the counter. */
+ ldr r10, [r11, #MX6Q_CCM_CCR]
+ orr r10, r10, #(0x1 << 27)
+ str r10, [r11, #MX6Q_CCM_CCR]
+
+ /* unmask all the GPC interrupts. */
+ ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
+ str r6, [r11, #MX6Q_GPC_IMR1]
+ str r7, [r11, #MX6Q_GPC_IMR2]
+ str r8, [r11, #MX6Q_GPC_IMR3]
+ str r9, [r11, #MX6Q_GPC_IMR4]
+
+ /*
+ * now delay for a short while (3usec)
+ * ARM is at 1GHz at this point
+ * so a short loop should be enough.
+ * this delay is required to ensure that
+ * the RBC counter can start counting in
+ * case an interrupt is already pending
+ * or in case an interrupt arrives just
+ * as ARM is about to assert DSM_request.
+ */
+ ldr r6, =2000
+rbc_loop:
+ sub r6, r6, #0x1
+ cmp r6, #0x0
+ bne rbc_loop
+
+ /* Zzz, enter stop mode */
+ wfi
+ nop
+ nop
+ nop
+ nop
+
+ /*
+ * run to here means there is pending
+ * wakeup source, system should auto
+ * resume, we need to restore MMDC IO first
+ */
+ ldr r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
+ ldr r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
+ ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET
+restore_mmdc_io:
+ ldr r8, [r0, r7]
+ add r7, r7, #0x4
+ ldr r9, [r0, r7]
+ add r7, r7, #0x4
+ str r9, [r11, r8]
+ sub r6, r6, #0x1
+ cmp r6, #0x0
+ bne restore_mmdc_io
+
+ ldr r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
+ /* let DDR out of self-refresh. */
+ ldr r7, [r11, #MX6Q_MMDC_MAPSR]
+ bic r7, r7, #(1 << 21)
+ str r7, [r11, #MX6Q_MMDC_MAPSR]
+
+poll_dvfs_clear_2:
+ ldr r7, [r11, #MX6Q_MMDC_MAPSR]
+ ands r7, r7, #(1 << 25)
+ bne poll_dvfs_clear_2
+ /* enable DDR auto power saving */
+ ldr r7, [r11, #MX6Q_MMDC_MAPSR]
+ bic r7, r7, #0x1
+ str r7, [r11, #MX6Q_MMDC_MAPSR]
+ /* return to suspend finish */
+ mov pc, lr
+
+resume:
+ /* invalidate L1 I-cache first */
+ mov r6, #0x0
+ mcr p15, 0, r6, c7, c5, 0
+ mcr p15, 0, r6, c7, c5, 0
+ mcr p15, 0, r6, c7, c5, 6
+ /* enable the Icache and branch prediction */
+ mov r6, #0x1800
+ mcr p15, 0, r6, c1, c0, 0
+ isb
+
+ /* get physical resume address from pm_info. */
+ ldr lr, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
+ /* clear core0's entry and parameter */
+ ldr r11, [r0, #PM_INFO_MX6Q_SRC_P_OFFSET]
+ mov r7, #0
+ str r7, [r11, #MX6Q_SRC_GPR1]
+ str r7, [r11, #MX6Q_SRC_GPR2]
+
+ ldr 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
+dsm_restore_mmdc_io:
+ ldr r8, [r0, r7]
+ add r7, r7, #0x4
+ ldr r9, [r0, r7]
+ add r7, r7, #0x4
+ str r9, [r11, r8]
+ sub r6, r6, #0x1
+ cmp r6, #0x0
+ bne dsm_restore_mmdc_io
+
+ ldr r11, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET]
+ /* let DDR out of self-refresh */
+ ldr r7, [r11, #MX6Q_MMDC_MAPSR]
+ bic r7, r7, #(1 << 21)
+ str r7, [r11, #MX6Q_MMDC_MAPSR]
+
+poll_dvfs_clear_1:
+ ldr r7, [r11, #MX6Q_MMDC_MAPSR]
+ ands r7, r7, #(1 << 25)
+ bne poll_dvfs_clear_1
+ /* enable DDR auto power saving */
+ ldr r7, [r11, #MX6Q_MMDC_MAPSR]
+ bic r7, r7, #0x1
+ str r7, [r11, #MX6Q_MMDC_MAPSR]
+ mov pc, lr
+ENDPROC(imx6_suspend)
--
1.7.9.5
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related [flat|nested] 8+ messages in thread[parent not found: <1389592718-9999-1-git-send-email-b20788-KZfg59tc24xl57MIdRCFDg@public.gmane.org>]
* [PATCH V3 2/5] ARM: dts: imx6dl: Remove unnecessary node [not found] ` <1389592718-9999-1-git-send-email-b20788-KZfg59tc24xl57MIdRCFDg@public.gmane.org> @ 2014-01-13 5:58 ` Anson Huang [not found] ` <1389592718-9999-2-git-send-email-b20788-KZfg59tc24xl57MIdRCFDg@public.gmane.org> 2014-01-13 5:58 ` [PATCH V3 3/5] ARM: imx: add suspend in ocram support on i.mx6dl Anson Huang ` (4 subsequent siblings) 5 siblings, 1 reply; 8+ messages in thread From: Anson Huang @ 2014-01-13 5:58 UTC (permalink / raw) To: shawn.guo-QSEj5FYQhm4dnm+yROfE0A, kernel-bIcnvbaLZ9MEGnE8C9+IrQ Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree-u79uwXL29TY76Z2rM5mHXA IOMUXC node is included in i.mx6qdl.dtsi, so no need to overwrite it, as it is a reused module from i.mx6q. Signed-off-by: Anson Huang <b20788-KZfg59tc24xl57MIdRCFDg@public.gmane.org> --- arch/arm/boot/dts/imx6dl.dtsi | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/arm/boot/dts/imx6dl.dtsi b/arch/arm/boot/dts/imx6dl.dtsi index e2ec0fb..d590d8d 100644 --- a/arch/arm/boot/dts/imx6dl.dtsi +++ b/arch/arm/boot/dts/imx6dl.dtsi @@ -61,10 +61,6 @@ }; aips1: aips-bus@02000000 { - iomuxc: iomuxc@020e0000 { - compatible = "fsl,imx6dl-iomuxc"; - }; - pxp: pxp@020f0000 { reg = <0x020f0000 0x4000>; interrupts = <0 98 IRQ_TYPE_LEVEL_HIGH>; -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply related [flat|nested] 8+ messages in thread
[parent not found: <1389592718-9999-2-git-send-email-b20788-KZfg59tc24xl57MIdRCFDg@public.gmane.org>]
* Re: [PATCH V3 2/5] ARM: dts: imx6dl: Remove unnecessary node [not found] ` <1389592718-9999-2-git-send-email-b20788-KZfg59tc24xl57MIdRCFDg@public.gmane.org> @ 2014-01-13 8:06 ` Shawn Guo 0 siblings, 0 replies; 8+ messages in thread From: Shawn Guo @ 2014-01-13 8:06 UTC (permalink / raw) To: Anson Huang Cc: kernel-bIcnvbaLZ9MEGnE8C9+IrQ, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree-u79uwXL29TY76Z2rM5mHXA On Mon, Jan 13, 2014 at 01:58:35PM +0800, Anson Huang wrote: > IOMUXC node is included in i.mx6qdl.dtsi, so no need > to overwrite it, as it is a reused module from i.mx6q. > > Signed-off-by: Anson Huang <b20788-KZfg59tc24xl57MIdRCFDg@public.gmane.org> > --- > arch/arm/boot/dts/imx6dl.dtsi | 4 ---- > 1 file changed, 4 deletions(-) > > diff --git a/arch/arm/boot/dts/imx6dl.dtsi b/arch/arm/boot/dts/imx6dl.dtsi > index e2ec0fb..d590d8d 100644 > --- a/arch/arm/boot/dts/imx6dl.dtsi > +++ b/arch/arm/boot/dts/imx6dl.dtsi > @@ -61,10 +61,6 @@ > }; > > aips1: aips-bus@02000000 { > - iomuxc: iomuxc@020e0000 { > - compatible = "fsl,imx6dl-iomuxc"; > - }; > - No. drivers/pinctrl/pinctrl-imx6dl.c needs it. Shawn > pxp: pxp@020f0000 { > reg = <0x020f0000 0x4000>; > interrupts = <0 98 IRQ_TYPE_LEVEL_HIGH>; > -- > 1.7.9.5 > > -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH V3 3/5] ARM: imx: add suspend in ocram support on i.mx6dl [not found] ` <1389592718-9999-1-git-send-email-b20788-KZfg59tc24xl57MIdRCFDg@public.gmane.org> 2014-01-13 5:58 ` [PATCH V3 2/5] ARM: dts: imx6dl: Remove unnecessary node Anson Huang @ 2014-01-13 5:58 ` Anson Huang 2014-01-13 5:58 ` [PATCH V3 4/5] ARM: dts: imx6sl: add compatible name for i.mx6q Anson Huang ` (3 subsequent siblings) 5 siblings, 0 replies; 8+ messages in thread From: Anson Huang @ 2014-01-13 5:58 UTC (permalink / raw) To: shawn.guo-QSEj5FYQhm4dnm+yROfE0A, kernel-bIcnvbaLZ9MEGnE8C9+IrQ Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree-u79uwXL29TY76Z2rM5mHXA i.MX6DL's suspend in ocram function is derived from i.MX6Q, the only difference is the offset of DDR IO pins. It can lower the DDR IO power from ~26mA@1.5V to ~15mA@1.5V, measured on i.MX6Q/DL SabreSD board, R25. Signed-off-by: Anson Huang <b20788-KZfg59tc24xl57MIdRCFDg@public.gmane.org> --- Changes since V2: Only do necessary change based on i.mx6q's suspend to ocram function, only cpu type and MMDC IOs' info need changed. arch/arm/mach-imx/pm-imx6q.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/arch/arm/mach-imx/pm-imx6q.c b/arch/arm/mach-imx/pm-imx6q.c index 8997f58..670070b 100644 --- a/arch/arm/mach-imx/pm-imx6q.c +++ b/arch/arm/mach-imx/pm-imx6q.c @@ -66,6 +66,7 @@ #define MX6Q_SUSPEND_OCRAM_SIZE 0x1000 #define MX6_MAX_MMDC_IO_NUM 33 #define MX6Q_MMDC_IO_NUM 33 +#define MX6DL_MMDC_IO_NUM 33 static void __iomem *ccm_base; static void __iomem *suspend_ocram_base; @@ -103,6 +104,18 @@ static u32 imx6q_mmdc_io_dsm_offset[] = { 0x74c /* GPR_ADDS */ }; +static u32 imx6dl_mmdc_io_dsm_offset[] = { + 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, DDRMODE_CTL, DDRMODE */ + 0x74c /* GPR_ADDS */ +}; + /* * This structure is for passing necessary data for low level ocram * suspend code(arch/arm/mach-imx/suspend-imx6.S), if this struct @@ -425,6 +438,16 @@ static int __init imx6q_ocram_suspend_init(void) readl_relaxed(*(&pm_info->iomuxc_base.vbase) + imx6q_mmdc_io_dsm_offset[i]); } + } else if (cpu_is_imx6dl()) { + pm_info->cpu_type = MXC_CPU_IMX6DL; + pm_info->mmdc_io_num = MX6DL_MMDC_IO_NUM; + for (i = 0; i < MX6DL_MMDC_IO_NUM; i++) { + pm_info->mmdc_io_val[i][0] = + imx6dl_mmdc_io_dsm_offset[i]; + pm_info->mmdc_io_val[i][1] = + readl_relaxed(*(&pm_info->iomuxc_base.vbase) + + imx6dl_mmdc_io_dsm_offset[i]); + } } imx6_suspend_in_ocram_fn = (void *)fncpy( -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH V3 4/5] ARM: dts: imx6sl: add compatible name for i.mx6q [not found] ` <1389592718-9999-1-git-send-email-b20788-KZfg59tc24xl57MIdRCFDg@public.gmane.org> 2014-01-13 5:58 ` [PATCH V3 2/5] ARM: dts: imx6dl: Remove unnecessary node Anson Huang 2014-01-13 5:58 ` [PATCH V3 3/5] ARM: imx: add suspend in ocram support on i.mx6dl Anson Huang @ 2014-01-13 5:58 ` Anson Huang 2014-01-13 5:58 ` [PATCH V3 5/5] ARM: imx: add suspend in ocram support on i.mx6sl Anson Huang ` (2 subsequent siblings) 5 siblings, 0 replies; 8+ messages in thread From: Anson Huang @ 2014-01-13 5:58 UTC (permalink / raw) To: shawn.guo-QSEj5FYQhm4dnm+yROfE0A, kernel-bIcnvbaLZ9MEGnE8C9+IrQ Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree-u79uwXL29TY76Z2rM5mHXA Add compatible name of i.mx6q for those reuse modules. Signed-off-by: Anson Huang <b20788-KZfg59tc24xl57MIdRCFDg@public.gmane.org> --- arch/arm/boot/dts/imx6sl.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/imx6sl.dtsi b/arch/arm/boot/dts/imx6sl.dtsi index a5ef3c3..5fbf42d 100644 --- a/arch/arm/boot/dts/imx6sl.dtsi +++ b/arch/arm/boot/dts/imx6sl.dtsi @@ -564,7 +564,7 @@ }; src: src@020d8000 { - compatible = "fsl,imx6sl-src", "fsl,imx51-src"; + compatible = "fsl,imx6sl-src", "fsl,imx6q-src", "fsl,imx51-src"; reg = <0x020d8000 0x4000>; interrupts = <0 91 IRQ_TYPE_LEVEL_HIGH>, <0 96 IRQ_TYPE_LEVEL_HIGH>; @@ -584,7 +584,7 @@ }; iomuxc: iomuxc@020e0000 { - compatible = "fsl,imx6sl-iomuxc"; + compatible = "fsl,imx6sl-iomuxc", "fsl,imx6q-iomuxc"; reg = <0x020e0000 0x4000>; }; -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH V3 5/5] ARM: imx: add suspend in ocram support on i.mx6sl [not found] ` <1389592718-9999-1-git-send-email-b20788-KZfg59tc24xl57MIdRCFDg@public.gmane.org> ` (2 preceding siblings ...) 2014-01-13 5:58 ` [PATCH V3 4/5] ARM: dts: imx6sl: add compatible name for i.mx6q Anson Huang @ 2014-01-13 5:58 ` Anson Huang 2014-01-13 8:05 ` [PATCH V3 1/5] ARM: imx: add suspend in ocram support on i.mx6q Shawn Guo 2014-01-13 8:09 ` Shawn Guo 5 siblings, 0 replies; 8+ messages in thread From: Anson Huang @ 2014-01-13 5:58 UTC (permalink / raw) To: shawn.guo-QSEj5FYQhm4dnm+yROfE0A, kernel-bIcnvbaLZ9MEGnE8C9+IrQ Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree-u79uwXL29TY76Z2rM5mHXA i.MX6SL's suspend in ocram function is derived from i.MX6Q, the only difference is the offset of DDR IO pins. It can lower the DDR IO power from ~10mA@1.2V to ~1mA@1.2V, measured on i.MX6SL EVK board, SH5. Signed-off-by: Anson Huang <b20788-KZfg59tc24xl57MIdRCFDg@public.gmane.org> --- Changes since V2: Do necessary change based on i.mx6q's suspend to ocram function, only cpu type, MMDC IOs' info need changed, and need to add i.mx6sl's special setting/operation during suspend/resume. arch/arm/mach-imx/Makefile | 2 +- arch/arm/mach-imx/pm-imx6q.c | 19 ++++++++++ arch/arm/mach-imx/suspend-imx6.S | 74 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index 3d96a45..f2df89f 100644 --- a/arch/arm/mach-imx/Makefile +++ b/arch/arm/mach-imx/Makefile @@ -105,7 +105,7 @@ ifeq ($(CONFIG_PM),y) AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a obj-$(CONFIG_SOC_IMX6Q) += pm-imx6q.o headsmp.o suspend-imx6.o # i.MX6SL reuses i.MX6Q code -obj-$(CONFIG_SOC_IMX6SL) += pm-imx6q.o headsmp.o +obj-$(CONFIG_SOC_IMX6SL) += pm-imx6q.o headsmp.o suspend-imx6.o endif # i.MX5 based machines diff --git a/arch/arm/mach-imx/pm-imx6q.c b/arch/arm/mach-imx/pm-imx6q.c index 670070b..5350b09 100644 --- a/arch/arm/mach-imx/pm-imx6q.c +++ b/arch/arm/mach-imx/pm-imx6q.c @@ -67,6 +67,7 @@ #define MX6_MAX_MMDC_IO_NUM 33 #define MX6Q_MMDC_IO_NUM 33 #define MX6DL_MMDC_IO_NUM 33 +#define MX6SL_MMDC_IO_NUM 19 static void __iomem *ccm_base; static void __iomem *suspend_ocram_base; @@ -116,6 +117,14 @@ static u32 imx6dl_mmdc_io_dsm_offset[] = { 0x74c /* GPR_ADDS */ }; +static u32 imx6sl_mmdc_io_dsm_offset[] = { + 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, DDRMODE_CTL, DDRMODE */ + 0x330, 0x334, 0x320, /* SDCKE0, SDCKE1, RESET */ +}; + /* * This structure is for passing necessary data for low level ocram * suspend code(arch/arm/mach-imx/suspend-imx6.S), if this struct @@ -448,6 +457,16 @@ static int __init imx6q_ocram_suspend_init(void) readl_relaxed(*(&pm_info->iomuxc_base.vbase) + imx6dl_mmdc_io_dsm_offset[i]); } + } else if (cpu_is_imx6sl()) { + pm_info->cpu_type = MXC_CPU_IMX6SL; + pm_info->mmdc_io_num = MX6SL_MMDC_IO_NUM; + for (i = 0; i < MX6SL_MMDC_IO_NUM; i++) { + pm_info->mmdc_io_val[i][0] = + imx6sl_mmdc_io_dsm_offset[i]; + pm_info->mmdc_io_val[i][1] = + readl_relaxed(*(&pm_info->iomuxc_base.vbase) + + imx6sl_mmdc_io_dsm_offset[i]); + } } imx6_suspend_in_ocram_fn = (void *)fncpy( diff --git a/arch/arm/mach-imx/suspend-imx6.S b/arch/arm/mach-imx/suspend-imx6.S index da1f806..db4fa8d 100644 --- a/arch/arm/mach-imx/suspend-imx6.S +++ b/arch/arm/mach-imx/suspend-imx6.S @@ -65,6 +65,7 @@ #define MX6Q_SRC_GPR1 0x20 #define MX6Q_SRC_GPR2 0x24 #define MX6Q_MMDC_MAPSR 0x404 +#define MX6Q_MMDC_MPDGCTRL0 0x83c #define MX6Q_GPC_IMR1 0x08 #define MX6Q_GPC_IMR2 0x0c #define MX6Q_GPC_IMR3 0x10 @@ -143,10 +144,15 @@ poll_dvfs_set_1: ands r7, r7, #(1 << 25) beq poll_dvfs_set_1 + ldr r10, [r0, #PM_INFO_CPU_TYPE_OFFSET] ldr r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET] ldr r6, =0x0 ldr r7, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET] ldr r8, =PM_INFO_MMDC_IO_VAL_OFFSET + /* i.MX6SL's last 3 IOs need special setting */ + cmp r10, #MXC_CPU_IMX6SL + bne set_mmdc_io_lpm + sub r7, r7, #0x3 set_mmdc_io_lpm: ldr r9, [r0, r8] str r6, [r11, r9] @@ -155,6 +161,20 @@ set_mmdc_io_lpm: cmp r7, #0x0 bne set_mmdc_io_lpm + cmp r10, #MXC_CPU_IMX6SL + bne set_mmdc_io_lpm_done + ldr r6, =0x1000 + ldr r9, [r0, r8] + str r6, [r11, r9] + add r8, r8, #0x8 + ldr r9, [r0, r8] + str r6, [r11, r9] + add r8, r8, #0x8 + ldr r6, =0x80000 + ldr r9, [r0, r8] + str r6, [r11, r9] +set_mmdc_io_lpm_done: + /* * mask all GPC interrupts before * enabling the RBC counters to @@ -239,6 +259,33 @@ restore_mmdc_io: cmp r6, #0x0 bne restore_mmdc_io + ldr r6, [r0, #PM_INFO_CPU_TYPE_OFFSET] + cmp r6, #MXC_CPU_IMX6SL + bne restore_mmdc_io_done + + ldr r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET] + /* reset read FIFO, RST_RD_FIFO */ + ldr r7, =MX6Q_MMDC_MPDGCTRL0 + ldr r6, [r11, r7] + orr r6, r6, #(1 << 31) + str r6, [r11, r7] +fifo_reset1_wait: + ldr r6, [r11, r7] + and r6, r6, #(1 << 31) + cmp r6, #0 + bne fifo_reset1_wait + + /* reset FIFO a second time */ + ldr r6, [r11, r7] + orr r6, r6, #(1 << 31) + str r6, [r11, r7] +fifo_reset2_wait: + ldr r6, [r11, r7] + and r6, r6, #(1 << 31) + cmp r6, #0 + bne fifo_reset2_wait +restore_mmdc_io_done: + ldr r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET] /* let DDR out of self-refresh. */ ldr r7, [r11, #MX6Q_MMDC_MAPSR] @@ -288,6 +335,33 @@ dsm_restore_mmdc_io: cmp r6, #0x0 bne dsm_restore_mmdc_io + ldr r6, [r0, #PM_INFO_CPU_TYPE_OFFSET] + cmp r6, #MXC_CPU_IMX6SL + bne dsm_restore_mmdc_io_done + + ldr r11, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET] + /* reset read FIFO, RST_RD_FIFO */ + ldr r7, =MX6Q_MMDC_MPDGCTRL0 + ldr r6, [r11, r7] + orr r6, r6, #(1 << 31) + str r6, [r11, r7] +dsm_fifo_reset1_wait: + ldr r6, [r11, r7] + and r6, r6, #(1 << 31) + cmp r6, #0 + bne dsm_fifo_reset1_wait + + /* reset FIFO a second time */ + ldr r6, [r11, r7] + orr r6, r6, #(1 << 31) + str r6, [r11, r7] +dsm_fifo_reset2_wait: + ldr r6, [r11, r7] + and r6, r6, #(1 << 31) + cmp r6, #0 + bne dsm_fifo_reset2_wait +dsm_restore_mmdc_io_done: + ldr r11, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET] /* let DDR out of self-refresh */ ldr r7, [r11, #MX6Q_MMDC_MAPSR] -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH V3 1/5] ARM: imx: add suspend in ocram support on i.mx6q [not found] ` <1389592718-9999-1-git-send-email-b20788-KZfg59tc24xl57MIdRCFDg@public.gmane.org> ` (3 preceding siblings ...) 2014-01-13 5:58 ` [PATCH V3 5/5] ARM: imx: add suspend in ocram support on i.mx6sl Anson Huang @ 2014-01-13 8:05 ` Shawn Guo 2014-01-13 8:09 ` Shawn Guo 5 siblings, 0 replies; 8+ messages in thread From: Shawn Guo @ 2014-01-13 8:05 UTC (permalink / raw) To: Anson Huang Cc: kernel-bIcnvbaLZ9MEGnE8C9+IrQ, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree-u79uwXL29TY76Z2rM5mHXA On Mon, Jan 13, 2014 at 01:58:34PM +0800, Anson Huang wrote: > When system enter suspend, we can set the DDR IO > to high-Z state to save DDR IO's power, this operation > can save many power(from ~26mA@1.5V to ~15mA@1.5V, > measured on i.MX6Q/DL SabreSD board, R25) of DDR IO. To > achieve that, we need to copy the suspend code to ocram > and run the low level hardware related code(set DDR IO > to high-Z) in ocram. > > If there is no ocram space available, then system will > still do suspend in DDR, hence no DDR IO will be set > to high-Z. > > The OCRAM usage layout is as below, > > ocram suspend region: > ======================== high address ====================== > . > . > . > ^ > ^ > ^ > imx6_suspend code > reserved region(to make imx6_suspend aligned with 8) > PM_INFO structure(imx6_cpu_pm_info) > ======================== low address ======================= > > Signed-off-by: Anson Huang <b20788-KZfg59tc24xl57MIdRCFDg@public.gmane.org> > --- > Changes since V2: > 1. save MMDC IO info during initialization for once, no > need to do save everytime before suspend; > 2. Improve ocram suspend initialization, put all of them > into a function; > 3. Improve the initialization of pm_info's modules, create > a function call for them. Thanks for the rework. > > arch/arm/mach-imx/Makefile | 3 +- > arch/arm/mach-imx/common.h | 4 +- > arch/arm/mach-imx/hardware.h | 4 +- > arch/arm/mach-imx/pm-imx6q.c | 244 +++++++++++++++++++++++++++++- > arch/arm/mach-imx/suspend-imx6.S | 306 ++++++++++++++++++++++++++++++++++++++ > 5 files changed, 554 insertions(+), 7 deletions(-) > create mode 100644 arch/arm/mach-imx/suspend-imx6.S > > diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile > index befcaf5..3d96a45 100644 > --- a/arch/arm/mach-imx/Makefile > +++ b/arch/arm/mach-imx/Makefile > @@ -102,7 +102,8 @@ obj-$(CONFIG_SOC_IMX6Q) += clk-imx6q.o mach-imx6q.o > obj-$(CONFIG_SOC_IMX6SL) += clk-imx6sl.o mach-imx6sl.o > > ifeq ($(CONFIG_PM),y) > -obj-$(CONFIG_SOC_IMX6Q) += pm-imx6q.o headsmp.o > +AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a > +obj-$(CONFIG_SOC_IMX6Q) += pm-imx6q.o headsmp.o suspend-imx6.o > # i.MX6SL reuses i.MX6Q code > obj-$(CONFIG_SOC_IMX6SL) += pm-imx6q.o headsmp.o > endif > diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h > index 4f4a95c..7c249e1 100644 > --- a/arch/arm/mach-imx/common.h > +++ b/arch/arm/mach-imx/common.h > @@ -1,5 +1,5 @@ > /* > - * Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved. > + * Copyright 2004-2014 Freescale Semiconductor, Inc. All Rights Reserved. > */ > > /* > @@ -145,10 +145,12 @@ void imx_cpu_die(unsigned int cpu); > int imx_cpu_kill(unsigned int cpu); > > #ifdef CONFIG_PM > +void imx6_suspend(void); > void imx6q_pm_init(void); > void imx6q_pm_set_ccm_base(void __iomem *base); > void imx5_pm_init(void); > #else > +static inline void imx6_suspend(void) {} > static inline void imx6q_pm_init(void) {} > static inline void imx6q_pm_set_ccm_base(void __iomem *base) {} > static inline void imx5_pm_init(void) {} > diff --git a/arch/arm/mach-imx/hardware.h b/arch/arm/mach-imx/hardware.h > index a3b0b04..abf43bb 100644 > --- a/arch/arm/mach-imx/hardware.h > +++ b/arch/arm/mach-imx/hardware.h > @@ -1,5 +1,5 @@ > /* > - * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. > + * Copyright 2004-2007, 2014 Freescale Semiconductor, Inc. All Rights Reserved. > * Copyright 2008 Juergen Beisert, kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org > * > * This program is free software; you can redistribute it and/or > @@ -20,7 +20,9 @@ > #ifndef __ASM_ARCH_MXC_HARDWARE_H__ > #define __ASM_ARCH_MXC_HARDWARE_H__ > > +#ifndef __ASSEMBLY__ > #include <asm/io.h> > +#endif > #include <asm/sizes.h> > > #define addr_in_module(addr, mod) \ > diff --git a/arch/arm/mach-imx/pm-imx6q.c b/arch/arm/mach-imx/pm-imx6q.c > index d45acc0..8997f58 100644 > --- a/arch/arm/mach-imx/pm-imx6q.c > +++ b/arch/arm/mach-imx/pm-imx6q.c > @@ -1,5 +1,5 @@ > /* > - * Copyright 2011-2013 Freescale Semiconductor, Inc. > + * Copyright 2011-2014 Freescale Semiconductor, Inc. > * Copyright 2011 Linaro Ltd. > * > * The code contained herein is licensed under the GNU General Public > @@ -14,16 +14,21 @@ > #include <linux/init.h> > #include <linux/io.h> > #include <linux/irq.h> > +#include <linux/genalloc.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/regmap.h> > #include <linux/suspend.h> > #include <asm/cacheflush.h> > +#include <asm/fncpy.h> > +#include <asm/hardware/cache-l2x0.h> While at it, we may want to just remove it, since it's used nowhere in the file now. > +#include <asm/mach/map.h> Since you have killed static mapping, you do not need this anymore. > #include <asm/proc-fns.h> > #include <asm/suspend.h> > -#include <asm/hardware/cache-l2x0.h> > +#include <asm/tlb.h> > > #include "common.h" > #include "hardware.h" > @@ -58,7 +63,69 @@ > #define CGPR 0x64 > #define BM_CGPR_INT_MEM_CLK_LPM (0x1 << 17) > > +#define MX6Q_SUSPEND_OCRAM_SIZE 0x1000 > +#define MX6_MAX_MMDC_IO_NUM 33 > +#define MX6Q_MMDC_IO_NUM 33 > + > static void __iomem *ccm_base; > +static void __iomem *suspend_ocram_base; > +static int (*imx6_suspend_in_ocram_fn)(void __iomem *ocram_vbase); > + > +/* > + * suspend ocram space layout: > + * ======================== high address ====================== > + * . > + * . > + * . > + * ^ > + * ^ > + * ^ > + * imx6_suspend code > + * reserved region(to make imx6_suspend aligned with 8) > + * PM_INFO structure(imx6_cpu_pm_info) > + * ======================== low address ======================= > + */ > + > +struct imx6_pm_base { > + phys_addr_t pbase; > + void __iomem *vbase; > +}; > + > +static u32 imx6q_mmdc_io_dsm_offset[] = { > + 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, DDRMODE_CTL, DDRMODE */ > + 0x74c /* GPR_ADDS */ > +}; > + > +/* > + * 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 fucntion 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 cpu_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; > + /* reserve some space to make ocram function's address aligned with 8 */ > + u32 reserved_size; > + 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 */ > +}; Since the address we get from gen_pool is 32-bytes aligned, we can ensure the 8-bytes alignment of the function pointer by enforcing the alignment of imx6_cpu_pm_info with __attribute__((__aligned__(8))). Then we can save that reserved_size? > > void imx6q_set_int_mem_clk_lpm(void) > { > @@ -177,7 +244,17 @@ int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode) > > static int imx6q_suspend_finish(unsigned long val) > { > - cpu_do_idle(); > + 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(); > + imx6_suspend_in_ocram_fn(suspend_ocram_base); > + } > + > return 0; > } > > @@ -187,7 +264,12 @@ static int imx6q_pm_enter(suspend_state_t state) > case PM_SUSPEND_MEM: > imx6q_set_lpm(STOP_POWER_OFF); > imx6q_enable_wb(true); > - imx6q_enable_rbc(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) > + imx6q_enable_rbc(true); > imx_gpc_pre_suspend(); > imx_anatop_pre_suspend(); > imx_set_cpu_jump(0, v7_cpu_resume); > @@ -218,12 +300,166 @@ void __init imx6q_pm_set_ccm_base(void __iomem *base) > ccm_base = base; > } > > +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) { > + ret = -ENODEV; > + goto out; > + } > + > + 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); > +out: > + return ret; > +} > + > +static int __init imx6q_ocram_suspend_init(void) > +{ > + 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, reserved_size; > + int i, ret = 0; > + > + 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 = dev_get_gen_pool(&pdev->dev); > + if (!ocram_pool) { > + pr_warn("%s: ocram pool unavailable!\n", __func__); > + ret = -ENODEV; > + goto put_node; > + } > + > + 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_node; > + } > + > + ocram_pbase = gen_pool_virt_to_phys(ocram_pool, ocram_base); > + reserved_size = 8 - ((ocram_pbase + sizeof(*pm_info)) % 8); > + > + suspend_ocram_base = __arm_ioremap_exec(ocram_pbase, > + MX6Q_SUSPEND_OCRAM_SIZE, false); > + > + pm_info = suspend_ocram_base; > + pm_info->pbase = ocram_pbase; > + pm_info->resume_addr = virt_to_phys(v7_cpu_resume); > + pm_info->pm_info_size = sizeof(*pm_info); > + pm_info->reserved_size = reserved_size; > + > + /* > + * ccm physical address is not used by asm code currently, > + * so get ccm virtual address directly, as we already have > + * it from ccm driver. > + */ > + pm_info->ccm_base.vbase = ccm_base; > + > + ret = imx6_pm_get_base(&pm_info->mmdc_base, "fsl,imx6q-mmdc"); > + if (ret) { > + pr_warn("%s: failed to get mmdc base %d!\n", __func__, ret); > + goto put_node; > + } > + > + ret = imx6_pm_get_base(&pm_info->src_base, "fsl,imx6q-src"); > + 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, "fsl,imx6q-iomuxc"); > + 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, "fsl,imx6q-gpc"); > + if (ret) { > + pr_warn("%s: failed to get gpc base %d!\n", __func__, ret); > + goto gpc_map_failed; > + } > + > + ret = imx6_pm_get_base(&pm_info->l2_base, "arm,pl310-cache"); > + if (ret) { > + pr_warn("%s: failed to get pl310-cache base %d!\n", > + __func__, ret); > + goto pl310_cache_map_failed; > + } > + > + if (cpu_is_imx6q()) { > + pm_info->cpu_type = MXC_CPU_IMX6Q; > + pm_info->mmdc_io_num = MX6Q_MMDC_IO_NUM; ARRAY_SIZE(imx6q_mmdc_io_dsm_offset), so that you can save the macro. > + for (i = 0; i < MX6Q_MMDC_IO_NUM; i++) { > + pm_info->mmdc_io_val[i][0] = > + imx6q_mmdc_io_dsm_offset[i]; > + pm_info->mmdc_io_val[i][1] = > + readl_relaxed(*(&pm_info->iomuxc_base.vbase) + > + imx6q_mmdc_io_dsm_offset[i]); > + } This for-loop could stay out of the if-check by using pm_info->mmdc_io_num. > + } > + > + imx6_suspend_in_ocram_fn = (void *)fncpy( > + suspend_ocram_base + sizeof(*pm_info) + reserved_size, > + &imx6_suspend, > + MX6Q_SUSPEND_OCRAM_SIZE - sizeof(*pm_info) - reserved_size); > + > + goto put_node; > + > +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_node: > + of_node_put(node); > + > + return ret; > +} > + > void __init imx6q_pm_init(void) > { > struct regmap *gpr; > + int ret; > > WARN_ON(!ccm_base); > > + ret = imx6q_ocram_suspend_init(); > + if (ret) > + pr_warn("%s: failed to initialize ocram suspend %d!\n", > + __func__, ret); > + > /* > * This is for SW workaround step #1 of ERR007265, see comments > * in imx6q_set_lpm for details of this errata. > diff --git a/arch/arm/mach-imx/suspend-imx6.S b/arch/arm/mach-imx/suspend-imx6.S > new file mode 100644 > index 0000000..da1f806 > --- /dev/null > +++ b/arch/arm/mach-imx/suspend-imx6.S > @@ -0,0 +1,306 @@ > +/* > + * Copyright 2014 Freescale Semiconductor, Inc. > + * > + * 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 > + */ > + > +#include <linux/linkage.h> > +#include <asm/hardware/cache-l2x0.h> > +#include "hardware.h" > + > +/* > + * ==================== low level suspend ==================== > + * > + * Better to follow below rules to use ARM registers: > + * r0: pm_info structure address; > + * r1 ~ r5: for saving pm_info members; > + * r6 ~ r10: free registers; > + * r11: io base address. > + * > + * suspend ocram space layout: > + * ======================== high address ====================== > + * . > + * . > + * . > + * ^ > + * ^ > + * ^ > + * imx6_suspend code > + * reserved region(to make imx6_suspend aligned with 8) > + * PM_INFO structure(imx6_cpu_pm_info) > + * ======================== low address ======================= > + */ > + > +/* > + * Below offsets are based on struct imx6_cpu_pm_info > + * which defined in arch/arm/mach-imx/pm-imx6q.c, this > + * structure contains necessary pm info for low level > + * suspend related code. > + */ > +#define PM_INFO_PBASE_OFFSET 0x0 > +#define PM_INFO_RESUME_ADDR_OFFSET 0x4 > +#define PM_INFO_CPU_TYPE_OFFSET 0x8 > +#define PM_INFO_PM_INFO_SIZE_OFFSET 0xC > +#define PM_INFO_MX6Q_MMDC_P_OFFSET 0x10 > +#define PM_INFO_MX6Q_MMDC_V_OFFSET 0x14 > +#define PM_INFO_MX6Q_SRC_P_OFFSET 0x18 > +#define PM_INFO_MX6Q_SRC_V_OFFSET 0x1C > +#define PM_INFO_MX6Q_IOMUXC_P_OFFSET 0x20 > +#define PM_INFO_MX6Q_IOMUXC_V_OFFSET 0x24 > +#define PM_INFO_MX6Q_CCM_P_OFFSET 0x28 > +#define PM_INFO_MX6Q_CCM_V_OFFSET 0x2C > +#define PM_INFO_MX6Q_GPC_P_OFFSET 0x30 > +#define PM_INFO_MX6Q_GPC_V_OFFSET 0x34 > +#define PM_INFO_MX6Q_L2_P_OFFSET 0x38 > +#define PM_INFO_MX6Q_L2_V_OFFSET 0x3C > +#define PM_INFO_RESERVED_SIZE_OFFSET 0x40 > +#define PM_INFO_MMDC_IO_NUM_OFFSET 0x44 > +#define PM_INFO_MMDC_IO_VAL_OFFSET 0x48 > + > +#define MX6Q_SRC_GPR1 0x20 > +#define MX6Q_SRC_GPR2 0x24 > +#define MX6Q_MMDC_MAPSR 0x404 > +#define MX6Q_GPC_IMR1 0x08 > +#define MX6Q_GPC_IMR2 0x0c > +#define MX6Q_GPC_IMR3 0x10 > +#define MX6Q_GPC_IMR4 0x14 > +#define MX6Q_CCM_CCR 0x0 > + > + .align 3 > + > + .macro sync_l2_cache > + > + /* sync L2 cache to drain L2's buffers to DRAM. */ > +#ifdef CONFIG_CACHE_L2X0 > + ldr r11, [r0, #PM_INFO_MX6Q_L2_V_OFFSET] > + mov r6, #0x0 > + str r6, [r11, #L2X0_CACHE_SYNC] > +1: > + ldr r6, [r11, #L2X0_CACHE_SYNC] > + ands r6, r6, #0x1 > + bne 1b > +#endif > + .endm > + > +ENTRY(imx6_suspend) > + ldr r1, [r0, #PM_INFO_PBASE_OFFSET] > + ldr r2, [r0, #PM_INFO_RESUME_ADDR_OFFSET] > + ldr r3, [r0, #PM_INFO_CPU_TYPE_OFFSET] > + ldr r4, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET] > + ldr r5, [r0, #PM_INFO_RESERVED_SIZE_OFFSET] > + > + /* > + * counting the resume address in iram > + * to set it in SRC register. > + */ > + ldr r6, =imx6_suspend > + ldr r7, =resume > + sub r7, r7, r6 > + add r8, r1, r4 > + add r8, r8, r5 > + add r9, r8, r7 > + > + /* > + * make sure TLB contain the addr we want, > + * as we will access them after MMDC IO floated. > + */ > + > + ldr r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET] > + ldr r6, [r11, #0x0] > + ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET] > + ldr r6, [r11, #0x0] > + > + /* use r11 to store the IO address */ > + ldr r11, [r0, #PM_INFO_MX6Q_SRC_V_OFFSET] > + /* store physical resume addr and pm_info address. */ > + str r9, [r11, #MX6Q_SRC_GPR1] > + str r1, [r11, #MX6Q_SRC_GPR2] > + > + /* need to sync L2 cache before DSM. */ > + sync_l2_cache > + > + ldr r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET] > + /* > + * put DDR explicitly into self-refresh and > + * disable automatic power savings. > + */ > + ldr r7, [r11, #MX6Q_MMDC_MAPSR] > + orr r7, r7, #0x1 > + str r7, [r11, #MX6Q_MMDC_MAPSR] > + > + /* make the DDR explicitly enter self-refresh. */ > + ldr r7, [r11, #MX6Q_MMDC_MAPSR] > + orr r7, r7, #(1 << 21) > + str r7, [r11, #MX6Q_MMDC_MAPSR] > + > +poll_dvfs_set_1: > + ldr r7, [r11, #0x404] s/0x404/MX6Q_MMDC_MAPSR Shawn > + ands r7, r7, #(1 << 25) > + beq poll_dvfs_set_1 > + > + ldr r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET] > + ldr r6, =0x0 > + ldr r7, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET] > + ldr r8, =PM_INFO_MMDC_IO_VAL_OFFSET > +set_mmdc_io_lpm: > + ldr r9, [r0, r8] > + str r6, [r11, r9] > + add r8, r8, #0x8 > + sub r7, r7, #0x1 > + cmp r7, #0x0 > + bne set_mmdc_io_lpm > + > + /* > + * mask all GPC interrupts before > + * enabling the RBC counters to > + * avoid the counter starting too > + * early if an interupt is already > + * pending. > + */ > + ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET] > + ldr r6, [r11, #MX6Q_GPC_IMR1] > + ldr r7, [r11, #MX6Q_GPC_IMR2] > + ldr r8, [r11, #MX6Q_GPC_IMR3] > + ldr r9, [r11, #MX6Q_GPC_IMR4] > + > + ldr r10, =0xffffffff > + str r10, [r11, #MX6Q_GPC_IMR1] > + str r10, [r11, #MX6Q_GPC_IMR2] > + str r10, [r11, #MX6Q_GPC_IMR3] > + str r10, [r11, #MX6Q_GPC_IMR4] > + > + /* > + * enable the RBC bypass counter here > + * to hold off the interrupts. RBC counter > + * = 32 (1ms), Minimum RBC delay should be > + * 400us for the analog LDOs to power down. > + */ > + ldr r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET] > + ldr r10, [r11, #MX6Q_CCM_CCR] > + bic r10, r10, #(0x3f << 21) > + orr r10, r10, #(0x20 << 21) > + str r10, [r11, #MX6Q_CCM_CCR] > + > + /* enable the counter. */ > + ldr r10, [r11, #MX6Q_CCM_CCR] > + orr r10, r10, #(0x1 << 27) > + str r10, [r11, #MX6Q_CCM_CCR] > + > + /* unmask all the GPC interrupts. */ > + ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET] > + str r6, [r11, #MX6Q_GPC_IMR1] > + str r7, [r11, #MX6Q_GPC_IMR2] > + str r8, [r11, #MX6Q_GPC_IMR3] > + str r9, [r11, #MX6Q_GPC_IMR4] > + > + /* > + * now delay for a short while (3usec) > + * ARM is at 1GHz at this point > + * so a short loop should be enough. > + * this delay is required to ensure that > + * the RBC counter can start counting in > + * case an interrupt is already pending > + * or in case an interrupt arrives just > + * as ARM is about to assert DSM_request. > + */ > + ldr r6, =2000 > +rbc_loop: > + sub r6, r6, #0x1 > + cmp r6, #0x0 > + bne rbc_loop > + > + /* Zzz, enter stop mode */ > + wfi > + nop > + nop > + nop > + nop > + > + /* > + * run to here means there is pending > + * wakeup source, system should auto > + * resume, we need to restore MMDC IO first > + */ > + ldr r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET] > + ldr r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET] > + ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET > +restore_mmdc_io: > + ldr r8, [r0, r7] > + add r7, r7, #0x4 > + ldr r9, [r0, r7] > + add r7, r7, #0x4 > + str r9, [r11, r8] > + sub r6, r6, #0x1 > + cmp r6, #0x0 > + bne restore_mmdc_io > + > + ldr r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET] > + /* let DDR out of self-refresh. */ > + ldr r7, [r11, #MX6Q_MMDC_MAPSR] > + bic r7, r7, #(1 << 21) > + str r7, [r11, #MX6Q_MMDC_MAPSR] > + > +poll_dvfs_clear_2: > + ldr r7, [r11, #MX6Q_MMDC_MAPSR] > + ands r7, r7, #(1 << 25) > + bne poll_dvfs_clear_2 > + /* enable DDR auto power saving */ > + ldr r7, [r11, #MX6Q_MMDC_MAPSR] > + bic r7, r7, #0x1 > + str r7, [r11, #MX6Q_MMDC_MAPSR] > + /* return to suspend finish */ > + mov pc, lr > + > +resume: > + /* invalidate L1 I-cache first */ > + mov r6, #0x0 > + mcr p15, 0, r6, c7, c5, 0 > + mcr p15, 0, r6, c7, c5, 0 > + mcr p15, 0, r6, c7, c5, 6 > + /* enable the Icache and branch prediction */ > + mov r6, #0x1800 > + mcr p15, 0, r6, c1, c0, 0 > + isb > + > + /* get physical resume address from pm_info. */ > + ldr lr, [r0, #PM_INFO_RESUME_ADDR_OFFSET] > + /* clear core0's entry and parameter */ > + ldr r11, [r0, #PM_INFO_MX6Q_SRC_P_OFFSET] > + mov r7, #0 > + str r7, [r11, #MX6Q_SRC_GPR1] > + str r7, [r11, #MX6Q_SRC_GPR2] > + > + ldr 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 > +dsm_restore_mmdc_io: > + ldr r8, [r0, r7] > + add r7, r7, #0x4 > + ldr r9, [r0, r7] > + add r7, r7, #0x4 > + str r9, [r11, r8] > + sub r6, r6, #0x1 > + cmp r6, #0x0 > + bne dsm_restore_mmdc_io > + > + ldr r11, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET] > + /* let DDR out of self-refresh */ > + ldr r7, [r11, #MX6Q_MMDC_MAPSR] > + bic r7, r7, #(1 << 21) > + str r7, [r11, #MX6Q_MMDC_MAPSR] > + > +poll_dvfs_clear_1: > + ldr r7, [r11, #MX6Q_MMDC_MAPSR] > + ands r7, r7, #(1 << 25) > + bne poll_dvfs_clear_1 > + /* enable DDR auto power saving */ > + ldr r7, [r11, #MX6Q_MMDC_MAPSR] > + bic r7, r7, #0x1 > + str r7, [r11, #MX6Q_MMDC_MAPSR] > + mov pc, lr > +ENDPROC(imx6_suspend) > -- > 1.7.9.5 > > -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH V3 1/5] ARM: imx: add suspend in ocram support on i.mx6q [not found] ` <1389592718-9999-1-git-send-email-b20788-KZfg59tc24xl57MIdRCFDg@public.gmane.org> ` (4 preceding siblings ...) 2014-01-13 8:05 ` [PATCH V3 1/5] ARM: imx: add suspend in ocram support on i.mx6q Shawn Guo @ 2014-01-13 8:09 ` Shawn Guo 5 siblings, 0 replies; 8+ messages in thread From: Shawn Guo @ 2014-01-13 8:09 UTC (permalink / raw) To: Anson Huang Cc: kernel-bIcnvbaLZ9MEGnE8C9+IrQ, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree-u79uwXL29TY76Z2rM5mHXA On Mon, Jan 13, 2014 at 01:58:34PM +0800, Anson Huang wrote: > + pm_info->mmdc_io_val[i][1] = > + readl_relaxed(*(&pm_info->iomuxc_base.vbase) + *(&pm_info->iomuxc_base.vbase) is just pm_info->iomuxc_base.vbase Shawn > + imx6q_mmdc_io_dsm_offset[i]); -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2014-01-13 8:09 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-01-13 5:58 [PATCH V3 1/5] ARM: imx: add suspend in ocram support on i.mx6q Anson Huang
[not found] ` <1389592718-9999-1-git-send-email-b20788-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
2014-01-13 5:58 ` [PATCH V3 2/5] ARM: dts: imx6dl: Remove unnecessary node Anson Huang
[not found] ` <1389592718-9999-2-git-send-email-b20788-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
2014-01-13 8:06 ` Shawn Guo
2014-01-13 5:58 ` [PATCH V3 3/5] ARM: imx: add suspend in ocram support on i.mx6dl Anson Huang
2014-01-13 5:58 ` [PATCH V3 4/5] ARM: dts: imx6sl: add compatible name for i.mx6q Anson Huang
2014-01-13 5:58 ` [PATCH V3 5/5] ARM: imx: add suspend in ocram support on i.mx6sl Anson Huang
2014-01-13 8:05 ` [PATCH V3 1/5] ARM: imx: add suspend in ocram support on i.mx6q Shawn Guo
2014-01-13 8:09 ` Shawn Guo
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).