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