[U-Boot] [PATCH v2 7/9] sun5/7i: add an implementation of the psci suspend function

Antoine Tenart antoine.tenart at free-electrons.com
Thu Oct 6 16:33:59 CEST 2016


Add the suspend psci function for sun5i and sun7i. When running on a
sun5i, this function put the ram into self-refresh, cut off some PLLs
and switch cpuclk to losc. When on a sun7i the suspend function
currently only cut off a few PLLs and switch cpuclk to osc24m. This
should be improved later.

Signed-off-by: Antoine Tenart <antoine.tenart at free-electrons.com>
---
 arch/arm/cpu/armv7/sunxi/Makefile             |   9 +-
 arch/arm/cpu/armv7/sunxi/psci_suspend.c       | 175 ++++++++++++++++++++++++++
 arch/arm/include/asm/arch-sunxi/clock_sun4i.h |   7 ++
 arch/arm/include/asm/arch-sunxi/dram_sun4i.h  |  11 ++
 4 files changed, 201 insertions(+), 1 deletion(-)
 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_suspend.c b/arch/arm/cpu/armv7/sunxi/psci_suspend.c
new file mode 100644
index 000000000000..6526cb51cd1c
--- /dev/null
+++ b/arch/arm/cpu/armv7/sunxi/psci_suspend.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2016 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/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>
+
+#ifndef CONFIG_MACH_SUN7I
+static void __secure sunxi_enter_selfrefresh(struct sunxi_dram_reg *dram)
+{
+	int i;
+
+	/* disable all dram host ports */
+	for (i = 0; i < 32; i++)
+		clrbits_le32(&dram->hpcr[i], DRAM_HPCR_PORT_EN);
+
+	/* disable auto-refresh */
+	setbits_le32(&dram->drr, DRAM_DRR_AUTO_REFRESH_OFF);
+
+	/* precharge all commands */
+	clrsetbits_le32(&dram->dcr, DRAM_DCR_CMD_MASK,
+			DRAM_DCR_CMD_PRECHARGE_ALL | DRAM_DCR_CMD_EXEC);
+
+	/* enter into self-refresh */
+	clrsetbits_le32(&dram->dcr, DRAM_DCR_CMD_MASK,
+			DRAM_DCR_CMD_SELF_REFRESH | DRAM_DCR_CMD_EXEC);
+
+	/* disable ITM */
+	setbits_le32(&dram->ccr, DRAM_CCR_ITM_OFF);
+
+	/* disable DRAM clock */
+	clrbits_le32(&dram->mcr, DRAM_MCR_DCLK_OUT);
+
+	/* disable all DLLs */
+	for (i = 0; i < 5; i++)
+		clrsetbits_le32(&dram->dllcr[i],
+				DRAM_DLLCR_DISABLE | DRAM_DLLCR_NRESET,
+				DRAM_DLLCR_DISABLE);
+}
+
+static void __secure sunxi_enable_dll(struct sunxi_dram_reg *dram, u32 pll)
+{
+	clrsetbits_le32(&dram->dllcr[pll],
+			DRAM_DLLCR_DISABLE | DRAM_DLLCR_NRESET,
+			DRAM_DLLCR_DISABLE);
+
+	clrbits_le32(&dram->dllcr[pll],
+		     DRAM_DLLCR_DISABLE | DRAM_DLLCR_NRESET);
+
+	clrsetbits_le32(&dram->dllcr[pll],
+			DRAM_DLLCR_DISABLE | DRAM_DLLCR_NRESET,
+			DRAM_DLLCR_NRESET);
+}
+
+static void __secure sunxi_leave_selfrefresh(struct sunxi_dram_reg *dram)
+{
+	int i;
+
+	/* enable DLL0 */
+	sunxi_enable_dll(dram, 0);
+
+	/* enable DRAM clock */
+	setbits_le32(&dram->mcr, DRAM_MCR_DCLK_OUT);
+
+	/* enable all other DLLs */
+	for (i = 1; i < 5; i++)
+		sunxi_enable_dll(dram, i);
+
+	/* enable ITM */
+	clrbits_le32(&dram->ccr, DRAM_CCR_ITM_OFF);
+
+	/* leave self-refresh */
+	clrsetbits_le32(&dram->dcr, DRAM_DCR_CMD_MASK,
+			DRAM_DCR_CMD_EXIT | DRAM_DCR_CMD_EXEC);
+
+	/* manually refresh */
+	clrsetbits_le32(&dram->dcr, DRAM_DCR_CMD_MASK,
+			DRAM_DCR_CMD_REFRESH | DRAM_DCR_CMD_EXEC);
+
+	/* enable auto-refresh */
+	clrbits_le32(&dram->drr, DRAM_DRR_AUTO_REFRESH_OFF);
+
+	/* enable all host ports */
+	for (i = 0; i < 32; i++)
+		setbits_le32(&dram->hpcr[i], DRAM_HPCR_PORT_EN);
+}
+#else
+static void __secure sunxi_enter_selfrefresh(struct sunxi_dram_reg *dram)
+{
+}
+
+static void __secure sunxi_leave_selfrefresh(struct sunxi_dram_reg *dram)
+{
+}
+#endif
+
+static void __secure sunxi_clock_enter_suspend(struct sunxi_ccm_reg *ccm)
+{
+#ifndef CONFIG_MACH_SUN7I
+	/* gate off DRAM clock */
+	clrbits_le32(&ccm->pll5_cfg, CCM_PLL5_CTRL_DDR_CLK);
+#endif
+
+	/* 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 PLLs */
+	clrbits_le32(&ccm->pll1_cfg, CCM_PLL1_CTRL_EN);
+	clrbits_le32(&ccm->pll2_cfg, CCM_PLL2_CTRL_EN);
+	clrbits_le32(&ccm->pll3_cfg, CCM_PLL3_CTRL_EN);
+	clrbits_le32(&ccm->pll4_cfg, CCM_PLL4_CTRL_EN);
+	clrbits_le32(&ccm->pll7_cfg, CCM_PLL7_CTRL_EN);
+
+#ifndef CONFIG_MACH_SUN7I
+	/* switch cpuclk to losc */
+	clrbits_le32(&ccm->cpu_ahb_apb0_cfg, 0x3 << CPU_CLK_SRC_SHIFT);
+#endif
+}
+
+static void __secure sunxi_clock_leave_suspend(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 PLLs */
+	setbits_le32(&ccm->pll1_cfg, CCM_PLL1_CTRL_EN);
+	setbits_le32(&ccm->pll2_cfg, CCM_PLL2_CTRL_EN);
+	setbits_le32(&ccm->pll3_cfg, CCM_PLL3_CTRL_EN);
+	setbits_le32(&ccm->pll4_cfg, CCM_PLL4_CTRL_EN);
+	setbits_le32(&ccm->pll7_cfg, CCM_PLL7_CTRL_EN);
+
+	/* switch cpuclk to pll */
+	clrsetbits_le32(&ccm->cpu_ahb_apb0_cfg, 0x3 << CPU_CLK_SRC_SHIFT,
+			CPU_CLK_SRC_PLL1 << CPU_CLK_SRC_SHIFT);
+
+#ifndef CONFIG_MACH_SUN7I
+	/* gate on DRAM clock */
+	setbits_le32(&ccm->pll5_cfg, CCM_PLL5_CTRL_DDR_CLK);
+#endif
+}
+
+void __secure psci_cpu_suspend(void)
+{
+	struct sunxi_dram_reg *dram =
+			(struct sunxi_dram_reg *)SUNXI_DRAMC_BASE;
+	struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+
+	sunxi_enter_selfrefresh(dram);
+	sunxi_clock_enter_suspend(ccm);
+
+	/* idle */
+	DSB;
+	wfi();
+
+	sunxi_clock_leave_suspend(ccm);
+	sunxi_leave_selfrefresh(dram);
+}
diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h
index d1c5ad0a739b..0e59781dcf8d 100644
--- a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h
+++ b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h
@@ -208,12 +208,17 @@ 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_PLL2_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)
 #define CCM_PLL3_CTRL_INTEGER_MODE	(0x1 << 15)
 #define CCM_PLL3_CTRL_EN		(0x1 << 31)
 
+#define CCM_PLL4_CTRL_EN		(0x1 << 31)
+
 #define CCM_PLL5_CTRL_M(n) (((n) & 0x3) << 0)
 #define CCM_PLL5_CTRL_M_MASK CCM_PLL5_CTRL_M(0x3)
 #define CCM_PLL5_CTRL_M_X(n) ((n) - 1)
@@ -251,6 +256,8 @@ struct sunxi_ccm_reg {
 #define CCM_PLL6_CTRL_K_SHIFT		4
 #define CCM_PLL6_CTRL_K_MASK		(0x3 << CCM_PLL6_CTRL_K_SHIFT)
 
+#define CCM_PLL7_CTRL_EN (0x1 << 31)
+
 #define CCM_GPS_CTRL_RESET (0x1 << 0)
 #define CCM_GPS_CTRL_GATE (0x1 << 1)
 
diff --git a/arch/arm/include/asm/arch-sunxi/dram_sun4i.h b/arch/arm/include/asm/arch-sunxi/dram_sun4i.h
index 139e3366a633..115d6697b94a 100644
--- a/arch/arm/include/asm/arch-sunxi/dram_sun4i.h
+++ b/arch/arm/include/asm/arch-sunxi/dram_sun4i.h
@@ -130,6 +130,14 @@ struct dram_para {
 #define DRAM_DCR_MODE_SEQ 0x0
 #define DRAM_DCR_MODE_INTERLEAVE 0x1
 
+#define DRAM_DCR_CMD(n) ((n) << 27)
+#define DRAM_DCR_CMD_MASK DRAM_DCR_CMD(0xf)
+#define DRAM_DCR_CMD_SELF_REFRESH DRAM_DCR_CMD(0x2)
+#define DRAM_DCR_CMD_REFRESH DRAM_DCR_CMD(0x3)
+#define DRAM_DCR_CMD_PRECHARGE_ALL DRAM_DCR_CMD(0x5)
+#define DRAM_DCR_CMD_EXIT DRAM_DCR_CMD(0x7)
+#define DRAM_DCR_CMD_EXEC (0x1 << 31)
+
 #define DRAM_CSR_DTERR  (0x1 << 20)
 #define DRAM_CSR_DTIERR (0x1 << 21)
 #define DRAM_CSR_FAILED (DRAM_CSR_DTERR | DRAM_CSR_DTIERR)
@@ -137,6 +145,7 @@ struct dram_para {
 #define DRAM_DRR_TRFC(n) ((n) & 0xff)
 #define DRAM_DRR_TREFI(n) (((n) & 0xffff) << 8)
 #define DRAM_DRR_BURST(n) ((((n) - 1) & 0xf) << 24)
+#define DRAM_DRR_AUTO_REFRESH_OFF (0x1 << 31)
 
 #define DRAM_MCR_MODE_NORM(n) (((n) & 0x3) << 0)
 #define DRAM_MCR_MODE_NORM_MASK DRAM_MCR_MOD_NORM(0x3)
@@ -176,6 +185,8 @@ struct dram_para {
 
 #define DRAM_CSEL_MAGIC 0x16237495
 
+#define DRAM_HPCR_PORT_EN 0x1
+
 unsigned long dramc_init(struct dram_para *para);
 
 #endif /* _SUNXI_DRAM_SUN4I_H */
-- 
2.10.1



More information about the U-Boot mailing list