1 /* 2 * acerhdf - A driver which monitors the temperature 3 * of the aspire one netbook, turns on/off the fan 4 * as soon as the upper/lower threshold is reached. 5 * 6 * (C) 2009 - Peter Feuerer peter (a) piie.net 7 * http://piie.net 8 * 2009 Borislav Petkov <petkovbb@gmail.com> 9 * 10 * Inspired by and many thanks to: 11 * o acerfand - Rachel Greenham 12 * o acer_ec.pl - Michael Kurz michi.kurz (at) googlemail.com 13 * - Petr Tomasek tomasek (#) etf,cuni,cz 14 * - Carlos Corbacho cathectic (at) gmail.com 15 * o lkml - Matthew Garrett 16 * - Borislav Petkov 17 * - Andreas Mohr 18 * 19 * This program is free software; you can redistribute it and/or modify 20 * it under the terms of the GNU General Public License as published by 21 * the Free Software Foundation; either version 2 of the License, or 22 * (at your option) any later version. 23 * 24 * This program is distributed in the hope that it will be useful, 25 * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 * GNU General Public License for more details. 28 * 29 * You should have received a copy of the GNU General Public License 30 * along with this program; if not, write to the Free Software 31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 32 */ 33 34 #define pr_fmt(fmt) "acerhdf: " fmt 35 36 #include <linux/kernel.h> 37 #include <linux/module.h> 38 #include <linux/fs.h> 39 #include <linux/dmi.h> 40 #include <acpi/acpi_drivers.h> 41 #include <linux/sched.h> 42 #include <linux/thermal.h> 43 #include <linux/platform_device.h> 44 45 /* 46 * The driver is started with "kernel mode off" by default. That means, the BIOS 47 * is still in control of the fan. In this mode the driver allows to read the 48 * temperature of the cpu and a userspace tool may take over control of the fan. 49 * If the driver is switched to "kernel mode" (e.g. via module parameter) the 50 * driver is in full control of the fan. If you want the module to be started in 51 * kernel mode by default, define the following: 52 */ 53 #undef START_IN_KERNEL_MODE 54 55 #define DRV_VER "0.5.17" 56 57 /* 58 * According to the Atom N270 datasheet, 59 * (http://download.intel.com/design/processor/datashts/320032.pdf) the 60 * CPU's optimal operating limits denoted in junction temperature as 61 * measured by the on-die thermal monitor are within 0 <= Tj <= 90. So, 62 * assume 89°C is critical temperature. 63 */ 64 #define ACERHDF_TEMP_CRIT 89 65 #define ACERHDF_FAN_OFF 0 66 #define ACERHDF_FAN_AUTO 1 67 68 /* 69 * No matter what value the user puts into the fanon variable, turn on the fan 70 * at 80 degree Celsius to prevent hardware damage 71 */ 72 #define ACERHDF_MAX_FANON 80 73 74 /* 75 * Maximum interval between two temperature checks is 15 seconds, as the die 76 * can get hot really fast under heavy load (plus we shouldn't forget about 77 * possible impact of _external_ aggressive sources such as heaters, sun etc.) 78 */ 79 #define ACERHDF_MAX_INTERVAL 15 80 81 #ifdef START_IN_KERNEL_MODE 82 static int kernelmode = 1; 83 #else 84 static int kernelmode; 85 #endif 86 87 static unsigned int interval = 10; 88 static unsigned int fanon = 63; 89 static unsigned int fanoff = 58; 90 static unsigned int verbose; 91 static unsigned int fanstate = ACERHDF_FAN_AUTO; 92 static char force_bios[16]; 93 static char force_product[16]; 94 static unsigned int prev_interval; 95 struct thermal_zone_device *thz_dev; 96 struct thermal_cooling_device *cl_dev; 97 struct platform_device *acerhdf_dev; 98 99 module_param(kernelmode, uint, 0); 100 MODULE_PARM_DESC(kernelmode, "Kernel mode fan control on / off"); 101 module_param(interval, uint, 0600); 102 MODULE_PARM_DESC(interval, "Polling interval of temperature check"); 103 module_param(fanon, uint, 0600); 104 MODULE_PARM_DESC(fanon, "Turn the fan on above this temperature"); 105 module_param(fanoff, uint, 0600); 106 MODULE_PARM_DESC(fanoff, "Turn the fan off below this temperature"); 107 module_param(verbose, uint, 0600); 108 MODULE_PARM_DESC(verbose, "Enable verbose dmesg output"); 109 module_param_string(force_bios, force_bios, 16, 0); 110 MODULE_PARM_DESC(force_bios, "Force BIOS version and omit BIOS check"); 111 module_param_string(force_product, force_product, 16, 0); 112 MODULE_PARM_DESC(force_product, "Force BIOS product and omit BIOS check"); 113 114 /* 115 * cmd_off: to switch the fan completely off / to check if the fan is off 116 * cmd_auto: to set the BIOS in control of the fan. The BIOS regulates then 117 * the fan speed depending on the temperature 118 */ 119 struct fancmd { 120 u8 cmd_off; 121 u8 cmd_auto; 122 }; 123 124 /* BIOS settings */ 125 struct bios_settings_t { 126 const char *vendor; 127 const char *product; 128 const char *version; 129 unsigned char fanreg; 130 unsigned char tempreg; 131 struct fancmd cmd; 132 }; 133 134 /* Register addresses and values for different BIOS versions */ 135 static const struct bios_settings_t bios_tbl[] = { 136 /* AOA110 */ 137 {"Acer", "AOA110", "v0.3109", 0x55, 0x58, {0x1f, 0x00} }, 138 {"Acer", "AOA110", "v0.3114", 0x55, 0x58, {0x1f, 0x00} }, 139 {"Acer", "AOA110", "v0.3301", 0x55, 0x58, {0xaf, 0x00} }, 140 {"Acer", "AOA110", "v0.3304", 0x55, 0x58, {0xaf, 0x00} }, 141 {"Acer", "AOA110", "v0.3305", 0x55, 0x58, {0xaf, 0x00} }, 142 {"Acer", "AOA110", "v0.3307", 0x55, 0x58, {0xaf, 0x00} }, 143 {"Acer", "AOA110", "v0.3308", 0x55, 0x58, {0x21, 0x00} }, 144 {"Acer", "AOA110", "v0.3309", 0x55, 0x58, {0x21, 0x00} }, 145 {"Acer", "AOA110", "v0.3310", 0x55, 0x58, {0x21, 0x00} }, 146 /* AOA150 */ 147 {"Acer", "AOA150", "v0.3114", 0x55, 0x58, {0x20, 0x00} }, 148 {"Acer", "AOA150", "v0.3301", 0x55, 0x58, {0x20, 0x00} }, 149 {"Acer", "AOA150", "v0.3304", 0x55, 0x58, {0x20, 0x00} }, 150 {"Acer", "AOA150", "v0.3305", 0x55, 0x58, {0x20, 0x00} }, 151 {"Acer", "AOA150", "v0.3307", 0x55, 0x58, {0x20, 0x00} }, 152 {"Acer", "AOA150", "v0.3308", 0x55, 0x58, {0x20, 0x00} }, 153 {"Acer", "AOA150", "v0.3309", 0x55, 0x58, {0x20, 0x00} }, 154 {"Acer", "AOA150", "v0.3310", 0x55, 0x58, {0x20, 0x00} }, 155 /* special BIOS / other */ 156 {"Gateway", "AOA110", "v0.3103", 0x55, 0x58, {0x21, 0x00} }, 157 {"Gateway", "AOA150", "v0.3103", 0x55, 0x58, {0x20, 0x00} }, 158 {"Packard Bell", "DOA150", "v0.3104", 0x55, 0x58, {0x21, 0x00} }, 159 {"Packard Bell", "AOA110", "v0.3105", 0x55, 0x58, {0x21, 0x00} }, 160 {"Packard Bell", "AOA150", "v0.3105", 0x55, 0x58, {0x20, 0x00} }, 161 /* pewpew-terminator */ 162 {"", "", "", 0, 0, {0, 0} } 163 }; 164 165 static const struct bios_settings_t *bios_cfg __read_mostly; 166 167 static int acerhdf_get_temp(int *temp) 168 { 169 u8 read_temp; 170 171 if (ec_read(bios_cfg->tempreg, &read_temp)) 172 return -EINVAL; 173 174 *temp = read_temp; 175 176 return 0; 177 } 178 179 static int acerhdf_get_fanstate(int *state) 180 { 181 u8 fan; 182 183 if (ec_read(bios_cfg->fanreg, &fan)) 184 return -EINVAL; 185 186 if (fan != bios_cfg->cmd.cmd_off) 187 *state = ACERHDF_FAN_AUTO; 188 else 189 *state = ACERHDF_FAN_OFF; 190 191 return 0; 192 } 193 194 static void acerhdf_change_fanstate(int state) 195 { 196 unsigned char cmd; 197 198 if (verbose) 199 pr_notice("fan %s\n", (state == ACERHDF_FAN_OFF) ? 200 "OFF" : "ON"); 201 202 if ((state != ACERHDF_FAN_OFF) && (state != ACERHDF_FAN_AUTO)) { 203 pr_err("invalid fan state %d requested, setting to auto!\n", 204 state); 205 state = ACERHDF_FAN_AUTO; 206 } 207 208 cmd = (state == ACERHDF_FAN_OFF) ? bios_cfg->cmd.cmd_off 209 : bios_cfg->cmd.cmd_auto; 210 fanstate = state; 211 212 ec_write(bios_cfg->fanreg, cmd); 213 } 214 215 static void acerhdf_check_param(struct thermal_zone_device *thermal) 216 { 217 if (fanon > ACERHDF_MAX_FANON) { 218 pr_err("fanon temperature too high, set to %d\n", 219 ACERHDF_MAX_FANON); 220 fanon = ACERHDF_MAX_FANON; 221 } 222 223 if (kernelmode && prev_interval != interval) { 224 if (interval > ACERHDF_MAX_INTERVAL) { 225 pr_err("interval too high, set to %d\n", 226 ACERHDF_MAX_INTERVAL); 227 interval = ACERHDF_MAX_INTERVAL; 228 } 229 if (verbose) 230 pr_notice("interval changed to: %d\n", 231 interval); 232 thermal->polling_delay = interval*1000; 233 prev_interval = interval; 234 } 235 } 236 237 /* 238 * This is the thermal zone callback which does the delayed polling of the fan 239 * state. We do check /sysfs-originating settings here in acerhdf_check_param() 240 * as late as the polling interval is since we can't do that in the respective 241 * accessors of the module parameters. 242 */ 243 static int acerhdf_get_ec_temp(struct thermal_zone_device *thermal, 244 unsigned long *t) 245 { 246 int temp, err = 0; 247 248 acerhdf_check_param(thermal); 249 250 err = acerhdf_get_temp(&temp); 251 if (err) 252 return err; 253 254 if (verbose) 255 pr_notice("temp %d\n", temp); 256 257 *t = temp; 258 return 0; 259 } 260 261 static int acerhdf_bind(struct thermal_zone_device *thermal, 262 struct thermal_cooling_device *cdev) 263 { 264 /* if the cooling device is the one from acerhdf bind it */ 265 if (cdev != cl_dev) 266 return 0; 267 268 if (thermal_zone_bind_cooling_device(thermal, 0, cdev)) { 269 pr_err("error binding cooling dev\n"); 270 return -EINVAL; 271 } 272 return 0; 273 } 274 275 static int acerhdf_unbind(struct thermal_zone_device *thermal, 276 struct thermal_cooling_device *cdev) 277 { 278 if (cdev != cl_dev) 279 return 0; 280 281 if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) { 282 pr_err("error unbinding cooling dev\n"); 283 return -EINVAL; 284 } 285 return 0; 286 } 287 288 static inline void acerhdf_revert_to_bios_mode(void) 289 { 290 acerhdf_change_fanstate(ACERHDF_FAN_AUTO); 291 kernelmode = 0; 292 if (thz_dev) 293 thz_dev->polling_delay = 0; 294 pr_notice("kernel mode fan control OFF\n"); 295 } 296 static inline void acerhdf_enable_kernelmode(void) 297 { 298 kernelmode = 1; 299 300 thz_dev->polling_delay = interval*1000; 301 thermal_zone_device_update(thz_dev); 302 pr_notice("kernel mode fan control ON\n"); 303 } 304 305 static int acerhdf_get_mode(struct thermal_zone_device *thermal, 306 enum thermal_device_mode *mode) 307 { 308 if (verbose) 309 pr_notice("kernel mode fan control %d\n", kernelmode); 310 311 *mode = (kernelmode) ? THERMAL_DEVICE_ENABLED 312 : THERMAL_DEVICE_DISABLED; 313 314 return 0; 315 } 316 317 /* 318 * set operation mode; 319 * enabled: the thermal layer of the kernel takes care about 320 * the temperature and the fan. 321 * disabled: the BIOS takes control of the fan. 322 */ 323 static int acerhdf_set_mode(struct thermal_zone_device *thermal, 324 enum thermal_device_mode mode) 325 { 326 if (mode == THERMAL_DEVICE_DISABLED && kernelmode) 327 acerhdf_revert_to_bios_mode(); 328 else if (mode == THERMAL_DEVICE_ENABLED && !kernelmode) 329 acerhdf_enable_kernelmode(); 330 331 return 0; 332 } 333 334 static int acerhdf_get_trip_type(struct thermal_zone_device *thermal, int trip, 335 enum thermal_trip_type *type) 336 { 337 if (trip == 0) 338 *type = THERMAL_TRIP_ACTIVE; 339 340 return 0; 341 } 342 343 static int acerhdf_get_trip_temp(struct thermal_zone_device *thermal, int trip, 344 unsigned long *temp) 345 { 346 if (trip == 0) 347 *temp = fanon; 348 349 return 0; 350 } 351 352 static int acerhdf_get_crit_temp(struct thermal_zone_device *thermal, 353 unsigned long *temperature) 354 { 355 *temperature = ACERHDF_TEMP_CRIT; 356 return 0; 357 } 358 359 /* bind callback functions to thermalzone */ 360 struct thermal_zone_device_ops acerhdf_dev_ops = { 361 .bind = acerhdf_bind, 362 .unbind = acerhdf_unbind, 363 .get_temp = acerhdf_get_ec_temp, 364 .get_mode = acerhdf_get_mode, 365 .set_mode = acerhdf_set_mode, 366 .get_trip_type = acerhdf_get_trip_type, 367 .get_trip_temp = acerhdf_get_trip_temp, 368 .get_crit_temp = acerhdf_get_crit_temp, 369 }; 370 371 372 /* 373 * cooling device callback functions 374 * get maximal fan cooling state 375 */ 376 static int acerhdf_get_max_state(struct thermal_cooling_device *cdev, 377 unsigned long *state) 378 { 379 *state = 1; 380 381 return 0; 382 } 383 384 static int acerhdf_get_cur_state(struct thermal_cooling_device *cdev, 385 unsigned long *state) 386 { 387 int err = 0, tmp; 388 389 err = acerhdf_get_fanstate(&tmp); 390 if (err) 391 return err; 392 393 *state = (tmp == ACERHDF_FAN_AUTO) ? 1 : 0; 394 return 0; 395 } 396 397 /* change current fan state - is overwritten when running in kernel mode */ 398 static int acerhdf_set_cur_state(struct thermal_cooling_device *cdev, 399 unsigned long state) 400 { 401 int cur_temp, cur_state, err = 0; 402 403 if (!kernelmode) 404 return 0; 405 406 err = acerhdf_get_temp(&cur_temp); 407 if (err) { 408 pr_err("error reading temperature, hand off control to BIOS\n"); 409 goto err_out; 410 } 411 412 err = acerhdf_get_fanstate(&cur_state); 413 if (err) { 414 pr_err("error reading fan state, hand off control to BIOS\n"); 415 goto err_out; 416 } 417 418 if (state == 0) { 419 /* turn fan off only if below fanoff temperature */ 420 if ((cur_state == ACERHDF_FAN_AUTO) && 421 (cur_temp < fanoff)) 422 acerhdf_change_fanstate(ACERHDF_FAN_OFF); 423 } else { 424 if (cur_state == ACERHDF_FAN_OFF) 425 acerhdf_change_fanstate(ACERHDF_FAN_AUTO); 426 } 427 return 0; 428 429 err_out: 430 acerhdf_revert_to_bios_mode(); 431 return -EINVAL; 432 } 433 434 /* bind fan callbacks to fan device */ 435 struct thermal_cooling_device_ops acerhdf_cooling_ops = { 436 .get_max_state = acerhdf_get_max_state, 437 .get_cur_state = acerhdf_get_cur_state, 438 .set_cur_state = acerhdf_set_cur_state, 439 }; 440 441 /* suspend / resume functionality */ 442 static int acerhdf_suspend(struct device *dev) 443 { 444 if (kernelmode) 445 acerhdf_change_fanstate(ACERHDF_FAN_AUTO); 446 447 if (verbose) 448 pr_notice("going suspend\n"); 449 450 return 0; 451 } 452 453 static int __devinit acerhdf_probe(struct platform_device *device) 454 { 455 return 0; 456 } 457 458 static int acerhdf_remove(struct platform_device *device) 459 { 460 return 0; 461 } 462 463 static struct dev_pm_ops acerhdf_pm_ops = { 464 .suspend = acerhdf_suspend, 465 .freeze = acerhdf_suspend, 466 }; 467 468 static struct platform_driver acerhdf_driver = { 469 .driver = { 470 .name = "acerhdf", 471 .owner = THIS_MODULE, 472 .pm = &acerhdf_pm_ops, 473 }, 474 .probe = acerhdf_probe, 475 .remove = acerhdf_remove, 476 }; 477 478 479 /* check hardware */ 480 static int acerhdf_check_hardware(void) 481 { 482 char const *vendor, *version, *product; 483 int i; 484 unsigned long prod_len = 0; 485 486 /* get BIOS data */ 487 vendor = dmi_get_system_info(DMI_SYS_VENDOR); 488 version = dmi_get_system_info(DMI_BIOS_VERSION); 489 product = dmi_get_system_info(DMI_PRODUCT_NAME); 490 491 492 pr_info("Acer Aspire One Fan driver, v.%s\n", DRV_VER); 493 494 if (force_bios[0]) { 495 version = force_bios; 496 pr_info("forcing BIOS version: %s\n", version); 497 kernelmode = 0; 498 } 499 500 if (force_product[0]) { 501 product = force_product; 502 pr_info("forcing BIOS product: %s\n", product); 503 kernelmode = 0; 504 } 505 506 prod_len = strlen(product); 507 508 if (verbose) 509 pr_info("BIOS info: %s %s, product: %s\n", 510 vendor, version, product); 511 512 /* search BIOS version and vendor in BIOS settings table */ 513 for (i = 0; bios_tbl[i].version[0]; i++) { 514 if (strlen(bios_tbl[i].product) >= prod_len && 515 !strncmp(bios_tbl[i].product, product, 516 strlen(bios_tbl[i].product)) && 517 !strcmp(bios_tbl[i].vendor, vendor) && 518 !strcmp(bios_tbl[i].version, version)) { 519 bios_cfg = &bios_tbl[i]; 520 break; 521 } 522 } 523 524 if (!bios_cfg) { 525 pr_err("unknown (unsupported) BIOS version %s/%s/%s, " 526 "please report, aborting!\n", vendor, product, version); 527 return -EINVAL; 528 } 529 530 /* 531 * if started with kernel mode off, prevent the kernel from switching 532 * off the fan 533 */ 534 if (!kernelmode) { 535 pr_notice("Fan control off, to enable do:\n"); 536 pr_notice("echo -n \"enabled\" > " 537 "/sys/class/thermal/thermal_zone0/mode\n"); 538 } 539 540 return 0; 541 } 542 543 static int acerhdf_register_platform(void) 544 { 545 int err = 0; 546 547 err = platform_driver_register(&acerhdf_driver); 548 if (err) 549 return err; 550 551 acerhdf_dev = platform_device_alloc("acerhdf", -1); 552 platform_device_add(acerhdf_dev); 553 554 return 0; 555 } 556 557 static void acerhdf_unregister_platform(void) 558 { 559 if (!acerhdf_dev) 560 return; 561 562 platform_device_del(acerhdf_dev); 563 platform_driver_unregister(&acerhdf_driver); 564 } 565 566 static int acerhdf_register_thermal(void) 567 { 568 cl_dev = thermal_cooling_device_register("acerhdf-fan", NULL, 569 &acerhdf_cooling_ops); 570 571 if (IS_ERR(cl_dev)) 572 return -EINVAL; 573 574 thz_dev = thermal_zone_device_register("acerhdf", 1, NULL, 575 &acerhdf_dev_ops, 0, 0, 0, 576 (kernelmode) ? interval*1000 : 0); 577 if (IS_ERR(thz_dev)) 578 return -EINVAL; 579 580 return 0; 581 } 582 583 static void acerhdf_unregister_thermal(void) 584 { 585 if (cl_dev) { 586 thermal_cooling_device_unregister(cl_dev); 587 cl_dev = NULL; 588 } 589 590 if (thz_dev) { 591 thermal_zone_device_unregister(thz_dev); 592 thz_dev = NULL; 593 } 594 } 595 596 static int __init acerhdf_init(void) 597 { 598 int err = 0; 599 600 err = acerhdf_check_hardware(); 601 if (err) 602 goto out_err; 603 604 err = acerhdf_register_platform(); 605 if (err) 606 goto err_unreg; 607 608 err = acerhdf_register_thermal(); 609 if (err) 610 goto err_unreg; 611 612 return 0; 613 614 err_unreg: 615 acerhdf_unregister_thermal(); 616 acerhdf_unregister_platform(); 617 618 out_err: 619 return -ENODEV; 620 } 621 622 static void __exit acerhdf_exit(void) 623 { 624 acerhdf_change_fanstate(ACERHDF_FAN_AUTO); 625 acerhdf_unregister_thermal(); 626 acerhdf_unregister_platform(); 627 } 628 629 MODULE_LICENSE("GPL"); 630 MODULE_AUTHOR("Peter Feuerer"); 631 MODULE_DESCRIPTION("Aspire One temperature and fan driver"); 632 MODULE_ALIAS("dmi:*:*Acer*:*:"); 633 MODULE_ALIAS("dmi:*:*Gateway*:*:"); 634 MODULE_ALIAS("dmi:*:*Packard Bell*:*:"); 635 636 module_init(acerhdf_init); 637 module_exit(acerhdf_exit); 638