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/dmi.h>
  39#include <linux/acpi.h>
  40#include <linux/thermal.h>
  41#include <linux/platform_device.h>
  42
  43/*
  44 * The driver is started with "kernel mode off" by default. That means, the BIOS
  45 * is still in control of the fan. In this mode the driver allows to read the
  46 * temperature of the cpu and a userspace tool may take over control of the fan.
  47 * If the driver is switched to "kernel mode" (e.g. via module parameter) the
  48 * driver is in full control of the fan. If you want the module to be started in
  49 * kernel mode by default, define the following:
  50 */
  51#undef START_IN_KERNEL_MODE
  52
  53#define DRV_VER "0.5.24"
  54
  55/*
  56 * According to the Atom N270 datasheet,
  57 * (http://download.intel.com/design/processor/datashts/320032.pdf) the
  58 * CPU's optimal operating limits denoted in junction temperature as
  59 * measured by the on-die thermal monitor are within 0 <= Tj <= 90. So,
  60 * assume 89°C is critical temperature.
  61 */
  62#define ACERHDF_TEMP_CRIT 89000
  63#define ACERHDF_FAN_OFF 0
  64#define ACERHDF_FAN_AUTO 1
  65
  66/*
  67 * No matter what value the user puts into the fanon variable, turn on the fan
  68 * at 80 degree Celsius to prevent hardware damage
  69 */
  70#define ACERHDF_MAX_FANON 80000
  71
  72/*
  73 * Maximum interval between two temperature checks is 15 seconds, as the die
  74 * can get hot really fast under heavy load (plus we shouldn't forget about
  75 * possible impact of _external_ aggressive sources such as heaters, sun etc.)
  76 */
  77#define ACERHDF_MAX_INTERVAL 15
  78
  79#ifdef START_IN_KERNEL_MODE
  80static int kernelmode = 1;
  81#else
  82static int kernelmode;
  83#endif
  84
  85static unsigned int interval = 10;
  86static unsigned int fanon = 63000;
  87static unsigned int fanoff = 58000;
  88static unsigned int verbose;
  89static unsigned int fanstate = ACERHDF_FAN_AUTO;
  90static char force_bios[16];
  91static char force_product[16];
  92static unsigned int prev_interval;
  93static struct thermal_zone_device *thz_dev;
  94static struct thermal_cooling_device *cl_dev;
  95static struct platform_device *acerhdf_dev;
  96
  97module_param(kernelmode, uint, 0);
  98MODULE_PARM_DESC(kernelmode, "Kernel mode fan control on / off");
  99module_param(interval, uint, 0600);
 100MODULE_PARM_DESC(interval, "Polling interval of temperature check");
 101module_param(fanon, uint, 0600);
 102MODULE_PARM_DESC(fanon, "Turn the fan on above this temperature");
 103module_param(fanoff, uint, 0600);
 104MODULE_PARM_DESC(fanoff, "Turn the fan off below this temperature");
 105module_param(verbose, uint, 0600);
 106MODULE_PARM_DESC(verbose, "Enable verbose dmesg output");
 107module_param_string(force_bios, force_bios, 16, 0);
 108MODULE_PARM_DESC(force_bios, "Force BIOS version and omit BIOS check");
 109module_param_string(force_product, force_product, 16, 0);
 110MODULE_PARM_DESC(force_product, "Force BIOS product and omit BIOS check");
 111
 112/*
 113 * cmd_off: to switch the fan completely off and check if the fan is off
 114 *      cmd_auto: to set the BIOS in control of the fan. The BIOS regulates then
 115 *              the fan speed depending on the temperature
 116 */
 117struct fancmd {
 118        u8 cmd_off;
 119        u8 cmd_auto;
 120};
 121
 122/* BIOS settings */
 123struct bios_settings_t {
 124        const char *vendor;
 125        const char *product;
 126        const char *version;
 127        unsigned char fanreg;
 128        unsigned char tempreg;
 129        struct fancmd cmd;
 130};
 131
 132/* Register addresses and values for different BIOS versions */
 133static const struct bios_settings_t bios_tbl[] = {
 134        /* AOA110 */
 135        {"Acer", "AOA110", "v0.3109", 0x55, 0x58, {0x1f, 0x00} },
 136        {"Acer", "AOA110", "v0.3114", 0x55, 0x58, {0x1f, 0x00} },
 137        {"Acer", "AOA110", "v0.3301", 0x55, 0x58, {0xaf, 0x00} },
 138        {"Acer", "AOA110", "v0.3304", 0x55, 0x58, {0xaf, 0x00} },
 139        {"Acer", "AOA110", "v0.3305", 0x55, 0x58, {0xaf, 0x00} },
 140        {"Acer", "AOA110", "v0.3307", 0x55, 0x58, {0xaf, 0x00} },
 141        {"Acer", "AOA110", "v0.3308", 0x55, 0x58, {0x21, 0x00} },
 142        {"Acer", "AOA110", "v0.3309", 0x55, 0x58, {0x21, 0x00} },
 143        {"Acer", "AOA110", "v0.3310", 0x55, 0x58, {0x21, 0x00} },
 144        /* AOA150 */
 145        {"Acer", "AOA150", "v0.3114", 0x55, 0x58, {0x1f, 0x00} },
 146        {"Acer", "AOA150", "v0.3301", 0x55, 0x58, {0x20, 0x00} },
 147        {"Acer", "AOA150", "v0.3304", 0x55, 0x58, {0x20, 0x00} },
 148        {"Acer", "AOA150", "v0.3305", 0x55, 0x58, {0x20, 0x00} },
 149        {"Acer", "AOA150", "v0.3307", 0x55, 0x58, {0x20, 0x00} },
 150        {"Acer", "AOA150", "v0.3308", 0x55, 0x58, {0x20, 0x00} },
 151        {"Acer", "AOA150", "v0.3309", 0x55, 0x58, {0x20, 0x00} },
 152        {"Acer", "AOA150", "v0.3310", 0x55, 0x58, {0x20, 0x00} },
 153        /* Acer 1410 */
 154        {"Acer", "Aspire 1410", "v0.3108", 0x55, 0x58, {0x9e, 0x00} },
 155        {"Acer", "Aspire 1410", "v0.3113", 0x55, 0x58, {0x9e, 0x00} },
 156        {"Acer", "Aspire 1410", "v0.3115", 0x55, 0x58, {0x9e, 0x00} },
 157        {"Acer", "Aspire 1410", "v0.3117", 0x55, 0x58, {0x9e, 0x00} },
 158        {"Acer", "Aspire 1410", "v0.3119", 0x55, 0x58, {0x9e, 0x00} },
 159        {"Acer", "Aspire 1410", "v0.3120", 0x55, 0x58, {0x9e, 0x00} },
 160        {"Acer", "Aspire 1410", "v1.3204", 0x55, 0x58, {0x9e, 0x00} },
 161        {"Acer", "Aspire 1410", "v1.3303", 0x55, 0x58, {0x9e, 0x00} },
 162        {"Acer", "Aspire 1410", "v1.3308", 0x55, 0x58, {0x9e, 0x00} },
 163        {"Acer", "Aspire 1410", "v1.3310", 0x55, 0x58, {0x9e, 0x00} },
 164        /* Acer 1810xx */
 165        {"Acer", "Aspire 1810TZ", "v0.3108", 0x55, 0x58, {0x9e, 0x00} },
 166        {"Acer", "Aspire 1810T",  "v0.3108", 0x55, 0x58, {0x9e, 0x00} },
 167        {"Acer", "Aspire 1810TZ", "v0.3113", 0x55, 0x58, {0x9e, 0x00} },
 168        {"Acer", "Aspire 1810T",  "v0.3113", 0x55, 0x58, {0x9e, 0x00} },
 169        {"Acer", "Aspire 1810TZ", "v0.3115", 0x55, 0x58, {0x9e, 0x00} },
 170        {"Acer", "Aspire 1810T",  "v0.3115", 0x55, 0x58, {0x9e, 0x00} },
 171        {"Acer", "Aspire 1810TZ", "v0.3117", 0x55, 0x58, {0x9e, 0x00} },
 172        {"Acer", "Aspire 1810T",  "v0.3117", 0x55, 0x58, {0x9e, 0x00} },
 173        {"Acer", "Aspire 1810TZ", "v0.3119", 0x55, 0x58, {0x9e, 0x00} },
 174        {"Acer", "Aspire 1810T",  "v0.3119", 0x55, 0x58, {0x9e, 0x00} },
 175        {"Acer", "Aspire 1810TZ", "v0.3120", 0x55, 0x58, {0x9e, 0x00} },
 176        {"Acer", "Aspire 1810T",  "v0.3120", 0x55, 0x58, {0x9e, 0x00} },
 177        {"Acer", "Aspire 1810TZ", "v1.3204", 0x55, 0x58, {0x9e, 0x00} },
 178        {"Acer", "Aspire 1810T",  "v1.3204", 0x55, 0x58, {0x9e, 0x00} },
 179        {"Acer", "Aspire 1810TZ", "v1.3303", 0x55, 0x58, {0x9e, 0x00} },
 180        {"Acer", "Aspire 1810T",  "v1.3303", 0x55, 0x58, {0x9e, 0x00} },
 181        {"Acer", "Aspire 1810TZ", "v1.3308", 0x55, 0x58, {0x9e, 0x00} },
 182        {"Acer", "Aspire 1810T",  "v1.3308", 0x55, 0x58, {0x9e, 0x00} },
 183        {"Acer", "Aspire 1810TZ", "v1.3310", 0x55, 0x58, {0x9e, 0x00} },
 184        {"Acer", "Aspire 1810T",  "v1.3310", 0x55, 0x58, {0x9e, 0x00} },
 185        {"Acer", "Aspire 1810TZ", "v1.3314", 0x55, 0x58, {0x9e, 0x00} },
 186        /* Acer 531 */
 187        {"Acer", "AO531h", "v0.3201", 0x55, 0x58, {0x20, 0x00} },
 188        /* Gateway */
 189        {"Gateway", "AOA110", "v0.3103", 0x55, 0x58, {0x21, 0x00} },
 190        {"Gateway", "AOA150", "v0.3103", 0x55, 0x58, {0x20, 0x00} },
 191        {"Gateway", "LT31",   "v1.3103", 0x55, 0x58, {0x9e, 0x00} },
 192        {"Gateway", "LT31",   "v1.3201", 0x55, 0x58, {0x9e, 0x00} },
 193        {"Gateway", "LT31",   "v1.3302", 0x55, 0x58, {0x9e, 0x00} },
 194        /* Packard Bell */
 195        {"Packard Bell", "DOA150", "v0.3104", 0x55, 0x58, {0x21, 0x00} },
 196        {"Packard Bell", "DOA150", "v0.3105", 0x55, 0x58, {0x20, 0x00} },
 197        {"Packard Bell", "AOA110", "v0.3105", 0x55, 0x58, {0x21, 0x00} },
 198        {"Packard Bell", "AOA150", "v0.3105", 0x55, 0x58, {0x20, 0x00} },
 199        {"Packard Bell", "DOTMU",  "v1.3303", 0x55, 0x58, {0x9e, 0x00} },
 200        {"Packard Bell", "DOTMU",  "v0.3120", 0x55, 0x58, {0x9e, 0x00} },
 201        {"Packard Bell", "DOTMU",  "v0.3108", 0x55, 0x58, {0x9e, 0x00} },
 202        {"Packard Bell", "DOTMU",  "v0.3113", 0x55, 0x58, {0x9e, 0x00} },
 203        {"Packard Bell", "DOTMU",  "v0.3115", 0x55, 0x58, {0x9e, 0x00} },
 204        {"Packard Bell", "DOTMU",  "v0.3117", 0x55, 0x58, {0x9e, 0x00} },
 205        {"Packard Bell", "DOTMU",  "v0.3119", 0x55, 0x58, {0x9e, 0x00} },
 206        {"Packard Bell", "DOTMU",  "v1.3204", 0x55, 0x58, {0x9e, 0x00} },
 207        {"Packard Bell", "DOTMA",  "v1.3201", 0x55, 0x58, {0x9e, 0x00} },
 208        {"Packard Bell", "DOTMA",  "v1.3302", 0x55, 0x58, {0x9e, 0x00} },
 209        /* pewpew-terminator */
 210        {"", "", "", 0, 0, {0, 0} }
 211};
 212
 213static const struct bios_settings_t *bios_cfg __read_mostly;
 214
 215static int acerhdf_get_temp(int *temp)
 216{
 217        u8 read_temp;
 218
 219        if (ec_read(bios_cfg->tempreg, &read_temp))
 220                return -EINVAL;
 221
 222        *temp = read_temp * 1000;
 223
 224        return 0;
 225}
 226
 227static int acerhdf_get_fanstate(int *state)
 228{
 229        u8 fan;
 230
 231        if (ec_read(bios_cfg->fanreg, &fan))
 232                return -EINVAL;
 233
 234        if (fan != bios_cfg->cmd.cmd_off)
 235                *state = ACERHDF_FAN_AUTO;
 236        else
 237                *state = ACERHDF_FAN_OFF;
 238
 239        return 0;
 240}
 241
 242static void acerhdf_change_fanstate(int state)
 243{
 244        unsigned char cmd;
 245
 246        if (verbose)
 247                pr_notice("fan %s\n", (state == ACERHDF_FAN_OFF) ?
 248                                "OFF" : "ON");
 249
 250        if ((state != ACERHDF_FAN_OFF) && (state != ACERHDF_FAN_AUTO)) {
 251                pr_err("invalid fan state %d requested, setting to auto!\n",
 252                        state);
 253                state = ACERHDF_FAN_AUTO;
 254        }
 255
 256        cmd = (state == ACERHDF_FAN_OFF) ? bios_cfg->cmd.cmd_off
 257                                         : bios_cfg->cmd.cmd_auto;
 258        fanstate = state;
 259
 260        ec_write(bios_cfg->fanreg, cmd);
 261}
 262
 263static void acerhdf_check_param(struct thermal_zone_device *thermal)
 264{
 265        if (fanon > ACERHDF_MAX_FANON) {
 266                pr_err("fanon temperature too high, set to %d\n",
 267                                ACERHDF_MAX_FANON);
 268                fanon = ACERHDF_MAX_FANON;
 269        }
 270
 271        if (kernelmode && prev_interval != interval) {
 272                if (interval > ACERHDF_MAX_INTERVAL) {
 273                        pr_err("interval too high, set to %d\n",
 274                                ACERHDF_MAX_INTERVAL);
 275                        interval = ACERHDF_MAX_INTERVAL;
 276                }
 277                if (verbose)
 278                        pr_notice("interval changed to: %d\n",
 279                                        interval);
 280                thermal->polling_delay = interval*1000;
 281                prev_interval = interval;
 282        }
 283}
 284
 285/*
 286 * This is the thermal zone callback which does the delayed polling of the fan
 287 * state. We do check /sysfs-originating settings here in acerhdf_check_param()
 288 * as late as the polling interval is since we can't do that in the respective
 289 * accessors of the module parameters.
 290 */
 291static int acerhdf_get_ec_temp(struct thermal_zone_device *thermal,
 292                               unsigned long *t)
 293{
 294        int temp, err = 0;
 295
 296        acerhdf_check_param(thermal);
 297
 298        err = acerhdf_get_temp(&temp);
 299        if (err)
 300                return err;
 301
 302        if (verbose)
 303                pr_notice("temp %d\n", temp);
 304
 305        *t = temp;
 306        return 0;
 307}
 308
 309static int acerhdf_bind(struct thermal_zone_device *thermal,
 310                        struct thermal_cooling_device *cdev)
 311{
 312        /* if the cooling device is the one from acerhdf bind it */
 313        if (cdev != cl_dev)
 314                return 0;
 315
 316        if (thermal_zone_bind_cooling_device(thermal, 0, cdev)) {
 317                pr_err("error binding cooling dev\n");
 318                return -EINVAL;
 319        }
 320        return 0;
 321}
 322
 323static int acerhdf_unbind(struct thermal_zone_device *thermal,
 324                          struct thermal_cooling_device *cdev)
 325{
 326        if (cdev != cl_dev)
 327                return 0;
 328
 329        if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) {
 330                pr_err("error unbinding cooling dev\n");
 331                return -EINVAL;
 332        }
 333        return 0;
 334}
 335
 336static inline void acerhdf_revert_to_bios_mode(void)
 337{
 338        acerhdf_change_fanstate(ACERHDF_FAN_AUTO);
 339        kernelmode = 0;
 340        if (thz_dev)
 341                thz_dev->polling_delay = 0;
 342        pr_notice("kernel mode fan control OFF\n");
 343}
 344static inline void acerhdf_enable_kernelmode(void)
 345{
 346        kernelmode = 1;
 347
 348        thz_dev->polling_delay = interval*1000;
 349        thermal_zone_device_update(thz_dev);
 350        pr_notice("kernel mode fan control ON\n");
 351}
 352
 353static int acerhdf_get_mode(struct thermal_zone_device *thermal,
 354                            enum thermal_device_mode *mode)
 355{
 356        if (verbose)
 357                pr_notice("kernel mode fan control %d\n", kernelmode);
 358
 359        *mode = (kernelmode) ? THERMAL_DEVICE_ENABLED
 360                             : THERMAL_DEVICE_DISABLED;
 361
 362        return 0;
 363}
 364
 365/*
 366 * set operation mode;
 367 * enabled: the thermal layer of the kernel takes care about
 368 *          the temperature and the fan.
 369 * disabled: the BIOS takes control of the fan.
 370 */
 371static int acerhdf_set_mode(struct thermal_zone_device *thermal,
 372                            enum thermal_device_mode mode)
 373{
 374        if (mode == THERMAL_DEVICE_DISABLED && kernelmode)
 375                acerhdf_revert_to_bios_mode();
 376        else if (mode == THERMAL_DEVICE_ENABLED && !kernelmode)
 377                acerhdf_enable_kernelmode();
 378
 379        return 0;
 380}
 381
 382static int acerhdf_get_trip_type(struct thermal_zone_device *thermal, int trip,
 383                                 enum thermal_trip_type *type)
 384{
 385        if (trip == 0)
 386                *type = THERMAL_TRIP_ACTIVE;
 387
 388        return 0;
 389}
 390
 391static int acerhdf_get_trip_temp(struct thermal_zone_device *thermal, int trip,
 392                                 unsigned long *temp)
 393{
 394        if (trip == 0)
 395                *temp = fanon;
 396
 397        return 0;
 398}
 399
 400static int acerhdf_get_crit_temp(struct thermal_zone_device *thermal,
 401                                 unsigned long *temperature)
 402{
 403        *temperature = ACERHDF_TEMP_CRIT;
 404        return 0;
 405}
 406
 407/* bind callback functions to thermalzone */
 408static struct thermal_zone_device_ops acerhdf_dev_ops = {
 409        .bind = acerhdf_bind,
 410        .unbind = acerhdf_unbind,
 411        .get_temp = acerhdf_get_ec_temp,
 412        .get_mode = acerhdf_get_mode,
 413        .set_mode = acerhdf_set_mode,
 414        .get_trip_type = acerhdf_get_trip_type,
 415        .get_trip_temp = acerhdf_get_trip_temp,
 416        .get_crit_temp = acerhdf_get_crit_temp,
 417};
 418
 419
 420/*
 421 * cooling device callback functions
 422 * get maximal fan cooling state
 423 */
 424static int acerhdf_get_max_state(struct thermal_cooling_device *cdev,
 425                                 unsigned long *state)
 426{
 427        *state = 1;
 428
 429        return 0;
 430}
 431
 432static int acerhdf_get_cur_state(struct thermal_cooling_device *cdev,
 433                                 unsigned long *state)
 434{
 435        int err = 0, tmp;
 436
 437        err = acerhdf_get_fanstate(&tmp);
 438        if (err)
 439                return err;
 440
 441        *state = (tmp == ACERHDF_FAN_AUTO) ? 1 : 0;
 442        return 0;
 443}
 444
 445/* change current fan state - is overwritten when running in kernel mode */
 446static int acerhdf_set_cur_state(struct thermal_cooling_device *cdev,
 447                                 unsigned long state)
 448{
 449        int cur_temp, cur_state, err = 0;
 450
 451        if (!kernelmode)
 452                return 0;
 453
 454        err = acerhdf_get_temp(&cur_temp);
 455        if (err) {
 456                pr_err("error reading temperature, hand off control to BIOS\n");
 457                goto err_out;
 458        }
 459
 460        err = acerhdf_get_fanstate(&cur_state);
 461        if (err) {
 462                pr_err("error reading fan state, hand off control to BIOS\n");
 463                goto err_out;
 464        }
 465
 466        if (state == 0) {
 467                /* turn fan off only if below fanoff temperature */
 468                if ((cur_state == ACERHDF_FAN_AUTO) &&
 469                    (cur_temp < fanoff))
 470                        acerhdf_change_fanstate(ACERHDF_FAN_OFF);
 471        } else {
 472                if (cur_state == ACERHDF_FAN_OFF)
 473                        acerhdf_change_fanstate(ACERHDF_FAN_AUTO);
 474        }
 475        return 0;
 476
 477err_out:
 478        acerhdf_revert_to_bios_mode();
 479        return -EINVAL;
 480}
 481
 482/* bind fan callbacks to fan device */
 483static struct thermal_cooling_device_ops acerhdf_cooling_ops = {
 484        .get_max_state = acerhdf_get_max_state,
 485        .get_cur_state = acerhdf_get_cur_state,
 486        .set_cur_state = acerhdf_set_cur_state,
 487};
 488
 489/* suspend / resume functionality */
 490static int acerhdf_suspend(struct device *dev)
 491{
 492        if (kernelmode)
 493                acerhdf_change_fanstate(ACERHDF_FAN_AUTO);
 494
 495        if (verbose)
 496                pr_notice("going suspend\n");
 497
 498        return 0;
 499}
 500
 501static int __devinit acerhdf_probe(struct platform_device *device)
 502{
 503        return 0;
 504}
 505
 506static int acerhdf_remove(struct platform_device *device)
 507{
 508        return 0;
 509}
 510
 511static const struct dev_pm_ops acerhdf_pm_ops = {
 512        .suspend = acerhdf_suspend,
 513        .freeze  = acerhdf_suspend,
 514};
 515
 516static struct platform_driver acerhdf_driver = {
 517        .driver = {
 518                .name  = "acerhdf",
 519                .owner = THIS_MODULE,
 520                .pm    = &acerhdf_pm_ops,
 521        },
 522        .probe = acerhdf_probe,
 523        .remove = acerhdf_remove,
 524};
 525
 526/* checks if str begins with start */
 527static int str_starts_with(const char *str, const char *start)
 528{
 529        unsigned long str_len = 0, start_len = 0;
 530
 531        str_len = strlen(str);
 532        start_len = strlen(start);
 533
 534        if (str_len >= start_len &&
 535                        !strncmp(str, start, start_len))
 536                return 1;
 537
 538        return 0;
 539}
 540
 541/* check hardware */
 542static int acerhdf_check_hardware(void)
 543{
 544        char const *vendor, *version, *product;
 545        const struct bios_settings_t *bt = NULL;
 546
 547        /* get BIOS data */
 548        vendor  = dmi_get_system_info(DMI_SYS_VENDOR);
 549        version = dmi_get_system_info(DMI_BIOS_VERSION);
 550        product = dmi_get_system_info(DMI_PRODUCT_NAME);
 551
 552        if (!vendor || !version || !product) {
 553                pr_err("error getting hardware information\n");
 554                return -EINVAL;
 555        }
 556
 557        pr_info("Acer Aspire One Fan driver, v.%s\n", DRV_VER);
 558
 559        if (force_bios[0]) {
 560                version = force_bios;
 561                pr_info("forcing BIOS version: %s\n", version);
 562                kernelmode = 0;
 563        }
 564
 565        if (force_product[0]) {
 566                product = force_product;
 567                pr_info("forcing BIOS product: %s\n", product);
 568                kernelmode = 0;
 569        }
 570
 571        if (verbose)
 572                pr_info("BIOS info: %s %s, product: %s\n",
 573                        vendor, version, product);
 574
 575        /* search BIOS version and vendor in BIOS settings table */
 576        for (bt = bios_tbl; bt->vendor[0]; bt++) {
 577                /*
 578                 * check if actual hardware BIOS vendor, product and version
 579                 * IDs start with the strings of BIOS table entry
 580                 */
 581                if (str_starts_with(vendor, bt->vendor) &&
 582                                str_starts_with(product, bt->product) &&
 583                                str_starts_with(version, bt->version)) {
 584                        bios_cfg = bt;
 585                        break;
 586                }
 587        }
 588
 589        if (!bios_cfg) {
 590                pr_err("unknown (unsupported) BIOS version %s/%s/%s, "
 591                        "please report, aborting!\n", vendor, product, version);
 592                return -EINVAL;
 593        }
 594
 595        /*
 596         * if started with kernel mode off, prevent the kernel from switching
 597         * off the fan
 598         */
 599        if (!kernelmode) {
 600                pr_notice("Fan control off, to enable do:\n");
 601                pr_notice("echo -n \"enabled\" > "
 602                        "/sys/class/thermal/thermal_zone0/mode\n");
 603        }
 604
 605        return 0;
 606}
 607
 608static int acerhdf_register_platform(void)
 609{
 610        int err = 0;
 611
 612        err = platform_driver_register(&acerhdf_driver);
 613        if (err)
 614                return err;
 615
 616        acerhdf_dev = platform_device_alloc("acerhdf", -1);
 617        if (!acerhdf_dev) {
 618                err = -ENOMEM;
 619                goto err_device_alloc;
 620        }
 621        err = platform_device_add(acerhdf_dev);
 622        if (err)
 623                goto err_device_add;
 624
 625        return 0;
 626
 627err_device_add:
 628        platform_device_put(acerhdf_dev);
 629err_device_alloc:
 630        platform_driver_unregister(&acerhdf_driver);
 631        return err;
 632}
 633
 634static void acerhdf_unregister_platform(void)
 635{
 636        platform_device_unregister(acerhdf_dev);
 637        platform_driver_unregister(&acerhdf_driver);
 638}
 639
 640static int acerhdf_register_thermal(void)
 641{
 642        cl_dev = thermal_cooling_device_register("acerhdf-fan", NULL,
 643                                                 &acerhdf_cooling_ops);
 644
 645        if (IS_ERR(cl_dev))
 646                return -EINVAL;
 647
 648        thz_dev = thermal_zone_device_register("acerhdf", 1, NULL,
 649                                              &acerhdf_dev_ops, 0, 0, 0,
 650                                              (kernelmode) ? interval*1000 : 0);
 651        if (IS_ERR(thz_dev))
 652                return -EINVAL;
 653
 654        return 0;
 655}
 656
 657static void acerhdf_unregister_thermal(void)
 658{
 659        if (cl_dev) {
 660                thermal_cooling_device_unregister(cl_dev);
 661                cl_dev = NULL;
 662        }
 663
 664        if (thz_dev) {
 665                thermal_zone_device_unregister(thz_dev);
 666                thz_dev = NULL;
 667        }
 668}
 669
 670static int __init acerhdf_init(void)
 671{
 672        int err = 0;
 673
 674        err = acerhdf_check_hardware();
 675        if (err)
 676                goto out_err;
 677
 678        err = acerhdf_register_platform();
 679        if (err)
 680                goto out_err;
 681
 682        err = acerhdf_register_thermal();
 683        if (err)
 684                goto err_unreg;
 685
 686        return 0;
 687
 688err_unreg:
 689        acerhdf_unregister_thermal();
 690        acerhdf_unregister_platform();
 691
 692out_err:
 693        return err;
 694}
 695
 696static void __exit acerhdf_exit(void)
 697{
 698        acerhdf_change_fanstate(ACERHDF_FAN_AUTO);
 699        acerhdf_unregister_thermal();
 700        acerhdf_unregister_platform();
 701}
 702
 703MODULE_LICENSE("GPL");
 704MODULE_AUTHOR("Peter Feuerer");
 705MODULE_DESCRIPTION("Aspire One temperature and fan driver");
 706MODULE_ALIAS("dmi:*:*Acer*:pnAOA*:");
 707MODULE_ALIAS("dmi:*:*Acer*:pnAspire*1410*:");
 708MODULE_ALIAS("dmi:*:*Acer*:pnAspire*1810*:");
 709MODULE_ALIAS("dmi:*:*Acer*:pnAO531*:");
 710MODULE_ALIAS("dmi:*:*Gateway*:pnAOA*:");
 711MODULE_ALIAS("dmi:*:*Gateway*:pnLT31*:");
 712MODULE_ALIAS("dmi:*:*Packard*Bell*:pnAOA*:");
 713MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOA*:");
 714MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTMU*:");
 715MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTMA*:");
 716
 717module_init(acerhdf_init);
 718module_exit(acerhdf_exit);
 719
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.