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);
        }

Reply via email to