From 07eca60229dc516417bb980ffa9ffd5220e0c9f5 Mon Sep 17 00:00:00 2001 From: kettenis Date: Wed, 10 Jul 2024 11:01:24 +0000 Subject: [PATCH] Implement support for deeper idle states offered by PSCI. Reduces the idle power usage of the Vivobook S15 by almost 50%. ok patrick@ --- sys/arch/arm64/arm64/cpu.c | 72 +++++++++++++++++++++++++++++++++++- sys/arch/arm64/include/cpu.h | 4 +- sys/dev/fdt/psci.c | 23 +----------- sys/dev/fdt/pscivar.h | 26 +++++++++++++ 4 files changed, 101 insertions(+), 24 deletions(-) diff --git a/sys/arch/arm64/arm64/cpu.c b/sys/arch/arm64/arm64/cpu.c index ae17940fbc8..000313fa895 100644 --- a/sys/arch/arm64/arm64/cpu.c +++ b/sys/arch/arm64/arm64/cpu.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.c,v 1.123 2024/07/02 19:59:54 kettenis Exp $ */ +/* $OpenBSD: cpu.c,v 1.124 2024/07/10 11:01:24 kettenis Exp $ */ /* * Copyright (c) 2016 Dale Rahn @@ -275,6 +275,7 @@ struct cfdriver cpu_cd = { void cpu_opp_init(struct cpu_info *, uint32_t); void cpu_psci_init(struct cpu_info *); +void cpu_psci_idle_cycle(void); void cpu_flush_bp_noop(void); void cpu_flush_bp_psci(void); @@ -1955,6 +1956,51 @@ cpu_psci_init(struct cpu_info *ci) uint32_t cluster; int idx, len, node; + /* + * Find the shallowest (for now) idle state for this CPU. + * This should be the first one that is listed. We'll use it + * in the idle loop. + */ + + len = OF_getproplen(ci->ci_node, "cpu-idle-states"); + if (len < (int)sizeof(uint32_t)) + return; + + states = malloc(len, M_TEMP, M_WAITOK); + OF_getpropintarray(ci->ci_node, "cpu-idle-states", states, len); + node = OF_getnodebyphandle(states[0]); + free(states, M_TEMP, len); + if (node) { + uint32_t entry, exit, residency, param; + int32_t features; + + param = OF_getpropint(node, "arm,psci-suspend-param", 0); + entry = OF_getpropint(node, "entry-latency-us", 0); + exit = OF_getpropint(node, "exit-latency-us", 0); + residency = OF_getpropint(node, "min-residency-us", 0); + ci->ci_psci_idle_latency += entry + exit + 2 * residency; + + /* Skip states that stop the local timer. */ + if (OF_getpropbool(node, "local-timer-stop")) + ci->ci_psci_idle_param = 0; + + /* Skip powerdown states. */ + features = psci_features(CPU_SUSPEND); + if (features == PSCI_NOT_SUPPORTED || + (features & PSCI_FEATURE_POWER_STATE_EXT) == 0) { + if (param & PSCI_POWER_STATE_POWERDOWN) + param = 0; + } else { + if (param & PSCI_POWER_STATE_EXT_POWERDOWN) + param = 0; + } + + if (param) { + ci->ci_psci_idle_param = param; + cpu_idle_cycle_fcn = cpu_psci_idle_cycle; + } + } + /* * Hunt for the deepest idle state for this CPU. This is * fairly complicated as it requires traversing quite a few @@ -2052,6 +2098,30 @@ cpu_psci_init(struct cpu_info *ci) OF_getpropint(node, "arm,psci-suspend-param", 0); } +void +cpu_psci_idle_cycle(void) +{ + struct cpu_info *ci = curcpu(); + struct timeval start, stop; + u_long itime; + + microuptime(&start); + + if (ci->ci_prev_sleep > ci->ci_psci_idle_latency) + psci_cpu_suspend(ci->ci_psci_idle_param, 0, 0); + else + cpu_wfi(); + + microuptime(&stop); + timersub(&stop, &start, &stop); + itime = stop.tv_sec * 1000000 + stop.tv_usec; + + ci->ci_last_itime = itime; + itime >>= 1; + ci->ci_prev_sleep = (ci->ci_prev_sleep + (ci->ci_prev_sleep >> 1) + + itime) >> 1; +} + #if NKSTAT > 0 struct cpu_kstats { diff --git a/sys/arch/arm64/include/cpu.h b/sys/arch/arm64/include/cpu.h index c4073465a7a..c9b0e109160 100644 --- a/sys/arch/arm64/include/cpu.h +++ b/sys/arch/arm64/include/cpu.h @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.h,v 1.47 2024/05/01 12:54:27 mpi Exp $ */ +/* $OpenBSD: cpu.h,v 1.48 2024/07/10 11:01:24 kettenis Exp $ */ /* * Copyright (c) 2016 Dale Rahn * @@ -146,6 +146,8 @@ struct cpu_info { uint64_t ci_ttbr1; vaddr_t ci_el1_stkend; + uint32_t ci_psci_idle_latency; + uint32_t ci_psci_idle_param; uint32_t ci_psci_suspend_param; struct opp_table *ci_opp_table; diff --git a/sys/dev/fdt/psci.c b/sys/dev/fdt/psci.c index 14bcd878690..72fea23cf3f 100644 --- a/sys/dev/fdt/psci.c +++ b/sys/dev/fdt/psci.c @@ -1,4 +1,4 @@ -/* $OpenBSD: psci.c,v 1.16 2024/04/13 14:20:48 kettenis Exp $ */ +/* $OpenBSD: psci.c,v 1.17 2024/07/10 11:01:24 kettenis Exp $ */ /* * Copyright (c) 2016 Jonathan Gray @@ -37,27 +37,6 @@ extern void (*powerdownfn)(void); #define SMCCC_ARCH_WORKAROUND_2 0x80007fff #define SMCCC_ARCH_WORKAROUND_3 0x80003fff -#define PSCI_VERSION 0x84000000 -#ifdef __LP64__ -#define CPU_SUSPEND 0xc4000001 -#else -#define CPU_SUSPEND 0x84000001 -#endif -#define CPU_OFF 0x84000002 -#ifdef __LP64__ -#define CPU_ON 0xc4000003 -#else -#define CPU_ON 0x84000003 -#endif -#define SYSTEM_OFF 0x84000008 -#define SYSTEM_RESET 0x84000009 -#define PSCI_FEATURES 0x8400000a -#ifdef __LP64__ -#define SYSTEM_SUSPEND 0xc400000e -#else -#define SYSTEM_SUSPEND 0x8400000e -#endif - struct psci_softc { struct device sc_dev; register_t (*sc_callfn)(register_t, register_t, register_t, diff --git a/sys/dev/fdt/pscivar.h b/sys/dev/fdt/pscivar.h index ba8942b0353..f11666a6185 100644 --- a/sys/dev/fdt/pscivar.h +++ b/sys/dev/fdt/pscivar.h @@ -10,12 +10,38 @@ #define PSCI_METHOD_HVC 1 #define PSCI_METHOD_SMC 2 +#define PSCI_VERSION 0x84000000 +#ifdef __LP64__ +#define CPU_SUSPEND 0xc4000001 +#else +#define CPU_SUSPEND 0x84000001 +#endif +#define CPU_OFF 0x84000002 +#ifdef __LP64__ +#define CPU_ON 0xc4000003 +#else +#define CPU_ON 0x84000003 +#endif +#define SYSTEM_OFF 0x84000008 +#define SYSTEM_RESET 0x84000009 +#define PSCI_FEATURES 0x8400000a +#ifdef __LP64__ +#define SYSTEM_SUSPEND 0xc400000e +#else +#define SYSTEM_SUSPEND 0x8400000e +#endif + +#define PSCI_FEATURE_POWER_STATE_EXT (1 << 1) +#define PSCI_POWER_STATE_POWERDOWN (1 << 16) +#define PSCI_POWER_STATE_EXT_POWERDOWN (1 << 30) + int psci_can_suspend(void); int32_t psci_system_suspend(register_t, register_t); int32_t psci_cpu_on(register_t, register_t, register_t); int32_t psci_cpu_off(void); int32_t psci_cpu_suspend(register_t, register_t, register_t); +int32_t psci_features(uint32_t); void psci_flush_bp(void); int psci_method(void); -- 2.20.1