> When I have this working I want to implement a PID controler for it.
> Since I'd like to see this get committed, where would be the preferred
> place to put the PID-controller? Kernel or userland?

kernel, definitely.  it would be helpful to have some kind of
watchdog to reset the fan to auto/high if something goes wrong, but
i don't know whether that's even possible.  while probably not a big
issue on a laptop, if the fan was on manual/low when the kernel
panicked and left to sit there for many hours, some things could
overheat.

i wrote this code last year to react to the thinkpad temperature
sensors and change the fan speed accordingly.  it makes my x301
completely silent most of the time and then occasionally turns the
fan on low when doing a make build or when firefox is trying to do
something as "complicated" as render a webpage.  it has no manual
control nor any watchdog.  there is also other unrelated junk in the
diff, but it is what i'm running against -current.

as for code implementing the sysctl, it would probably be something
created under machdep, like the recently added machdep.lidsuspend.


Index: acpithinkpad.c
===================================================================
RCS file: /cvs/src/sys/dev/acpi/acpithinkpad.c,v
retrieving revision 1.24
diff -u -p -r1.24 acpithinkpad.c
--- acpithinkpad.c      7 Aug 2010 16:21:20 -0000       1.24
+++ acpithinkpad.c      29 Nov 2010 19:02:40 -0000
@@ -1,6 +1,6 @@
 /*     $OpenBSD: acpithinkpad.c,v 1.24 2010/08/07 16:21:20 deraadt Exp $       
*/
 /*
- * Copyright (c) 2008 joshua stein <j...@openbsd.org>
+ * Copyright (c) 2008, 2010 joshua stein <j...@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -59,10 +59,11 @@
 #define        THINKPAD_BUTTON_VOLUME_DOWN     0x1016
 #define        THINKPAD_BUTTON_VOLUME_MUTE     0x1017
 #define        THINKPAD_BUTTON_THINKVANTAGE    0x1018
+#define        THINKPAD_BUTTON_THINKVANTAGE2   0x4120
 #define        THINKPAD_BUTTON_FN_F11          0x100b
 #define        THINKPAD_BUTTON_HIBERNATE       0x100c
-#define        THINKPAD_LID_OPEN               0x5001
-#define        THINKPAD_LID_CLOSED             0x5002
+#define        THINKPAD_LID_CLOSED             0x5001
+#define        THINKPAD_LID_OPEN               0x5002
 #define        THINKPAD_TABLET_SCREEN_NORMAL   0x500a
 #define        THINKPAD_TABLET_SCREEN_ROTATED  0x5009
 #define        THINKPAD_BRIGHTNESS_CHANGED     0x5010
@@ -71,11 +72,23 @@
 #define        THINKPAD_POWER_CHANGED          0x6030
 #define        THINKPAD_SWITCH_WIRELESS        0x7000
 
-#define THINKPAD_NSENSORS 9
-#define THINKPAD_NTEMPSENSORS 8
+#define        THINKPAD_NSENSORS               9
+#define        THINKPAD_NTEMPSENSORS           8
 
-#define THINKPAD_ECOFFSET_FANLO                0x84
-#define THINKPAD_ECOFFSET_FANHI                0x85
+#define        THINKPAD_ECBRIGHTNESS_OFFSET    0x31
+#define        THINKPAD_ECBRIGHTNESS_LEVEL_MASK 0x1f
+
+#define        THINKPAD_ECOFFSET_FANLO         0x84
+#define        THINKPAD_ECOFFSET_FANHI         0x85
+
+#define        THINKPAD_ECOFFSET_FANLEVEL      0x2f
+
+#define        THINKPAD_ECFANLEVEL_AUTO        7
+
+/* highest temperatures (from any sensor) allowed at each fan level (after
+ * level 2's max temp, it will be put in auto mode) */
+#define        THINKPAD_MAX_TEMP_LEVEL1        55
+#define        THINKPAD_MAX_TEMP_LEVEL2        65
 
 struct acpithinkpad_softc {
        struct device            sc_dev;
@@ -88,7 +101,15 @@ struct acpithinkpad_softc {
        struct ksensordev        sc_sensdev;
 };
 
-extern void acpiec_read(struct acpiec_softc *, u_int8_t, int, u_int8_t *);
+/* models (according to smbios version in bios.c) that need a brightness 
helper */
+extern char *hw_ver;
+const char *acpithinkpad_models_to_help[] = {
+       "ThinkPad X61s",
+       "ThinkPad T61",
+};
+
+int    thinkpad_need_helper = 0;
+int    staged_temp_setting = 0;
 
 int    thinkpad_match(struct device *, void *, void *);
 void   thinkpad_attach(struct device *, struct device *, void *);
@@ -100,11 +121,14 @@ int       thinkpad_cmos(struct acpithinkpad_so
 int    thinkpad_volume_down(struct acpithinkpad_softc *);
 int    thinkpad_volume_up(struct acpithinkpad_softc *);
 int    thinkpad_volume_mute(struct acpithinkpad_softc *);
+int    thinkpad_brightness_get(struct acpithinkpad_softc *);
 int    thinkpad_brightness_up(struct acpithinkpad_softc *);
 int    thinkpad_brightness_down(struct acpithinkpad_softc *);
+void   thinkpad_set_fan_level(struct acpithinkpad_softc *, uint8_t);
 
 void    thinkpad_sensor_attach(struct acpithinkpad_softc *sc);
 void    thinkpad_sensor_refresh(void *);
+void   thinkpad_video_setup(struct acpithinkpad_softc *);
 
 struct cfattach acpithinkpad_ca = {
        sizeof(struct acpithinkpad_softc), thinkpad_match, thinkpad_attach
@@ -158,13 +182,16 @@ thinkpad_sensor_attach(struct acpithinkp
        sensor_attach(&sc->sc_sensdev, &sc->sc_sens[i]);
 
        sensordev_install(&sc->sc_sensdev);
+
+       /* initialize fan level to auto */
+       thinkpad_set_fan_level(sc, THINKPAD_ECFANLEVEL_AUTO);
 }
 
 void
 thinkpad_sensor_refresh(void *arg)
 {
        struct acpithinkpad_softc *sc = arg;
-       u_int8_t lo, hi, i;
+       u_int8_t lo, hi, i, fanlevel, maxtemp = 0;
        int64_t tmp;
        char sname[5];
 
@@ -176,12 +203,42 @@ thinkpad_sensor_refresh(void *arg)
                sc->sc_sens[i].value = (tmp * 1000000) + 273150000;
                if (tmp > 127 || tmp < -127)
                        sc->sc_sens[i].flags = SENSOR_FINVALID;
+               else if (tmp > maxtemp)
+                       maxtemp = 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);
+
+       /* read fan level */
+       acpiec_read(sc->sc_ec, THINKPAD_ECOFFSET_FANLEVEL, 1, &fanlevel);
+
+       /* set the level according to the highest temperature just seen */
+       if (!maxtemp || (maxtemp > THINKPAD_MAX_TEMP_LEVEL2)) {
+               if (fanlevel != THINKPAD_ECFANLEVEL_AUTO) {
+                       if (staged_temp_setting == THINKPAD_ECFANLEVEL_AUTO)
+                               thinkpad_set_fan_level(sc,
+                                       THINKPAD_ECFANLEVEL_AUTO);
+                       else
+                               staged_temp_setting = THINKPAD_ECFANLEVEL_AUTO;
+               }
+       } else if (maxtemp > THINKPAD_MAX_TEMP_LEVEL1) {
+               if (fanlevel != 2) {
+                       if (staged_temp_setting == 2)
+                               thinkpad_set_fan_level(sc, 2);
+                       else
+                               staged_temp_setting = 2;
+               }
+       } else if (maxtemp <= THINKPAD_MAX_TEMP_LEVEL1) {
+               if (fanlevel != 1) {
+                       if (staged_temp_setting == 1)
+                               thinkpad_set_fan_level(sc, 1);
+                       else
+                               staged_temp_setting = 1;
+               }
+       }
 }
 
 void
@@ -189,19 +246,35 @@ thinkpad_attach(struct device *parent, s
 {
        struct acpithinkpad_softc *sc = (struct acpithinkpad_softc *)self;
        struct acpi_attach_args *aa = aux;
+       int i;
 
        sc->sc_acpi = (struct acpi_softc *)parent;
        sc->sc_devnode = aa->aaa_node;
 
-       printf("\n");
+       if (hw_ver) {
+               for (i = 0; i < nitems(acpithinkpad_models_to_help); i++)
+                       if (!strcmp(hw_ver, acpithinkpad_models_to_help[i])) {
+                               thinkpad_need_helper = 1;
+                               break;
+                       }
+       } else
+               thinkpad_need_helper = 1;
+
+       /* for models that don't need a brightness helper, adjust brightness on
+        * our own */
+       if (!thinkpad_need_helper)
+               thinkpad_brightness_get(sc);
 
        /* Set event mask to receive everything */
        thinkpad_enable_events(sc);
        thinkpad_sensor_attach(sc);
+       thinkpad_video_setup(sc);
 
        /* Run thinkpad_hotkey on button presses */
        aml_register_notify(sc->sc_devnode, aa->aaa_dev,
            thinkpad_hotkey, sc, ACPIDEV_POLL);
+
+       printf("\n");
 }
 
 int
@@ -245,6 +318,19 @@ thinkpad_enable_events(struct acpithinkp
        return (0);
 }
 
+void
+thinkpad_video_setup(struct acpithinkpad_softc *sc)
+{
+       struct aml_value arg;
+
+       /* Disable brightness delay */
+       bzero(&arg, sizeof(arg));
+       arg.type = AML_OBJTYPE_INTEGER;
+       arg.v_integer = 0;
+       (void) aml_evalname(sc->sc_acpi, sc->sc_devnode, "PWMS",
+           1, &arg, NULL);
+}
+
 int
 thinkpad_hotkey(struct aml_node *node, int notify_type, void *arg)
 {
@@ -270,11 +356,13 @@ thinkpad_hotkey(struct aml_node *node, i
 
                switch (event) {
                case THINKPAD_BUTTON_BRIGHTNESS_UP:
-                       thinkpad_brightness_up(sc);
+                       if (thinkpad_need_helper)
+                               thinkpad_brightness_up(sc);
                        handled = 1;
                        break;
                case THINKPAD_BUTTON_BRIGHTNESS_DOWN:
-                       thinkpad_brightness_down(sc);
+                       if (thinkpad_need_helper)
+                               thinkpad_brightness_down(sc);
                        handled = 1;
                        break;
                case THINKPAD_BUTTON_WIRELESS:
@@ -288,18 +376,6 @@ thinkpad_hotkey(struct aml_node *node, i
 #endif
                        handled = 1;
                        break;
-               case THINKPAD_BUTTON_HIBERNATE:
-               case THINKPAD_BUTTON_FN_F1:
-               case THINKPAD_BUTTON_LOCK_SCREEN:
-               case THINKPAD_BUTTON_BATTERY_INFO:
-               case THINKPAD_BUTTON_FN_F6:
-               case THINKPAD_BUTTON_EXTERNAL_SCREEN:
-               case THINKPAD_BUTTON_POINTER_SWITCH:
-               case THINKPAD_BUTTON_EJECT:
-               case THINKPAD_BUTTON_THINKLIGHT:
-               case THINKPAD_BUTTON_FN_SPACE:
-                       handled = 1;
-                       break;
                case THINKPAD_BUTTON_VOLUME_MUTE:
                        thinkpad_volume_mute(sc);
                        handled = 1;
@@ -312,22 +388,26 @@ thinkpad_hotkey(struct aml_node *node, i
                        thinkpad_volume_up(sc);
                        handled = 1;
                        break;
+               case THINKPAD_BUTTON_HIBERNATE:
+               case THINKPAD_BUTTON_FN_F1:
+               case THINKPAD_BUTTON_LOCK_SCREEN:
+               case THINKPAD_BUTTON_BATTERY_INFO:
+               case THINKPAD_BUTTON_FN_F6:
+               case THINKPAD_BUTTON_EXTERNAL_SCREEN:
+               case THINKPAD_BUTTON_POINTER_SWITCH:
+               case THINKPAD_BUTTON_EJECT:
+               case THINKPAD_BUTTON_THINKLIGHT:
+               case THINKPAD_BUTTON_FN_SPACE:
                case THINKPAD_BUTTON_THINKVANTAGE:
                case THINKPAD_BUTTON_FN_F11:
-                       handled = 1;
-                       break;
-               case THINKPAD_LID_OPEN:
                case THINKPAD_LID_CLOSED:
+               case THINKPAD_LID_OPEN:
                case THINKPAD_TABLET_SCREEN_NORMAL:
                case THINKPAD_TABLET_SCREEN_ROTATED:
                case THINKPAD_BRIGHTNESS_CHANGED:
                case THINKPAD_TABLET_PEN_INSERTED:
                case THINKPAD_TABLET_PEN_REMOVED:
-                       handled = 1;
-                       break;
                case THINKPAD_POWER_CHANGED:
-                       handled = 1;
-                       break;
                case THINKPAD_SWITCH_WIRELESS:
                        handled = 1;
                        break;
@@ -426,6 +506,18 @@ thinkpad_volume_mute(struct acpithinkpad
 }
 
 int
+thinkpad_brightness_get(struct acpithinkpad_softc *sc)
+{
+       uint8_t val;
+       int level;
+
+       acpiec_read(sc->sc_acpi->sc_ec, THINKPAD_ECBRIGHTNESS_OFFSET, 1, &val);
+       level = val & THINKPAD_ECBRIGHTNESS_LEVEL_MASK;
+
+       return level;
+}
+
+int
 thinkpad_brightness_up(struct acpithinkpad_softc *sc)
 {
        return (thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_UP));
@@ -435,4 +527,10 @@ int
 thinkpad_brightness_down(struct acpithinkpad_softc *sc)
 {
        return (thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_DOWN));
+}
+
+void
+thinkpad_set_fan_level(struct acpithinkpad_softc *sc, uint8_t level)
+{
+       acpiec_write(sc->sc_ec, THINKPAD_ECOFFSET_FANLEVEL, 1, &level);
 }

Reply via email to