Add the suspend psci function for sun5i and sun7i. Thus function switches the cpu clk source to osc24M or to losc depending on the SoC family.
Signed-off-by: Antoine Tenart <antoine.ten...@free-electrons.com> --- arch/arm/cpu/armv7/sunxi/Makefile | 9 ++- arch/arm/cpu/armv7/sunxi/psci.c | 40 +--------- arch/arm/cpu/armv7/sunxi/psci.h | 58 ++++++++++++++ arch/arm/cpu/armv7/sunxi/psci_suspend.c | 108 ++++++++++++++++++++++++++ arch/arm/include/asm/arch-sunxi/clock_sun4i.h | 2 + 5 files changed, 178 insertions(+), 39 deletions(-) create mode 100644 arch/arm/cpu/armv7/sunxi/psci.h create mode 100644 arch/arm/cpu/armv7/sunxi/psci_suspend.c diff --git a/arch/arm/cpu/armv7/sunxi/Makefile b/arch/arm/cpu/armv7/sunxi/Makefile index b35b9df4a9d6..80667268a0fc 100644 --- a/arch/arm/cpu/armv7/sunxi/Makefile +++ b/arch/arm/cpu/armv7/sunxi/Makefile @@ -13,7 +13,14 @@ obj-$(CONFIG_MACH_SUN6I) += tzpc.o obj-$(CONFIG_MACH_SUN8I_H3) += tzpc.o ifndef CONFIG_SPL_BUILD -obj-$(CONFIG_ARMV7_PSCI) += psci.o +ifdef CONFIG_ARMV7_PSCI +obj-$(CONFIG_MACH_SUN6I) += psci.o +obj-$(CONFIG_MACH_SUN7I) += psci.o +obj-$(CONFIG_MACH_SUN8I) += psci.o + +obj-$(CONFIG_MACH_SUN5I) += psci_suspend.o +obj-$(CONFIG_MACH_SUN7I) += psci_suspend.o +endif endif ifdef CONFIG_SPL_BUILD diff --git a/arch/arm/cpu/armv7/sunxi/psci.c b/arch/arm/cpu/armv7/sunxi/psci.c index b3a34de1aafe..a86608cf3493 100644 --- a/arch/arm/cpu/armv7/sunxi/psci.c +++ b/arch/arm/cpu/armv7/sunxi/psci.c @@ -22,6 +22,8 @@ #include <linux/bitops.h> +#include "psci.h" + #define __irq __attribute__ ((interrupt ("IRQ"))) #define GICD_BASE (SUNXI_GIC400_BASE + GIC_DIST_OFFSET) @@ -38,44 +40,6 @@ #define SUN8I_R40_PWR_CLAMP(cpu) (0x120 + (cpu) * 0x4) #define SUN8I_R40_SRAMC_SOFT_ENTRY_REG0 (0xbc) -static void __secure cp15_write_cntp_tval(u32 tval) -{ - asm volatile ("mcr p15, 0, %0, c14, c2, 0" : : "r" (tval)); -} - -static void __secure cp15_write_cntp_ctl(u32 val) -{ - asm volatile ("mcr p15, 0, %0, c14, c2, 1" : : "r" (val)); -} - -static u32 __secure cp15_read_cntp_ctl(void) -{ - u32 val; - - asm volatile ("mrc p15, 0, %0, c14, c2, 1" : "=r" (val)); - - return val; -} - -#define ONE_MS (COUNTER_FREQUENCY / 1000) - -static void __secure __mdelay(u32 ms) -{ - u32 reg = ONE_MS * ms; - - cp15_write_cntp_tval(reg); - isb(); - cp15_write_cntp_ctl(3); - - do { - isb(); - reg = cp15_read_cntp_ctl(); - } while (!(reg & BIT(2))); - - cp15_write_cntp_ctl(0); - isb(); -} - static void __secure clamp_release(u32 __maybe_unused *clamp) { #if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN7I) || \ diff --git a/arch/arm/cpu/armv7/sunxi/psci.h b/arch/arm/cpu/armv7/sunxi/psci.h new file mode 100644 index 000000000000..248c25772e65 --- /dev/null +++ b/arch/arm/cpu/armv7/sunxi/psci.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2016 + * Author: Chen-Yu Tsai <w...@csie.org> + * + * Based on assembly code by Marc Zyngier <marc.zyng...@arm.com>, + * which was based on code by Carl van Schaik <c...@ok-labs.com>. + * + * SPDX-License-Identifier: GPL-2.0 + */ +#ifndef _SUNXI_PSCI_H_ +#define _SUNXI_PSCI_H_ + +#include <asm/armv7.h> +#include <linux/bitops.h> + +#ifdef CONFIG_TIMER_CLK_FREQ +static void __secure cp15_write_cntp_tval(u32 tval) +{ + asm volatile ("mcr p15, 0, %0, c14, c2, 0" : : "r" (tval)); +} + +static void __secure cp15_write_cntp_ctl(u32 val) +{ + asm volatile ("mcr p15, 0, %0, c14, c2, 1" : : "r" (val)); +} + +static u32 __secure cp15_read_cntp_ctl(void) +{ + u32 val; + + asm volatile ("mrc p15, 0, %0, c14, c2, 1" : "=r" (val)); + + return val; +} + +#define ONE_MS (COUNTER_FREQUENCY / 1000) + +void __secure __mdelay(u32 ms) +{ + u32 reg = ONE_MS * ms; + + cp15_write_cntp_tval(reg); + isb(); + cp15_write_cntp_ctl(3); + + do { + isb(); + reg = cp15_read_cntp_ctl(); + } while (!(reg & BIT(2))); + + cp15_write_cntp_ctl(0); + isb(); +} +#else +#define __mdelay(ms) +#endif + +#endif diff --git a/arch/arm/cpu/armv7/sunxi/psci_suspend.c b/arch/arm/cpu/armv7/sunxi/psci_suspend.c new file mode 100644 index 000000000000..0a8f2c165891 --- /dev/null +++ b/arch/arm/cpu/armv7/sunxi/psci_suspend.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2017 Antoine Tenart <antoine.ten...@free-electrons.com> + * + * Based on Allwinner code. + * Copyright 2007-2012 (C) Allwinner Technology Co., Ltd. + * + * SPDX-License-Identifier: GPL-2.0 + */ +#include <config.h> +#include <common.h> + +#include <asm/atomic.h> +#include <asm/arch/clock.h> +#include <asm/arch/dram.h> +#include <asm/armv7.h> +#include <asm/io.h> +#include <asm/psci.h> +#include <asm/secure.h> +#include <asm/system.h> + +#include <linux/bitops.h> + +#include "psci.h" + +#if defined(CONFIG_MACH_SUN5I) +#define NR_CPUS 1 +#elif defined(CONFIG_MACH_SUN7I) +#define NR_CPUS 2 +#endif + +/* + * The PSCI suspend function switch cpuclk to another source and disable + * pll1. As this function is called per-CPU, it should only do this when + * all the CPUs are in idle state. + * + * The 'cnt' variable keeps track of the number of CPU which are in the idle + * state. The last one setup cpuclk for idle. + * + * The 'clk_state' varibale holds the cpu clk state (idle or normal). + */ +atomic_t __secure_data cnt, clk_state; + +#define CLK_NORMAL 0 +#define CLK_IDLE 1 + +static void __secure sunxi_clock_enter_idle(struct sunxi_ccm_reg *ccm) +{ + /* switch cpuclk to osc24m */ + clrsetbits_le32(&ccm->cpu_ahb_apb0_cfg, 0x3 << CPU_CLK_SRC_SHIFT, + CPU_CLK_SRC_OSC24M << CPU_CLK_SRC_SHIFT); + + /* disable pll1 */ + clrbits_le32(&ccm->pll1_cfg, CCM_PLL1_CTRL_EN); + +#ifndef CONFIG_MACH_SUN7I + /* + * Switch cpuclk to losc. Based on my experience this didn't worked for + * sun7i, hence the ifndef. + */ + clrbits_le32(&ccm->cpu_ahb_apb0_cfg, 0x3 << CPU_CLK_SRC_SHIFT); +#endif +} + +static void __secure sunxi_clock_leave_idle(struct sunxi_ccm_reg *ccm) +{ +#ifndef CONFIG_MACH_SUN7I + /* switch cpuclk to osc24m */ + clrsetbits_le32(&ccm->cpu_ahb_apb0_cfg, 0x3 << CPU_CLK_SRC_SHIFT, + CPU_CLK_SRC_OSC24M << CPU_CLK_SRC_SHIFT); +#endif + + /* enable pll1 */ + setbits_le32(&ccm->pll1_cfg, CCM_PLL1_CTRL_EN); + + /* wait for the clock to lock */ + while (readl(&ccm->pll1_cfg) & BIT(28)); + + /* switch cpuclk to pll1 */ + clrsetbits_le32(&ccm->cpu_ahb_apb0_cfg, 0x3 << CPU_CLK_SRC_SHIFT, + CPU_CLK_SRC_PLL1 << CPU_CLK_SRC_SHIFT); +} + +void __secure psci_cpu_suspend(void) +{ + struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + if (atomic_inc_return(&cnt) == NR_CPUS) { + /* wait for any sunxi_clock_leave_idle() to finish */ + while (atomic_read(&clk_state) != CLK_NORMAL) + __mdelay(1); + + sunxi_clock_enter_idle(ccm); + atomic_set(&clk_state, CLK_IDLE); + } + + /* idle */ + DSB; + wfi(); + + if (atomic_dec_return(&cnt) == NR_CPUS - 1) { + /* wait for any sunxi_clock_enter_idle() to finish */ + while (atomic_read(&clk_state) != CLK_IDLE) + __mdelay(1); + + sunxi_clock_leave_idle(ccm); + atomic_set(&clk_state, CLK_NORMAL); + } +} diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h index d1c5ad0a739b..12b0f22a1368 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h @@ -208,6 +208,8 @@ struct sunxi_ccm_reg { #define CCM_AHB_GATE_DLL (0x1 << 15) #define CCM_AHB_GATE_ACE (0x1 << 16) +#define CCM_PLL1_CTRL_EN (0x1 << 31) + #define CCM_PLL3_CTRL_M_SHIFT 0 #define CCM_PLL3_CTRL_M_MASK (0x7f << CCM_PLL3_CTRL_M_SHIFT) #define CCM_PLL3_CTRL_M(n) (((n) & 0x7f) << 0) -- 2.11.0 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot