[U-Boot] [PATCH v4 09/11] sun5/7i: add an implementation of the psci suspend function

Antoine Tenart antoine.tenart at free-electrons.com
Sun Apr 30 13:29:54 UTC 2017


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 at 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 <wens at csie.org>
+ *
+ * Based on assembly code by Marc Zyngier <marc.zyngier at arm.com>,
+ * which was based on code by Carl van Schaik <carl at 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.tenart at 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



More information about the U-Boot mailing list