sys/power: Generic sleep types
Pull out the sleep types (stype) from ACPI, as was previously being done in D48732, and pass this sleep type to `power_pm_fn` instead of passing the existing sleep state. This is a little awkward because we already kinda have generic sleep states (`POWER_SLEEP_STATE_*`), but these are not precise enough to build upon. This revision also adds generic equivalents to `hw.acpi.suspend_state` etc sysctls, e.g. `kern.power.suspend`. Reviewed by: markj, mckusick (mentor) Approved by: markj, mckusick (mentor) Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D52036
This commit is contained in:
+32
-26
@@ -4,6 +4,10 @@
|
|||||||
* Copyright (c) 2000, 2001 Michael Smith
|
* Copyright (c) 2000, 2001 Michael Smith
|
||||||
* Copyright (c) 2000 BSDi
|
* Copyright (c) 2000 BSDi
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
|
* Copyright (c) 2025 The FreeBSD Foundation
|
||||||
|
*
|
||||||
|
* Portions of this software were developed by Aymeric Wibo
|
||||||
|
* <obiwac@freebsd.org> under sponsorship from the FreeBSD Foundation.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions
|
* modification, are permitted provided that the following conditions
|
||||||
@@ -181,7 +185,8 @@ static const char *acpi_sstate2sname(int sstate);
|
|||||||
static int acpi_supported_sleep_state_sysctl(SYSCTL_HANDLER_ARGS);
|
static int acpi_supported_sleep_state_sysctl(SYSCTL_HANDLER_ARGS);
|
||||||
static int acpi_sleep_state_sysctl(SYSCTL_HANDLER_ARGS);
|
static int acpi_sleep_state_sysctl(SYSCTL_HANDLER_ARGS);
|
||||||
static int acpi_debug_objects_sysctl(SYSCTL_HANDLER_ARGS);
|
static int acpi_debug_objects_sysctl(SYSCTL_HANDLER_ARGS);
|
||||||
static int acpi_pm_func(u_long cmd, void *arg, ...);
|
static int acpi_stype_to_sstate(struct acpi_softc *sc, enum power_stype stype);
|
||||||
|
static int acpi_pm_func(u_long cmd, void *arg, enum power_stype stype);
|
||||||
static void acpi_enable_pcie(void);
|
static void acpi_enable_pcie(void);
|
||||||
static void acpi_reset_interfaces(device_t dev);
|
static void acpi_reset_interfaces(device_t dev);
|
||||||
|
|
||||||
@@ -741,6 +746,28 @@ acpi_attach(device_t dev)
|
|||||||
return_VALUE (error);
|
return_VALUE (error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
acpi_stype_to_sstate(struct acpi_softc *sc, enum power_stype stype)
|
||||||
|
{
|
||||||
|
switch (stype) {
|
||||||
|
case POWER_STYPE_AWAKE:
|
||||||
|
return (ACPI_STATE_S0);
|
||||||
|
case POWER_STYPE_STANDBY:
|
||||||
|
return (sc->acpi_standby_sx);
|
||||||
|
case POWER_STYPE_SUSPEND_TO_MEM:
|
||||||
|
return (ACPI_STATE_S3);
|
||||||
|
case POWER_STYPE_HIBERNATE:
|
||||||
|
return (ACPI_STATE_S4);
|
||||||
|
case POWER_STYPE_POWEROFF:
|
||||||
|
return (ACPI_STATE_S5);
|
||||||
|
case POWER_STYPE_SUSPEND_TO_IDLE:
|
||||||
|
case POWER_STYPE_COUNT:
|
||||||
|
case POWER_STYPE_UNKNOWN:
|
||||||
|
return (ACPI_STATE_UNKNOWN);
|
||||||
|
}
|
||||||
|
return (ACPI_STATE_UNKNOWN);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
acpi_set_power_children(device_t dev, int state)
|
acpi_set_power_children(device_t dev, int state)
|
||||||
{
|
{
|
||||||
@@ -4621,12 +4648,10 @@ acpi_reset_interfaces(device_t dev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
acpi_pm_func(u_long cmd, void *arg, ...)
|
acpi_pm_func(u_long cmd, void *arg, enum power_stype stype)
|
||||||
{
|
{
|
||||||
int state, acpi_state;
|
int error, sstate;
|
||||||
int error;
|
|
||||||
struct acpi_softc *sc;
|
struct acpi_softc *sc;
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
error = 0;
|
error = 0;
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
@@ -4636,27 +4661,8 @@ acpi_pm_func(u_long cmd, void *arg, ...)
|
|||||||
error = EINVAL;
|
error = EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
sstate = acpi_stype_to_sstate(sc, stype);
|
||||||
va_start(ap, arg);
|
if (ACPI_FAILURE(acpi_EnterSleepState(sc, sstate)))
|
||||||
state = va_arg(ap, int);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
switch (state) {
|
|
||||||
case POWER_SLEEP_STATE_STANDBY:
|
|
||||||
acpi_state = sc->acpi_standby_sx;
|
|
||||||
break;
|
|
||||||
case POWER_SLEEP_STATE_SUSPEND:
|
|
||||||
acpi_state = sc->acpi_suspend_sx;
|
|
||||||
break;
|
|
||||||
case POWER_SLEEP_STATE_HIBERNATE:
|
|
||||||
acpi_state = ACPI_STATE_S4;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
error = EINVAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ACPI_FAILURE(acpi_EnterSleepState(sc, acpi_state)))
|
|
||||||
error = ENXIO;
|
error = ENXIO;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -40,6 +40,7 @@
|
|||||||
#include <sys/ktr.h>
|
#include <sys/ktr.h>
|
||||||
#include <sys/lock.h>
|
#include <sys/lock.h>
|
||||||
#include <sys/mutex.h>
|
#include <sys/mutex.h>
|
||||||
|
#include <sys/power.h>
|
||||||
#include <sys/selinfo.h>
|
#include <sys/selinfo.h>
|
||||||
#include <sys/sx.h>
|
#include <sys/sx.h>
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
|
|||||||
+86
-6
@@ -3,6 +3,10 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2001 Mitsuru IWASAKI
|
* Copyright (c) 2001 Mitsuru IWASAKI
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
|
* Copyright (c) 2025 The FreeBSD Foundation
|
||||||
|
*
|
||||||
|
* Portions of this software were developed by Aymeric Wibo
|
||||||
|
* <obiwac@freebsd.org> under sponsorship from the FreeBSD Foundation.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions
|
* modification, are permitted provided that the following conditions
|
||||||
@@ -30,20 +34,83 @@
|
|||||||
#include <sys/eventhandler.h>
|
#include <sys/eventhandler.h>
|
||||||
#include <sys/power.h>
|
#include <sys/power.h>
|
||||||
#include <sys/proc.h>
|
#include <sys/proc.h>
|
||||||
|
#include <sys/sbuf.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
#include <sys/systm.h>
|
#include <sys/systm.h>
|
||||||
#include <sys/taskqueue.h>
|
#include <sys/taskqueue.h>
|
||||||
|
|
||||||
|
enum power_stype power_standby_stype = POWER_STYPE_STANDBY;
|
||||||
|
enum power_stype power_suspend_stype = POWER_STYPE_SUSPEND_TO_IDLE;
|
||||||
|
enum power_stype power_hibernate_stype = POWER_STYPE_HIBERNATE;
|
||||||
|
|
||||||
static u_int power_pm_type = POWER_PM_TYPE_NONE;
|
static u_int power_pm_type = POWER_PM_TYPE_NONE;
|
||||||
static power_pm_fn_t power_pm_fn = NULL;
|
static power_pm_fn_t power_pm_fn = NULL;
|
||||||
static void *power_pm_arg = NULL;
|
static void *power_pm_arg = NULL;
|
||||||
static struct task power_pm_task;
|
static struct task power_pm_task;
|
||||||
|
|
||||||
|
enum power_stype
|
||||||
|
power_name_to_stype(const char *name)
|
||||||
|
{
|
||||||
|
enum power_stype stype;
|
||||||
|
|
||||||
|
for (stype = 0; stype < POWER_STYPE_COUNT; stype++) {
|
||||||
|
if (strcasecmp(name, power_stype_names[stype]) == 0)
|
||||||
|
return (stype);
|
||||||
|
}
|
||||||
|
return (POWER_STYPE_UNKNOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
power_stype_to_name(enum power_stype stype)
|
||||||
|
{
|
||||||
|
if (stype == POWER_STYPE_UNKNOWN)
|
||||||
|
return ("NONE");
|
||||||
|
if (stype < POWER_STYPE_AWAKE || stype >= POWER_STYPE_COUNT)
|
||||||
|
return (NULL);
|
||||||
|
return (power_stype_names[stype]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
power_sysctl_stype(SYSCTL_HANDLER_ARGS)
|
||||||
|
{
|
||||||
|
char name[10];
|
||||||
|
int err;
|
||||||
|
enum power_stype new_stype, old_stype;
|
||||||
|
|
||||||
|
old_stype = *(enum power_stype *)oidp->oid_arg1;
|
||||||
|
strlcpy(name, power_stype_to_name(old_stype), sizeof(name));
|
||||||
|
err = sysctl_handle_string(oidp, name, sizeof(name), req);
|
||||||
|
if (err != 0 || req->newptr == NULL)
|
||||||
|
return (err);
|
||||||
|
|
||||||
|
new_stype = power_name_to_stype(name);
|
||||||
|
if (new_stype == POWER_STYPE_UNKNOWN)
|
||||||
|
return (EINVAL);
|
||||||
|
/* TODO Check to see if the new stype is supported. */
|
||||||
|
if (new_stype != old_stype)
|
||||||
|
*(enum power_stype *)oidp->oid_arg1 = new_stype;
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SYSCTL_NODE(_kern, OID_AUTO, power, CTLFLAG_RW, 0,
|
||||||
|
"Generic power management related sysctls");
|
||||||
|
|
||||||
|
SYSCTL_PROC(_kern_power, OID_AUTO, standby, CTLTYPE_STRING | CTLFLAG_RW,
|
||||||
|
&power_standby_stype, 0, power_sysctl_stype, "A",
|
||||||
|
"Sleep type to enter on standby");
|
||||||
|
SYSCTL_PROC(_kern_power, OID_AUTO, suspend, CTLTYPE_STRING | CTLFLAG_RW,
|
||||||
|
&power_suspend_stype, 0, power_sysctl_stype, "A",
|
||||||
|
"Sleep type to enter on suspend");
|
||||||
|
SYSCTL_PROC(_kern_power, OID_AUTO, hibernate, CTLTYPE_STRING | CTLFLAG_RW,
|
||||||
|
&power_hibernate_stype, 0, power_sysctl_stype, "A",
|
||||||
|
"Sleep type to enter on hibernate");
|
||||||
|
|
||||||
static void
|
static void
|
||||||
power_pm_deferred_fn(void *arg, int pending)
|
power_pm_deferred_fn(void *arg, int pending)
|
||||||
{
|
{
|
||||||
int state = (intptr_t)arg;
|
enum power_stype stype = (intptr_t)arg;
|
||||||
|
|
||||||
power_pm_fn(POWER_CMD_SUSPEND, power_pm_arg, state);
|
power_pm_fn(POWER_CMD_SUSPEND, power_pm_arg, stype);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
@@ -75,14 +142,27 @@ power_pm_get_type(void)
|
|||||||
void
|
void
|
||||||
power_pm_suspend(int state)
|
power_pm_suspend(int state)
|
||||||
{
|
{
|
||||||
|
enum power_stype stype;
|
||||||
|
|
||||||
if (power_pm_fn == NULL)
|
if (power_pm_fn == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (state != POWER_SLEEP_STATE_STANDBY &&
|
switch (state) {
|
||||||
state != POWER_SLEEP_STATE_SUSPEND &&
|
case POWER_SLEEP_STATE_STANDBY:
|
||||||
state != POWER_SLEEP_STATE_HIBERNATE)
|
stype = power_standby_stype;
|
||||||
|
break;
|
||||||
|
case POWER_SLEEP_STATE_SUSPEND:
|
||||||
|
stype = power_suspend_stype;
|
||||||
|
break;
|
||||||
|
case POWER_SLEEP_STATE_HIBERNATE:
|
||||||
|
stype = power_hibernate_stype;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("%s: unknown sleep state %d\n", __func__, state);
|
||||||
return;
|
return;
|
||||||
power_pm_task.ta_context = (void *)(intptr_t)state;
|
}
|
||||||
|
|
||||||
|
power_pm_task.ta_context = (void *)(intptr_t)stype;
|
||||||
taskqueue_enqueue(taskqueue_thread, &power_pm_task);
|
taskqueue_enqueue(taskqueue_thread, &power_pm_task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+49
-3
@@ -3,6 +3,10 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2001 Mitsuru IWASAKI
|
* Copyright (c) 2001 Mitsuru IWASAKI
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
|
* Copyright (c) 2025 The FreeBSD Foundation
|
||||||
|
*
|
||||||
|
* Portions of this software were developed by Aymeric Wibo
|
||||||
|
* <obiwac@freebsd.org> under sponsorship from the FreeBSD Foundation.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions
|
* modification, are permitted provided that the following conditions
|
||||||
@@ -31,6 +35,7 @@
|
|||||||
#ifdef _KERNEL
|
#ifdef _KERNEL
|
||||||
|
|
||||||
#include <sys/_eventhandler.h>
|
#include <sys/_eventhandler.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
/* Power management system type */
|
/* Power management system type */
|
||||||
#define POWER_PM_TYPE_ACPI 0x01
|
#define POWER_PM_TYPE_ACPI 0x01
|
||||||
@@ -39,13 +44,54 @@
|
|||||||
/* Commands for Power management function */
|
/* Commands for Power management function */
|
||||||
#define POWER_CMD_SUSPEND 0x00
|
#define POWER_CMD_SUSPEND 0x00
|
||||||
|
|
||||||
/* Sleep state */
|
/*
|
||||||
|
* Sleep state.
|
||||||
|
*
|
||||||
|
* These are high-level sleep states that the system can enter. They map to
|
||||||
|
* a specific generic sleep type (enum power_stype).
|
||||||
|
*/
|
||||||
#define POWER_SLEEP_STATE_STANDBY 0x00
|
#define POWER_SLEEP_STATE_STANDBY 0x00
|
||||||
#define POWER_SLEEP_STATE_SUSPEND 0x01
|
#define POWER_SLEEP_STATE_SUSPEND 0x01
|
||||||
#define POWER_SLEEP_STATE_HIBERNATE 0x02
|
#define POWER_SLEEP_STATE_HIBERNATE 0x02
|
||||||
|
|
||||||
typedef int (*power_pm_fn_t)(u_long, void*, ...);
|
/*
|
||||||
extern int power_pm_register(u_int, power_pm_fn_t, void *);
|
* Sleep type.
|
||||||
|
*
|
||||||
|
* These are the specific generic methods of entering a sleep state. E.g.
|
||||||
|
* POWER_SLEEP_STATE_SUSPEND could be set to enter either suspend-to-RAM (which
|
||||||
|
* is S3 on ACPI systems), or suspend-to-idle (S0ix on ACPI systems). This
|
||||||
|
* would be done through the kern.power.suspend sysctl.
|
||||||
|
*/
|
||||||
|
enum power_stype {
|
||||||
|
POWER_STYPE_AWAKE,
|
||||||
|
POWER_STYPE_STANDBY,
|
||||||
|
POWER_STYPE_SUSPEND_TO_MEM,
|
||||||
|
POWER_STYPE_SUSPEND_TO_IDLE,
|
||||||
|
POWER_STYPE_HIBERNATE,
|
||||||
|
POWER_STYPE_POWEROFF,
|
||||||
|
POWER_STYPE_COUNT,
|
||||||
|
POWER_STYPE_UNKNOWN,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char * const power_stype_names[POWER_STYPE_COUNT] = {
|
||||||
|
[POWER_STYPE_AWAKE] = "awake",
|
||||||
|
[POWER_STYPE_STANDBY] = "standby",
|
||||||
|
[POWER_STYPE_SUSPEND_TO_MEM] = "s2mem",
|
||||||
|
[POWER_STYPE_SUSPEND_TO_IDLE] = "s2idle",
|
||||||
|
[POWER_STYPE_HIBERNATE] = "hibernate",
|
||||||
|
[POWER_STYPE_POWEROFF] = "poweroff",
|
||||||
|
};
|
||||||
|
|
||||||
|
extern enum power_stype power_standby_stype;
|
||||||
|
extern enum power_stype power_suspend_stype;
|
||||||
|
extern enum power_stype power_hibernate_stype;
|
||||||
|
|
||||||
|
extern enum power_stype power_name_to_stype(const char *_name);
|
||||||
|
extern const char *power_stype_to_name(enum power_stype _stype);
|
||||||
|
|
||||||
|
typedef int (*power_pm_fn_t)(u_long _cmd, void* _arg, enum power_stype _stype);
|
||||||
|
extern int power_pm_register(u_int _pm_type, power_pm_fn_t _pm_fn,
|
||||||
|
void *_pm_arg);
|
||||||
extern u_int power_pm_get_type(void);
|
extern u_int power_pm_get_type(void);
|
||||||
extern void power_pm_suspend(int);
|
extern void power_pm_suspend(int);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user