From 7ffb7c47f83a8c981410aeccbaad3e321c82b5ad Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Tue, 19 Aug 2014 00:32:10 +0200 Subject: ARM: dts: vf610: add global power controller (GPC) Add global power controller module (GPC) to Vybrid device tree. --- arch/arm/boot/dts/vfxxx.dtsi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/boot/dts/vfxxx.dtsi b/arch/arm/boot/dts/vfxxx.dtsi index e68e0c244318..58a0e4ebb319 100644 --- a/arch/arm/boot/dts/vfxxx.dtsi +++ b/arch/arm/boot/dts/vfxxx.dtsi @@ -336,6 +336,11 @@ clocks = <&clks VF610_CLK_USBC0>; status = "disabled"; }; + + gpc: gpc@4006c000 { + compatible = "fsl,vf610-gpc"; + reg = <0x4006c000 0x1000>; + }; }; aips1: aips-bus@40080000 { -- cgit v1.2.3 From ea8be3e041fc74a8a47fef71377dadbe3abeac0d Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Tue, 19 Aug 2014 00:32:47 +0200 Subject: ARM: dts: vf610: add on-chip SRAM --- arch/arm/boot/dts/vfxxx.dtsi | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/arch/arm/boot/dts/vfxxx.dtsi b/arch/arm/boot/dts/vfxxx.dtsi index 58a0e4ebb319..8d04d8add4a0 100644 --- a/arch/arm/boot/dts/vfxxx.dtsi +++ b/arch/arm/boot/dts/vfxxx.dtsi @@ -49,6 +49,27 @@ compatible = "simple-bus"; ranges; + ocram0: sram@3f000000 { + compatible = "mmio-sram"; + reg = <0x3f000000 0x40000>; + }; + + ocram1: sram@3f040000 { + compatible = "mmio-sram"; + reg = <0x3f040000 0x40000>; + }; + + gfxram0: sram@3f400000 { + compatible = "mmio-sram"; + reg = <0x3f400000 0x80000>; + }; + + /* used by L2 cache */ + gfxram1: sram@3f480000 { + compatible = "mmio-sram"; + reg = <0x3f480000 0x80000>; + }; + aips0: aips-bus@40000000 { compatible = "fsl,aips-bus", "simple-bus"; #address-cells = <1>; -- cgit v1.2.3 From 67c0c92b809942e411e78222c75035e12619b8b0 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Mon, 15 Sep 2014 14:42:38 +0200 Subject: ARM: dts: vf610-colibri: GPIO wakeup key Enable GPIO wakeup key on Vybrid PAD 41 which is routed to the Colibri default wakeup pin SO-DIMM 45. --- arch/arm/boot/dts/vf-colibri-eval-v3.dtsi | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/arch/arm/boot/dts/vf-colibri-eval-v3.dtsi b/arch/arm/boot/dts/vf-colibri-eval-v3.dtsi index 7201b85bd719..4d67ce21b8a1 100644 --- a/arch/arm/boot/dts/vf-colibri-eval-v3.dtsi +++ b/arch/arm/boot/dts/vf-colibri-eval-v3.dtsi @@ -7,6 +7,8 @@ * (at your option) any later version. */ +#include + / { chosen { bootargs = "console=ttyLP0,115200"; @@ -39,6 +41,20 @@ vin-supply = <&sys_5v0_reg>; }; }; + + gpio-keys { + compatible = "gpio-keys"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpiokeys>; + + power { + label = "Wake-Up"; + gpios = <&gpio2 9 GPIO_ACTIVE_HIGH>; + linux,code = ; + debounce-interval = <10>; + gpio-key,wakeup; + }; + }; }; &bl { @@ -203,3 +219,13 @@ &usbh1 { vbus-supply = <&usbh_vbus_reg>; }; + +&iomuxc { + vf610-colibri { + pinctrl_gpiokeys: gpiokeys { + fsl,pins = < + VF610_PAD_PTB19__GPIO_41 0x219d + >; + }; + }; +}; -- cgit v1.2.3 From cf39c3959171b1b6d0c87f4e3b04e7a1a1072228 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Wed, 20 Aug 2014 12:58:50 +0200 Subject: ARM: imx: gpc: Support vf610 global power controller Support Vybrid SoC which has a similar global power controller found in i.MX6 SoC's. The extension for the GIC interrupt controller can be reused. However, Vybrid's GPC has no CPU powerdown flag, hence we write this conditional. --- arch/arm/mach-imx/Kconfig | 1 + arch/arm/mach-imx/common.h | 3 +- arch/arm/mach-imx/gpc.c | 61 +++++++++++++++++++++++++++-------------- arch/arm/mach-imx/mach-imx6q.c | 2 +- arch/arm/mach-imx/mach-imx6sl.c | 2 +- arch/arm/mach-imx/mach-vf610.c | 8 ++++++ 6 files changed, 54 insertions(+), 23 deletions(-) diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index 5da85e09050e..dd765bc63abd 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -633,6 +633,7 @@ config SOC_VF610 bool "Vybrid Family VF610 support" select ARM_GIC select PINCTRL_VF610 + select HAVE_IMX_GPC select PL310_ERRATA_769419 if CACHE_L2X0 select HAVE_NAND_FSL_NFC diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h index 59ce8f30e8ac..246d301a7bea 100644 --- a/arch/arm/mach-imx/common.h +++ b/arch/arm/mach-imx/common.h @@ -102,7 +102,8 @@ static inline void imx_scu_map_io(void) {} static inline void imx_smp_prepare(void) {} #endif void imx_src_init(void); -void imx_gpc_init(void); +void imx6_gpc_init(void); +void vf610_gpc_init(void); void imx_gpc_pre_suspend(bool arm_power_off); void imx_gpc_post_resume(void); void imx_gpc_mask_all(void); diff --git a/arch/arm/mach-imx/gpc.c b/arch/arm/mach-imx/gpc.c index 82ea74e68482..4ac7a48065d1 100644 --- a/arch/arm/mach-imx/gpc.c +++ b/arch/arm/mach-imx/gpc.c @@ -18,40 +18,42 @@ #include #include "common.h" -#define GPC_IMR1 0x008 +#define IMX6_GPC_IMR1 0x008 +#define VF610_GPC_IMR1 0x044 #define GPC_PGC_CPU_PDN 0x2a0 #define IMR_NUM 4 static void __iomem *gpc_base; +static void __iomem *gpc_imr_base; +static bool has_cpu_pdn; static u32 gpc_wake_irqs[IMR_NUM]; static u32 gpc_saved_imrs[IMR_NUM]; void imx_gpc_pre_suspend(bool arm_power_off) { - void __iomem *reg_imr1 = gpc_base + GPC_IMR1; int i; /* Tell GPC to power off ARM core when suspend */ - if (arm_power_off) + if (arm_power_off && has_cpu_pdn) writel_relaxed(0x1, gpc_base + GPC_PGC_CPU_PDN); for (i = 0; i < IMR_NUM; i++) { - gpc_saved_imrs[i] = readl_relaxed(reg_imr1 + i * 4); - writel_relaxed(~gpc_wake_irqs[i], reg_imr1 + i * 4); + gpc_saved_imrs[i] = readl_relaxed(gpc_imr_base + i * 4); + writel_relaxed(~gpc_wake_irqs[i], gpc_imr_base + i * 4); } } void imx_gpc_post_resume(void) { - void __iomem *reg_imr1 = gpc_base + GPC_IMR1; int i; /* Keep ARM core powered on for other low-power modes */ - writel_relaxed(0x0, gpc_base + GPC_PGC_CPU_PDN); + if (has_cpu_pdn) + writel_relaxed(0x0, gpc_base + GPC_PGC_CPU_PDN); for (i = 0; i < IMR_NUM; i++) - writel_relaxed(gpc_saved_imrs[i], reg_imr1 + i * 4); + writel_relaxed(gpc_saved_imrs[i], gpc_imr_base + i * 4); } static int imx_gpc_irq_set_wake(struct irq_data *d, unsigned int on) @@ -72,23 +74,21 @@ static int imx_gpc_irq_set_wake(struct irq_data *d, unsigned int on) void imx_gpc_mask_all(void) { - void __iomem *reg_imr1 = gpc_base + GPC_IMR1; int i; for (i = 0; i < IMR_NUM; i++) { - gpc_saved_imrs[i] = readl_relaxed(reg_imr1 + i * 4); - writel_relaxed(~0, reg_imr1 + i * 4); + gpc_saved_imrs[i] = readl_relaxed(gpc_imr_base + i * 4); + writel_relaxed(~0, gpc_imr_base + i * 4); } } void imx_gpc_restore_all(void) { - void __iomem *reg_imr1 = gpc_base + GPC_IMR1; int i; for (i = 0; i < IMR_NUM; i++) - writel_relaxed(gpc_saved_imrs[i], reg_imr1 + i * 4); + writel_relaxed(gpc_saved_imrs[i], gpc_imr_base + i * 4); } void imx_gpc_irq_unmask(struct irq_data *d) @@ -100,7 +100,7 @@ void imx_gpc_irq_unmask(struct irq_data *d) if (d->irq < 32) return; - reg = gpc_base + GPC_IMR1 + (d->irq / 32 - 1) * 4; + reg = gpc_imr_base + (d->irq / 32 - 1) * 4; val = readl_relaxed(reg); val &= ~(1 << d->irq % 32); writel_relaxed(val, reg); @@ -115,27 +115,48 @@ void imx_gpc_irq_mask(struct irq_data *d) if (d->irq < 32) return; - reg = gpc_base + GPC_IMR1 + (d->irq / 32 - 1) * 4; + reg = gpc_imr_base + (d->irq / 32 - 1) * 4; val = readl_relaxed(reg); val |= 1 << (d->irq % 32); writel_relaxed(val, reg); } -void __init imx_gpc_init(void) +static void __init imx_gpc_init(void) { - struct device_node *np; int i; - np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-gpc"); - gpc_base = of_iomap(np, 0); WARN_ON(!gpc_base); /* Initially mask all interrupts */ for (i = 0; i < IMR_NUM; i++) - writel_relaxed(~0, gpc_base + GPC_IMR1 + i * 4); + writel_relaxed(~0, gpc_imr_base + i * 4); /* Register GPC as the secondary interrupt controller behind GIC */ gic_arch_extn.irq_mask = imx_gpc_irq_mask; gic_arch_extn.irq_unmask = imx_gpc_irq_unmask; gic_arch_extn.irq_set_wake = imx_gpc_irq_set_wake; } + +void __init imx6_gpc_init(void) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-gpc"); + gpc_base = of_iomap(np, 0); + gpc_imr_base = gpc_base + IMX6_GPC_IMR1; + has_cpu_pdn = true; + + imx_gpc_init(); +} + +void __init vf610_gpc_init(void) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "fsl,vf610-gpc"); + gpc_base = of_iomap(np, 0); + gpc_imr_base = gpc_base + VF610_GPC_IMR1; + has_cpu_pdn = false; + + imx_gpc_init(); +} diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c index 5057d61298b7..070031067955 100644 --- a/arch/arm/mach-imx/mach-imx6q.c +++ b/arch/arm/mach-imx/mach-imx6q.c @@ -390,7 +390,7 @@ static void __init imx6q_init_irq(void) imx_init_revision_from_anatop(); imx_init_l2cache(); imx_src_init(); - imx_gpc_init(); + imx6_gpc_init(); irqchip_init(); } diff --git a/arch/arm/mach-imx/mach-imx6sl.c b/arch/arm/mach-imx/mach-imx6sl.c index 24bfaaf944c8..f0b4c60c7d69 100644 --- a/arch/arm/mach-imx/mach-imx6sl.c +++ b/arch/arm/mach-imx/mach-imx6sl.c @@ -64,7 +64,7 @@ static void __init imx6sl_init_irq(void) imx_init_revision_from_anatop(); imx_init_l2cache(); imx_src_init(); - imx_gpc_init(); + imx6_gpc_init(); irqchip_init(); } diff --git a/arch/arm/mach-imx/mach-vf610.c b/arch/arm/mach-imx/mach-vf610.c index 2e7c75b66fe0..8684733831c3 100644 --- a/arch/arm/mach-imx/mach-vf610.c +++ b/arch/arm/mach-imx/mach-vf610.c @@ -11,6 +11,13 @@ #include #include #include +#include "common.h" + +static void __init vf610_init_irq(void) +{ + vf610_gpc_init(); + irqchip_init(); +} static const char * const vf610_dt_compat[] __initconst = { "fsl,vf500", @@ -23,5 +30,6 @@ static const char * const vf610_dt_compat[] __initconst = { DT_MACHINE_START(VYBRID_VF610, "Freescale Vybrid VF5xx/VF6xx (Device Tree)") .l2c_aux_val = 0, .l2c_aux_mask = ~0, + .init_irq = vf610_init_irq, .dt_compat = vf610_dt_compat, MACHINE_END -- cgit v1.2.3 From 571afa818c7ab872c03130ab87c0cde40c3ec321 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Fri, 19 Sep 2014 23:04:28 +0200 Subject: ARM: imx: clk-gate2: allow custom gate configuration The 2-bit gates found i.MX and Vybrid SoC support different clock configuration: 0b00: clk disabled 0b01: clk enabled in RUN mode but disabled in WAIT and STOP mode 0b10: clk enabled in RUN, WAIT and STOP mode (only Vybrid) 0b11: clk enabled in RUN and WAIT mode For some clocks, we might want to configure different behaviour, e.g. a memory clock should be on even in STOP mode. Add a new function imx_clk_gate2_cgr which allow to configure specific gate values through the cgr_val parameter. --- arch/arm/mach-imx/clk-gate2.c | 7 +++++-- arch/arm/mach-imx/clk-vf610.c | 3 +++ arch/arm/mach-imx/clk.h | 13 ++++++++++--- include/dt-bindings/clock/vf610-clock.h | 3 ++- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-imx/clk-gate2.c b/arch/arm/mach-imx/clk-gate2.c index 5a75cdc81891..4145325ebddc 100644 --- a/arch/arm/mach-imx/clk-gate2.c +++ b/arch/arm/mach-imx/clk-gate2.c @@ -31,6 +31,7 @@ struct clk_gate2 { struct clk_hw hw; void __iomem *reg; u8 bit_idx; + u8 cgr_val; u8 flags; spinlock_t *lock; unsigned int *share_count; @@ -50,7 +51,8 @@ static int clk_gate2_enable(struct clk_hw *hw) goto out; reg = readl(gate->reg); - reg |= 3 << gate->bit_idx; + reg &= ~(3 << gate->bit_idx); + reg |= gate->cgr_val << gate->bit_idx; writel(reg, gate->reg); out: @@ -110,7 +112,7 @@ static struct clk_ops clk_gate2_ops = { struct clk *clk_register_gate2(struct device *dev, const char *name, const char *parent_name, unsigned long flags, - void __iomem *reg, u8 bit_idx, + void __iomem *reg, u8 bit_idx, u8 cgr_val, u8 clk_gate2_flags, spinlock_t *lock, unsigned int *share_count) { @@ -125,6 +127,7 @@ struct clk *clk_register_gate2(struct device *dev, const char *name, /* struct clk_gate2 assignments */ gate->reg = reg; gate->bit_idx = bit_idx; + gate->cgr_val = cgr_val; gate->flags = clk_gate2_flags; gate->lock = lock; gate->share_count = share_count; diff --git a/arch/arm/mach-imx/clk-vf610.c b/arch/arm/mach-imx/clk-vf610.c index ab3791c51fd9..8e38a21d5cef 100644 --- a/arch/arm/mach-imx/clk-vf610.c +++ b/arch/arm/mach-imx/clk-vf610.c @@ -118,6 +118,7 @@ static struct clk_onecell_data clk_data; static unsigned int const clks_init_on[] __initconst = { VF610_CLK_SYS_BUS, VF610_CLK_DDR_SEL, + VF610_CLK_DDRMC, }; static struct clk * __init vf610_get_fixed_clock( @@ -232,6 +233,8 @@ static void __init vf610_clocks_init(struct device_node *ccm_node) clk[VF610_CLK_PLL4_MAIN_DIV] = clk_register_divider_table(NULL, "pll4_audio_div", "pll4_audio", 0, CCM_CACRR, 6, 3, 0, pll4_audio_div_table, &imx_ccm_lock); clk[VF610_CLK_PLL6_MAIN_DIV] = imx_clk_divider("pll6_video_div", "pll6_video", CCM_CACRR, 21, 1); + clk[VF610_CLK_DDRMC] = imx_clk_gate2_cgr("ddrmc", "ddr_sel", CCM_CCGR6, CCM_CCGRx_CGn(14), 0x2); + clk[VF610_CLK_USBPHY0] = imx_clk_gate("usbphy0", "pll3_usb_otg", PLL3_CTRL, 6); clk[VF610_CLK_USBPHY1] = imx_clk_gate("usbphy1", "pll7_usb_host", PLL7_CTRL, 6); diff --git a/arch/arm/mach-imx/clk.h b/arch/arm/mach-imx/clk.h index 6a07903a28bc..8aec27129ec7 100644 --- a/arch/arm/mach-imx/clk.h +++ b/arch/arm/mach-imx/clk.h @@ -30,7 +30,7 @@ struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name, struct clk *clk_register_gate2(struct device *dev, const char *name, const char *parent_name, unsigned long flags, - void __iomem *reg, u8 bit_idx, + void __iomem *reg, u8 bit_idx, u8 cgr_val, u8 clk_gate_flags, spinlock_t *lock, unsigned int *share_count); @@ -44,7 +44,7 @@ static inline struct clk *imx_clk_gate2(const char *name, const char *parent, void __iomem *reg, u8 shift) { return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg, - shift, 0, &imx_ccm_lock, NULL); + shift, 0x3, 0, &imx_ccm_lock, NULL); } static inline struct clk *imx_clk_gate2_shared(const char *name, @@ -52,7 +52,14 @@ static inline struct clk *imx_clk_gate2_shared(const char *name, unsigned int *share_count) { return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg, - shift, 0, &imx_ccm_lock, share_count); + shift, 0x3, 0, &imx_ccm_lock, share_count); +} + +static inline struct clk *imx_clk_gate2_cgr(const char *name, + const char *parent, void __iomem *reg, u8 shift, u8 cgr_val) +{ + return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg, + shift, cgr_val, 0, &imx_ccm_lock, NULL); } struct clk *imx_clk_pfd(const char *name, const char *parent_name, diff --git a/include/dt-bindings/clock/vf610-clock.h b/include/dt-bindings/clock/vf610-clock.h index 6e9f260832f5..0b1a100007ad 100644 --- a/include/dt-bindings/clock/vf610-clock.h +++ b/include/dt-bindings/clock/vf610-clock.h @@ -194,6 +194,7 @@ #define VF610_PLL7_BYPASS 181 #define VF610_CLK_SNVS 182 #define VF610_CLK_TCON0 183 -#define VF610_CLK_END 184 +#define VF610_CLK_DDRMC 184 +#define VF610_CLK_END 185 #endif /* __DT_BINDINGS_CLOCK_VF610_H */ -- cgit v1.2.3 From a27af4540b44ed17365933cb003a97e7a10d39ec Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Tue, 19 Aug 2014 00:33:44 +0200 Subject: ARM: vf610: initial suspend/resume support This patch adds initial suspend/resume support for Vybrid SoC. The standby sleep state puts the module in STOP mode which allows to the SoC to be woken by a regular interrupt. To save power the main PLL1 is bypassed and uses the 24MHz on-chip oscillator. However, memory clock need to be at full speed, hence use PLL2 only to keep memory clock. This provides the best balance between low-power requirement while have the ability to use any IRQ as wake-up source. The mem sleep state (Suspend-to-RAM) is currently not supported. --- 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 | 7 + arch/arm/mach-imx/pm-vf610.c | 424 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 445 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 f5ac685a29fc..2b17bb35d0bb 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 8e38a21d5cef..de8283d4f81f 100644 --- a/arch/arm/mach-imx/clk-vf610.c +++ b/arch/arm/mach-imx/clk-vf610.c @@ -13,6 +13,7 @@ #include #include "clk.h" +#include "common.h" #define CCM_CCR (ccm_base + 0x00) #define CCM_CSR (ccm_base + 0x04) @@ -160,6 +161,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 246d301a7bea..c0a536f821ea 100644 --- a/arch/arm/mach-imx/common.h +++ b/arch/arm/mach-imx/common.h @@ -79,6 +79,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, @@ -124,6 +131,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) {} @@ -134,6 +142,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 8684733831c3..4ba460263349 100644 --- a/arch/arm/mach-imx/mach-vf610.c +++ b/arch/arm/mach-imx/mach-vf610.c @@ -19,6 +19,12 @@ static void __init vf610_init_irq(void) irqchip_init(); } +static void __init vf610_init_machine(void) +{ + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); + vf610_pm_init(); +} + static const char * const vf610_dt_compat[] __initconst = { "fsl,vf500", "fsl,vf510", @@ -31,5 +37,6 @@ DT_MACHINE_START(VYBRID_VF610, "Freescale Vybrid VF5xx/VF6xx (Device Tree)") .l2c_aux_val = 0, .l2c_aux_mask = ~0, .init_irq = vf610_init_irq, + .init_machine = vf610_init_machine, .dt_compat = vf610_dt_compat, MACHINE_END diff --git a/arch/arm/mach-imx/pm-vf610.c b/arch/arm/mach-imx/pm-vf610.c new file mode 100644 index 000000000000..e85e1b7e0e1c --- /dev/null +++ b/arch/arm/mach-imx/pm-vf610.c @@ -0,0 +1,424 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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_PLL2_PFD 0x100 +#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_POWERDOWN (0x1 << 12) +#define BM_PLL_ENABLE (0x1 << 13) +#define BM_PLL_BYPASS (0x1 << 16) +#define BM_PLL_LOCK (0x1 << 31) +#define BM_PLL_PFD2_CLKGATE (0x1 << 15) + +#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; + u32 ccm_ccsr; +} __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_set(void __iomem *pll_base, u32 mask) +{ + writel_relaxed(readl_relaxed(pll_base) | mask, pll_base); +} + +static void vf610_clr(void __iomem *pll_base, u32 mask) +{ + writel_relaxed(readl_relaxed(pll_base) & ~mask, 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: + /* Store clock settings */ + pm_info->ccm_ccsr = ccsr; + + ccr |= BM_CCR_FIRC_EN; + writel_relaxed(ccr, ccm_base + CCR); + + /* Enable PLL2 for DDR clock */ + vf610_set(anatop + ANATOP_PLL2_CTRL, BM_PLL_ENABLE); + vf610_clr(anatop + ANATOP_PLL2_CTRL, BM_PLL_POWERDOWN); + vf610_clr(anatop + ANATOP_PLL2_CTRL, BM_PLL_BYPASS); + while (!(readl(anatop + ANATOP_PLL2_CTRL) & BM_PLL_LOCK)); + vf610_clr(anatop + ANATOP_PLL2_PFD, BM_PLL_PFD2_CLKGATE); + + /* Switch internal OSC's */ + ccsr &= ~BM_CCSR_FAST_CLK_SEL; + ccsr &= ~BM_CCSR_SLOW_CLK_SEL; + + /* Select PLL2 as DDR clock */ + ccsr &= ~BM_CCSR_DDRC_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_set(anatop + ANATOP_PLL1_CTRL, BM_PLL_BYPASS); + vf610_clr(anatop + ANATOP_PLL3_CTRL, BM_PLL_ENABLE); + vf610_clr(anatop + ANATOP_PLL5_CTRL, BM_PLL_ENABLE); + vf610_clr(anatop + ANATOP_PLL7_CTRL, BM_PLL_ENABLE); + break; + case VF610_RUN: + vf610_clr(anatop + ANATOP_PLL1_CTRL, BM_PLL_BYPASS); + vf610_set(anatop + ANATOP_PLL3_CTRL, BM_PLL_ENABLE); + vf610_set(anatop + ANATOP_PLL5_CTRL, BM_PLL_ENABLE); + vf610_set(anatop + ANATOP_PLL7_CTRL, BM_PLL_ENABLE); + + /* Restore clock settings */ + writel_relaxed(pm_info->ccm_ccsr, ccm_base + CCSR); + + /* Disable PLL2 if not needed */ + if (pm_info->ccm_ccsr & BM_CCSR_DDRC_CLK_SEL) + vf610_set(anatop + ANATOP_PLL2_CTRL, BM_PLL_POWERDOWN); + + 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_STOP); + imx_gpc_pre_suspend(false); + + /* zzZZZzzz */ + 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; +} + +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->gpc_base, socdata->gpc_compat); + if (ret) { + pr_warn("%s: failed to get gpc base %d!\n", __func__, ret); + goto gpc_map_failed; + } + + goto put_node; + +gpc_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); + } +} + -- cgit v1.2.3 From 0a85a752f28dc184afaec78cd28ed62dd65ffbe6 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Tue, 2 Dec 2014 15:49:13 +0100 Subject: mmc: sdhci-pltfm: fix abort due to missing runtime PM When entering suspend while the device is in runtime PM, the sdhci_[suspend|resume]_host function are called without taking care of runtime PM. On the Vybrid SoC, this leads to external abort because during runtime suspend, the clocks required for bus access are disabled. [ 37.772967] Unhandled fault: imprecise external abort (0x1c06) at 0x76f5f000 [ 37.780304] Internal error: : 1c06 [#1] ARM [ 37.784670] Modules linked in: [ 37.787908] CPU: 0 PID: 428 Comm: sh Not tainted 3.18.0-rc5-00119-geefd097-dirty #1540 [ 37.796142] task: 8e246c00 ti: 8ca6c000 task.ti: 8ca6c000 [ 37.801785] PC is at esdhc_writel_le+0x40/0xec [ 37.806431] LR is at sdhci_set_card_detection+0xe0/0xe4 [ 37.811877] pc : [<803f0584>] lr : [<803eaaa0>] psr: 400f0013 [ 37.811877] sp : 8ca6dd28 ip : 00000001 fp : 8ca6dd3c [ 37.823766] r10: 807a233c r9 : 00000000 r8 : 8e8b7210 [ 37.829194] r7 : 802d8a08 r6 : 8082e928 r5 : 00000000 r4 : 00000002 [ 37.835974] r3 : 8ea34e90 r2 : 00000038 r1 : 00000000 r0 : 8ea32ac0 ... --- drivers/mmc/host/sdhci-pltfm.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index c5b01d6bb85d..0bc864ef914e 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -31,6 +31,7 @@ #include #include #include +#include #ifdef CONFIG_PPC #include #endif @@ -237,17 +238,29 @@ EXPORT_SYMBOL_GPL(sdhci_pltfm_unregister); #ifdef CONFIG_PM int sdhci_pltfm_suspend(struct device *dev) { + int ret; struct sdhci_host *host = dev_get_drvdata(dev); - return sdhci_suspend_host(host); + pm_runtime_get_sync(dev); + ret = sdhci_suspend_host(host); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; } EXPORT_SYMBOL_GPL(sdhci_pltfm_suspend); int sdhci_pltfm_resume(struct device *dev) { + int ret; struct sdhci_host *host = dev_get_drvdata(dev); - return sdhci_resume_host(host); + pm_runtime_get_sync(dev); + ret = sdhci_resume_host(host); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; } EXPORT_SYMBOL_GPL(sdhci_pltfm_resume); -- cgit v1.2.3 From b10e586c3ae7bf6f0c75e0fa8e457ef356df8557 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Thu, 27 Nov 2014 21:52:45 +0100 Subject: tty: serial: fsl_lpuart: support suspend/resume In order to allow wake support in STOP sleep mode, clocks are needed. Use imx_clk_gate2_cgr to disable automatic clock gating in low power mode STOP. This allows to enable wake by UART using: echo enabled > /sys/class/tty/ttyLP0/power/wakeup However, if wake is not enabled, the driver should disable the clocks explicitly to save power. --- arch/arm/mach-imx/clk-vf610.c | 8 ++++---- drivers/tty/serial/fsl_lpuart.c | 7 +++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-imx/clk-vf610.c b/arch/arm/mach-imx/clk-vf610.c index de8283d4f81f..0d24f286c8dc 100644 --- a/arch/arm/mach-imx/clk-vf610.c +++ b/arch/arm/mach-imx/clk-vf610.c @@ -269,10 +269,10 @@ static void __init vf610_clocks_init(struct device_node *ccm_node) clk[VF610_CLK_PIT] = imx_clk_gate2("pit", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(7)); - clk[VF610_CLK_UART0] = imx_clk_gate2("uart0", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(7)); - clk[VF610_CLK_UART1] = imx_clk_gate2("uart1", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(8)); - clk[VF610_CLK_UART2] = imx_clk_gate2("uart2", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(9)); - clk[VF610_CLK_UART3] = imx_clk_gate2("uart3", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(10)); + clk[VF610_CLK_UART0] = imx_clk_gate2_cgr("uart0", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(7), 0x2); + clk[VF610_CLK_UART1] = imx_clk_gate2_cgr("uart1", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(8), 0x2); + clk[VF610_CLK_UART2] = imx_clk_gate2_cgr("uart2", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(9), 0x2); + clk[VF610_CLK_UART3] = imx_clk_gate2_cgr("uart3", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(10), 0x2); clk[VF610_CLK_I2C0] = imx_clk_gate2("i2c0", "ipg_bus", CCM_CCGR4, CCM_CCGRx_CGn(6)); clk[VF610_CLK_I2C1] = imx_clk_gate2("i2c1", "ipg_bus", CCM_CCGR4, CCM_CCGRx_CGn(7)); diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 6dd53af546a3..bac4b0722996 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1855,6 +1855,8 @@ static int lpuart_suspend(struct device *dev) struct lpuart_port *sport = dev_get_drvdata(dev); uart_suspend_port(&lpuart_reg, &sport->port); + if (sport->port.suspended && !sport->port.irq_wake) + clk_disable_unprepare(sport->clk); return 0; } @@ -1863,6 +1865,11 @@ static int lpuart_resume(struct device *dev) { struct lpuart_port *sport = dev_get_drvdata(dev); + if (sport->port.suspended && !sport->port.irq_wake) + clk_prepare_enable(sport->clk); + + /* Reinitialize FIFO's to flush characters parsed at wrong baud rate */ + lpuart_setup_watermark(sport); uart_resume_port(&lpuart_reg, &sport->port); return 0; -- cgit v1.2.3