Index: sys/dev/acpica/acpi_thermal.c diff -u -p sys/dev/acpica/acpi_thermal.c.orig sys/dev/acpica/acpi_thermal.c --- sys/dev/acpica/acpi_thermal.c.orig Tue Feb 22 09:40:13 2005 +++ sys/dev/acpica/acpi_thermal.c Wed Jul 13 21:21:18 2005 @@ -32,6 +32,7 @@ __FBSDID("$FreeBSD: src/sys/dev/acpica/a #include #include #include +#include #include #include #include @@ -42,6 +43,8 @@ __FBSDID("$FreeBSD: src/sys/dev/acpica/a #include #include +#include "cpufreq_if.h" + #include "acpi.h" #include @@ -106,8 +109,16 @@ struct acpi_tz_softc { struct acpi_tz_zone tz_zone; /*Thermal zone parameters*/ int tz_validchecks; + + /* passive cooling */ + struct proc *tz_cooling_proc; + int tz_cooling_enabled; + int tz_cooling_active; + int tz_cooling_updated; }; +#define CPUFREQ_MAX_LEVELS 64 + static int acpi_tz_probe(device_t dev); static int acpi_tz_attach(device_t dev); static int acpi_tz_establish(struct acpi_tz_softc *sc); @@ -118,12 +129,15 @@ static void acpi_tz_getparam(struct acpi int *data); static void acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what); static int acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS); +static int acpi_tz_cooling_sysctl(SYSCTL_HANDLER_ARGS); static void acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context); static void acpi_tz_signal(struct acpi_tz_softc *sc, int flags); static void acpi_tz_timeout(struct acpi_tz_softc *sc, int flags); static void acpi_tz_power_profile(void *arg); static void acpi_tz_thread(void *arg); +static int acpi_tz_cooling_is_available(struct acpi_tz_softc *); +static int acpi_tz_cooling_thread_start(struct acpi_tz_softc *); static device_method_t acpi_tz_methods[] = { /* Device interface */ @@ -183,6 +197,10 @@ acpi_tz_attach(device_t dev) sc->tz_requested = TZ_ACTIVE_NONE; sc->tz_active = TZ_ACTIVE_NONE; sc->tz_thflags = TZ_THFLAG_NONE; + sc->tz_cooling_proc = NULL; + sc->tz_cooling_enabled = (device_get_unit(dev) == 0); /* XXX: */ + sc->tz_cooling_active = 0; + sc->tz_cooling_updated = 0; /* * Parse the current state of the thermal zone and build control @@ -231,7 +249,10 @@ acpi_tz_attach(device_t dev) SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), OID_AUTO, "active", CTLTYPE_INT | CTLFLAG_RW, sc, 0, acpi_tz_active_sysctl, "I", ""); - + SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), + OID_AUTO, "passive_cooling", CTLTYPE_INT | CTLFLAG_RW, + sc, 0, acpi_tz_cooling_sysctl, "I", ""); + SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), OID_AUTO, "thermal_flags", CTLFLAG_RD, &sc->tz_thflags, 0, "thermal zone flags"); @@ -263,6 +284,15 @@ acpi_tz_attach(device_t dev) } } + if (sc->tz_cooling_enabled) { + if (!acpi_tz_cooling_is_available(sc)) + sc->tz_cooling_enabled = 0; + else if ((error = acpi_tz_cooling_thread_start(sc)) != 0) { + sc->tz_cooling_enabled = 0; + goto out; + } + } + /* * Flag the event handler for a manual invocation by our timeout. * We defer it like this so that the rest of the subsystem has time @@ -602,6 +632,34 @@ acpi_tz_active_sysctl(SYSCTL_HANDLER_ARG return (0); } +static int +acpi_tz_cooling_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct acpi_tz_softc *sc; + int enabled, error; + + sc = (struct acpi_tz_softc *)oidp->oid_arg1; + enabled = sc->tz_cooling_enabled; + error = sysctl_handle_int(oidp, &enabled, 0, req); + + /* Error or no new value */ + if (error != 0 || req->newptr == NULL) + return (error); + if (enabled != 0 && enabled != 1) + return (EINVAL); + + if (enabled) { + if (!acpi_tz_cooling_is_available(sc)) + error = ENODEV; + else + error = acpi_tz_cooling_thread_start(sc); + if (error) + enabled = 0; + } + sc->tz_cooling_enabled = enabled; + return (error); +} + static void acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context) { @@ -767,4 +825,151 @@ acpi_tz_thread(void *arg) else ACPI_UNLOCK(thermal); } +} + +static int +acpi_tz_cpufreq_restore(struct acpi_tz_softc *sc) +{ + device_t dev; + int error; + + if (!sc->tz_cooling_updated) + return (0); + if ((dev = devclass_get_device(devclass_find("cpufreq"), 0)) == NULL) + return (ENXIO); + ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev), + "temperature %d.%dC: resuming clock speed\n", + TZ_KELVTOC(sc->tz_temperature)); + error = CPUFREQ_SET(dev, NULL, CPUFREQ_PRIO_KERN); + if (error == 0) + sc->tz_cooling_updated = 0; + return (error); +} + +static int +acpi_tz_cpufreq_update(struct acpi_tz_softc *sc, int req) +{ + device_t dev; + struct cf_level *levels; + int num_levels, error, freq, desired_freq, perf, i; + + levels = malloc(CPUFREQ_MAX_LEVELS * sizeof(*levels), M_TEMP, M_NOWAIT); + if (levels == NULL) + return (ENOMEM); + + if ((dev = devclass_get_device(devclass_find("cpufreq"), 0)) == NULL) { + error = ENXIO; + goto out; + } + + error = CPUFREQ_GET(dev, &levels[0]); + if (error) + goto out; + freq = levels[0].total_set.freq; + + num_levels = CPUFREQ_MAX_LEVELS; + error = CPUFREQ_LEVELS(dev, levels, &num_levels); + if (error) { + if (error == E2BIG) + printf("cpufreq: need to increase CPUFREQ_MAX_LEVELS\n"); + goto out; + } + + perf = 100 * freq / levels[0].total_set.freq - req; + if (perf < 0) + perf = 0; + else if (perf > 100) + perf = 100; + desired_freq = levels[0].total_set.freq * perf / 100; + + for (i = 0; i < num_levels; i++) + if (levels[i].total_set.freq <= desired_freq) + break; + if (i == num_levels) + i--; + if (i == 0) { + error = acpi_tz_cpufreq_restore(sc); + sc->tz_cooling_active = 0; + goto out; + } + if (levels[i].total_set.freq != freq) { + ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev), + "temperature %d.%dC: %screasing clock speed " + "from %d MHz to %d MHz\n", + TZ_KELVTOC(sc->tz_temperature), + (freq > levels[i].total_set.freq) ? "de" : "in", + freq, levels[i].total_set.freq); + error = CPUFREQ_SET(dev, &levels[i], CPUFREQ_PRIO_KERN); + if (error == 0) + sc->tz_cooling_updated = 1; + } + +out: + if (levels) + free(levels, M_TEMP); + return (error); +} + +/* + * Passive cooling thread. + */ +static void +acpi_tz_cooling_thread(void *arg) +{ + struct acpi_tz_softc *sc; + int temperature, perf; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + + sc = (struct acpi_tz_softc *)arg; + + temperature = sc->tz_temperature; + while (sc->tz_cooling_enabled) { + if (sc->tz_temperature >= sc->tz_zone.psv) + sc->tz_cooling_active = 1; + if (sc->tz_cooling_active) { + perf = sc->tz_zone.tc1 * (sc->tz_temperature - temperature) + + sc->tz_zone.tc2 * (sc->tz_temperature - sc->tz_zone.psv); + perf /= 10; + if (perf != 0) + acpi_tz_cpufreq_update(sc, perf); /* XXX: _PSL? */ + } + temperature = sc->tz_temperature; + tsleep(&sc->tz_cooling_proc, PZERO, "cooling", + hz * sc->tz_zone.tsp / 10); + } + if (sc->tz_cooling_active) { + acpi_tz_cpufreq_restore(sc); + sc->tz_cooling_active = 0; + } + ACPI_LOCK(thermal); + sc->tz_cooling_proc = NULL; + ACPI_UNLOCK(thermal); + kthread_exit(0); +} + +static int +acpi_tz_cooling_is_available(struct acpi_tz_softc *sc) +{ + return (sc->tz_zone.tc1 != -1 && sc->tz_zone.tc2 != -1 && + sc->tz_zone.tsp != -1 && sc->tz_zone.psv != -1); +} + +static int +acpi_tz_cooling_thread_start(struct acpi_tz_softc *sc) +{ + int error = 0; + char name[20]; + + ACPI_LOCK(thermal); + if (sc->tz_cooling_proc == NULL) { + snprintf(name, sizeof(name), "acpi_cooling%d", + device_get_unit(sc->tz_dev)); + error = kthread_create(acpi_tz_cooling_thread, sc, + &sc->tz_cooling_proc, RFHIGHPID, 0, name); + if (error != 0) + device_printf(sc->tz_dev, "could not create thread - %d", error); + } + ACPI_UNLOCK(thermal); + return (0); } Index: usr.sbin/powerd/powerd.c diff -u -p usr.sbin/powerd/powerd.c.orig usr.sbin/powerd/powerd.c --- usr.sbin/powerd/powerd.c.orig Mon Apr 11 05:42:55 2005 +++ usr.sbin/powerd/powerd.c Wed Jul 13 21:14:47 2005 @@ -29,6 +29,7 @@ __FBSDID("$FreeBSD: src/usr.sbin/powerd/powerd.c,v 1.6 2005/04/10 20:42:55 njl Exp $"); #include +#include #include #include #include @@ -167,8 +168,10 @@ static int set_freq(int freq) { - if (sysctl(freq_mib, 4, NULL, NULL, &freq, sizeof(freq))) - return (-1); + if (sysctl(freq_mib, 4, NULL, NULL, &freq, sizeof(freq))) { + if (errno != EPERM) + return (-1); + } return (0); }