
Hi,
On Wed, Oct 26, 2016 at 02:10:31PM +0200, Antoine Tenart wrote:
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.tenart@free-electrons.com
arch/arm/cpu/armv7/sunxi/Makefile | 9 ++- arch/arm/cpu/armv7/sunxi/psci.c | 2 +- arch/arm/cpu/armv7/sunxi/psci_suspend.c | 108 ++++++++++++++++++++++++++ arch/arm/include/asm/arch-sunxi/clock_sun4i.h | 6 ++ 4 files changed, 123 insertions(+), 2 deletions(-) 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 766b8c79d93d..4525bc7bf26b 100644 --- a/arch/arm/cpu/armv7/sunxi/psci.c +++ b/arch/arm/cpu/armv7/sunxi/psci.c @@ -48,7 +48,7 @@ static u32 __secure cp15_read_cntp_ctl(void)
#define ONE_MS (CONFIG_TIMER_CLK_FREQ / 1000)
-static void __secure __mdelay(u32 ms) +void __secure __mdelay(u32 ms) { u32 reg = ONE_MS * ms;
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..e5c000ff2d3d --- /dev/null +++ b/arch/arm/cpu/armv7/sunxi/psci_suspend.c @@ -0,0 +1,108 @@ +/*
- Copyright (C) 2016 Antoine Tenart antoine.tenart@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>
+void __mdelay(u32);
+#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 */
- clrbits_le32(&ccm->cpu_ahb_apb0_cfg, 0x3 << CPU_CLK_SRC_SHIFT);
+#endif
Some kind of comment here would be nice.
- /* disable ldo */
- clrbits_le32(&ccm->osc24m_cfg, OSC24M_LDO_EN);
I realise I'm the one who suggested doing this, but that might turn out to be wrong. Have you tested devices that use the oscillator directly, like the PWM?
+}
+static void __secure sunxi_clock_leave_idle(struct sunxi_ccm_reg *ccm) +{
- /* enable ldo */
- setbits_le32(&ccm->osc24m_cfg, OSC24M_LDO_EN);
+#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);
It might be worth waiting for the PLL to lock by polling the bit 28 before switching the mux to it.
- /* 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..0d7df5bba543 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h @@ -90,6 +90,10 @@ struct sunxi_ccm_reg { u32 gmac_clk_cfg; /* 0x164 */ };
+/* osc24m_cfg bit field */ +#define OSC24M_EN (0x1 << 0) +#define OSC24M_LDO_EN (0x1 << 16)
/* apb1 bit field */ #define APB1_CLK_SRC_OSC24M (0x0 << 24) #define APB1_CLK_SRC_PLL6 (0x1 << 24) @@ -208,6 +212,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.10.1
Looks good otherwise, Thanks!
Maxime