With the warming weather, I won't be able to keep running my T60 with the fan unplugged for much longer. The following diffs stop the fan (when plugged) from tormenting me every other minute, by allowing me to set the temperature thresholds for passive/active cooling.
This is my first patch and the first time I dived into the kernel; clue sticks/suggestions are more than welcome! $ sysctl machdep machdep.console_device=ttyC0 machdep.bios.diskinfo.128=bootdev = 0xa0010204, cylinders = 1024, heads = 16, sectors = 63 machdep.bios.cksumlen=1 machdep.allowaperture=2 machdep.cpuvendor=GenuineIntel machdep.cpuid=1782 machdep.cpufeature=-1074004993 machdep.kbdreset=0 machdep.xcrypt=0 machdep.lidsuspend=0 machdep.thinkpadfan_ctl=1 machdep.thinkpadfan_passive_trigger=45 machdep.thinkpadfan_active_trigger=70 Index: acpithinkpad.c =================================================================== RCS file: /cvs/src/sys/dev/acpi/acpithinkpad.c,v retrieving revision 1.25 diff -u -r1.25 acpithinkpad.c --- acpithinkpad.c 2 Jan 2011 04:56:57 -0000 1.25 +++ acpithinkpad.c 16 Apr 2011 04:38:27 -0000 @@ -74,13 +74,17 @@ #define THINKPAD_NSENSORS 9 #define THINKPAD_NTEMPSENSORS 8 +#define THINKPAD_ECOFFSET_FANCTL 0x2f #define THINKPAD_ECOFFSET_FANLO 0x84 #define THINKPAD_ECOFFSET_FANHI 0x85 +#define THINKPAD_FAN_STOP 0x00 +#define THINKPAD_FAN_AUTO 0x80 + struct acpithinkpad_softc { struct device sc_dev; - struct acpiec_softc *sc_ec; + struct acpiec_softc *sc_ec; struct acpi_softc *sc_acpi; struct aml_node *sc_devnode; @@ -106,6 +110,8 @@ void thinkpad_sensor_attach(struct acpithinkpad_softc *sc); void thinkpad_sensor_refresh(void *); +void thinkpad_update_fan_state(struct acpithinkpad_softc *, int); + struct cfattach acpithinkpad_ca = { sizeof(struct acpithinkpad_softc), thinkpad_match, thinkpad_attach }; @@ -116,6 +122,9 @@ const char *acpithinkpad_hids[] = { ACPI_DEV_THINKPAD, 0 }; +u_int8_t tpfan_state; +int tpfan_ctl, tpfan_passive_temp, tpfan_active_temp; + int thinkpad_match(struct device *parent, void *match, void *aux) { @@ -148,7 +157,7 @@ /* Add temperature probes */ strlcpy(sc->sc_sensdev.xname, DEVNAME(sc), sizeof(sc->sc_sensdev.xname)); - for (i=0; i<THINKPAD_NTEMPSENSORS; i++) { + for (i = 0; i < THINKPAD_NTEMPSENSORS; i++) { sc->sc_sens[i].type = SENSOR_TEMP; sensor_attach(&sc->sc_sensdev, &sc->sc_sens[i]); } @@ -165,23 +174,50 @@ { struct acpithinkpad_softc *sc = arg; u_int8_t lo, hi, i; + int max_tmp; int64_t tmp; char sname[5]; /* Refresh sensor readings */ - for (i=0; i<THINKPAD_NTEMPSENSORS; i++) { + max_tmp = 0; + for (i = 0; i < THINKPAD_NTEMPSENSORS; i++) { snprintf(sname, sizeof(sname), "TMP%d", i); aml_evalinteger(sc->sc_acpi, sc->sc_ec->sc_devnode, sname, 0, 0, &tmp); sc->sc_sens[i].value = (tmp * 1000000) + 273150000; if (tmp > 127 || tmp < -127) sc->sc_sens[i].flags = SENSOR_FINVALID; + else if (tmp > max_tmp) + max_tmp = tmp; } /* Read fan RPM */ acpiec_read(sc->sc_ec, THINKPAD_ECOFFSET_FANLO, 1, &lo); acpiec_read(sc->sc_ec, THINKPAD_ECOFFSET_FANHI, 1, &hi); sc->sc_sens[i].value = ((hi << 8L) + lo); + + /* update fan state */ + if (tpfan_ctl || (!tpfan_ctl && tpfan_state != THINKPAD_FAN_AUTO)) + thinkpad_update_fan_state(sc, max_tmp); +} + +void +thinkpad_update_fan_state(struct acpithinkpad_softc *sc, int temp) +{ + u_int8_t new_state; + + if (temp >= tpfan_active_temp || !tpfan_ctl) + new_state = THINKPAD_FAN_AUTO; + else if (temp <= tpfan_passive_temp && tpfan_ctl) + new_state = THINKPAD_FAN_STOP; + else + return; + + if (new_state != tpfan_state) { + tpfan_state = new_state; + acpiec_write(sc->sc_ec, THINKPAD_ECOFFSET_FANCTL, 1, + &new_state); + } } void @@ -202,6 +238,10 @@ /* Run thinkpad_hotkey on button presses */ aml_register_notify(sc->sc_devnode, aa->aaa_dev, thinkpad_hotkey, sc, ACPIDEV_POLL); + + /* initial fan state */ + tpfan_state = THINKPAD_FAN_AUTO; + tpfan_ctl = tpfan_passive_temp = tpfan_active_temp = 0; } int @@ -236,8 +276,7 @@ bzero(&arg, sizeof(arg)); arg.type = AML_OBJTYPE_INTEGER; arg.v_integer = 1; - if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "MHKC", - 1, &arg, NULL)) { + if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "MHKC", 1, &arg, NULL)) { printf("%s: couldn't enable hotkeys\n", DEVNAME(sc)); return (1); } @@ -283,7 +322,8 @@ break; case THINKPAD_BUTTON_SUSPEND: #ifndef SMALL_KERNEL - if (acpi_record_event(sc->sc_acpi, APM_USER_SUSPEND_REQ)) + if (acpi_record_event(sc->sc_acpi, + APM_USER_SUSPEND_REQ)) acpi_addtask(sc->sc_acpi, acpi_sleep_task, sc->sc_acpi, ACPI_STATE_S3); #endif @@ -357,8 +397,7 @@ bzero(&arg, sizeof(arg)); arg.type = AML_OBJTYPE_INTEGER; arg.v_integer = bluetooth ^ THINKPAD_BLUETOOTH_ENABLED; - if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SBDC", - 1, &arg, NULL)) { + if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SBDC", 1, &arg, NULL)) { printf("%s: couldn't toggle bluetooth\n", DEVNAME(sc)); return (1); } @@ -372,8 +411,7 @@ struct aml_value arg; int64_t wan; - if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GWAN", - 0, NULL, &wan)) + if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GWAN", 0, NULL, &wan)) return (1); if (!(wan & THINKPAD_WAN_PRESENT)) @@ -382,8 +420,7 @@ bzero(&arg, sizeof(arg)); arg.type = AML_OBJTYPE_INTEGER; arg.v_integer = wan ^ THINKPAD_WAN_ENABLED; - if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SWAN", - 1, &arg, NULL)) { + if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SWAN", 1, &arg, NULL)) { printf("%s: couldn't toggle wan\n", DEVNAME(sc)); return (1); } Index: cpu.h =================================================================== RCS file: /cvs/src/sys/arch/amd64/include/cpu.h,v retrieving revision 1.66 diff -u -r1.66 cpu.h --- cpu.h 13 Apr 2011 02:49:12 -0000 1.66 +++ cpu.h 16 Apr 2011 04:39:54 -0000 @@ -344,23 +344,29 @@ #define CPU_APMHALT 11 /* halt -p hack */ #define CPU_XCRYPT 12 /* supports VIA xcrypt in userland */ #define CPU_LIDSUSPEND 13 /* lid close causes a suspend */ -#define CPU_MAXID 14 /* number of valid machdep ids */ +#define CPU_THINKPADFAN_CTL 14 /* thinkpad fan control */ +#define CPU_THINKPADFAN_PASSIVE 15 /* passive/active cooling */ +#define CPU_THINKPADFAN_ACTIVE 16 /* trigger temperature */ +#define CPU_MAXID 17 /* number of valid machdep ids */ #define CTL_MACHDEP_NAMES { \ - { 0, 0 }, \ - { "console_device", CTLTYPE_STRUCT }, \ - { "bios", CTLTYPE_INT }, \ - { "blk2chr", CTLTYPE_STRUCT }, \ - { "chr2blk", CTLTYPE_STRUCT }, \ - { "allowaperture", CTLTYPE_INT }, \ - { "cpuvendor", CTLTYPE_STRING }, \ - { "cpuid", CTLTYPE_INT }, \ - { "cpufeature", CTLTYPE_INT }, \ - { "apmwarn", CTLTYPE_INT }, \ - { "kbdreset", CTLTYPE_INT }, \ - { "apmhalt", CTLTYPE_INT }, \ - { "xcrypt", CTLTYPE_INT }, \ - { "lidsuspend", CTLTYPE_INT }, \ + { 0, 0 }, \ + { "console_device", CTLTYPE_STRUCT }, \ + { "bios", CTLTYPE_INT }, \ + { "blk2chr", CTLTYPE_STRUCT }, \ + { "chr2blk", CTLTYPE_STRUCT }, \ + { "allowaperture", CTLTYPE_INT }, \ + { "cpuvendor", CTLTYPE_STRING }, \ + { "cpuid", CTLTYPE_INT }, \ + { "cpufeature", CTLTYPE_INT }, \ + { "apmwarn", CTLTYPE_INT }, \ + { "kbdreset", CTLTYPE_INT }, \ + { "apmhalt", CTLTYPE_INT }, \ + { "xcrypt", CTLTYPE_INT }, \ + { "lidsuspend", CTLTYPE_INT }, \ + { "thinkpadfan_ctl", CTLTYPE_INT }, \ + { "thinkpadfan_passive_trigger", CTLTYPE_INT }, \ + { "thinkpadfan_active_trigger", CTLTYPE_INT }, \ } /* Index: machdep.c =================================================================== RCS file: /cvs/src/sys/arch/amd64/amd64/machdep.c,v retrieving revision 1.137 diff -u -r1.137 machdep.c --- machdep.c 13 Apr 2011 02:49:12 -0000 1.137 +++ machdep.c 16 Apr 2011 04:41:50 -0000 @@ -467,8 +467,10 @@ size_t newlen, struct proc *p) { extern int amd64_has_xcrypt; - dev_t consdev; - dev_t dev; + extern int acpi_thinkpad_enabled; + extern int tpfan_ctl, tpfan_passive_temp, tpfan_active_temp; + dev_t consdev, dev; + int error, ctlval; switch (name[0]) { case CPU_CONSDEV: @@ -518,6 +520,41 @@ return (sysctl_rdint(oldp, oldlenp, newp, amd64_has_xcrypt)); case CPU_LIDSUSPEND: return (sysctl_int(oldp, oldlenp, newp, newlen, &lid_suspend)); + case CPU_THINKPADFAN_CTL: + if (!acpi_thinkpad_enabled) + return (sysctl_rdint(oldp, oldlenp, newp, tpfan_ctl)); + ctlval = tpfan_ctl; + error = sysctl_int(oldp, oldlenp, newp, newlen, &ctlval); + if (error) + return (error); + if (ctlval != 1 && ctlval != 0) + return (EINVAL); + tpfan_ctl = ctlval; + return (0); + case CPU_THINKPADFAN_PASSIVE: + if (!acpi_thinkpad_enabled) + return (sysctl_rdint(oldp, oldlenp, newp, + tpfan_passive_temp)); + ctlval = tpfan_passive_temp; + error = sysctl_int(oldp, oldlenp, newp, newlen, &ctlval); + if(error) + return (error); + if(ctlval < 0) + return (EINVAL); + tpfan_passive_temp = ctlval; + return (0); + case CPU_THINKPADFAN_ACTIVE: + if (!acpi_thinkpad_enabled) + return (sysctl_rdint(oldp, oldlenp, newp, + tpfan_active_temp)); + ctlval = tpfan_active_temp; + error = sysctl_int(oldp, oldlenp, newp, newlen, &ctlval); + if(error) + return (error); + if(ctlval < 0) + return (EINVAL); + tpfan_active_temp = ctlval; + return (0); default: return (EOPNOTSUPP); }