Ping.

Shenwei

> -----Original Message-----
> From: linux-kernel-ow...@vger.kernel.org
> [mailto:linux-kernel-ow...@vger.kernel.org] On Behalf Of Shenwei Wang
> Sent: 2015年7月31日 16:34
> To: shawn....@linaro.org; t...@linutronix.de; ja...@lakedaemon.net
> Cc: linux-arm-ker...@lists.infradead.org; linux-kernel@vger.kernel.org; Huang
> Yongcai-B20788
> Subject: [PATCH v8 2/2] ARM: imx: support suspend states on imx7D
> 
> IMX7D contains a new version of GPC IP block (GPCv2). It has two major 
> functions:
> power management and wakeup source management.
> 
> GPCv2 provides low power mode control for Cortex-A7 and Cortex-M4 domains.
> And it can support WAIT, STOP, and DSM(Deep Sleep Mode) modes.
> After configuring the GPCv2 module, the platform can enter into a selected 
> mode
> either automatically triggered by ARM WFI instruction or manually by software.
> The system will exit the low power states by the predefined wakeup sources
> which are managed by the gpcv2 irqchip driver.
> 
> This patch adds a new suspend driver to manage the power states on IMX7D.
> It currently supports "SUSPEND_STANDBY" and "SUSPEND_MEM" states.
> 
> Signed-off-by: Shenwei Wang <shenwei.w...@freescale.com>
> Signed-off-by: Anson Huang <b20...@freescale.com>
> ---
>  arch/arm/mach-imx/Kconfig        |   1 +
>  arch/arm/mach-imx/Makefile       |   2 +
>  arch/arm/mach-imx/common.h       |   4 +
>  arch/arm/mach-imx/pm-imx7.c      | 917
> +++++++++++++++++++++++++++++++++++++++
>  arch/arm/mach-imx/suspend-imx7.S | 529 ++++++++++++++++++++++
>  5 files changed, 1453 insertions(+)
>  create mode 100644 arch/arm/mach-imx/pm-imx7.c  create mode 100644
> arch/arm/mach-imx/suspend-imx7.S
> 
> diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index
> 8ceda28..54f8553 100644
> --- a/arch/arm/mach-imx/Kconfig
> +++ b/arch/arm/mach-imx/Kconfig
> @@ -562,6 +562,7 @@ config SOC_IMX7D
>       select ARM_GIC
>       select HAVE_IMX_ANATOP
>       select HAVE_IMX_MMDC
> +     select IMX_GPCV2
>       help
>               This enables support for Freescale i.MX7 Dual processor.
> 
> diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index
> fb689d8..60f13fd 100644
> --- a/arch/arm/mach-imx/Makefile
> +++ b/arch/arm/mach-imx/Makefile
> @@ -88,6 +88,8 @@ obj-$(CONFIG_SOC_IMX7D) += mach-imx7d.o
> 
>  ifeq ($(CONFIG_SUSPEND),y)
>  AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a
> +AFLAGS_suspend-imx7.o :=-Wa,-march=armv7-a
> +obj-$(CONFIG_SOC_IMX7D)      += suspend-imx7.o pm-imx7.o
>  obj-$(CONFIG_SOC_IMX6) += suspend-imx6.o
>  obj-$(CONFIG_SOC_IMX53) += suspend-imx53.o  endif diff --git
> a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h index
> 21e4e86..c5767f4 100644
> --- a/arch/arm/mach-imx/common.h
> +++ b/arch/arm/mach-imx/common.h
> @@ -126,6 +126,10 @@ static const u32 imx53_suspend_sz;  static inline void
> imx6_suspend(void __iomem *ocram_vbase) {}  #endif
> 
> +void ca7_cpu_resume(void);
> +void imx7_suspend(void __iomem *ocram_vbase);
> +u32 imx_gpcv2_get_wakeup_source(u32 **sources);
> +
>  void imx6_pm_ccm_init(const char *ccm_compat);  void imx6q_pm_init(void);
> void imx6dl_pm_init(void); diff --git a/arch/arm/mach-imx/pm-imx7.c
> b/arch/arm/mach-imx/pm-imx7.c new file mode 100644 index 0000000..052c2cb
> --- /dev/null
> +++ b/arch/arm/mach-imx/pm-imx7.c
> @@ -0,0 +1,917 @@
> +/*
> + * Copyright (C) 2015 Freescale Semiconductor, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/mfd/syscon.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/regmap.h>
> +#include <linux/suspend.h>
> +#include <linux/slab.h>
> +#include <asm/suspend.h>
> +#include <asm/fncpy.h>
> +
> +#include "common.h"
> +
> +#define GPC_LPCR_A7_BSC              0x0
> +#define GPC_LPCR_A7_AD               0x4
> +#define GPC_LPCR_M4          0x8
> +
> +#define GPC_SLPCR            0x14
> +#define GPC_PGC_ACK_SEL_A7   0x24
> +
> +#define GPC_SLOTx_CFG(x)     (0xb0 + 4 * (x))
> +
> +#define GPC_PGC_CPU_MAPPING  0xec
> +
> +#define GPC_PGC_C0           0x800
> +#define GPC_PGC_C1           0x840
> +#define GPC_PGC_SCU          0x880
> +#define GPC_PGC_SCU_TIMING   0x890
> +#define GPC_PGC_FM           0xa00
> +#define GPC_PGC_MIPI_PHY     0xc00
> +#define GPC_PGC_PCIE_PHY     0xc40
> +#define GPC_PGC_USB_OTG1_PHY 0xc80
> +#define GPC_PGC_USB_OTG2_PHY 0xcc0
> +#define GPC_PGC_USB_HSIC_PHY 0xd00
> +
> +#define ANADIG_ARM_PLL               0x60
> +#define ANADIG_DDR_PLL               0x70
> +#define ANADIG_SYS_PLL               0xb0
> +#define ANADIG_ENET_PLL              0xe0
> +#define ANADIG_AUDIO_PLL     0xf0
> +#define ANADIG_VIDEO_PLL     0x130
> +
> +#define BM_LPCR_A7_AD_L2PGE                  (0x1 << 16)
> +#define BM_LPCR_A7_AD_EN_C1_PUP                      (0x1 << 11)
> +#define BM_LPCR_A7_AD_EN_C1_IRQ_PUP          (0x1 << 10)
> +#define BM_LPCR_A7_AD_EN_C0_PUP                      (0x1 << 9)
> +#define BM_LPCR_A7_AD_EN_C0_IRQ_PUP          (0x1 << 8)
> +#define BM_LPCR_A7_AD_EN_PLAT_PDN            (0x1 << 4)
> +#define BM_LPCR_A7_AD_EN_C1_PDN                      (0x1 << 3)
> +#define BM_LPCR_A7_AD_EN_C1_WFI_PDN          (0x1 << 2)
> +#define BM_LPCR_A7_AD_EN_C0_PDN                      (0x1 << 1)
> +#define BM_LPCR_A7_AD_EN_C0_WFI_PDN          (0x1)
> +
> +#define BM_LPCR_A7_BSC_IRQ_SRC_A7_WAKEUP     (0x7 << 28)
> +#define BM_LPCR_A7_BSC_CPU_CLK_ON_LPM                (0x1 << 14)
> +#define BM_LPCR_A7_BSC_LPM1                  (0x3 << 2)
> +#define BM_LPCR_A7_BSC_LPM0                  (0x3)
> +
> +#define BM_LPCR_M4_MASK_DSM_TRIGGER          (0x1 << 31)
> +
> +#define BM_SLPCR_EN_DSM                              (0x1 << 31)
> +#define BM_SLPCR_RBC_EN                              (0x1 << 30)
> +#define BM_SLPCR_VSTBY                               (0x1 << 2)
> +#define BM_SLPCR_SBYOS                               (0x1 << 1)
> +#define BM_SLPCR_BYPASS_PMIC_READY           (0x1)
> +
> +#define BM_GPC_PGC_ACK_SEL_A7_DUMMY_PUP              (0x1 << 31)
> +#define BM_GPC_PGC_ACK_SEL_A7_DUMMY_PDN              (0x1 << 15)
> +
> +#define BM_ANADIG_ARM_PLL_OVERRIDE           (0x1 << 20)
> +#define BM_ANADIG_DDR_PLL_OVERRIDE           (0x1 << 19)
> +#define BM_ANADIG_SYS_PLL_PFDx_OVERRIDE              (0x1FF << 17)
> +#define BM_ANADIG_ENET_PLL_OVERRIDE          (0x1 << 13)
> +#define BM_ANADIG_AUDIO_PLL_OVERRIDE         (0x1 << 24)
> +#define BM_ANADIG_VIDEO_PLL_OVERRIDE         (0x1 << 24)
> +
> +#define A7_LPM_WAIT          0x5
> +#define A7_LPM_STOP          0xa
> +#define GPC_MAX_SLOT_NUMBER  10
> +
> +#define REG_SET                      0x4
> +#define REG_CLR                      0x8
> +
> +#define MX7_MAX_DDRC_NUM             32
> +#define MX7_MAX_DDRC_PHY_NUM         16
> +
> +#define READ_DATA_FROM_HARDWARE              0
> +#define MX7_SUSPEND_OCRAM_SIZE               0x1000
> +
> +enum gpcv2_mode {
> +     GPC_WAIT_CLOCKED,
> +     GPC_WAIT_UNCLOCKED,
> +     GPC_STOP_POWER_ON,
> +     GPC_STOP_POWER_OFF,
> +};
> +
> +/*
> + * GPCv2 has the following power domains, and each domain can be
> +power-up
> + * and power-down via GPC settings.
> + *
> + *   Core 0 of A7 power domain
> + *   Core1 of A7 power domain
> + *   SCU/L2 cache RAM of A7 power domain
> + *   Fastmix and megamix power domain
> + *   USB OTG1 PHY power domain
> + *   USB OTG2 PHY power domain
> + *   PCIE PHY power domain
> + *   USB HSIC PHY power domain
> + *   Core 0 of M4 power domain
> + */
> +
> +enum gpcv2_slot {
> +     CORE0_A7,
> +     CORE1_A7,
> +     SCU_A7,
> +     FAST_MEGA_MIX,
> +     MIPI_PHY,
> +     PCIE_PHY,
> +     USB_OTG1_PHY,
> +     USB_OTG2_PHY,
> +     USB_HSIC_PHY,
> +     CORE0_M4,
> +};
> +
> +struct imx_gpcv2;
> +
> +struct imx_gpcv2_suspend {
> +     void (*set_mode)(struct imx_gpcv2 *, enum gpcv2_mode mode);
> +     void (*lpm_cpu_power_gate)(struct imx_gpcv2 *, u32, bool);
> +     void (*lpm_plat_power_gate)(struct imx_gpcv2 *, bool);
> +     void (*lpm_env_setup)(struct imx_gpcv2 *);
> +     void (*lpm_env_clean)(struct imx_gpcv2 *);
> +
> +     void (*set_slot)(struct imx_gpcv2 *cd, u32 index,
> +                     enum gpcv2_slot slot, bool mode);
> +     void (*set_act)(struct imx_gpcv2 *cd,
> +                     enum gpcv2_slot slot, bool mode);
> +     void (*clear_slots)(struct imx_gpcv2 *);
> +     void (*lpm_enable_core)(struct imx_gpcv2 *,
> +                     bool enable, u32 offset);
> +
> +     void (*standby)(struct imx_gpcv2 *);
> +     void (*suspend)(struct imx_gpcv2 *);
> +
> +     void (*suspend_fn_in_ocram)(void __iomem *ocram_vbase);
> +     void __iomem *ocram_vbase;
> +};
> +
> +struct imx_gpcv2 {
> +     u32 *wakeupmix_mask;
> +     u32 *lpsrmix_mask;
> +     u32 *mfmix_mask;
> +     spinlock_t lock;
> +
> +     struct imx_gpcv2_suspend *pm;
> +     struct regmap *anatop;
> +     struct regmap *gpcv2;
> +
> +     u32 (*get_wakeup_source)(u32 **);
> +};
> +
> +struct imx7_pm_base {
> +     phys_addr_t pbase;
> +     void __iomem *vbase;
> +};
> +
> +struct imx7_pm_socdata {
> +     u32 ddr_type;
> +     const char *ddrc_compat;
> +     const char *ddrc_phy_compat;
> +     const char *src_compat;
> +     const char *iomuxc_gpr_compat;
> +     const char *ccm_compat;
> +     const char *gpc_compat;
> +     const char *anatop_compat;
> +     const u32 ddrc_num;
> +     const u32 (*ddrc_offset)[2];
> +     const u32 ddrc_phy_num;
> +     const u32 (*ddrc_phy_offset)[2];
> +};
> +
> +/*
> + * This structure is for passing necessary data for low level ocram
> + * suspend code(arch/arm/mach-imx/suspend-imx7.S), if this struct
> + * definition is changed, the offset definition in
> + * arch/arm/mach-imx/suspend-imx7.S must be also changed accordingly,
> + * otherwise, the suspend to ocram function will be broken!
> + */
> +
> +struct imx7_cpu_pm_info {
> +     u32 m4_reserve0;
> +     u32 m4_reserve1;
> +     u32 m4_reserve2;
> +
> +     /* The physical address of pm_info. */
> +     phys_addr_t pbase;
> +
> +     /* The physical resume address for asm code */
> +     phys_addr_t resume_addr;
> +     u32 ddr_type;
> +
> +     u32 pm_info_size;
> +
> +     struct imx7_pm_base ddrc_base;
> +     struct imx7_pm_base ddrc_phy_base;
> +     struct imx7_pm_base src_base;
> +     struct imx7_pm_base iomuxc_gpr_base;
> +     struct imx7_pm_base ccm_base;
> +     struct imx7_pm_base gpc_base;
> +     struct imx7_pm_base l2_base;
> +     struct imx7_pm_base anatop_base;
> +
> +     u32 ttbr1;
> +
> +     /* Number of DDRC which need saved/restored. */
> +     u32 ddrc_num;
> +
> +     /* To save offset and value */
> +     u32 ddrc_val[MX7_MAX_DDRC_NUM][2];
> +
> +     /* Number of DDRC which need saved/restored. */
> +     u32 ddrc_phy_num;
> +
> +     /* To save offset and value */
> +     u32 ddrc_phy_val[MX7_MAX_DDRC_NUM][2];
> +} __aligned(8);
> +
> +static const u32 imx7d_ddrc_ddr3_setting[][2] __initconst = {
> +     { 0x0, READ_DATA_FROM_HARDWARE },
> +     { 0x1a0, READ_DATA_FROM_HARDWARE },
> +     { 0x1a4, READ_DATA_FROM_HARDWARE },
> +     { 0x1a8, READ_DATA_FROM_HARDWARE },
> +     { 0x64, READ_DATA_FROM_HARDWARE },
> +     { 0x490, 0x00000001 },
> +     { 0xd0, 0xc0020001 },
> +     { 0xd4, READ_DATA_FROM_HARDWARE },
> +     { 0xdc, READ_DATA_FROM_HARDWARE },
> +     { 0xe0, READ_DATA_FROM_HARDWARE },
> +     { 0xe4, READ_DATA_FROM_HARDWARE },
> +     { 0xf4, READ_DATA_FROM_HARDWARE },
> +     { 0x100, READ_DATA_FROM_HARDWARE },
> +     { 0x104, READ_DATA_FROM_HARDWARE },
> +     { 0x108, READ_DATA_FROM_HARDWARE },
> +     { 0x10c, READ_DATA_FROM_HARDWARE },
> +     { 0x110, READ_DATA_FROM_HARDWARE },
> +     { 0x114, READ_DATA_FROM_HARDWARE },
> +     { 0x120, 0x03030803 },
> +     { 0x180, READ_DATA_FROM_HARDWARE },
> +     { 0x190, READ_DATA_FROM_HARDWARE },
> +     { 0x194, READ_DATA_FROM_HARDWARE },
> +     { 0x200, READ_DATA_FROM_HARDWARE },
> +     { 0x204, READ_DATA_FROM_HARDWARE },
> +     { 0x214, READ_DATA_FROM_HARDWARE },
> +     { 0x218, READ_DATA_FROM_HARDWARE },
> +     { 0x240, 0x06000601 },
> +     { 0x244, READ_DATA_FROM_HARDWARE },
> +};
> +
> +static const u32 imx7d_ddrc_phy_ddr3_setting[][2] __initconst = {
> +     { 0x0, READ_DATA_FROM_HARDWARE },
> +     { 0x4, READ_DATA_FROM_HARDWARE },
> +     { 0x10, READ_DATA_FROM_HARDWARE },
> +     { 0x9c, READ_DATA_FROM_HARDWARE },
> +     { 0x20, READ_DATA_FROM_HARDWARE },
> +     { 0x30, READ_DATA_FROM_HARDWARE },
> +     { 0x50, 0x01000010 },
> +     { 0x50, 0x00000010 },
> +     { 0xc0, 0x0e407304 },
> +     { 0xc0, 0x0e447304 },
> +     { 0xc0, 0x0e447306 },
> +     { 0xc0, 0x0e447304 },
> +     { 0xc0, 0x0e407306 },
> +};
> +
> +static const struct imx7_pm_socdata imx7d_pm_data_ddr3 __initconst = {
> +     .iomuxc_gpr_compat = "fsl,imx7d-iomuxc",
> +     .ddrc_phy_compat = "fsl,imx7d-ddrc-phy",
> +     .anatop_compat = "fsl,imx7d-anatop",
> +     .ddrc_compat = "fsl,imx7d-ddrc",
> +     .ccm_compat = "fsl,imx7d-ccm",
> +     .src_compat = "fsl,imx7d-src",
> +     .gpc_compat = "fsl,imx7d-gpc",
> +     .ddrc_phy_num = ARRAY_SIZE(imx7d_ddrc_phy_ddr3_setting),
> +     .ddrc_phy_offset = imx7d_ddrc_phy_ddr3_setting,
> +     .ddrc_num = ARRAY_SIZE(imx7d_ddrc_ddr3_setting),
> +     .ddrc_offset = imx7d_ddrc_ddr3_setting, };
> +
> +static struct imx_gpcv2 *gpcv2_instance;
> +
> +static void imx_gpcv2_lpm_clear_slots(struct imx_gpcv2 *gpc) {
> +     int i;
> +
> +     for (i = 0; i < GPC_MAX_SLOT_NUMBER; i++)
> +             regmap_write(gpc->gpcv2, GPC_SLOTx_CFG(i), 0x0);
> +
> +     regmap_write(gpc->gpcv2, GPC_PGC_ACK_SEL_A7,
> +                     BM_GPC_PGC_ACK_SEL_A7_DUMMY_PUP |
> +                     BM_GPC_PGC_ACK_SEL_A7_DUMMY_PDN);
> +}
> +
> +static void imx_gpcv2_lpm_enable_core(struct imx_gpcv2 *gpc,
> +                     bool enable, u32 offset)
> +{
> +     regmap_write(gpc->gpcv2, offset, enable); }
> +
> +static void imx_gpcv2_lpm_slot_setup(struct imx_gpcv2 *gpc,
> +             u32 index, enum gpcv2_slot slot, bool powerup) {
> +     u32 val;
> +
> +     if (index >= GPC_MAX_SLOT_NUMBER) {
> +             pr_err("Invalid slot index!\n");
> +             return;
> +     }
> +
> +     val = (powerup ? 0x2 : 0x1) << (slot * 2);
> +     regmap_write(gpc->gpcv2, GPC_SLOTx_CFG(index), val); }
> +
> +static void imx_gpcv2_lpm_set_ack(struct imx_gpcv2 *gpc,
> +             enum gpcv2_slot slot, bool powerup)
> +{
> +     u32 val;
> +
> +     regmap_read(gpc->gpcv2, GPC_PGC_ACK_SEL_A7, &val);
> +
> +     /* clear dummy ack */
> +     val &= ~(powerup ? BM_GPC_PGC_ACK_SEL_A7_DUMMY_PUP :
> +                             BM_GPC_PGC_ACK_SEL_A7_DUMMY_PDN);
> +
> +     val |= 1 << (slot + (powerup ? 16 : 0));
> +     regmap_write(gpc->gpcv2, GPC_PGC_ACK_SEL_A7, val); }
> +
> +static void imx_gpcv2_lpm_env_setup(struct imx_gpcv2 *gpc) {
> +     /* PLL and PFDs overwrite set */
> +     regmap_write(gpc->anatop, ANADIG_ARM_PLL + REG_SET,
> +                     BM_ANADIG_ARM_PLL_OVERRIDE);
> +     regmap_write(gpc->anatop, ANADIG_DDR_PLL + REG_SET,
> +                     BM_ANADIG_DDR_PLL_OVERRIDE);
> +     regmap_write(gpc->anatop, ANADIG_SYS_PLL + REG_SET,
> +                     BM_ANADIG_SYS_PLL_PFDx_OVERRIDE);
> +     regmap_write(gpc->anatop, ANADIG_ENET_PLL + REG_SET,
> +                     BM_ANADIG_ENET_PLL_OVERRIDE);
> +     regmap_write(gpc->anatop, ANADIG_AUDIO_PLL + REG_SET,
> +                     BM_ANADIG_AUDIO_PLL_OVERRIDE);
> +     regmap_write(gpc->anatop, ANADIG_VIDEO_PLL + REG_SET,
> +                     BM_ANADIG_VIDEO_PLL_OVERRIDE);
> +}
> +
> +static void imx_gpcv2_lpm_env_clean(struct imx_gpcv2 *gpc) {
> +     /* PLL and PFDs overwrite clear */
> +     regmap_write(gpc->anatop, ANADIG_ARM_PLL + REG_CLR,
> +                     BM_ANADIG_ARM_PLL_OVERRIDE);
> +     regmap_write(gpc->anatop, ANADIG_DDR_PLL + REG_CLR,
> +                     BM_ANADIG_DDR_PLL_OVERRIDE);
> +     regmap_write(gpc->anatop, ANADIG_SYS_PLL + REG_CLR,
> +                     BM_ANADIG_SYS_PLL_PFDx_OVERRIDE);
> +     regmap_write(gpc->anatop, ANADIG_ENET_PLL + REG_CLR,
> +                     BM_ANADIG_ENET_PLL_OVERRIDE);
> +     regmap_write(gpc->anatop, ANADIG_AUDIO_PLL + REG_CLR,
> +                     BM_ANADIG_AUDIO_PLL_OVERRIDE);
> +     regmap_write(gpc->anatop, ANADIG_VIDEO_PLL + REG_CLR,
> +                     BM_ANADIG_VIDEO_PLL_OVERRIDE);
> +}
> +
> +static void imx_gpcv2_lpm_set_mode(struct imx_gpcv2 *gpc,
> +             enum gpcv2_mode mode)
> +{
> +     u32 lpcr, slpcr;
> +
> +     regmap_read(gpc->gpcv2, GPC_LPCR_A7_BSC, &lpcr);
> +     regmap_read(gpc->gpcv2, GPC_SLPCR, &slpcr);
> +
> +     /* all cores' LPM settings must be same */
> +     lpcr &= ~(BM_LPCR_A7_BSC_LPM0 | BM_LPCR_A7_BSC_LPM1);
> +     lpcr |= BM_LPCR_A7_BSC_CPU_CLK_ON_LPM;
> +
> +     slpcr &= ~(BM_SLPCR_EN_DSM | BM_SLPCR_VSTBY | BM_SLPCR_RBC_EN
> |
> +             BM_SLPCR_SBYOS | BM_SLPCR_BYPASS_PMIC_READY);
> +
> +     switch (mode) {
> +     case GPC_WAIT_CLOCKED:
> +             break;
> +     case GPC_WAIT_UNCLOCKED:
> +             lpcr |= A7_LPM_WAIT;
> +             lpcr &= ~BM_LPCR_A7_BSC_CPU_CLK_ON_LPM;
> +             break;
> +     case GPC_STOP_POWER_ON:
> +             lpcr |= A7_LPM_STOP;
> +             lpcr &= ~BM_LPCR_A7_BSC_CPU_CLK_ON_LPM;
> +             slpcr |= (BM_SLPCR_EN_DSM | BM_SLPCR_RBC_EN |
> +                     BM_SLPCR_BYPASS_PMIC_READY);
> +             break;
> +     case GPC_STOP_POWER_OFF:
> +             lpcr |= A7_LPM_STOP;
> +             lpcr &= ~BM_LPCR_A7_BSC_CPU_CLK_ON_LPM;
> +             slpcr |= (BM_SLPCR_EN_DSM | BM_SLPCR_RBC_EN |
> +                     BM_SLPCR_SBYOS | BM_SLPCR_VSTBY  |
> +                     BM_SLPCR_BYPASS_PMIC_READY);
> +             break;
> +     default:
> +             return;
> +     }
> +     regmap_write(gpc->gpcv2, GPC_LPCR_A7_BSC, lpcr);
> +     regmap_write(gpc->gpcv2, GPC_SLPCR, slpcr); }
> +
> +static void imx_gpcv2_lpm_cpu_power_gate(struct imx_gpcv2 *gpc,
> +                             u32 cpu, bool engate)
> +{
> +     u32 val;
> +
> +     const u32 val_pdn[2] = {
> +             BM_LPCR_A7_AD_EN_C0_PDN | BM_LPCR_A7_AD_EN_C0_PUP,
> +             BM_LPCR_A7_AD_EN_C1_PDN | BM_LPCR_A7_AD_EN_C1_PUP,
> +     };
> +
> +     regmap_read(gpc->gpcv2, GPC_LPCR_A7_AD, &val);
> +     if (engate)
> +             val |= val_pdn[cpu];
> +     else
> +             val &= ~val_pdn[cpu];
> +
> +     regmap_write(gpc->gpcv2, GPC_LPCR_A7_AD, val); }
> +
> +static void imx_lpm_plat_power_gate(struct imx_gpcv2 *gpc, bool engate)
> +{
> +     u32 val;
> +
> +     regmap_read(gpc->gpcv2, GPC_LPCR_A7_AD, &val);
> +     val &= ~(BM_LPCR_A7_AD_EN_PLAT_PDN | BM_LPCR_A7_AD_L2PGE);
> +     if (engate)
> +             val |= BM_LPCR_A7_AD_EN_PLAT_PDN | BM_LPCR_A7_AD_L2PGE;
> +
> +     regmap_write(gpc->gpcv2, GPC_LPCR_A7_AD, val); }
> +
> +static void imx_gpcv2_lpm_standby(struct imx_gpcv2 *gpc) {
> +     struct imx_gpcv2_suspend *pm = gpc->pm;
> +
> +     pm->lpm_env_setup(gpc);
> +     /* pm->set_mode(gpc, GPC_STOP_POWER_OFF); */
> +     pm->set_mode(gpc, GPC_WAIT_UNCLOCKED);
> +
> +     /* Zzz ... */
> +     cpu_do_idle();
> +
> +     pm->set_mode(gpc, GPC_WAIT_CLOCKED);
> +     pm->lpm_env_clean(gpc);
> +}
> +
> +static int gpcv2_suspend_finish(unsigned long val) {
> +     struct imx_gpcv2_suspend *pm = (struct imx_gpcv2_suspend *)val;
> +
> +     if (!pm->suspend_fn_in_ocram) {
> +             cpu_do_idle();
> +     } else {
> +             /*
> +              * call low level suspend function in ocram,
> +              * as we need to float DDR IO.
> +              */
> +             local_flush_tlb_all();
> +             pm->suspend_fn_in_ocram(pm->ocram_vbase);
> +     }
> +
> +     return 0;
> +}
> +
> +static void imx_gpcv2_lpm_suspend(struct imx_gpcv2 *gpc) {
> +     struct imx_gpcv2_suspend *pm = gpc->pm;
> +     u32 *sources;
> +     int i, num;
> +
> +     pm->lpm_env_setup(gpc);
> +     pm->set_mode(gpc, GPC_STOP_POWER_OFF);
> +
> +     /* enable core0 power down/up with low power mode */
> +     pm->lpm_cpu_power_gate(gpc, 0, true);
> +
> +     /* enable plat power down with low power mode */
> +     pm->lpm_plat_power_gate(gpc, true);
> +
> +     /*
> +      * To avoid confuse, we use slot 0~4 for power down,
> +      * slot 5~9 for power up.
> +      *
> +      * Power down slot sequence:
> +      * Slot0 -> CORE0
> +      * Slot1 -> Mega/Fast MIX
> +      * Slot2 -> SCU
> +      *
> +      * Power up slot sequence:
> +      * Slot5 -> Mega/Fast MIX
> +      * Slot6 -> SCU
> +      * Slot7 -> CORE0
> +      */
> +     pm->set_slot(gpc, 0, CORE0_A7, false);
> +     pm->set_slot(gpc, 2, SCU_A7, false);
> +
> +     if (gpc->get_wakeup_source) {
> +             pm->set_slot(gpc, 1, FAST_MEGA_MIX, false);
> +             pm->set_slot(gpc, 5, FAST_MEGA_MIX, true);
> +             pm->lpm_enable_core(gpc, true, GPC_PGC_FM);
> +             num = gpc->get_wakeup_source(&sources);
> +             for (i = 0; i < num; i++) {
> +                     if ((~sources[i] & gpc->mfmix_mask[i]) == 0)
> +                             continue;
> +                     pm->lpm_enable_core(gpc, false, GPC_PGC_FM);
> +                     break;
> +             }
> +     }
> +
> +     pm->set_slot(gpc, 6, SCU_A7, true);
> +     pm->set_slot(gpc, 7, CORE0_A7, true);
> +
> +     /* Set Power down act */
> +     pm->set_act(gpc, SCU_A7, false);
> +
> +     /* Set Power up act */
> +     pm->set_act(gpc, CORE0_A7, true);
> +
> +     /* enable core0, scu */
> +     pm->lpm_enable_core(gpc, true, GPC_PGC_C0);
> +     pm->lpm_enable_core(gpc, true, GPC_PGC_SCU);
> +
> +     cpu_suspend((unsigned long)pm, gpcv2_suspend_finish);
> +
> +     pm->lpm_env_clean(gpc);
> +     pm->set_mode(gpc, GPC_WAIT_CLOCKED);
> +     pm->lpm_cpu_power_gate(gpc, 0, false);
> +     pm->lpm_plat_power_gate(gpc, false);
> +
> +     pm->lpm_enable_core(gpc, false, GPC_PGC_C0);
> +     pm->lpm_enable_core(gpc, false, GPC_PGC_SCU);
> +     pm->lpm_enable_core(gpc, false, GPC_PGC_FM);
> +     pm->clear_slots(gpc);
> +}
> +
> +static int imx_gpcv2_pm_enter(suspend_state_t state) {
> +     struct imx_gpcv2_suspend *pm;
> +
> +     BUG_ON(!gpcv2_instance);
> +     pm = gpcv2_instance->pm;
> +
> +     switch (state) {
> +     case PM_SUSPEND_STANDBY:
> +             pm->standby(gpcv2_instance);
> +             break;
> +
> +     case PM_SUSPEND_MEM:
> +             pm->suspend(gpcv2_instance);
> +             break;
> +     default:
> +             return -EINVAL;
> +     }
> +
> +     return 0;
> +}
> +
> +static int imx_gpcv2_pm_valid(suspend_state_t state) {
> +     return state == PM_SUSPEND_MEM || state == PM_SUSPEND_STANDBY; }
> +
> +static int __init imx_get_base_from_node(struct device_node *node,
> +                     struct imx7_pm_base *base)
> +{
> +     struct resource res;
> +     int ret = 0;
> +
> +     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) {
> +             iounmap(base->vbase);
> +             ret = -ENOMEM;
> +     }
> +put_node:
> +     of_node_put(node);
> +
> +     return ret;
> +}
> +
> +static int __init imx_get_base_from_dt(struct imx7_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 imx_get_exec_base_from_dt(struct imx7_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 = __arm_ioremap_exec(res.start, resource_size(&res),
> +false);
> +
> +     if (!base->vbase)
> +             ret = -ENOMEM;
> +
> +put_node:
> +     of_node_put(node);
> +out:
> +     return ret;
> +}
> +
> +static int __init imx_gpcv2_suspend_init(struct imx_gpcv2_suspend *pm,
> +                     const struct imx7_pm_socdata *socdata) {
> +     struct imx7_pm_base aips_base[3] = { {0, 0}, {0, 0}, {0, 0} };
> +     struct imx7_pm_base sram_base = {0, 0};
> +     struct imx7_cpu_pm_info *pm_info;
> +     struct device_node *node = NULL;
> +     int i, ret = 0;
> +
> +     const u32 (*ddrc_phy_offset_array)[2];
> +     const u32 (*ddrc_offset_array)[2];
> +
> +     if (!socdata || !pm) {
> +             pr_warn("%s: invalid argument!\n", __func__);
> +             return -EINVAL;
> +     }
> +
> +     for (i = 0; i < 3; i++) {
> +             node = of_find_compatible_node(node, NULL, "fsl,aips-bus");
> +             if (!node) {
> +                     pr_warn("%s: failed to find aips %d node!\n",
> +                                     __func__, i);
> +                     break;
> +             }
> +             ret = imx_get_base_from_node(node, &aips_base[i]);
> +             if (ret) {
> +                     pr_warn("%s: failed to get aips[%d] base %d!\n",
> +                                     __func__, i, ret);
> +             }
> +     }
> +
> +     ret = imx_get_exec_base_from_dt(&sram_base, "fsl,lpm-sram");
> +     if (ret) {
> +             pr_warn("%s: failed to get lpm-sram base %d!\n",
> +                             __func__, ret);
> +             goto lpm_sram_map_failed;
> +     }
> +
> +     pm_info = sram_base.vbase;
> +     pm_info->pbase = sram_base.pbase;
> +     pm_info->resume_addr = virt_to_phys(ca7_cpu_resume);
> +     pm_info->pm_info_size = sizeof(*pm_info);
> +
> +     ret = imx_get_base_from_dt(&pm_info->ccm_base,
> socdata->ccm_compat);
> +     if (ret) {
> +             pr_warn("%s: failed to get ccm base %d!\n", __func__, ret);
> +             goto ccm_map_failed;
> +     }
> +
> +     ret = imx_get_base_from_dt(&pm_info->ddrc_base,
> socdata->ddrc_compat);
> +     if (ret) {
> +             pr_warn("%s: failed to get ddrc base %d!\n", __func__, ret);
> +             goto ddrc_map_failed;
> +     }
> +
> +     ret = imx_get_base_from_dt(&pm_info->ddrc_phy_base,
> +                             socdata->ddrc_phy_compat);
> +     if (ret) {
> +             pr_warn("%s: failed to get ddrc_phy base %d!\n", __func__, ret);
> +             goto ddrc_phy_map_failed;
> +     }
> +
> +     ret = imx_get_base_from_dt(&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 = imx_get_base_from_dt(&pm_info->iomuxc_gpr_base,
> +                             socdata->iomuxc_gpr_compat);
> +     if (ret) {
> +             pr_warn("%s: failed to get iomuxc_gpr base %d!\n",
> +                                     __func__, ret);
> +             goto iomuxc_gpr_map_failed;
> +     }
> +
> +     ret = imx_get_base_from_dt(&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;
> +     }
> +
> +     ret = imx_get_base_from_dt(&pm_info->anatop_base,
> +                             socdata->anatop_compat);
> +     if (ret) {
> +             pr_warn("%s: failed to get anatop base %d!\n", __func__, ret);
> +             goto anatop_map_failed;
> +     }
> +
> +     pm_info->ddrc_num = socdata->ddrc_num;
> +     ddrc_offset_array = socdata->ddrc_offset;
> +     pm_info->ddrc_phy_num = socdata->ddrc_phy_num;
> +     ddrc_phy_offset_array = socdata->ddrc_phy_offset;
> +
> +     /* initialize DDRC settings */
> +     for (i = 0; i < pm_info->ddrc_num; i++) {
> +             pm_info->ddrc_val[i][0] = ddrc_offset_array[i][0];
> +             if (ddrc_offset_array[i][1] == READ_DATA_FROM_HARDWARE)
> +                     pm_info->ddrc_val[i][1] =
> +                             readl_relaxed(pm_info->ddrc_base.vbase +
> +                             ddrc_offset_array[i][0]);
> +             else
> +                     pm_info->ddrc_val[i][1] = ddrc_offset_array[i][1];
> +     }
> +
> +     /* initialize DDRC PHY settings */
> +     for (i = 0; i < pm_info->ddrc_phy_num; i++) {
> +             pm_info->ddrc_phy_val[i][0] =
> +                     ddrc_phy_offset_array[i][0];
> +             if (ddrc_phy_offset_array[i][1] == READ_DATA_FROM_HARDWARE)
> +                     pm_info->ddrc_phy_val[i][1] =
> +                             readl_relaxed(pm_info->ddrc_phy_base.vbase +
> +                             ddrc_phy_offset_array[i][0]);
> +             else
> +                     pm_info->ddrc_phy_val[i][1] =
> +                             ddrc_phy_offset_array[i][1];
> +     }
> +
> +     pm->suspend_fn_in_ocram = fncpy(
> +             sram_base.vbase + sizeof(*pm_info),
> +             &imx7_suspend,
> +             MX7_SUSPEND_OCRAM_SIZE - sizeof(*pm_info));
> +     pm->ocram_vbase = sram_base.vbase;
> +
> +     goto put_node;
> +
> +anatop_map_failed:
> +     iounmap(pm_info->anatop_base.vbase);
> +gpc_map_failed:
> +     iounmap(pm_info->gpc_base.vbase);
> +iomuxc_gpr_map_failed:
> +     iounmap(pm_info->iomuxc_gpr_base.vbase);
> +src_map_failed:
> +     iounmap(pm_info->src_base.vbase);
> +ddrc_phy_map_failed:
> +     iounmap(pm_info->ddrc_phy_base.vbase);
> +ddrc_map_failed:
> +     iounmap(pm_info->ddrc_base.vbase);
> +ccm_map_failed:
> +     iounmap(pm_info->ccm_base.vbase);
> +lpm_sram_map_failed:
> +     iounmap(sram_base.vbase);
> +put_node:
> +     of_node_put(node);
> +
> +     return ret;
> +}
> +
> +static const struct platform_suspend_ops imx_gpcv2_pm_ops = {
> +     .enter = imx_gpcv2_pm_enter,
> +     .valid = imx_gpcv2_pm_valid,
> +};
> +
> +static int __init imx_gpcv2_pm_init(void) {
> +     struct imx_gpcv2_suspend *pm;
> +     struct imx_gpcv2 *gpc;
> +     int val, num;
> +
> +     pm = kzalloc(sizeof(struct imx_gpcv2_suspend), GFP_KERNEL);
> +     if (!pm) {
> +             pr_warn("[GPCv2] %s init failed\r\n", __func__);
> +             return -ENOMEM;
> +     }
> +
> +     gpc = kzalloc(sizeof(struct imx_gpcv2), GFP_KERNEL);
> +     if (!gpc) {
> +             pr_warn("[GPCv2] %s init failed\r\n", __func__);
> +             kfree(pm);
> +             return -ENOMEM;
> +     }
> +
> +     imx_gpcv2_suspend_init(pm, &imx7d_pm_data_ddr3);
> +
> +     pm->lpm_env_setup = imx_gpcv2_lpm_env_setup;
> +     pm->lpm_env_clean = imx_gpcv2_lpm_env_clean;
> +
> +     pm->lpm_cpu_power_gate = imx_gpcv2_lpm_cpu_power_gate;
> +     pm->lpm_plat_power_gate = imx_lpm_plat_power_gate;
> +     pm->lpm_enable_core = imx_gpcv2_lpm_enable_core;
> +     pm->set_mode = imx_gpcv2_lpm_set_mode;
> +
> +     pm->clear_slots = imx_gpcv2_lpm_clear_slots;
> +     pm->set_slot = imx_gpcv2_lpm_slot_setup;
> +     pm->set_act = imx_gpcv2_lpm_set_ack;
> +
> +     pm->standby = imx_gpcv2_lpm_standby;
> +     pm->suspend = imx_gpcv2_lpm_suspend;
> +
> +     gpc->anatop = syscon_regmap_lookup_by_compatible("fsl,imx6q-anatop");
> +     if (IS_ERR(gpc->anatop))
> +             goto error_exit;
> +
> +     gpc->gpcv2 = syscon_regmap_lookup_by_compatible("fsl,imx7d-gpc");
> +     if (IS_ERR(gpc->gpcv2))
> +             goto error_exit;
> +
> +     /* only external IRQs to wake up LPM and core 0/1 */
> +     regmap_read(gpc->gpcv2, GPC_LPCR_A7_BSC, &val);
> +     val |= BM_LPCR_A7_BSC_IRQ_SRC_A7_WAKEUP;
> +     regmap_write(gpc->gpcv2, GPC_LPCR_A7_BSC, val);
> +     /* mask m4 dsm trigger */
> +     regmap_read(gpc->gpcv2, GPC_LPCR_M4, &val);
> +     val |= BM_LPCR_M4_MASK_DSM_TRIGGER;
> +     regmap_write(gpc->gpcv2, GPC_LPCR_M4, val);
> +
> +     /* set mega/fast mix in A7 domain */
> +     regmap_write(gpc->gpcv2, GPC_PGC_CPU_MAPPING, 0x1);
> +     /* set SCU timing */
> +     val = (0x59 << 10) | 0x5B | (0x51 << 20);
> +     regmap_write(gpc->gpcv2, GPC_PGC_SCU_TIMING, val);
> +
> +     gpc->pm = pm;
> +     gpc->get_wakeup_source = imx_gpcv2_get_wakeup_source;
> +     gpcv2_instance = gpc;
> +
> +     num = imx_gpcv2_get_wakeup_source(0);
> +
> +     /*
> +      * The IP blocks which may be the wakeup sources are allocated into
> +      * several power domains. MFMIX, LPSRMX, and WAKEUPMIX are three of
> +      * those power domains. If a bit is '1' in the mask, it means the IP
> +      * block is inside the power domain. The mask will be used to decide
> +      * if a power domain should be shutdown or not when system goes into
> +      * suspend states.
> +      */
> +
> +     if (num)
> +             gpc->wakeupmix_mask = kzalloc(sizeof(u32)*num*3, GFP_KERNEL);
> +
> +     if (!gpc->wakeupmix_mask)
> +             goto error_exit;
> +
> +     /*
> +      * Shutdown LPSRMIX and WAKEUP power domain has not been
> +      * implemented in this patch yet.
> +      */
> +
> +     gpc->lpsrmix_mask = gpc->wakeupmix_mask + num;
> +     gpc->mfmix_mask = gpc->wakeupmix_mask + num*2;
> +
> +     /* Mask the wakeup sources in M/F power domain */
> +     gpc->mfmix_mask[0] = 0x54010000;
> +     gpc->mfmix_mask[1] = 0xC00;
> +     gpc->mfmix_mask[2] = 0x0;
> +     gpc->mfmix_mask[3] = 0x400010;
> +
> +     suspend_set_ops(&imx_gpcv2_pm_ops);
> +     return 0;
> +
> +error_exit:
> +     kfree(pm);
> +     kfree(gpc);
> +     return -ENODEV;
> +}
> +
> +device_initcall(imx_gpcv2_pm_init);
> diff --git a/arch/arm/mach-imx/suspend-imx7.S
> b/arch/arm/mach-imx/suspend-imx7.S
> new file mode 100644
> index 0000000..8c3f516
> --- /dev/null
> +++ b/arch/arm/mach-imx/suspend-imx7.S
> @@ -0,0 +1,529 @@
> +/*
> + * Copyright (C) 2015 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/asm-offsets.h>
> +#include "hardware.h"
> +
> +/*
> + * ==================== low level suspend ====================
> + *
> + * Better to follow below rules to use ARM registers:
> + * r0: pm_info structure address;
> + * r1 ~ r4: for saving pm_info members;
> + * r5 ~ r10: free registers;
> + * r11: io base address.
> + *
> + * suspend ocram space layout:
> + * ======================== high address ======================
> + *                              .
> + *                              .
> + *                              .
> + *                              ^
> + *                              ^
> + *                              ^
> + *                      imx7_suspend code
> + *              PM_INFO structure(imx7_cpu_pm_info)
> + * ======================== low address =======================  */
> +
> +/*
> + * Below offsets are based on struct imx7_cpu_pm_info
> + * which defined in arch/arm/mach-imx/pm-imx7.c, this
> + * structure contains necessary pm info for low level
> + * suspend related code.
> + */
> +#define PM_INFO_M4_RESERVE0_OFFSET           0x0
> +#define PM_INFO_M4_RESERVE1_OFFSET           0x4
> +#define PM_INFO_M4_RESERVE2_OFFSET           0x8
> +#define PM_INFO_PBASE_OFFSET                 0xc
> +#define PM_INFO_RESUME_ADDR_OFFSET           0x10
> +#define PM_INFO_DDR_TYPE_OFFSET                      0x14
> +#define PM_INFO_PM_INFO_SIZE_OFFSET          0x18
> +#define PM_INFO_MX7_DDRC_P_OFFSET            0x1c
> +#define PM_INFO_MX7_DDRC_V_OFFSET            0x20
> +#define PM_INFO_MX7_DDRC_PHY_P_OFFSET                0x24
> +#define PM_INFO_MX7_DDRC_PHY_V_OFFSET                0x28
> +#define PM_INFO_MX7_SRC_P_OFFSET             0x2c
> +#define PM_INFO_MX7_SRC_V_OFFSET             0x30
> +#define PM_INFO_MX7_IOMUXC_GPR_P_OFFSET              0x34
> +#define PM_INFO_MX7_IOMUXC_GPR_V_OFFSET              0x38
> +#define PM_INFO_MX7_CCM_P_OFFSET             0x3c
> +#define PM_INFO_MX7_CCM_V_OFFSET             0x40
> +#define PM_INFO_MX7_GPC_P_OFFSET             0x44
> +#define PM_INFO_MX7_GPC_V_OFFSET             0x48
> +#define PM_INFO_MX7_L2_P_OFFSET                      0x4c
> +#define PM_INFO_MX7_L2_V_OFFSET                      0x50
> +#define PM_INFO_MX7_ANATOP_P_OFFSET          0x54
> +#define PM_INFO_MX7_ANATOP_V_OFFSET          0x58
> +#define PM_INFO_MX7_TTBR1_V_OFFSET           0x5c
> +#define PM_INFO_DDRC_REG_NUM_OFFSET          0x60
> +#define PM_INFO_DDRC_REG_OFFSET                      0x64
> +#define PM_INFO_DDRC_VALUE_OFFSET            0x68
> +#define PM_INFO_DDRC_PHY_REG_NUM_OFFSET              0x164
> +#define PM_INFO_DDRC_PHY_REG_OFFSET          0x168
> +#define PM_INFO_DDRC_PHY_VALUE_OFFSET                0x16c
> +
> +#define MX7_SRC_GPR1 0x74
> +#define MX7_SRC_GPR2 0x78
> +#define GPC_PGC_FM   0xa00
> +#define ANADIG_SNVS_MISC_CTRL        0x380
> +#define DDRC_STAT    0x4
> +#define DDRC_PWRCTL  0x30
> +#define DDRC_PSTAT   0x3fc
> +#define DDRC_PCTRL_0 0x490
> +#define DDRC_DFIMISC 0x1b0
> +#define DDRC_SWCTL   0x320
> +#define DDRC_SWSTAT  0x324
> +#define DDRPHY_LP_CON0       0x18
> +
> +     .align 3
> +
> +     .macro  disable_l1_dcache
> +
> +     /*
> +      * Flush all data from the L1 data cache before disabling
> +      * SCTLR.C bit.
> +      */
> +     push    {r0 - r10, lr}
> +     ldr     r7, =v7_flush_dcache_all
> +     mov     lr, pc
> +     mov     pc, r7
> +     pop     {r0 - r10, lr}
> +
> +     /* disable d-cache */
> +     mrc     p15, 0, r7, c1, c0, 0
> +     bic     r7, r7, #(1 << 2)
> +     mcr     p15, 0, r7, c1, c0, 0
> +     dsb
> +     isb
> +
> +     push    {r0 - r10, lr}
> +     ldr     r7, =v7_flush_dcache_all
> +     mov     lr, pc
> +     mov     pc, r7
> +     pop     {r0 - r10, lr}
> +
> +     .endm
> +
> +
> +     .macro enable_l1_dcache
> +
> +     /* Enable L1 data cache. */
> +     mrc     p15, 0, r6, c1, c0, 0
> +     orr     r6, r6, #0x4
> +     mcr     p15, 0, r6, c1, c0, 0
> +
> +     dsb
> +     isb
> +
> +     .endm
> +
> +
> +     .macro ddrc_enter_self_refresh
> +
> +     ldr     r11, [r0, #PM_INFO_MX7_DDRC_V_OFFSET]
> +
> +     /* let DDR out of self-refresh */
> +     ldr     r7, =0x0
> +     str     r7, [r11, #DDRC_PWRCTL]
> +
> +     /* wait rw port_busy clear */
> +     ldr     r6, =(0x1 << 16)
> +     orr     r6, r6, #0x1
> +1:
> +     ldr     r7, [r11, #DDRC_PSTAT]
> +     ands    r7, r7, r6
> +     bne     1b
> +
> +     /* enter self-refresh bit 5 */
> +     ldr     r7, =(0x1 << 5)
> +     str     r7, [r11, #DDRC_PWRCTL]
> +
> +     /* wait until self-refresh mode entered */
> +2:
> +     ldr     r7, [r11, #DDRC_STAT]
> +     and     r7, r7, #0x3
> +     cmp     r7, #0x3
> +     bne     2b
> +3:
> +     ldr     r7, [r11, #DDRC_STAT]
> +     ands    r7, r7, #0x20
> +     beq     3b
> +
> +     /* disable dram clk */
> +     ldr     r7, [r11, #DDRC_PWRCTL]
> +     orr     r7, r7, #(1 << 3)
> +     str     r7, [r11, #DDRC_PWRCTL]
> +
> +     .endm
> +
> +     .macro ddrc_exit_self_refresh
> +
> +     cmp     r5, #0x0
> +     ldreq   r11, [r0, #PM_INFO_MX7_DDRC_V_OFFSET]
> +     ldrne   r11, [r0, #PM_INFO_MX7_DDRC_P_OFFSET]
> +
> +     /* let DDR out of self-refresh */
> +     ldr     r7, =0x0
> +     str     r7, [r11, #DDRC_PWRCTL]
> +
> +     /* wait until self-refresh mode entered */
> +4:
> +     ldr     r7, [r11, #DDRC_STAT]
> +     and     r7, r7, #0x3
> +     cmp     r7, #0x3
> +     beq     4b
> +
> +     /* enable auto self-refresh */
> +     ldr     r7, [r11, #DDRC_PWRCTL]
> +     orr     r7, r7, #(1 << 0)
> +     str     r7, [r11, #DDRC_PWRCTL]
> +
> +     .endm
> +
> +     .macro wait_delay
> +5:
> +     subs    r6, r6, #0x1
> +     bne     5b
> +
> +     .endm
> +
> +     .macro ddr_enter_retention
> +
> +     ldr     r11, [r0, #PM_INFO_MX7_DDRC_V_OFFSET]
> +
> +     /* let DDR out of self-refresh */
> +     ldr     r7, =0x0
> +     str     r7, [r11, #DDRC_PCTRL_0]
> +
> +     /* wait rw port_busy clear */
> +     ldr     r6, =(0x1 << 16)
> +     orr     r6, r6, #0x1
> +6:
> +     ldr     r7, [r11, #DDRC_PSTAT]
> +     ands    r7, r7, r6
> +     bne     6b
> +
> +     ldr     r11, [r0, #PM_INFO_MX7_DDRC_V_OFFSET]
> +     /* enter self-refresh bit 5 */
> +     ldr     r7, =(0x1 << 5)
> +     str     r7, [r11, #DDRC_PWRCTL]
> +
> +     /* wait until self-refresh mode entered */
> +7:
> +     ldr     r7, [r11, #DDRC_STAT]
> +     and     r7, r7, #0x3
> +     cmp     r7, #0x3
> +     bne     7b
> +8:
> +     ldr     r7, [r11, #DDRC_STAT]
> +     ands    r7, r7, #0x20
> +     beq     8b
> +
> +     /* disable dram clk */
> +     ldr     r7, =(0x1 << 5)
> +     orr     r7, r7, #(1 << 3)
> +     str     r7, [r11, #DDRC_PWRCTL]
> +
> +     /* reset ddr_phy  */
> +     ldr     r11, [r0, #PM_INFO_MX7_ANATOP_V_OFFSET]
> +     ldr     r7, =0x0
> +     str     r7, [r11, #ANADIG_SNVS_MISC_CTRL]
> +
> +     /* delay 7 us */
> +     ldr     r6, =6000
> +     wait_delay
> +
> +     ldr     r11, [r0, #PM_INFO_MX7_SRC_V_OFFSET]
> +     ldr     r6, =0x1000
> +     ldr     r7, [r11, r6]
> +     orr     r7, r7, #0x1
> +     str     r7, [r11, r6]
> +     /* turn off ddr power */
> +     ldr     r11, [r0, #PM_INFO_MX7_ANATOP_V_OFFSET]
> +     ldr     r7, =(0x1 << 29)
> +     str     r7, [r11, #ANADIG_SNVS_MISC_CTRL]
> +
> +     .endm
> +
> +     .macro ddr_exit_retention
> +
> +     cmp     r5, #0x0
> +     ldreq   r1, [r0, #PM_INFO_MX7_ANATOP_V_OFFSET]
> +     ldrne   r1, [r0, #PM_INFO_MX7_ANATOP_P_OFFSET]
> +     ldreq   r2, [r0, #PM_INFO_MX7_SRC_V_OFFSET]
> +     ldrne   r2, [r0, #PM_INFO_MX7_SRC_P_OFFSET]
> +     ldreq   r3, [r0, #PM_INFO_MX7_DDRC_V_OFFSET]
> +     ldrne   r3, [r0, #PM_INFO_MX7_DDRC_P_OFFSET]
> +     ldreq   r4, [r0, #PM_INFO_MX7_DDRC_PHY_V_OFFSET]
> +     ldrne   r4, [r0, #PM_INFO_MX7_DDRC_PHY_P_OFFSET]
> +     ldreq   r10, [r0, #PM_INFO_MX7_CCM_V_OFFSET]
> +     ldrne   r10, [r0, #PM_INFO_MX7_CCM_P_OFFSET]
> +     ldreq   r11, [r0, #PM_INFO_MX7_IOMUXC_GPR_V_OFFSET]
> +     ldrne   r11, [r0, #PM_INFO_MX7_IOMUXC_GPR_P_OFFSET]
> +
> +     /* turn on ddr power */
> +     ldr     r7, =(0x1 << 29)
> +     str     r7, [r1, #ANADIG_SNVS_MISC_CTRL]
> +
> +     ldr     r6, =50
> +     wait_delay
> +
> +     ldr     r7, =0x0
> +     str     r7, [r1, #ANADIG_SNVS_MISC_CTRL]
> +
> +     /* clear ddr_phy reset */
> +     ldr     r6, =0x1000
> +     ldr     r7, [r2, r6]
> +     orr     r7, r7, #0x3
> +     str     r7, [r2, r6]
> +     ldr     r7, [r2, r6]
> +     bic     r7, r7, #0x1
> +     str     r7, [r2, r6]
> +
> +     ldr     r6, [r0, #PM_INFO_DDRC_REG_NUM_OFFSET]
> +     ldr     r7, =PM_INFO_DDRC_REG_OFFSET
> +     add     r7, r7, r0
> +9:
> +     ldr     r8, [r7], #0x4
> +     ldr     r9, [r7], #0x4
> +     str     r9, [r3, r8]
> +     subs    r6, r6, #0x1
> +     bne     9b
> +     ldr     r7, =0x20
> +     str     r7, [r3, #DDRC_PWRCTL]
> +     ldr     r7, =0x0
> +     str     r7, [r3, #DDRC_DFIMISC]
> +
> +     /* do PHY, clear ddr_phy reset */
> +     ldr     r6, =0x1000
> +     ldr     r7, [r2, r6]
> +     bic     r7, r7, #0x2
> +     str     r7, [r2, r6]
> +
> +     ldr     r7, =(0x1 << 30)
> +     str     r7, [r1, #ANADIG_SNVS_MISC_CTRL]
> +
> +     /* need to delay ~5mS */
> +     ldr     r6, =0x100000
> +     wait_delay
> +
> +     ldr     r6, [r0, #PM_INFO_DDRC_PHY_REG_NUM_OFFSET]
> +     ldr     r7, =PM_INFO_DDRC_PHY_REG_OFFSET
> +     add     r7, r7, r0
> +
> +10:
> +     ldr     r8, [r7], #0x4
> +     ldr     r9, [r7], #0x4
> +     str     r9, [r4, r8]
> +     subs    r6, r6, #0x1
> +     bne     10b
> +
> +     ldr     r7, =0x0
> +     add     r9, r10, #0x4000
> +     str     r7, [r9, #0x130]
> +
> +     cmp     r5, #0x0
> +     beq     101f
> +     ldr     r7, =0x170
> +     orr     r7, r7, #0x8
> +     str     r7, [r11, #0x20]
> +
> +101:
> +     ldr     r7, =0x2
> +     add     r9, r10, #0x4000
> +     str     r7, [r9, #0x130]
> +
> +     ldr     r7, =0xf
> +     str     r7, [r4, #DDRPHY_LP_CON0]
> +
> +     /* wait until self-refresh mode entered */
> +11:
> +     ldr     r7, [r3, #DDRC_STAT]
> +     and     r7, r7, #0x3
> +     cmp     r7, #0x3
> +     bne     11b
> +     ldr     r7, =0x0
> +     str     r7, [r3, #DDRC_SWCTL]
> +     ldr     r7, =0x1
> +     str     r7, [r3, #DDRC_DFIMISC]
> +     ldr     r7, =0x1
> +     str     r7, [r3, #DDRC_SWCTL]
> +12:
> +     ldr     r7, [r3, #DDRC_SWSTAT]
> +     and     r7, r7, #0x1
> +     cmp     r7, #0x1
> +     bne     12b
> +13:
> +     ldr     r7, [r3, #DDRC_STAT]
> +     and     r7, r7, #0x20
> +     cmp     r7, #0x20
> +     bne     13b
> +
> +     /* let DDR out of self-refresh */
> +     ldr     r7, =0x0
> +     str     r7, [r3, #DDRC_PWRCTL]
> +14:
> +     ldr     r7, [r3, #DDRC_STAT]
> +     and     r7, r7, #0x30
> +     cmp     r7, #0x0
> +     bne     14b
> +
> +15:
> +     ldr     r7, [r3, #DDRC_STAT]
> +     and     r7, r7, #0x3
> +     cmp     r7, #0x1
> +     bne     15b
> +
> +     /* enable port */
> +     ldr     r7, =0x1
> +     str     r7, [r3, #DDRC_PCTRL_0]
> +
> +     .endm
> +
> +ENTRY(imx7_suspend)
> +     push    {r4-r12}
> +
> +     /*
> +      * The value of r0 is mapped the same in origin table and IRAM table,
> +      * thus no need to care r0 here.
> +      */
> +     ldr     r1, [r0, #PM_INFO_PBASE_OFFSET]
> +     ldr     r2, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
> +     ldr     r3, [r0, #PM_INFO_DDR_TYPE_OFFSET]
> +     ldr     r4, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET]
> +
> +     /*
> +      * counting the resume address in iram
> +      * to set it in SRC register.
> +      */
> +     ldr     r6, =imx7_suspend
> +     ldr     r7, =resume
> +     sub     r7, r7, r6
> +     add     r8, r1, r4
> +     add     r9, r8, r7
> +
> +     ldr     r11, [r0, #PM_INFO_MX7_SRC_V_OFFSET]
> +     /* store physical resume addr and pm_info address. */
> +     str     r9, [r11, #MX7_SRC_GPR1]
> +     str     r1, [r11, #MX7_SRC_GPR2]
> +
> +     disable_l1_dcache
> +
> +     /*
> +      * make sure TLB contain the addr we want,
> +      * as we will access them after DDR is in
> +      * self-refresh mode.
> +      */
> +     ldr     r6, [r0, #PM_INFO_MX7_IOMUXC_GPR_V_OFFSET]
> +     ldr     r7, [r0, #0x0]
> +     ldr     r6, [r0, #PM_INFO_MX7_CCM_V_OFFSET]
> +     add     r6, #0x4000
> +     ldr     r7, [r6]
> +     ldr     r6, [r0, #PM_INFO_MX7_ANATOP_V_OFFSET]
> +     ldr     r7, [r6, #0x0]
> +     ldr     r6, [r0, #PM_INFO_MX7_SRC_V_OFFSET]
> +     ldr     r7, [r6, #0x0]
> +     add     r6, #0x1000
> +     ldr     r7, [r6]
> +     ldr     r6, [r0, #PM_INFO_MX7_DDRC_V_OFFSET]
> +     ldr     r7, [r6, #0x0]
> +     ldr     r7, [r6, #0x490]
> +     ldr     r6, [r0, #PM_INFO_MX7_DDRC_PHY_V_OFFSET]
> +     ldr     r7, [r6, #0x0]
> +
> +     ldr     r11, [r0, #PM_INFO_MX7_GPC_V_OFFSET]
> +     ldr     r7, [r11, #GPC_PGC_FM]
> +     cmp     r7, #0
> +     beq     ddr_only_self_refresh
> +
> +     ddr_enter_retention
> +     b       ddr_retention_enter_out
> +ddr_only_self_refresh:
> +     ddrc_enter_self_refresh
> +ddr_retention_enter_out:
> +
> +     /* Zzz, enter stop mode */
> +     wfi
> +     nop
> +     nop
> +     nop
> +     nop
> +
> +     mov     r5, #0x0
> +
> +     ldr     r11, [r0, #PM_INFO_MX7_GPC_V_OFFSET]
> +     ldr     r7, [r11, #GPC_PGC_FM]
> +     cmp     r7, #0
> +     beq     wfi_ddr_self_refresh_out
> +
> +     ddr_exit_retention
> +     b       wfi_ddr_retention_out
> +wfi_ddr_self_refresh_out:
> +     ddrc_exit_self_refresh
> +wfi_ddr_retention_out:
> +
> +     ldr     r11, [r0, #PM_INFO_MX7_IOMUXC_GPR_V_OFFSET]
> +     ldr     r7, =0x170
> +     orr     r7, r7, #0x8
> +     str     r7, [r11, #0x20]
> +
> +     /* clear core0's entry and parameter */
> +     ldr     r11, [r0, #PM_INFO_MX7_SRC_V_OFFSET]
> +     mov     r7, #0x0
> +     str     r7, [r11, #MX7_SRC_GPR1]
> +     str     r7, [r11, #MX7_SRC_GPR2]
> +
> +     enable_l1_dcache
> +
> +     pop     {r4-r12}
> +     /* 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, 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_MX7_SRC_P_OFFSET]
> +     mov     r7, #0x0
> +     str     r7, [r11, #MX7_SRC_GPR1]
> +     str     r7, [r11, #MX7_SRC_GPR2]
> +
> +     mov     r5, #0x1
> +
> +     ldr     r11, [r0, #PM_INFO_MX7_GPC_P_OFFSET]
> +     ldr     r7, [r11, #GPC_PGC_FM]
> +     cmp     r7, #0
> +     beq     dsm_ddr_self_refresh_out
> +
> +     ddr_exit_retention
> +     b       dsm_ddr_retention_out
> +dsm_ddr_self_refresh_out:
> +     ddrc_exit_self_refresh
> +dsm_ddr_retention_out:
> +
> +     mov     pc, lr
> +ENDPROC(imx7_suspend)
> +
> +ENTRY(ca7_cpu_resume)
> +     bl      v7_invalidate_l1
> +     b       cpu_resume
> +ENDPROC(ca7_cpu_resume)
> --
> 2.5.0.rc2
> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majord...@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
N�Р骒r��y����b�X�肚�v�^�)藓{.n�+�伐�{��赙zXФ�≤�}��财�z�&j:+v�����赙zZ+��+zf"�h���~����i���z��wア�?�ㄨ��&�)撷f��^j谦y�m��@A�a囤�
0鹅h���i

Reply via email to