summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2024-07-10 11:01:25 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2024-07-10 11:01:25 +0000
commitab036c590b63ce5163ef805b2da6588aa0045ae0 (patch)
treeb7d6a14fa66472378e2b9e931819d09aeb5ba270
parent3bfdd09ce0b7e845645947fc5443cb4e218ecd31 (diff)
Implement support for deeper idle states offered by PSCI. Reduces the
idle power usage of the Vivobook S15 by almost 50%. ok patrick@
-rw-r--r--sys/arch/arm64/arm64/cpu.c72
-rw-r--r--sys/arch/arm64/include/cpu.h4
-rw-r--r--sys/dev/fdt/psci.c23
-rw-r--r--sys/dev/fdt/pscivar.h26
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 <drahn@dalerahn.com>
@@ -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);
@@ -1956,6 +1957,51 @@ cpu_psci_init(struct cpu_info *ci)
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
* nodes in the device tree. The first step is to look up the
@@ -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 <drahn@dalerahn.com>
*
@@ -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 <jsg@openbsd.org>
@@ -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);