
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@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@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 */