linux/drivers/platform/x86/acerhdf.c
<<
>>
Prefs
   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.18"
  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 89000
  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 80000
  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
  82static int kernelmode = 1;
  83#else
  84static int kernelmode;
  85#endif
  86
  87static unsigned int interval = 10;
  88static unsigned int fanon = 63000;
  89static unsigned int fanoff = 58000;
  90static unsigned int verbose;
  91static unsigned int fanstate = ACERHDF_FAN_AUTO;
  92static char force_bios[16];
  93static char force_product[16];
  94static unsigned int prev_interval;
  95struct thermal_zone_device *thz_dev;
  96struct thermal_cooling_device *cl_dev;
  97struct platform_device *acerhdf_dev;
  98
  99module_param(kernelmode, uint, 0);
 100MODULE_PARM_DESC(kernelmode, "Kernel mode fan control on / off");
 101module_param(interval, uint, 0600);
 102MODULE_PARM_DESC(interval, "Polling interval of temperature check");
 103module_param(fanon, uint, 0600);
 104MODULE_PARM_DESC(fanon, "Turn the fan on above this temperature");
 105module_param(fanoff, uint, 0600);
 106MODULE_PARM_DESC(fanoff, "Turn the fan off below this temperature");
 107module_param(verbose, uint, 0600);
 108MODULE_PARM_DESC(verbose, "Enable verbose dmesg output");
 109module_param_string(force_bios, force_bios, 16, 0);
 110MODULE_PARM_DESC(force_bios, "Force BIOS version and omit BIOS check");
 111module_param_string(force_product, force_product, 16, 0);
 112MODULE_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 */
 119struct fancmd {
 120        u8 cmd_off;
 121        u8 cmd_auto;
 122};
 123
 124/* BIOS settings */
 125struct 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 */
 135static 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
 165static const struct bios_settings_t *bios_cfg __read_mostly;
 166
 167static 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 * 1000;
 175
 176        return 0;
 177}
 178
 179static 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
 194static 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
 215static 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 */
 243static 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
 261static 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
 275static 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
 288static 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}
 296static 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
 305static 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 */
 323static 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
 334static 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
 343static 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
 352static 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 */
 360struct 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 */
 376static int acerhdf_get_max_state(struct thermal_cooling_device *cdev,
 377                                 unsigned long *state)
 378{
 379        *state = 1;
 380
 381        return 0;
 382}
 383
 384static 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 */
 398static 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
 429err_out:
 430        acerhdf_revert_to_bios_mode();
 431        return -EINVAL;
 432}
 433
 434/* bind fan callbacks to fan device */
 435struct 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 */
 442static 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
 453static int __devinit acerhdf_probe(struct platform_device *device)
 454{
 455        return 0;
 456}
 457
 458static int acerhdf_remove(struct platform_device *device)
 459{
 460        return 0;
 461}
 462
 463static struct dev_pm_ops acerhdf_pm_ops = {
 464        .suspend = acerhdf_suspend,
 465        .freeze  = acerhdf_suspend,
 466};
 467
 468static 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 */
 480static 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
 543static 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
 557static 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
 566static 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
 583static 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
 596static 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
 614err_unreg:
 615        acerhdf_unregister_thermal();
 616        acerhdf_unregister_platform();
 617
 618out_err:
 619        return -ENODEV;
 620}
 621
 622static void __exit acerhdf_exit(void)
 623{
 624        acerhdf_change_fanstate(ACERHDF_FAN_AUTO);
 625        acerhdf_unregister_thermal();
 626        acerhdf_unregister_platform();
 627}
 628
 629MODULE_LICENSE("GPL");
 630MODULE_AUTHOR("Peter Feuerer");
 631MODULE_DESCRIPTION("Aspire One temperature and fan driver");
 632MODULE_ALIAS("dmi:*:*Acer*:*:");
 633MODULE_ALIAS("dmi:*:*Gateway*:*:");
 634MODULE_ALIAS("dmi:*:*Packard Bell*:*:");
 635
 636module_init(acerhdf_init);
 637module_exit(acerhdf_exit);
 638