Implement support for deeper idle states offered by PSCI. Reduces the
authorkettenis <kettenis@openbsd.org>
Wed, 10 Jul 2024 11:01:24 +0000 (11:01 +0000)
committerkettenis <kettenis@openbsd.org>
Wed, 10 Jul 2024 11:01:24 +0000 (11:01 +0000)
idle power usage of the Vivobook S15 by almost 50%.

ok patrick@

sys/arch/arm64/arm64/cpu.c
sys/arch/arm64/include/cpu.h
sys/dev/fdt/psci.c
sys/dev/fdt/pscivar.h

index ae17940..000313f 100644 (file)
@@ -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);
@@ -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 {
index c407346..c9b0e10 100644 (file)
@@ -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;
index 14bcd87..72fea23 100644 (file)
@@ -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,
index ba8942b..f11666a 100644 (file)
 #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);