From ba5ad174f4b89249eeb2724491bc0ea7e0d4fa0c Mon Sep 17 00:00:00 2001 From: kettenis Date: Wed, 29 May 2024 12:21:33 +0000 Subject: [PATCH] Implement the guts for "suspend-to-idle" on amd64. This enables suspend on machines that don't support S3. In its current state it doesn't save a lot of power, but this should improve over time. Implementation of wakeup methods is incomplete which means that some machine can't resume at the moment. ok mglocker@, mlarkin@, stsp@, deraadt@ --- sys/arch/amd64/amd64/acpi_machdep.c | 8 ++++- sys/arch/amd64/amd64/cpu.c | 51 ++++++++++++++++++++++++++++- sys/arch/amd64/amd64/intr.c | 12 ++++++- sys/arch/amd64/include/cpu.h | 4 ++- sys/arch/i386/i386/cpu.c | 4 ++- sys/dev/acpi/acpi.c | 6 +++- sys/dev/acpi/acpi_x86.c | 36 +++++++++++++------- sys/dev/acpi/tpm.c | 5 ++- 8 files changed, 107 insertions(+), 19 deletions(-) diff --git a/sys/arch/amd64/amd64/acpi_machdep.c b/sys/arch/amd64/amd64/acpi_machdep.c index bf7add7efd7..2403f03f943 100644 --- a/sys/arch/amd64/amd64/acpi_machdep.c +++ b/sys/arch/amd64/amd64/acpi_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: acpi_machdep.c,v 1.109 2024/05/26 13:37:31 kettenis Exp $ */ +/* $OpenBSD: acpi_machdep.c,v 1.110 2024/05/29 12:21:33 kettenis Exp $ */ /* * Copyright (c) 2005 Thorsten Lockert * @@ -377,6 +377,9 @@ acpi_attach_machdep(struct acpi_softc *sc) int acpi_sleep_cpu(struct acpi_softc *sc, int state) { + if (state == ACPI_STATE_S0) + return cpu_suspend_primary(); + rtcstop(); #if NLAPIC > 0 lapic_disable(); @@ -458,6 +461,9 @@ acpi_sleep_cpu(struct acpi_softc *sc, int state) void acpi_resume_cpu(struct acpi_softc *sc, int state) { + if (state == ACPI_STATE_S0) + return; + cpu_init_msrs(&cpu_info_primary); cpu_fix_msrs(&cpu_info_primary); diff --git a/sys/arch/amd64/amd64/cpu.c b/sys/arch/amd64/amd64/cpu.c index 2eabcbe19e0..6714c0237f9 100644 --- a/sys/arch/amd64/amd64/cpu.c +++ b/sys/arch/amd64/amd64/cpu.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.c,v 1.188 2024/05/14 01:42:07 guenther Exp $ */ +/* $OpenBSD: cpu.c,v 1.189 2024/05/29 12:21:33 kettenis Exp $ */ /* $NetBSD: cpu.c,v 1.1 2003/04/26 18:39:26 fvdl Exp $ */ /*- @@ -1461,3 +1461,52 @@ wbinvd_on_all_cpus(void) return 0; } #endif + +int cpu_suspended; + +#ifdef SUSPEND + +void +cpu_suspend_cycle(void) +{ + cpu_idle_cycle_fcn(); +} + +int +cpu_suspend_primary(void) +{ + struct cpu_info *ci = curcpu(); + int count = 0; + + printf("suspend\n"); + + /* Mask clock interrupts. */ + local_pic.pic_hwmask(&local_pic, 0); + + /* + * All non-wakeup interrupts should be masked at this point; + * re-enable interrupts such that wakeup interrupts actually + * wake us up. Set a flag such that drivers can tell we're + * suspended and change their behaviour accordingly. They can + * wake us up by clearing the flag. + */ + cpu_suspended = 1; + ci->ci_ilevel = IPL_NONE; + intr_enable(); + + while (cpu_suspended) { + cpu_suspend_cycle(); + count++; + } + + intr_disable(); + ci->ci_ilevel = IPL_HIGH; + + /* Unmask clock interrupts. */ + local_pic.pic_hwunmask(&local_pic, 0); + + printf("resume %d\n", count); + return 0; +} + +#endif diff --git a/sys/arch/amd64/amd64/intr.c b/sys/arch/amd64/amd64/intr.c index 9b1182e9545..5865f0b8c5c 100644 --- a/sys/arch/amd64/amd64/intr.c +++ b/sys/arch/amd64/amd64/intr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: intr.c,v 1.57 2024/05/26 13:37:31 kettenis Exp $ */ +/* $OpenBSD: intr.c,v 1.58 2024/05/29 12:21:33 kettenis Exp $ */ /* $NetBSD: intr.c,v 1.3 2003/03/03 22:16:20 fvdl Exp $ */ /* @@ -524,12 +524,22 @@ intr_disestablish(struct intrhand *ih) int intr_handler(struct intrframe *frame, struct intrhand *ih) { + extern int cpu_suspended; struct cpu_info *ci = curcpu(); int floor; int rc; #ifdef MULTIPROCESSOR int need_lock; +#endif + /* + * We may not be able to mask MSIs, so block non-wakeup + * interrupts while we're suspended. + */ + if (cpu_suspended && (ih->ih_flags & IPL_WAKEUP) == 0) + return 0; + +#ifdef MULTIPROCESSOR if (ih->ih_flags & IPL_MPSAFE) need_lock = 0; else diff --git a/sys/arch/amd64/include/cpu.h b/sys/arch/amd64/include/cpu.h index 57e3c62210c..fab34e5bd9d 100644 --- a/sys/arch/amd64/include/cpu.h +++ b/sys/arch/amd64/include/cpu.h @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.h,v 1.170 2024/05/21 23:16:06 jsg Exp $ */ +/* $OpenBSD: cpu.h,v 1.171 2024/05/29 12:21:33 kettenis Exp $ */ /* $NetBSD: cpu.h,v 1.1 2003/04/26 18:39:39 fvdl Exp $ */ /*- @@ -401,6 +401,8 @@ extern int cpu_meltdown; extern u_int cpu_mwait_size; extern u_int cpu_mwait_states; +int cpu_suspend_primary(void); + /* cacheinfo.c */ void x86_print_cacheinfo(struct cpu_info *); diff --git a/sys/arch/i386/i386/cpu.c b/sys/arch/i386/i386/cpu.c index 3b01d17cd2d..9e891cb43d5 100644 --- a/sys/arch/i386/i386/cpu.c +++ b/sys/arch/i386/i386/cpu.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.c,v 1.114 2023/10/24 13:20:10 claudio Exp $ */ +/* $OpenBSD: cpu.c,v 1.115 2024/05/29 12:21:33 kettenis Exp $ */ /* $NetBSD: cpu.c,v 1.1.2.7 2000/06/26 02:04:05 sommerfeld Exp $ */ /*- @@ -925,3 +925,5 @@ wbinvd_on_all_cpus(void) return 0; } #endif + +int cpu_suspended; diff --git a/sys/dev/acpi/acpi.c b/sys/dev/acpi/acpi.c index f53c84e143c..71c30b59a5b 100644 --- a/sys/dev/acpi/acpi.c +++ b/sys/dev/acpi/acpi.c @@ -1,4 +1,4 @@ -/* $OpenBSD: acpi.c,v 1.428 2024/05/13 19:56:37 kettenis Exp $ */ +/* $OpenBSD: acpi.c,v 1.429 2024/05/29 12:21:33 kettenis Exp $ */ /* * Copyright (c) 2005 Thorsten Lockert * Copyright (c) 2005 Jordan Hargrave @@ -2085,6 +2085,7 @@ acpi_powerdown_task(void *arg0, int dummy) int acpi_interrupt(void *arg) { + extern int cpu_suspended; struct acpi_softc *sc = (struct acpi_softc *)arg; uint32_t processed = 0, idx, jdx; uint16_t sts, en; @@ -2137,6 +2138,9 @@ acpi_interrupt(void *arg) ACPI_PM1_PWRBTN_STS); sts &= ~ACPI_PM1_PWRBTN_STS; + if (cpu_suspended) + cpu_suspended = 0; + acpi_addtask(sc, acpi_pbtn_task, sc, 0); } if (sts & ACPI_PM1_SLPBTN_STS) { diff --git a/sys/dev/acpi/acpi_x86.c b/sys/dev/acpi/acpi_x86.c index e8a8efdc045..97def00c044 100644 --- a/sys/dev/acpi/acpi_x86.c +++ b/sys/dev/acpi/acpi_x86.c @@ -1,4 +1,4 @@ -/* $OpenBSD: acpi_x86.c,v 1.20 2024/05/28 09:40:40 kettenis Exp $ */ +/* $OpenBSD: acpi_x86.c,v 1.21 2024/05/29 12:21:33 kettenis Exp $ */ /* * Copyright (c) 2005 Thorsten Lockert * Copyright (c) 2005 Jordan Hargrave @@ -31,13 +31,18 @@ int sleep_showstate(void *v, int sleepmode) { struct acpi_softc *sc = v; + int fallback_state = -1; switch (sleepmode) { case SLEEP_SUSPEND: sc->sc_state = ACPI_STATE_S3; +#ifdef __amd64__ + fallback_state = ACPI_STATE_S0; /* No S3, use S0 */ +#endif break; case SLEEP_HIBERNATE: sc->sc_state = ACPI_STATE_S4; + fallback_state = ACPI_STATE_S5; /* No S4, use S5 */ break; default: return (EOPNOTSUPP); @@ -45,10 +50,10 @@ sleep_showstate(void *v, int sleepmode) if (sc->sc_sleeptype[sc->sc_state].slp_typa == -1 || sc->sc_sleeptype[sc->sc_state].slp_typb == -1) { - if (sc->sc_state == ACPI_STATE_S4) { - sc->sc_state = ACPI_STATE_S5; /* No S4, use S5 */ - printf("%s: S4 unavailable, using S5\n", - sc->sc_dev.dv_xname); + if (fallback_state != -1) { + printf("%s: S%d unavailable, using S%d\n", + sc->sc_dev.dv_xname, sc->sc_state, fallback_state); + sc->sc_state = fallback_state; } else { printf("%s: state S%d unavailable\n", sc->sc_dev.dv_xname, sc->sc_state); @@ -57,8 +62,10 @@ sleep_showstate(void *v, int sleepmode) } /* 1st suspend AML step: _TTS(tostate) */ - if (aml_node_setval(sc, sc->sc_tts, sc->sc_state) != 0) - return (EINVAL); + if (sc->sc_state != ACPI_STATE_S0) { + if (aml_node_setval(sc, sc->sc_tts, sc->sc_state) != 0) + return (EINVAL); + } acpi_indicator(sc, ACPI_SST_WAKING); /* blink */ return 0; } @@ -69,8 +76,10 @@ sleep_setstate(void *v) struct acpi_softc *sc = v; /* 2nd suspend AML step: _PTS(tostate) */ - if (aml_node_setval(sc, sc->sc_pts, sc->sc_state) != 0) - return (EINVAL); + if (sc->sc_state != ACPI_STATE_S0) { + if (aml_node_setval(sc, sc->sc_pts, sc->sc_state) != 0) + return (EINVAL); + } acpi_indicator(sc, ACPI_SST_WAKING); /* blink */ return 0; } @@ -85,7 +94,8 @@ gosleep(void *v) acpi_indicator(sc, ACPI_SST_SLEEPING); /* 3rd suspend AML step: _GTS(tostate) */ - aml_node_setval(sc, sc->sc_gts, sc->sc_state); + if (sc->sc_state != ACPI_STATE_S0) + aml_node_setval(sc, sc->sc_gts, sc->sc_state); /* Clear fixed event status */ acpi_write_pmreg(sc, ACPIREG_PM1_STS, 0, ACPI_PM1_ALL_STS); @@ -110,8 +120,10 @@ sleep_resume(void *v) acpibtn_disable_psw(); /* disable _LID for wakeup */ /* 3rd resume AML step: _TTS(runstate) */ - if (aml_node_setval(sc, sc->sc_tts, ACPI_STATE_S0) != 0) - return (EINVAL); + if (sc->sc_state != ACPI_STATE_S0) { + if (aml_node_setval(sc, sc->sc_tts, ACPI_STATE_S0) != 0) + return (EINVAL); + } acpi_indicator(sc, ACPI_SST_WAKING); /* blink */ return 0; } diff --git a/sys/dev/acpi/tpm.c b/sys/dev/acpi/tpm.c index e4bf6dc60a1..9bd79fe2fe1 100644 --- a/sys/dev/acpi/tpm.c +++ b/sys/dev/acpi/tpm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tpm.c,v 1.19 2024/05/13 01:15:50 jsg Exp $ */ +/* $OpenBSD: tpm.c,v 1.20 2024/05/29 12:21:33 kettenis Exp $ */ /* * Minimal interface to Trusted Platform Module chips implementing the @@ -376,6 +376,9 @@ tpm_suspend(struct tpm_softc *sc) uint8_t *command; size_t commandlen; + if (sc->sc_acpi->sc_state == ACPI_STATE_S0) + return 0; + DPRINTF(("%s: saving state preparing for suspend\n", sc->sc_dev.dv_xname)); -- 2.20.1