Index: conf/files =================================================================== RCS file: /home/ncvs/src/sys/conf/files,v retrieving revision 1.576 diff -u -r1.576 files --- conf/files 20 Oct 2001 18:50:31 -0000 1.576 +++ conf/files 25 Oct 2001 14:23:02 -0000 @@ -814,6 +814,7 @@ kern/subr_module.c standard kern/subr_param.c standard kern/subr_pcpu.c standard +kern/subr_power.c standard kern/subr_prf.c standard kern/subr_prof.c standard kern/subr_rman.c standard Index: dev/acpica/acpi.c =================================================================== RCS file: /home/ncvs/src/sys/dev/acpica/acpi.c,v retrieving revision 1.42 diff -u -r1.42 acpi.c --- dev/acpica/acpi.c 4 Oct 2001 23:21:09 -0000 1.42 +++ dev/acpica/acpi.c 25 Oct 2001 14:23:02 -0000 @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -126,6 +127,8 @@ static void acpi_system_eventhandler_wakeup(void *arg, int state); static int acpi_sleep_state_sysctl(SYSCTL_HANDLER_ARGS); +static int acpi_pm_func(u_long cmd, void *arg, ...); + static device_method_t acpi_methods[] = { /* Device interface */ DEVMETHOD(device_identify, acpi_identify), @@ -185,7 +188,10 @@ return(EPERM); break; case MOD_UNLOAD: - return(EBUSY); + if ((!cold) && power_pm_get_type() == POWER_PM_TYPE_ACPI) { + return(EBUSY); + } + break; default: break; } @@ -268,6 +274,13 @@ int error; FUNCTION_TRACE(__func__); + + if (power_pm_get_type() != POWER_PM_TYPE_NONE && + power_pm_get_type() != POWER_PM_TYPE_ACPI) { + device_printf(dev, "Other PM system enabled.\n"); + return_VALUE(ENXIO); + } + ACPI_LOCK; if ((status = AcpiGetTableHeader(ACPI_TABLE_XSDT, 1, &th)) != AE_OK) { @@ -375,6 +388,12 @@ SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), OID_AUTO, "lid_switch_state", CTLTYPE_STRING | CTLFLAG_RW, &sc->acpi_lid_switch_sx, 0, acpi_sleep_state_sysctl, "A", ""); + SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), + OID_AUTO, "standby_state", CTLTYPE_STRING | CTLFLAG_RW, + &sc->acpi_standby_sx, 0, acpi_sleep_state_sysctl, "A", ""); + SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), + OID_AUTO, "suspend_state", CTLTYPE_STRING | CTLFLAG_RW, + &sc->acpi_suspend_sx, 0, acpi_sleep_state_sysctl, "A", ""); /* * Dispatch the default sleep state to devices. @@ -383,6 +402,8 @@ sc->acpi_power_button_sx = ACPI_POWER_BUTTON_DEFAULT_SX; sc->acpi_sleep_button_sx = ACPI_SLEEP_BUTTON_DEFAULT_SX; sc->acpi_lid_switch_sx = ACPI_LID_SWITCH_DEFAULT_SX; + sc->acpi_standby_sx = ACPI_STATE_S1; + sc->acpi_suspend_sx = ACPI_STATE_S3; acpi_enable_fixed_events(sc); @@ -425,6 +446,10 @@ if (debugpoint && !strcmp(debugpoint, "running")) acpi_EnterDebugger(); #endif + + /* Register ACPI again to pass the correct argument of pm_func. */ + power_pm_register(POWER_PM_TYPE_ACPI, acpi_pm_func, sc); + error = 0; out: @@ -1847,3 +1872,61 @@ } SYSINIT(acpi_debugging, SI_SUB_TUNABLES, SI_ORDER_ANY, acpi_set_debugging, NULL); #endif + +static int +acpi_pm_func(u_long cmd, void *arg, ...) +{ + int state, acpi_state; + int error; + struct acpi_softc *sc; + va_list ap; + + error = 0; + switch (cmd) { + case POWER_CMD_SUSPEND: + sc = (struct acpi_softc *)arg; + if (sc == NULL) { + error = EINVAL; + goto out; + } + + va_start(ap, arg); + 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; + } + + acpi_SetSleepState(sc, acpi_state); + break; + + default: + error = EINVAL; + goto out; + } + +out: + return (error); +} + +static void +acpi_pm_register(void *arg) +{ + + power_pm_register(POWER_PM_TYPE_ACPI, acpi_pm_func, NULL); +} + +SYSINIT(power, SI_SUB_KLD, SI_ORDER_ANY, acpi_pm_register, 0); + Index: dev/acpica/acpivar.h =================================================================== RCS file: /home/ncvs/src/sys/dev/acpica/acpivar.h,v retrieving revision 1.18 diff -u -r1.18 acpivar.h --- dev/acpica/acpivar.h 6 Oct 2001 04:09:26 -0000 1.18 +++ dev/acpica/acpivar.h 25 Oct 2001 14:23:02 -0000 @@ -57,6 +57,9 @@ int acpi_sleep_button_sx; int acpi_lid_switch_sx; + int acpi_standby_sx; + int acpi_suspend_sx; + bus_dma_tag_t acpi_waketag; bus_dmamap_t acpi_wakemap; vm_offset_t acpi_wakeaddr; Index: dev/syscons/syscons.c =================================================================== RCS file: /home/ncvs/src/sys/dev/syscons/syscons.c,v retrieving revision 1.376 diff -u -r1.376 syscons.c --- dev/syscons/syscons.c 24 Oct 2001 18:30:04 -0000 1.376 +++ dev/syscons/syscons.c 25 Oct 2001 14:24:04 -0000 @@ -31,9 +31,6 @@ #include "opt_syscons.h" #include "opt_splash.h" #include "opt_ddb.h" -#ifdef __i386__ -#include "opt_apm.h" -#endif #include #include @@ -53,6 +50,7 @@ #include #include #include +#include #include #include @@ -3230,18 +3228,12 @@ #endif break; -#ifdef DEV_APM case SUSP: - apm_suspend(PMST_SUSPEND); + power_pm_suspend(POWER_SLEEP_STATE_SUSPEND); break; case STBY: - apm_suspend(PMST_STANDBY); + power_pm_suspend(POWER_SLEEP_STATE_STANDBY); break; -#else - case SUSP: - case STBY: - break; -#endif case DBG: #ifndef SC_DISABLE_DDBKEY Index: i386/apm/apm.c =================================================================== RCS file: /home/ncvs/src/sys/i386/apm/apm.c,v retrieving revision 1.123 diff -u -r1.123 apm.c --- i386/apm/apm.c 12 Sep 2001 08:37:24 -0000 1.123 +++ i386/apm/apm.c 25 Oct 2001 14:24:04 -0000 @@ -32,9 +32,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -53,6 +55,8 @@ static int apm_bioscall(void); static int apm_check_function_supported __P((u_int version, u_int func)); +static int apm_pm_func(u_long, void*, ...); + static u_long apm_version; int apm_evindex; @@ -742,6 +746,32 @@ /* device driver definitions */ /* + * Module event + */ + +static int +apm_modevent(struct module *mod, int event, void *junk) +{ + + switch (event) { + case MOD_LOAD: + if (!cold) { + return (EPERM); + } + break; + case MOD_UNLOAD: + if ((!cold) && power_pm_get_type() == POWER_PM_TYPE_APM) { + return (EBUSY); + } + break; + default: + break; + } + + return (0); +} + +/* * Create "connection point" */ static void @@ -749,6 +779,11 @@ { device_t child; + if (!cold) { + printf("Don't load this driver from userland!!\n"); + return; + } + child = BUS_ADD_CHILD(parent, 0, "apm", 0); if (child == NULL) panic("apm_identify"); @@ -777,6 +812,12 @@ return ENXIO; } + if (power_pm_get_type() != POWER_PM_TYPE_NONE && + power_pm_get_type() != POWER_PM_TYPE_APM) { + printf("apm: Other PM system enabled.\n"); + return ENXIO; + } + if (resource_int_value("apm", 0, "flags", &flags) != 0) flags = 0; @@ -1038,10 +1079,13 @@ EVENTHANDLER_REGISTER(shutdown_final, apm_power_off, NULL, SHUTDOWN_PRI_LAST); + /* Register APM again to pass the correct argument of pm_func. */ + power_pm_register(POWER_PM_TYPE_APM, apm_pm_func, sc); + sc->initialized = 1; sc->suspending = 0; - make_dev(&apm_cdevsw, 0, 0, 5, 0660, "apm"); + make_dev(&apm_cdevsw, 0, 0, 5, 0664, "apm"); make_dev(&apm_cdevsw, 8, 0, 5, 0660, "apmctl"); return 0; } @@ -1315,4 +1359,57 @@ static devclass_t apm_devclass; -DRIVER_MODULE(apm, nexus, apm_driver, apm_devclass, 0, 0); +DRIVER_MODULE(apm, nexus, apm_driver, apm_devclass, apm_modevent, 0); +MODULE_VERSION(apm, 1); + +static int +apm_pm_func(u_long cmd, void *arg, ...) +{ + int state, apm_state; + int error; + va_list ap; + + error = 0; + switch (cmd) { + case POWER_CMD_SUSPEND: + va_start(ap, arg); + state = va_arg(ap, int); + va_end(ap); + + switch (state) { + case POWER_SLEEP_STATE_STANDBY: + apm_state = PMST_STANDBY; + break; + case POWER_SLEEP_STATE_SUSPEND: + case POWER_SLEEP_STATE_HIBERNATE: + apm_state = PMST_SUSPEND; + break; + default: + error = EINVAL; + goto out; + } + + apm_suspend(apm_state); + break; + + default: + error = EINVAL; + goto out; + } + +out: + return (error); +} + +static void +apm_pm_register(void *arg) +{ + int disabled = 0; + + resource_int_value("apm", 0, "disabled", &disabled); + if (disabled == 0) { + power_pm_register(POWER_PM_TYPE_APM, apm_pm_func, NULL); + } +} + +SYSINIT(power, SI_SUB_KLD, SI_ORDER_ANY, apm_pm_register, 0); Index: i386/isa/clock.c =================================================================== RCS file: /home/ncvs/src/sys/i386/isa/clock.c,v retrieving revision 1.177 diff -u -r1.177 clock.c --- i386/isa/clock.c 9 Oct 2001 16:06:28 -0000 1.177 +++ i386/isa/clock.c 25 Oct 2001 14:24:04 -0000 @@ -49,7 +49,6 @@ */ #include "opt_clock.h" -#include "opt_apm.h" #include "opt_mca.h" #include @@ -63,6 +62,7 @@ #include #include #include +#include #include #ifdef CLK_CALIBRATION_LOOP @@ -823,7 +823,6 @@ * Curse Intel for leaving the counter out of the I/O APIC. */ -#ifdef DEV_APM /* * We can not use the TSC if we support APM. Precise timekeeping * on an APM'ed machine is at best a fools pursuit, since @@ -834,13 +833,12 @@ * We don't know at this point whether APM is going to be used * or not, nor when it might be activated. Play it safe. */ - { - int disabled = 0; - resource_int_value("apm", 0, "disabled", &disabled); - if (disabled == 0) + if (power_pm_get_type() == POWER_PM_TYPE_APM) { + if (bootverbose || 1) { + printf("TSC initialization skipped: APM enabled.\n"); + } return; } -#endif /* DEV_APM */ if (tsc_present && tsc_freq != 0 && !tsc_is_broken) { tsc_timecounter.tc_frequency = tsc_freq; Index: sys/kernel.h =================================================================== RCS file: /home/ncvs/src/sys/sys/kernel.h,v retrieving revision 1.95 diff -u -r1.95 kernel.h --- sys/kernel.h 23 Oct 2001 22:35:25 -0000 1.95 +++ sys/kernel.h 25 Oct 2001 14:24:26 -0000 @@ -119,8 +119,8 @@ SI_SUB_WITNESS = 0x1A80000, /* witness initialization */ SI_SUB_LOCK = 0x1B00000, /* lockmgr locks */ SI_SUB_EVENTHANDLER = 0x1C00000, /* eventhandler init */ - SI_SUB_CPU = 0x2000000, /* CPU resource(s)*/ - SI_SUB_KLD = 0x2100000, /* KLD and module setup */ + SI_SUB_KLD = 0x2000000, /* KLD and module setup */ + SI_SUB_CPU = 0x2100000, /* CPU resource(s)*/ SI_SUB_INTRINSIC = 0x2200000, /* proc 0*/ SI_SUB_VM_CONF = 0x2300000, /* config VM, set limits*/ SI_SUB_RUN_QUEUE = 0x2400000, /* set up run queue*/ --- /dev/null Thu Oct 25 23:44:00 2001 +++ sys/power.h Thu Oct 25 23:24:26 2001 @@ -0,0 +1,51 @@ +/*- + * Copyright (c) 2001 Mitsuru IWASAKI + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _SYS_POWER_H_ +#define _SYS_POWER_H_ + +/* Power management system type */ +#define POWER_PM_TYPE_APM 0x00 +#define POWER_PM_TYPE_ACPI 0x01 +#define POWER_PM_TYPE_NONE 0xff + +/* Commands for Power management function */ +#define POWER_CMD_SUSPEND 0x00 + +/* Sleep state */ +#define POWER_SLEEP_STATE_STANDBY 0x00 +#define POWER_SLEEP_STATE_SUSPEND 0x01 +#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 *); +extern u_int power_pm_get_type(void); +extern void power_pm_suspend(int); + +#endif /* !_SYS_POWER_H_ */ + --- /dev/null Thu Oct 25 23:44:00 2001 +++ kern/subr_power.c Thu Oct 25 23:24:26 2001 @@ -0,0 +1,78 @@ +/*- + * Copyright (c) 2001 Mitsuru IWASAKI + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include + +#include + +static u_int power_pm_type = POWER_PM_TYPE_NONE; +static power_pm_fn_t power_pm_fn = NULL; +static void *power_pm_arg = NULL; + +int +power_pm_register(u_int pm_type, power_pm_fn_t pm_fn, void *pm_arg) +{ + int error; + + if (power_pm_type == POWER_PM_TYPE_NONE || + power_pm_type == pm_type) { + power_pm_type = pm_type; + power_pm_fn = pm_fn; + power_pm_arg = pm_arg; + error = 0; + } else { + error = ENXIO; + } + + return (error); +} + +u_int +power_pm_get_type(void) +{ + + return (power_pm_type); +} + +void +power_pm_suspend(int state) +{ + if (power_pm_fn == NULL) { + return; + } + + if (state != POWER_SLEEP_STATE_STANDBY && + state != POWER_SLEEP_STATE_SUSPEND && + state != POWER_SLEEP_STATE_HIBERNATE) { + return; + } + + power_pm_fn(POWER_CMD_SUSPEND, power_pm_arg, state); +} +