From: Stefan Agner <stefan@agner.ch> To: shawn.guo@freescale.com, kernel@pengutronix.de, linus.walleij@linaro.org, gnurou@gmail.com Cc: linux@arm.linux.org.uk, jingchang.lu@freescale.com, b20788@freescale.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, stefan@agner.ch Subject: [PATCH 9/9] ARM: vf610: initial suspend/resume support Date: Mon, 22 Sep 2014 19:09:30 +0200 [thread overview] Message-ID: <2436672a230a1b8e4b2ab770df4eae2448539805.1411404079.git.stefan@agner.ch> (raw) In-Reply-To: <cover.1411404079.git.stefan@agner.ch> In-Reply-To: <cover.1411404079.git.stefan@agner.ch> This patch adds initial suspend/resume support for Vybrid SoC. The standby sleep state puts the module in LP-RUN mode which allows to the SoC to be woken by a regular interrupt. The mem sleep state (Suspend-to-RAM) uses the STOP mode which puts the memory in self-refresh mode but keeps the memory clock enabled. To wake the SoC a power management wakeup IRQ need to be enabled using irq_set_irq_wake (e.g. through a GPIO). Signed-off-by: Stefan Agner <stefan@agner.ch> --- arch/arm/mach-imx/Makefile | 1 + arch/arm/mach-imx/clk-vf610.c | 3 + arch/arm/mach-imx/common.h | 10 + arch/arm/mach-imx/mach-vf610.c | 1 + arch/arm/mach-imx/pm-vf610.c | 428 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 443 insertions(+) create mode 100644 arch/arm/mach-imx/pm-vf610.c diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index 6e4fcd8..0f558aa 100644 --- a/arch/arm/mach-imx/Makefile +++ b/arch/arm/mach-imx/Makefile @@ -103,6 +103,7 @@ AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a obj-$(CONFIG_SOC_IMX6) += suspend-imx6.o endif obj-$(CONFIG_SOC_IMX6) += pm-imx6.o +obj-$(CONFIG_SOC_VF610) += pm-vf610.o obj-$(CONFIG_SOC_IMX50) += mach-imx50.o obj-$(CONFIG_SOC_IMX51) += mach-imx51.o diff --git a/arch/arm/mach-imx/clk-vf610.c b/arch/arm/mach-imx/clk-vf610.c index 1034e78..fbaa708 100644 --- a/arch/arm/mach-imx/clk-vf610.c +++ b/arch/arm/mach-imx/clk-vf610.c @@ -13,6 +13,7 @@ #include <dt-bindings/clock/vf610-clock.h> #include "clk.h" +#include "common.h" #define CCM_CCR (ccm_base + 0x00) #define CCM_CSR (ccm_base + 0x04) @@ -131,6 +132,8 @@ static void __init vf610_clocks_init(struct device_node *ccm_node) ccm_base = of_iomap(np, 0); BUG_ON(!ccm_base); + vf610_pm_set_ccm_base(ccm_base); + clk[VF610_CLK_SLOW_CLK_SEL] = imx_clk_mux("slow_clk_sel", CCM_CCSR, 4, 1, slow_sels, ARRAY_SIZE(slow_sels)); clk[VF610_CLK_FASK_CLK_SEL] = imx_clk_mux("fast_clk_sel", CCM_CCSR, 5, 1, fast_sels, ARRAY_SIZE(fast_sels)); diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h index ca7a0d9..6dd360f 100644 --- a/arch/arm/mach-imx/common.h +++ b/arch/arm/mach-imx/common.h @@ -80,6 +80,13 @@ enum mxc_cpu_pwr_mode { STOP_POWER_OFF, /* STOP + SRPG */ }; +enum vf610_cpu_pwr_mode { + VF610_RUN, + VF610_LP_RUN, + VF610_STOP, + VF610_LP_STOP, +}; + enum mx3_cpu_pwr_mode { MX3_RUN, MX3_WAIT, @@ -125,6 +132,7 @@ int imx_cpu_kill(unsigned int cpu); #ifdef CONFIG_SUSPEND void v7_cpu_resume(void); void imx6_suspend(void __iomem *ocram_vbase); +void vf610_suspend(void); #else static inline void v7_cpu_resume(void) {} static inline void imx6_suspend(void __iomem *ocram_vbase) {} @@ -135,6 +143,8 @@ void imx6dl_pm_init(void); void imx6sl_pm_init(void); void imx6sx_pm_init(void); void imx6q_pm_set_ccm_base(void __iomem *base); +void vf610_pm_init(void); +void vf610_pm_set_ccm_base(void __iomem *base); #ifdef CONFIG_PM void imx51_pm_init(void); diff --git a/arch/arm/mach-imx/mach-vf610.c b/arch/arm/mach-imx/mach-vf610.c index eef3496..79f0461 100644 --- a/arch/arm/mach-imx/mach-vf610.c +++ b/arch/arm/mach-imx/mach-vf610.c @@ -25,6 +25,7 @@ static void __init vf610_init_machine(void) { mxc_arch_reset_init_dt(); of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); + vf610_pm_init(); } static const char * const vf610_dt_compat[] __initconst = { diff --git a/arch/arm/mach-imx/pm-vf610.c b/arch/arm/mach-imx/pm-vf610.c new file mode 100644 index 0000000..87dbbac --- /dev/null +++ b/arch/arm/mach-imx/pm-vf610.c @@ -0,0 +1,428 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2014 Toradex AG + * + * 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/delay.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/genalloc.h> +#include <linux/mfd/syscon.h> +#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/regmap.h> +#include <linux/suspend.h> +#include <linux/clk.h> +#include <asm/cacheflush.h> +#include <asm/fncpy.h> +#include <asm/proc-fns.h> +#include <asm/suspend.h> +#include <asm/tlb.h> + +#include "common.h" + +#define CCR 0x0 +#define BM_CCR_FIRC_EN (0x1 << 16) +#define BM_CCR_FXOSC_EN (0x1 << 12) + +#define CCSR 0x8 +#define BM_CCSR_DDRC_CLK_SEL (0x1 << 6) +#define BM_CCSR_FAST_CLK_SEL (0x1 << 5) +#define BM_CCSR_SLOW_CLK_SEL (0x1 << 4) +#define BM_CCSR_SYS_CLK_SEL_MASK (0x7 << 0) + +#define CLPCR 0x2c +#define BM_CLPCR_ARM_CLK_DIS_ON_LPM (0x1 << 5) +#define BM_CLPCR_SBYOS (0x1 << 6) +#define BM_CLPCR_DIS_REF_OSC (0x1 << 7) +#define BM_CLPCR_ANADIG_STOP_MODE (0x1 << 8) +#define BM_CLPCR_FXOSC_BYPSEN (0x1 << 10) +#define BM_CLPCR_FXOSC_PWRDWN (0x1 << 11) +#define BM_CLPCR_MASK_CORE0_WFI (0x1 << 22) +#define BM_CLPCR_MASK_CORE1_WFI (0x1 << 23) +#define BM_CLPCR_MASK_SCU_IDLE (0x1 << 24) +#define BM_CLPCR_MASK_L2CC_IDLE (0x1 << 25) + +#define CGPR 0x64 +#define BM_CGPR_INT_MEM_CLK_LPM (0x1 << 17) + +#define GPC_PGCR 0x0 +#define BM_PGCR_DS_STOP (0x1 << 7) +#define BM_PGCR_DS_LPSTOP (0x1 << 6) +#define BM_PGCR_WB_STOP (0x1 << 4) +#define BM_PGCR_HP_OFF (0x1 << 3) + +#define GPC_LPMR 0x40 +#define BM_LPMR_RUN 0x0 +#define BM_LPMR_STOP 0x2 + +#define ANATOP_PLL1_CTRL 0x270 +#define ANATOP_PLL2_CTRL 0x30 +#define ANATOP_PLL3_CTRL 0x10 +#define ANATOP_PLL4_CTRL 0x70 +#define ANATOP_PLL5_CTRL 0xe0 +#define ANATOP_PLL6_CTRL 0xa0 +#define ANATOP_PLL7_CTRL 0x10 +#define BM_PLL_ENABLE (0x1 << 13) +#define BM_PLL_LOCK (0x1 << 31) + +#define VF610_SUSPEND_OCRAM_SIZE 0x4000 + +#define VF_UART0_BASE_ADDR 0x40027000 +#define VF_UART1_BASE_ADDR 0x40028000 +#define VF_UART2_BASE_ADDR 0x40029000 +#define VF_UART3_BASE_ADDR 0x4002a000 +#define VF_UART_BASE_ADDR(n) VF_UART##n##_BASE_ADDR +#define VF_UART_BASE(n) VF_UART_BASE_ADDR(n) +#define VF_UART_PHYSICAL_BASE VF_UART_BASE(CONFIG_DEBUG_VF_UART_PORT) + +static void __iomem *ccm_base; +static void __iomem *suspend_ocram_base; + +/* + * suspend ocram space layout: + * ======================== high address ====================== + * . + * . + * . + * ^ + * ^ + * ^ + * vf610_suspend code + * PM_INFO structure(vf610_cpu_pm_info) + * ======================== low address ======================= + */ + +struct vf610_pm_base { + phys_addr_t pbase; + void __iomem *vbase; +}; + +struct vf610_pm_socdata { + const char *src_compat; + const char *gpc_compat; + const char *anatop_compat; +}; + +static const struct vf610_pm_socdata vf610_pm_data __initconst = { + .src_compat = "fsl,vf610-src", + .gpc_compat = "fsl,vf610-gpc", + .anatop_compat = "fsl,vf610-anatop", +}; + +/* + * This structure is for passing necessary data for low level ocram + * suspend code(arch/arm/mach-imx/suspend-vf610.S), if this struct + * definition is changed, the offset definition in + * arch/arm/mach-imx/suspend-vf610.S must be also changed accordingly, + * otherwise, the suspend to ocram function will be broken! + */ +struct vf610_cpu_pm_info { + phys_addr_t pbase; /* The physical address of pm_info. */ + phys_addr_t resume_addr; /* The physical resume address for asm code */ + u32 cpu_type; /* Currently not used, leave it for alignment */ + u32 pm_info_size; /* Size of pm_info. */ + struct vf610_pm_base anatop_base; + struct vf610_pm_base src_base; + struct vf610_pm_base ccm_base; + struct vf610_pm_base gpc_base; + struct vf610_pm_base l2_base; +} __aligned(8); + +#ifdef DEBUG +static void uart_reinit(unsigned long int rate, unsigned long int baud) +{ + void __iomem *membase = ioremap(VF_UART_PHYSICAL_BASE, 0x1000); + u8 tmp; + u16 sbr, brfa; + + /* UART_C2 */ + __raw_writeb(0, membase + 0x3); + + sbr = (u16) (rate / (baud * 16)); + brfa = (rate / baud) - (sbr * 16); + + tmp = ((sbr & 0x1f00) >> 8); + __raw_writeb(tmp, membase + 0x0); + tmp = sbr & 0x00ff; + __raw_writeb(tmp, membase + 0x1); + + /* UART_C4 */ + __raw_writeb(brfa & 0xf, membase + 0xa); + + /* UART_C2 */ + __raw_writeb(0xac, membase + 0x3); + + iounmap(membase); +} +#else +static void uart_reinit(unsigned long int rate, unsigned long int baud) {} +#endif + +static void vf610_enable_pll(void __iomem *pll_base) +{ + writel_relaxed(readl_relaxed(pll_base) | BM_PLL_ENABLE, pll_base); +} + +static void vf610_disable_pll(void __iomem *pll_base) +{ + writel_relaxed(readl_relaxed(pll_base) & ~BM_PLL_ENABLE, pll_base); +} + +int vf610_set_lpm(enum vf610_cpu_pwr_mode mode) +{ + u32 ccr = readl_relaxed(ccm_base + CCR); + u32 ccsr = readl_relaxed(ccm_base + CCSR); + u32 cclpcr = readl_relaxed(ccm_base + CLPCR); + struct vf610_cpu_pm_info *pm_info = suspend_ocram_base; + void __iomem *gpc_base = pm_info->gpc_base.vbase; + u32 gpc_pgcr = readl_relaxed(gpc_base + GPC_PGCR); + void __iomem *anatop = pm_info->anatop_base.vbase; + + switch (mode) { + case VF610_STOP: + cclpcr &= ~BM_CLPCR_ANADIG_STOP_MODE; + cclpcr |= BM_CLPCR_ARM_CLK_DIS_ON_LPM; + cclpcr &= ~BM_CLPCR_SBYOS; + writel_relaxed(cclpcr, ccm_base + CLPCR); + + gpc_pgcr |= BM_PGCR_DS_STOP; + gpc_pgcr |= BM_PGCR_HP_OFF; + writel_relaxed(gpc_pgcr, gpc_base + GPC_PGCR); + + writel_relaxed(BM_LPMR_STOP, gpc_base + GPC_LPMR); + /* fall-through */ + case VF610_LP_RUN: + ccr |= BM_CCR_FIRC_EN; + writel_relaxed(ccr, ccm_base + CCR); + + /* Switch internal OSC's */ + ccsr &= ~BM_CCSR_FAST_CLK_SEL; + ccsr &= ~BM_CCSR_SLOW_CLK_SEL; + writel_relaxed(ccsr, ccm_base + CCSR); + + ccsr &= ~BM_CCSR_SYS_CLK_SEL_MASK; + writel_relaxed(ccsr, ccm_base + CCSR); + uart_reinit(4000000UL, 115200); + + vf610_disable_pll(anatop + ANATOP_PLL1_CTRL); + vf610_disable_pll(anatop + ANATOP_PLL3_CTRL); + vf610_disable_pll(anatop + ANATOP_PLL5_CTRL); + vf610_disable_pll(anatop + ANATOP_PLL7_CTRL); + break; + case VF610_RUN: + vf610_enable_pll(anatop + ANATOP_PLL1_CTRL); + vf610_enable_pll(anatop + ANATOP_PLL3_CTRL); + vf610_enable_pll(anatop + ANATOP_PLL5_CTRL); + vf610_enable_pll(anatop + ANATOP_PLL7_CTRL); + + ccsr &= ~BM_CCSR_SYS_CLK_SEL_MASK; + ccsr |= 0x4; + writel_relaxed(ccsr, ccm_base + CCSR); + uart_reinit(83368421UL, 115200); + + writel_relaxed(BM_LPMR_RUN, gpc_base + GPC_LPMR); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int vf610_pm_enter(suspend_state_t state) +{ + switch (state) { + case PM_SUSPEND_STANDBY: + vf610_set_lpm(VF610_LP_RUN); + imx_gpc_pre_suspend(false); + + /* zzZZZzzz */ + cpu_do_idle(); + + imx_gpc_post_resume(); + vf610_set_lpm(VF610_RUN); + break; + case PM_SUSPEND_MEM: + vf610_set_lpm(VF610_STOP); + imx_gpc_pre_suspend(false); + + cpu_do_idle(); + + imx_gpc_post_resume(); + vf610_set_lpm(VF610_RUN); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int vf610_pm_valid(suspend_state_t state) +{ + return (state == PM_SUSPEND_STANDBY || state == PM_SUSPEND_MEM); +} + +static const struct platform_suspend_ops vf610_pm_ops = { + .enter = vf610_pm_enter, + .valid = vf610_pm_valid, +}; + +void __init vf610_pm_set_ccm_base(void __iomem *base) +{ + ccm_base = base; +} + +static int __init imx_pm_get_base(struct vf610_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 vf610_suspend_init(const struct vf610_pm_socdata *socdata) +{ + phys_addr_t ocram_pbase; + struct device_node *node; + struct platform_device *pdev; + struct vf610_cpu_pm_info *pm_info; + struct gen_pool *ocram_pool; + unsigned long ocram_base; + int ret = 0; + + suspend_set_ops(&vf610_pm_ops); + + if (!socdata) { + pr_warn("%s: invalid argument!\n", __func__); + return -EINVAL; + } + + node = of_find_compatible_node(NULL, NULL, "mmio-sram"); + if (!node) { + pr_warn("%s: failed to find ocram node!\n", __func__); + return -ENODEV; + } + + pdev = of_find_device_by_node(node); + if (!pdev) { + pr_warn("%s: failed to find ocram device!\n", __func__); + ret = -ENODEV; + goto put_node; + } + + ocram_pool = dev_get_gen_pool(&pdev->dev); + if (!ocram_pool) { + pr_warn("%s: ocram pool unavailable!\n", __func__); + ret = -ENODEV; + goto put_node; + } + + ocram_base = gen_pool_alloc(ocram_pool, VF610_SUSPEND_OCRAM_SIZE); + if (!ocram_base) { + pr_warn("%s: unable to alloc ocram!\n", __func__); + ret = -ENOMEM; + goto put_node; + } + + ocram_pbase = gen_pool_virt_to_phys(ocram_pool, ocram_base); + + suspend_ocram_base = __arm_ioremap_exec(ocram_pbase, + VF610_SUSPEND_OCRAM_SIZE, false); + + pm_info = suspend_ocram_base; + pm_info->pbase = ocram_pbase; + pm_info->pm_info_size = sizeof(*pm_info); + + /* + * ccm physical address is not used by asm code currently, + * so get ccm virtual address directly, as we already have + * it from ccm driver. + */ + pm_info->ccm_base.vbase = ccm_base; + + ret = imx_pm_get_base(&pm_info->anatop_base, socdata->anatop_compat); + if (ret) { + pr_warn("%s: failed to get anatop base %d!\n", __func__, ret); + goto put_node; + } + + ret = imx_pm_get_base(&pm_info->src_base, socdata->src_compat); + if (ret) { + pr_warn("%s: failed to get src base %d!\n", __func__, ret); + goto src_map_failed; + } + + ret = imx_pm_get_base(&pm_info->gpc_base, socdata->gpc_compat); + if (ret) { + pr_warn("%s: failed to get gpc base %d!\n", __func__, ret); + goto gpc_map_failed; + } + + ret = imx_pm_get_base(&pm_info->l2_base, "arm,pl310-cache"); + if (ret) { + pr_warn("%s: failed to get pl310-cache base %d!\n", + __func__, ret); + goto pl310_cache_map_failed; + } + + goto put_node; + +pl310_cache_map_failed: + iounmap(&pm_info->gpc_base.vbase); +gpc_map_failed: + iounmap(&pm_info->src_base.vbase); +src_map_failed: + iounmap(&pm_info->anatop_base.vbase); +put_node: + of_node_put(node); + + return ret; +} + +void __init vf610_pm_init(void) +{ + int ret; + + WARN_ON(!ccm_base); + + if (IS_ENABLED(CONFIG_SUSPEND)) { + ret = vf610_suspend_init(&vf610_pm_data); + if (ret) + pr_warn("%s: No DDR LPM support with suspend %d!\n", + __func__, ret); + } +} + -- 2.1.0
WARNING: multiple messages have this Message-ID (diff)
From: stefan@agner.ch (Stefan Agner) To: linux-arm-kernel@lists.infradead.org Subject: [PATCH 9/9] ARM: vf610: initial suspend/resume support Date: Mon, 22 Sep 2014 19:09:30 +0200 [thread overview] Message-ID: <2436672a230a1b8e4b2ab770df4eae2448539805.1411404079.git.stefan@agner.ch> (raw) In-Reply-To: <cover.1411404079.git.stefan@agner.ch> This patch adds initial suspend/resume support for Vybrid SoC. The standby sleep state puts the module in LP-RUN mode which allows to the SoC to be woken by a regular interrupt. The mem sleep state (Suspend-to-RAM) uses the STOP mode which puts the memory in self-refresh mode but keeps the memory clock enabled. To wake the SoC a power management wakeup IRQ need to be enabled using irq_set_irq_wake (e.g. through a GPIO). Signed-off-by: Stefan Agner <stefan@agner.ch> --- arch/arm/mach-imx/Makefile | 1 + arch/arm/mach-imx/clk-vf610.c | 3 + arch/arm/mach-imx/common.h | 10 + arch/arm/mach-imx/mach-vf610.c | 1 + arch/arm/mach-imx/pm-vf610.c | 428 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 443 insertions(+) create mode 100644 arch/arm/mach-imx/pm-vf610.c diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index 6e4fcd8..0f558aa 100644 --- a/arch/arm/mach-imx/Makefile +++ b/arch/arm/mach-imx/Makefile @@ -103,6 +103,7 @@ AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a obj-$(CONFIG_SOC_IMX6) += suspend-imx6.o endif obj-$(CONFIG_SOC_IMX6) += pm-imx6.o +obj-$(CONFIG_SOC_VF610) += pm-vf610.o obj-$(CONFIG_SOC_IMX50) += mach-imx50.o obj-$(CONFIG_SOC_IMX51) += mach-imx51.o diff --git a/arch/arm/mach-imx/clk-vf610.c b/arch/arm/mach-imx/clk-vf610.c index 1034e78..fbaa708 100644 --- a/arch/arm/mach-imx/clk-vf610.c +++ b/arch/arm/mach-imx/clk-vf610.c @@ -13,6 +13,7 @@ #include <dt-bindings/clock/vf610-clock.h> #include "clk.h" +#include "common.h" #define CCM_CCR (ccm_base + 0x00) #define CCM_CSR (ccm_base + 0x04) @@ -131,6 +132,8 @@ static void __init vf610_clocks_init(struct device_node *ccm_node) ccm_base = of_iomap(np, 0); BUG_ON(!ccm_base); + vf610_pm_set_ccm_base(ccm_base); + clk[VF610_CLK_SLOW_CLK_SEL] = imx_clk_mux("slow_clk_sel", CCM_CCSR, 4, 1, slow_sels, ARRAY_SIZE(slow_sels)); clk[VF610_CLK_FASK_CLK_SEL] = imx_clk_mux("fast_clk_sel", CCM_CCSR, 5, 1, fast_sels, ARRAY_SIZE(fast_sels)); diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h index ca7a0d9..6dd360f 100644 --- a/arch/arm/mach-imx/common.h +++ b/arch/arm/mach-imx/common.h @@ -80,6 +80,13 @@ enum mxc_cpu_pwr_mode { STOP_POWER_OFF, /* STOP + SRPG */ }; +enum vf610_cpu_pwr_mode { + VF610_RUN, + VF610_LP_RUN, + VF610_STOP, + VF610_LP_STOP, +}; + enum mx3_cpu_pwr_mode { MX3_RUN, MX3_WAIT, @@ -125,6 +132,7 @@ int imx_cpu_kill(unsigned int cpu); #ifdef CONFIG_SUSPEND void v7_cpu_resume(void); void imx6_suspend(void __iomem *ocram_vbase); +void vf610_suspend(void); #else static inline void v7_cpu_resume(void) {} static inline void imx6_suspend(void __iomem *ocram_vbase) {} @@ -135,6 +143,8 @@ void imx6dl_pm_init(void); void imx6sl_pm_init(void); void imx6sx_pm_init(void); void imx6q_pm_set_ccm_base(void __iomem *base); +void vf610_pm_init(void); +void vf610_pm_set_ccm_base(void __iomem *base); #ifdef CONFIG_PM void imx51_pm_init(void); diff --git a/arch/arm/mach-imx/mach-vf610.c b/arch/arm/mach-imx/mach-vf610.c index eef3496..79f0461 100644 --- a/arch/arm/mach-imx/mach-vf610.c +++ b/arch/arm/mach-imx/mach-vf610.c @@ -25,6 +25,7 @@ static void __init vf610_init_machine(void) { mxc_arch_reset_init_dt(); of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); + vf610_pm_init(); } static const char * const vf610_dt_compat[] __initconst = { diff --git a/arch/arm/mach-imx/pm-vf610.c b/arch/arm/mach-imx/pm-vf610.c new file mode 100644 index 0000000..87dbbac --- /dev/null +++ b/arch/arm/mach-imx/pm-vf610.c @@ -0,0 +1,428 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2014 Toradex AG + * + * 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/delay.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/genalloc.h> +#include <linux/mfd/syscon.h> +#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/regmap.h> +#include <linux/suspend.h> +#include <linux/clk.h> +#include <asm/cacheflush.h> +#include <asm/fncpy.h> +#include <asm/proc-fns.h> +#include <asm/suspend.h> +#include <asm/tlb.h> + +#include "common.h" + +#define CCR 0x0 +#define BM_CCR_FIRC_EN (0x1 << 16) +#define BM_CCR_FXOSC_EN (0x1 << 12) + +#define CCSR 0x8 +#define BM_CCSR_DDRC_CLK_SEL (0x1 << 6) +#define BM_CCSR_FAST_CLK_SEL (0x1 << 5) +#define BM_CCSR_SLOW_CLK_SEL (0x1 << 4) +#define BM_CCSR_SYS_CLK_SEL_MASK (0x7 << 0) + +#define CLPCR 0x2c +#define BM_CLPCR_ARM_CLK_DIS_ON_LPM (0x1 << 5) +#define BM_CLPCR_SBYOS (0x1 << 6) +#define BM_CLPCR_DIS_REF_OSC (0x1 << 7) +#define BM_CLPCR_ANADIG_STOP_MODE (0x1 << 8) +#define BM_CLPCR_FXOSC_BYPSEN (0x1 << 10) +#define BM_CLPCR_FXOSC_PWRDWN (0x1 << 11) +#define BM_CLPCR_MASK_CORE0_WFI (0x1 << 22) +#define BM_CLPCR_MASK_CORE1_WFI (0x1 << 23) +#define BM_CLPCR_MASK_SCU_IDLE (0x1 << 24) +#define BM_CLPCR_MASK_L2CC_IDLE (0x1 << 25) + +#define CGPR 0x64 +#define BM_CGPR_INT_MEM_CLK_LPM (0x1 << 17) + +#define GPC_PGCR 0x0 +#define BM_PGCR_DS_STOP (0x1 << 7) +#define BM_PGCR_DS_LPSTOP (0x1 << 6) +#define BM_PGCR_WB_STOP (0x1 << 4) +#define BM_PGCR_HP_OFF (0x1 << 3) + +#define GPC_LPMR 0x40 +#define BM_LPMR_RUN 0x0 +#define BM_LPMR_STOP 0x2 + +#define ANATOP_PLL1_CTRL 0x270 +#define ANATOP_PLL2_CTRL 0x30 +#define ANATOP_PLL3_CTRL 0x10 +#define ANATOP_PLL4_CTRL 0x70 +#define ANATOP_PLL5_CTRL 0xe0 +#define ANATOP_PLL6_CTRL 0xa0 +#define ANATOP_PLL7_CTRL 0x10 +#define BM_PLL_ENABLE (0x1 << 13) +#define BM_PLL_LOCK (0x1 << 31) + +#define VF610_SUSPEND_OCRAM_SIZE 0x4000 + +#define VF_UART0_BASE_ADDR 0x40027000 +#define VF_UART1_BASE_ADDR 0x40028000 +#define VF_UART2_BASE_ADDR 0x40029000 +#define VF_UART3_BASE_ADDR 0x4002a000 +#define VF_UART_BASE_ADDR(n) VF_UART##n##_BASE_ADDR +#define VF_UART_BASE(n) VF_UART_BASE_ADDR(n) +#define VF_UART_PHYSICAL_BASE VF_UART_BASE(CONFIG_DEBUG_VF_UART_PORT) + +static void __iomem *ccm_base; +static void __iomem *suspend_ocram_base; + +/* + * suspend ocram space layout: + * ======================== high address ====================== + * . + * . + * . + * ^ + * ^ + * ^ + * vf610_suspend code + * PM_INFO structure(vf610_cpu_pm_info) + * ======================== low address ======================= + */ + +struct vf610_pm_base { + phys_addr_t pbase; + void __iomem *vbase; +}; + +struct vf610_pm_socdata { + const char *src_compat; + const char *gpc_compat; + const char *anatop_compat; +}; + +static const struct vf610_pm_socdata vf610_pm_data __initconst = { + .src_compat = "fsl,vf610-src", + .gpc_compat = "fsl,vf610-gpc", + .anatop_compat = "fsl,vf610-anatop", +}; + +/* + * This structure is for passing necessary data for low level ocram + * suspend code(arch/arm/mach-imx/suspend-vf610.S), if this struct + * definition is changed, the offset definition in + * arch/arm/mach-imx/suspend-vf610.S must be also changed accordingly, + * otherwise, the suspend to ocram function will be broken! + */ +struct vf610_cpu_pm_info { + phys_addr_t pbase; /* The physical address of pm_info. */ + phys_addr_t resume_addr; /* The physical resume address for asm code */ + u32 cpu_type; /* Currently not used, leave it for alignment */ + u32 pm_info_size; /* Size of pm_info. */ + struct vf610_pm_base anatop_base; + struct vf610_pm_base src_base; + struct vf610_pm_base ccm_base; + struct vf610_pm_base gpc_base; + struct vf610_pm_base l2_base; +} __aligned(8); + +#ifdef DEBUG +static void uart_reinit(unsigned long int rate, unsigned long int baud) +{ + void __iomem *membase = ioremap(VF_UART_PHYSICAL_BASE, 0x1000); + u8 tmp; + u16 sbr, brfa; + + /* UART_C2 */ + __raw_writeb(0, membase + 0x3); + + sbr = (u16) (rate / (baud * 16)); + brfa = (rate / baud) - (sbr * 16); + + tmp = ((sbr & 0x1f00) >> 8); + __raw_writeb(tmp, membase + 0x0); + tmp = sbr & 0x00ff; + __raw_writeb(tmp, membase + 0x1); + + /* UART_C4 */ + __raw_writeb(brfa & 0xf, membase + 0xa); + + /* UART_C2 */ + __raw_writeb(0xac, membase + 0x3); + + iounmap(membase); +} +#else +static void uart_reinit(unsigned long int rate, unsigned long int baud) {} +#endif + +static void vf610_enable_pll(void __iomem *pll_base) +{ + writel_relaxed(readl_relaxed(pll_base) | BM_PLL_ENABLE, pll_base); +} + +static void vf610_disable_pll(void __iomem *pll_base) +{ + writel_relaxed(readl_relaxed(pll_base) & ~BM_PLL_ENABLE, pll_base); +} + +int vf610_set_lpm(enum vf610_cpu_pwr_mode mode) +{ + u32 ccr = readl_relaxed(ccm_base + CCR); + u32 ccsr = readl_relaxed(ccm_base + CCSR); + u32 cclpcr = readl_relaxed(ccm_base + CLPCR); + struct vf610_cpu_pm_info *pm_info = suspend_ocram_base; + void __iomem *gpc_base = pm_info->gpc_base.vbase; + u32 gpc_pgcr = readl_relaxed(gpc_base + GPC_PGCR); + void __iomem *anatop = pm_info->anatop_base.vbase; + + switch (mode) { + case VF610_STOP: + cclpcr &= ~BM_CLPCR_ANADIG_STOP_MODE; + cclpcr |= BM_CLPCR_ARM_CLK_DIS_ON_LPM; + cclpcr &= ~BM_CLPCR_SBYOS; + writel_relaxed(cclpcr, ccm_base + CLPCR); + + gpc_pgcr |= BM_PGCR_DS_STOP; + gpc_pgcr |= BM_PGCR_HP_OFF; + writel_relaxed(gpc_pgcr, gpc_base + GPC_PGCR); + + writel_relaxed(BM_LPMR_STOP, gpc_base + GPC_LPMR); + /* fall-through */ + case VF610_LP_RUN: + ccr |= BM_CCR_FIRC_EN; + writel_relaxed(ccr, ccm_base + CCR); + + /* Switch internal OSC's */ + ccsr &= ~BM_CCSR_FAST_CLK_SEL; + ccsr &= ~BM_CCSR_SLOW_CLK_SEL; + writel_relaxed(ccsr, ccm_base + CCSR); + + ccsr &= ~BM_CCSR_SYS_CLK_SEL_MASK; + writel_relaxed(ccsr, ccm_base + CCSR); + uart_reinit(4000000UL, 115200); + + vf610_disable_pll(anatop + ANATOP_PLL1_CTRL); + vf610_disable_pll(anatop + ANATOP_PLL3_CTRL); + vf610_disable_pll(anatop + ANATOP_PLL5_CTRL); + vf610_disable_pll(anatop + ANATOP_PLL7_CTRL); + break; + case VF610_RUN: + vf610_enable_pll(anatop + ANATOP_PLL1_CTRL); + vf610_enable_pll(anatop + ANATOP_PLL3_CTRL); + vf610_enable_pll(anatop + ANATOP_PLL5_CTRL); + vf610_enable_pll(anatop + ANATOP_PLL7_CTRL); + + ccsr &= ~BM_CCSR_SYS_CLK_SEL_MASK; + ccsr |= 0x4; + writel_relaxed(ccsr, ccm_base + CCSR); + uart_reinit(83368421UL, 115200); + + writel_relaxed(BM_LPMR_RUN, gpc_base + GPC_LPMR); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int vf610_pm_enter(suspend_state_t state) +{ + switch (state) { + case PM_SUSPEND_STANDBY: + vf610_set_lpm(VF610_LP_RUN); + imx_gpc_pre_suspend(false); + + /* zzZZZzzz */ + cpu_do_idle(); + + imx_gpc_post_resume(); + vf610_set_lpm(VF610_RUN); + break; + case PM_SUSPEND_MEM: + vf610_set_lpm(VF610_STOP); + imx_gpc_pre_suspend(false); + + cpu_do_idle(); + + imx_gpc_post_resume(); + vf610_set_lpm(VF610_RUN); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int vf610_pm_valid(suspend_state_t state) +{ + return (state == PM_SUSPEND_STANDBY || state == PM_SUSPEND_MEM); +} + +static const struct platform_suspend_ops vf610_pm_ops = { + .enter = vf610_pm_enter, + .valid = vf610_pm_valid, +}; + +void __init vf610_pm_set_ccm_base(void __iomem *base) +{ + ccm_base = base; +} + +static int __init imx_pm_get_base(struct vf610_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 vf610_suspend_init(const struct vf610_pm_socdata *socdata) +{ + phys_addr_t ocram_pbase; + struct device_node *node; + struct platform_device *pdev; + struct vf610_cpu_pm_info *pm_info; + struct gen_pool *ocram_pool; + unsigned long ocram_base; + int ret = 0; + + suspend_set_ops(&vf610_pm_ops); + + if (!socdata) { + pr_warn("%s: invalid argument!\n", __func__); + return -EINVAL; + } + + node = of_find_compatible_node(NULL, NULL, "mmio-sram"); + if (!node) { + pr_warn("%s: failed to find ocram node!\n", __func__); + return -ENODEV; + } + + pdev = of_find_device_by_node(node); + if (!pdev) { + pr_warn("%s: failed to find ocram device!\n", __func__); + ret = -ENODEV; + goto put_node; + } + + ocram_pool = dev_get_gen_pool(&pdev->dev); + if (!ocram_pool) { + pr_warn("%s: ocram pool unavailable!\n", __func__); + ret = -ENODEV; + goto put_node; + } + + ocram_base = gen_pool_alloc(ocram_pool, VF610_SUSPEND_OCRAM_SIZE); + if (!ocram_base) { + pr_warn("%s: unable to alloc ocram!\n", __func__); + ret = -ENOMEM; + goto put_node; + } + + ocram_pbase = gen_pool_virt_to_phys(ocram_pool, ocram_base); + + suspend_ocram_base = __arm_ioremap_exec(ocram_pbase, + VF610_SUSPEND_OCRAM_SIZE, false); + + pm_info = suspend_ocram_base; + pm_info->pbase = ocram_pbase; + pm_info->pm_info_size = sizeof(*pm_info); + + /* + * ccm physical address is not used by asm code currently, + * so get ccm virtual address directly, as we already have + * it from ccm driver. + */ + pm_info->ccm_base.vbase = ccm_base; + + ret = imx_pm_get_base(&pm_info->anatop_base, socdata->anatop_compat); + if (ret) { + pr_warn("%s: failed to get anatop base %d!\n", __func__, ret); + goto put_node; + } + + ret = imx_pm_get_base(&pm_info->src_base, socdata->src_compat); + if (ret) { + pr_warn("%s: failed to get src base %d!\n", __func__, ret); + goto src_map_failed; + } + + ret = imx_pm_get_base(&pm_info->gpc_base, socdata->gpc_compat); + if (ret) { + pr_warn("%s: failed to get gpc base %d!\n", __func__, ret); + goto gpc_map_failed; + } + + ret = imx_pm_get_base(&pm_info->l2_base, "arm,pl310-cache"); + if (ret) { + pr_warn("%s: failed to get pl310-cache base %d!\n", + __func__, ret); + goto pl310_cache_map_failed; + } + + goto put_node; + +pl310_cache_map_failed: + iounmap(&pm_info->gpc_base.vbase); +gpc_map_failed: + iounmap(&pm_info->src_base.vbase); +src_map_failed: + iounmap(&pm_info->anatop_base.vbase); +put_node: + of_node_put(node); + + return ret; +} + +void __init vf610_pm_init(void) +{ + int ret; + + WARN_ON(!ccm_base); + + if (IS_ENABLED(CONFIG_SUSPEND)) { + ret = vf610_suspend_init(&vf610_pm_data); + if (ret) + pr_warn("%s: No DDR LPM support with suspend %d!\n", + __func__, ret); + } +} + -- 2.1.0
next prev parent reply other threads:[~2014-09-22 17:09 UTC|newest] Thread overview: 52+ messages / expand[flat|nested] mbox.gz Atom feed top 2014-09-22 17:09 [PATCH 0/9] ARM: vf610: Suspend/resume support Stefan Agner 2014-09-22 17:09 ` Stefan Agner 2014-09-22 17:09 ` [PATCH 1/9] ARM: dts: vf610: Add system reset controller (SRC) Stefan Agner 2014-09-22 17:09 ` Stefan Agner 2014-09-22 17:09 ` Stefan Agner 2014-09-24 9:16 ` Linus Walleij 2014-09-24 9:16 ` Linus Walleij 2014-09-24 9:16 ` Linus Walleij 2014-09-24 16:41 ` Stefan Agner 2014-09-24 16:41 ` Stefan Agner 2014-09-25 13:08 ` Philipp Zabel 2014-09-25 13:08 ` Philipp Zabel 2014-09-22 17:09 ` [PATCH 2/9] ARM: dts: vf610: add global power controller (GPC) Stefan Agner 2014-09-22 17:09 ` Stefan Agner 2014-09-22 17:09 ` Stefan Agner 2014-09-22 17:09 ` [PATCH 3/9] ARM: dts: vf610: add on-chip SRAM Stefan Agner 2014-09-22 17:09 ` Stefan Agner 2014-09-22 17:09 ` Stefan Agner 2014-09-22 17:09 ` [PATCH 4/9] ARM: dts: vf610-colibri: GPIO power key Stefan Agner 2014-09-22 17:09 ` Stefan Agner 2014-09-22 17:09 ` Stefan Agner 2014-09-22 17:09 ` [PATCH 5/9] gpio: vf610: Extend with wakeup support Stefan Agner 2014-09-22 17:09 ` Stefan Agner 2014-09-24 9:19 ` Linus Walleij 2014-09-24 9:19 ` Linus Walleij 2014-09-24 9:19 ` Linus Walleij 2014-09-24 16:33 ` Stefan Agner 2014-09-24 16:33 ` Stefan Agner 2014-09-24 10:06 ` Lucas Stach 2014-09-24 10:06 ` Lucas Stach 2014-09-24 16:51 ` Stefan Agner 2014-09-24 16:51 ` Stefan Agner 2014-09-22 17:09 ` [PATCH 6/9] ARM: imx: gpc: Support vf610 global power controller Stefan Agner 2014-09-22 17:09 ` Stefan Agner 2014-09-22 17:09 ` [PATCH 7/9] ARM: imx: src: Support vf610 system reset controller Stefan Agner 2014-09-22 17:09 ` Stefan Agner 2014-09-22 17:09 ` [PATCH 8/9] ARM: imx: clk-gate2: allow custom gate configuration Stefan Agner 2014-09-22 17:09 ` Stefan Agner 2014-09-28 2:02 ` Shawn Guo 2014-09-28 2:02 ` Shawn Guo 2014-09-28 2:02 ` Shawn Guo 2014-09-22 17:09 ` Stefan Agner [this message] 2014-09-22 17:09 ` [PATCH 9/9] ARM: vf610: initial suspend/resume support Stefan Agner 2014-09-23 15:36 ` [PATCH 0/9] ARM: vf610: Suspend/resume support Bill Pringlemeir 2014-09-24 8:22 ` Stefan Agner 2014-09-24 16:33 ` Bill Pringlemeir 2014-09-28 3:08 ` Shawn Guo 2014-09-29 12:47 ` Stefan Agner 2014-09-29 15:39 ` Bill Pringlemeir 2014-09-28 3:15 ` Shawn Guo 2014-09-28 3:15 ` Shawn Guo 2014-09-28 3:15 ` Shawn Guo
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=2436672a230a1b8e4b2ab770df4eae2448539805.1411404079.git.stefan@agner.ch \ --to=stefan@agner.ch \ --cc=b20788@freescale.com \ --cc=gnurou@gmail.com \ --cc=jingchang.lu@freescale.com \ --cc=kernel@pengutronix.de \ --cc=linus.walleij@linaro.org \ --cc=linux-arm-kernel@lists.infradead.org \ --cc=linux-gpio@vger.kernel.org \ --cc=linux-kernel@vger.kernel.org \ --cc=linux@arm.linux.org.uk \ --cc=shawn.guo@freescale.com \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.