Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753694AbbHUNph (ORCPT ); Fri, 21 Aug 2015 09:45:37 -0400 Received: from mail-by2on0116.outbound.protection.outlook.com ([207.46.100.116]:32800 "EHLO na01-by2-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1753221AbbHUNpc (ORCPT ); Fri, 21 Aug 2015 09:45:32 -0400 From: Shenwei Wang To: "shawn.guo@linaro.org" , "tglx@linutronix.de" , "jason@lakedaemon.net" CC: "linux-arm-kernel@lists.infradead.org" , "linux-kernel@vger.kernel.org" , Huang Anson Subject: RE: [PATCH v8 2/2] ARM: imx: support suspend states on imx7D Thread-Topic: [PATCH v8 2/2] ARM: imx: support suspend states on imx7D Thread-Index: AQHQy9jL5RmQnwqyGkCIlmUrTnk7lJ4Wl4gQ Date: Fri, 21 Aug 2015 13:45:28 +0000 Message-ID: References: <1438378439-11569-1-git-send-email-shenwei.wang@freescale.com> <1438378439-11569-3-git-send-email-shenwei.wang@freescale.com> In-Reply-To: <1438378439-11569-3-git-send-email-shenwei.wang@freescale.com> Accept-Language: zh-CN, en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=Shenwei.Wang@freescale.com; x-originating-ip: [192.88.168.50] x-microsoft-exchange-diagnostics: 1;BY1PR03MB1371;5:DQhuSqqHFz00LkzYMGk3BC52xaIf485SiM0y/NrPWuTKy6Rljch/vE12xtrICpTPNvHC27y0RgcQV0xKwkhJBJo7CBeK8O2fBGPcC2ygEjZwP0PSGl7kCMlN1YAJrf4XvVvv7TuZjIDtyAFHTrRiyw==;24:ay9s6eT4+HXQ1P6EaLBj0SM7oqaRT3O7/DxIG0BcYX0NMAEzZ1LULDAoZbvbBo1qamP5yeF1EMtRHMhylEZ9fwkOEKMWSvnPY5bkhX+alP0=;20:qJ/Bl/wQPDXCY8gSjavMfK13VtBFlCIDeOrGCLprFpxPKo5KRe69tEK0s2cJNAh6mWgY8oIaHk72Wp9mWBo77w== x-microsoft-antispam: UriScan:;BCL:0;PCL:0;RULEID:(42134001)(42139001);SRVR:BY1PR03MB1371; x-microsoft-antispam-prvs: x-exchange-antispam-report-test: UriScan:; x-exchange-antispam-report-cfa-test: BCL:0;PCL:0;RULEID:(601004)(8121501046)(5005006)(3002001);SRVR:BY1PR03MB1371;BCL:0;PCL:0;RULEID:;SRVR:BY1PR03MB1371; x-forefront-prvs: 067553F396 x-forefront-antispam-report: SFV:NSPM;SFS:(10019020)(6009001)(13464003)(199003)(189002)(5001860100001)(97736004)(101416001)(19580395003)(189998001)(5001770100001)(102836002)(2501003)(122556002)(68736005)(77096005)(2420400006)(2950100001)(2656002)(107886002)(4001540100001)(5001830100001)(5007970100001)(87936001)(54356999)(40100003)(5003600100002)(5004730100002)(19580405001)(86362001)(5002640100001)(74316001)(105586002)(46102003)(15975445007)(2900100001)(5001960100002)(81156007)(92566002)(76576001)(10710500002)(106116001)(77156002)(66066001)(575784001)(99286002)(33656002)(62966003)(2201001)(64706001)(10400500002)(76176999)(50986999)(106356001)(2004002)(4001430100001)(559001)(569005);DIR:OUT;SFP:1102;SCL:1;SRVR:BY1PR03MB1371;H:CY1PR0301MB0843.namprd03.prod.outlook.com;FPR:;SPF:None;PTR:InfoNoRecords;A:1;MX:1;LANG:en; Content-Type: text/plain; charset="gb2312" MIME-Version: 1.0 X-OriginatorOrg: freescale.com X-MS-Exchange-CrossTenant-originalarrivaltime: 21 Aug 2015 13:45:28.7731 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 710a03f5-10f6-4d38-9ff4-a80b81da590d X-MS-Exchange-Transport-CrossTenantHeadersStamped: BY1PR03MB1371 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: 8bit X-MIME-Autoconverted: from base64 to 8bit by mail.home.local id t7LDjnIW001330 Content-Length: 43042 Lines: 1528 Ping. Shenwei > -----Original Message----- > From: linux-kernel-owner@vger.kernel.org > [mailto:linux-kernel-owner@vger.kernel.org] On Behalf Of Shenwei Wang > Sent: 2015??7??31?? 16:34 > To: shawn.guo@linaro.org; tglx@linutronix.de; jason@lakedaemon.net > Cc: linux-arm-kernel@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 > Signed-off-by: Anson Huang > --- > 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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#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 > +#include > +#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 majordomo@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?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m???? ????????I?