From f1842a13d838c3a94044961bbf70910f54a49956 Mon Sep 17 00:00:00 2001 From: iwasaki Date: Thu, 1 Nov 2001 16:34:07 +0000 Subject: Some fix for the recent apm module changes. - Now that apm loadable module can inform its existence to other kernel components (e.g. i386/isa/clock.c:startrtclock()'s TCS hack). - Exchange priority of SI_SUB_CPU and SI_SUB_KLD for above purpose. - Add simple arbitration mechanism for APM vs. ACPI. This prevents the kernel enables both of them. - Remove obsolete `#ifdef DEV_APM' related code. - Add abstracted interface for Powermanagement operations. Public apm(4) functions, such as apm_suspend(), should be replaced new interfaces. Currently only power_pm_suspend (successor of apm_suspend) is implemented. Reviewed by: peter, arch@ and audit@ --- sys/i386/apm/apm.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++-- sys/i386/bios/apm.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++-- sys/i386/i386/tsc.c | 11 +++--- sys/i386/isa/clock.c | 11 +++--- 4 files changed, 200 insertions(+), 18 deletions(-) (limited to 'sys/i386') diff --git a/sys/i386/apm/apm.c b/sys/i386/apm/apm.c index 1b7cbc5..85d4bd2 100644 --- a/sys/i386/apm/apm.c +++ b/sys/i386/apm/apm.c @@ -32,9 +32,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -53,6 +55,8 @@ static void apm_resume __P((void)); 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,30 @@ apm_not_halt_cpu(void) /* 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 +777,11 @@ apm_identify(driver_t *driver, device_t parent) { 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 +810,12 @@ apm_probe(device_t dev) 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 +1077,13 @@ apm_attach(device_t dev) 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 +1357,56 @@ static driver_t apm_driver = { 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); diff --git a/sys/i386/bios/apm.c b/sys/i386/bios/apm.c index 1b7cbc5..85d4bd2 100644 --- a/sys/i386/bios/apm.c +++ b/sys/i386/bios/apm.c @@ -32,9 +32,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -53,6 +55,8 @@ static void apm_resume __P((void)); 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,30 @@ apm_not_halt_cpu(void) /* 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 +777,11 @@ apm_identify(driver_t *driver, device_t parent) { 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 +810,12 @@ apm_probe(device_t dev) 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 +1077,13 @@ apm_attach(device_t dev) 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 +1357,56 @@ static driver_t apm_driver = { 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); diff --git a/sys/i386/i386/tsc.c b/sys/i386/i386/tsc.c index 13e11a4..a7aaea0 100644 --- a/sys/i386/i386/tsc.c +++ b/sys/i386/i386/tsc.c @@ -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 @@ startrtclock() * 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,11 @@ startrtclock() * 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) + 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; diff --git a/sys/i386/isa/clock.c b/sys/i386/isa/clock.c index 13e11a4..a7aaea0 100644 --- a/sys/i386/isa/clock.c +++ b/sys/i386/isa/clock.c @@ -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 @@ startrtclock() * 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,11 @@ startrtclock() * 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) + 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; -- cgit v1.1