Hello all.

This is a bit improved version of the patch showed up on tech@ in...
er-r-r... don't mind. This makes my ThinkPad usable under high load
for more than a year, and should not cause problems mentioned before
(spinning higher and lower constantly) by using two temperature marks
instead of one.

I also found that disengaged mode should be enabled separately off
other modes, so now Alexander Polyakov's X100e should behave correctly.

Any ThinkPad users willing to test?
--
  WBR,
    Vadim Zhukov


Index: sys/dev/acpi/acpithinkpad.c
===================================================================
RCS file: /cvs/src/sys/dev/acpi/acpithinkpad.c,v
retrieving revision 1.28
diff -u -p -r1.28 acpithinkpad.c
--- sys/dev/acpi/acpithinkpad.c 6 Jun 2011 06:13:46 -0000       1.28
+++ sys/dev/acpi/acpithinkpad.c 9 Dec 2012 19:55:43 -0000
@@ -76,11 +76,25 @@
 #define        THINKPAD_POWER_CHANGED          0x6030
 #define        THINKPAD_SWITCH_WIRELESS        0x7000
 
-#define THINKPAD_NSENSORS 9
-#define THINKPAD_NTEMPSENSORS 8
+#define THINKPAD_NSENSORS      10
+#define THINKPAD_NTEMPSENSORS   8
+
+#define THINKPAD_SENSOR_FANRPM (THINKPAD_NTEMPSENSORS + 0)
+#define THINKPAD_SENSOR_FANMODE        (THINKPAD_NTEMPSENSORS + 1)
 
 #define THINKPAD_ECOFFSET_FANLO                0x84
 #define THINKPAD_ECOFFSET_FANHI                0x85
+#define THINKPAD_ECOFFSET_FANMODE      0x2f
+
+/* not used: #define THINKPAD_FANMODE_MIN              0x00 */
+#define THINKPAD_FANMODE_MAX           0x07
+#define THINKPAD_FANMODE_AUTO          0x80
+#define THINKPAD_FANMODE_DISENGAGED    0x40
+#define THINKPAD_FANMODE_FORCEMAX      (THINKPAD_FANMODE_MAX | 
THINKPAD_FANMODE_DISENGAGED)
+
+/* critical temperature marks, in Celsius */
+#define THINKPAD_TEMP_OUCH_HMARK       80
+#define THINKPAD_TEMP_OUCH_LMARK       70
 
 struct acpithinkpad_softc {
        struct device            sc_dev;
@@ -90,7 +104,10 @@ struct acpithinkpad_softc {
        struct aml_node         *sc_devnode;
 
        struct ksensor           sc_sens[THINKPAD_NSENSORS];
+#define sc_sensfanmode          sc_sens[THINKPAD_SENSOR_FANMODE]
        struct ksensordev        sc_sensdev;
+
+       u_int8_t                 sc_fanmodeinit;
 };
 
 extern void acpiec_read(struct acpiec_softc *, u_int8_t, int, u_int8_t *);
@@ -108,7 +125,7 @@ int thinkpad_volume_mute(struct acpithin
 int    thinkpad_brightness_up(struct acpithinkpad_softc *);
 int    thinkpad_brightness_down(struct acpithinkpad_softc *);
 
-void    thinkpad_sensor_attach(struct acpithinkpad_softc *sc);
+void    thinkpad_sensor_attach(struct acpithinkpad_softc *);
 void    thinkpad_sensor_refresh(void *);
 
 #if NAUDIO > 0 && NWSKBD > 0
@@ -165,8 +182,14 @@ thinkpad_sensor_attach(struct acpithinkp
        }
 
        /* Add fan probe */
-       sc->sc_sens[i].type = SENSOR_FANRPM;
-       sensor_attach(&sc->sc_sensdev, &sc->sc_sens[i]);
+       sc->sc_sens[THINKPAD_SENSOR_FANRPM].type = SENSOR_FANRPM;
+       sensor_attach(&sc->sc_sensdev, &sc->sc_sens[THINKPAD_SENSOR_FANRPM]);
+
+       /* Add fan mode indicator */
+       sc->sc_sens[THINKPAD_SENSOR_FANMODE].type = SENSOR_INTEGER;
+       sensor_attach(&sc->sc_sensdev, &sc->sc_sens[THINKPAD_SENSOR_FANMODE]);
+       acpiec_read(sc->sc_ec, THINKPAD_ECOFFSET_FANMODE, 1,
+           &sc->sc_fanmodeinit);
 
        sensordev_install(&sc->sc_sensdev);
 }
@@ -176,8 +199,10 @@ thinkpad_sensor_refresh(void *arg)
 {
        struct acpithinkpad_softc *sc = arg;
        u_int8_t lo, hi, i;
-       int64_t tmp;
+       int64_t tmp, maxtmp = -127;    /* minimal correct value, see below */
+       int updatemode = 0;            /* should we bother BIOS? */
        char sname[5];
+       const char *desc;
 
        /* Refresh sensor readings */
        for (i=0; i<THINKPAD_NTEMPSENSORS; i++) {
@@ -185,14 +210,59 @@ thinkpad_sensor_refresh(void *arg)
                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)
+               if (tmp > 127 || tmp < -127) {
                        sc->sc_sens[i].flags = SENSOR_FINVALID;
+                       sc->sc_sens[i].status = SENSOR_S_UNKNOWN;
+               }
+               if ((sc->sc_sens[i].flags & SENSOR_FINVALID) == SENSOR_FINVALID)
+                       continue;
+               if (tmp > maxtmp)
+                       maxtmp = tmp;
+               if (tmp > THINKPAD_TEMP_OUCH_HMARK)
+                       sc->sc_sens[i].status = SENSOR_S_CRIT;
+               else if (tmp > THINKPAD_TEMP_OUCH_LMARK)
+                       sc->sc_sens[i].status = SENSOR_S_WARN;
+               else
+                       sc->sc_sens[i].status = SENSOR_S_OK;
        }
 
        /* 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);
+       sc->sc_sens[THINKPAD_SENSOR_FANRPM].value = ((hi << 8L) + lo);
+
+       /*
+        * Update fan mode based on max temperature seen.
+        * If temperature is between THINKPAD_TEMP_OUCH_LMARK and
+        * THINKPAD_TEMP_OUCH_HMARK, we try to avoid setting fan mode
+        * to avoid extra spinning.
+        */
+       
+       if (maxtmp > THINKPAD_TEMP_OUCH_HMARK) {
+               sc->sc_sensfanmode.value = THINKPAD_FANMODE_FORCEMAX;
+               sc->sc_sensfanmode.status = SENSOR_S_CRIT;
+               desc = "disengaged";
+               updatemode = 1;
+       } else if (maxtmp <= THINKPAD_TEMP_OUCH_LMARK) {
+               sc->sc_sensfanmode.value = sc->sc_fanmodeinit;
+               sc->sc_sensfanmode.status = SENSOR_S_OK;
+               desc = "auto";
+               updatemode = 1;
+       } else {
+               if (sc->sc_sensfanmode.status == SENSOR_S_UNSPEC) {
+                       /* Better safe than sorry */
+                       sc->sc_sensfanmode.value = THINKPAD_FANMODE_FORCEMAX;
+                       desc = "disengaged";
+                       updatemode = 1;
+               }
+               sc->sc_sensfanmode.status = SENSOR_S_WARN;
+       }
+       snprintf(sc->sc_sensfanmode.desc, sizeof(sc->sc_sensfanmode.desc),
+           "fan mode: %s", desc);
+
+       if (updatemode)
+               acpiec_write(sc->sc_ec, THINKPAD_ECOFFSET_FANMODE, 1,
+                   (u_int8_t*)&sc->sc_sensfanmode.value);
 }
 
 void
Index: share/man/man4/acpithinkpad.4
===================================================================
RCS file: /cvs/src/share/man/man4/acpithinkpad.4,v
retrieving revision 1.2
diff -u -p -r1.2 acpithinkpad.4
--- share/man/man4/acpithinkpad.4       31 May 2008 22:02:03 -0000      1.2
+++ share/man/man4/acpithinkpad.4       9 Dec 2012 19:55:43 -0000
@@ -28,6 +28,17 @@ The
 driver provides ACPI support for IBM/Lenovo ThinkPad laptops.
 It responds to various hotkey button presses such as the brightness adjustment
 and wireless toggle keys.
+.Pp
+Also
+.Nm
+monitors temperature and when it goes too high (current limit is set
+to 80C), fan mode is changed to so called
+.Dq disengaged
+mode.
+When temperature goes back under 70C, fan mode control is returned
+to firmware.
+This prevents laptops like X201, with hot CPUs, from being shooted down by
+.Xr acpitz 4 .
 .Sh SEE ALSO
 .Xr acpi 4 ,
 .Xr intro 4

Reply via email to