.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * acerhdf - A driver which monitors the temperature |
---|
3 | 4 | * of the aspire one netbook, turns on/off the fan |
---|
4 | 5 | * as soon as the upper/lower threshold is reached. |
---|
5 | 6 | * |
---|
6 | | - * (C) 2009 - Peter Feuerer peter (a) piie.net |
---|
7 | | - * http://piie.net |
---|
| 7 | + * (C) 2009 - Peter Kaestle peter (a) piie.net |
---|
| 8 | + * https://piie.net |
---|
8 | 9 | * 2009 Borislav Petkov bp (a) alien8.de |
---|
9 | 10 | * |
---|
10 | 11 | * Inspired by and many thanks to: |
---|
.. | .. |
---|
15 | 16 | * o lkml - Matthew Garrett |
---|
16 | 17 | * - Borislav Petkov |
---|
17 | 18 | * - 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 | 19 | */ |
---|
33 | 20 | |
---|
34 | 21 | #define pr_fmt(fmt) "acerhdf: " fmt |
---|
.. | .. |
---|
86 | 73 | static unsigned int fanon = 60000; |
---|
87 | 74 | static unsigned int fanoff = 53000; |
---|
88 | 75 | static unsigned int verbose; |
---|
| 76 | +static unsigned int list_supported; |
---|
89 | 77 | static unsigned int fanstate = ACERHDF_FAN_AUTO; |
---|
90 | 78 | static char force_bios[16]; |
---|
91 | 79 | static char force_product[16]; |
---|
.. | .. |
---|
104 | 92 | MODULE_PARM_DESC(fanoff, "Turn the fan off below this temperature"); |
---|
105 | 93 | module_param(verbose, uint, 0600); |
---|
106 | 94 | MODULE_PARM_DESC(verbose, "Enable verbose dmesg output"); |
---|
| 95 | +module_param(list_supported, uint, 0600); |
---|
| 96 | +MODULE_PARM_DESC(list_supported, "List supported models and BIOS versions"); |
---|
107 | 97 | module_param_string(force_bios, force_bios, 16, 0); |
---|
108 | | -MODULE_PARM_DESC(force_bios, "Force BIOS version and omit BIOS check"); |
---|
| 98 | +MODULE_PARM_DESC(force_bios, "Pretend system has this known supported BIOS version"); |
---|
109 | 99 | module_param_string(force_product, force_product, 16, 0); |
---|
110 | | -MODULE_PARM_DESC(force_product, "Force BIOS product and omit BIOS check"); |
---|
| 100 | +MODULE_PARM_DESC(force_product, "Pretend system is this known supported model"); |
---|
111 | 101 | |
---|
112 | 102 | /* |
---|
113 | 103 | * cmd_off: to switch the fan completely off and check if the fan is off |
---|
.. | .. |
---|
130 | 120 | .moff = 0xff, |
---|
131 | 121 | }; |
---|
132 | 122 | |
---|
133 | | -/* BIOS settings */ |
---|
| 123 | +/* BIOS settings - only used during probe */ |
---|
134 | 124 | struct bios_settings { |
---|
135 | 125 | const char *vendor; |
---|
136 | 126 | const char *product; |
---|
.. | .. |
---|
141 | 131 | int mcmd_enable; |
---|
142 | 132 | }; |
---|
143 | 133 | |
---|
| 134 | +/* This could be a daughter struct in the above, but not worth the redirect */ |
---|
| 135 | +struct ctrl_settings { |
---|
| 136 | + u8 fanreg; |
---|
| 137 | + u8 tempreg; |
---|
| 138 | + struct fancmd cmd; |
---|
| 139 | + int mcmd_enable; |
---|
| 140 | +}; |
---|
| 141 | + |
---|
| 142 | +static struct ctrl_settings ctrl_cfg __read_mostly; |
---|
| 143 | + |
---|
144 | 144 | /* Register addresses and values for different BIOS versions */ |
---|
145 | | -static const struct bios_settings bios_tbl[] = { |
---|
| 145 | +static const struct bios_settings bios_tbl[] __initconst = { |
---|
146 | 146 | /* AOA110 */ |
---|
147 | 147 | {"Acer", "AOA110", "v0.3109", 0x55, 0x58, {0x1f, 0x00}, 0}, |
---|
148 | 148 | {"Acer", "AOA110", "v0.3114", 0x55, 0x58, {0x1f, 0x00}, 0}, |
---|
.. | .. |
---|
224 | 224 | {"Acer", "Aspire 5739G", "V1.3311", 0x55, 0x58, {0x20, 0x00}, 0}, |
---|
225 | 225 | /* Acer TravelMate 7730 */ |
---|
226 | 226 | {"Acer", "TravelMate 7730G", "v0.3509", 0x55, 0x58, {0xaf, 0x00}, 0}, |
---|
| 227 | + /* Acer Aspire 7551 */ |
---|
| 228 | + {"Acer", "Aspire 7551", "V1.18", 0x93, 0xa8, {0x14, 0x04}, 1}, |
---|
227 | 229 | /* Acer TravelMate TM8573T */ |
---|
228 | 230 | {"Acer", "TM8573T", "V1.13", 0x93, 0xa8, {0x14, 0x04}, 1}, |
---|
229 | 231 | /* Gateway */ |
---|
.. | .. |
---|
257 | 259 | {"", "", "", 0, 0, {0, 0}, 0} |
---|
258 | 260 | }; |
---|
259 | 261 | |
---|
260 | | -static const struct bios_settings *bios_cfg __read_mostly; |
---|
261 | | - |
---|
262 | 262 | /* |
---|
263 | 263 | * this struct is used to instruct thermal layer to use bang_bang instead of |
---|
264 | 264 | * default governor for acerhdf |
---|
.. | .. |
---|
271 | 271 | { |
---|
272 | 272 | u8 read_temp; |
---|
273 | 273 | |
---|
274 | | - if (ec_read(bios_cfg->tempreg, &read_temp)) |
---|
| 274 | + if (ec_read(ctrl_cfg.tempreg, &read_temp)) |
---|
275 | 275 | return -EINVAL; |
---|
276 | 276 | |
---|
277 | 277 | *temp = read_temp * 1000; |
---|
.. | .. |
---|
283 | 283 | { |
---|
284 | 284 | u8 fan; |
---|
285 | 285 | |
---|
286 | | - if (ec_read(bios_cfg->fanreg, &fan)) |
---|
| 286 | + if (ec_read(ctrl_cfg.fanreg, &fan)) |
---|
287 | 287 | return -EINVAL; |
---|
288 | 288 | |
---|
289 | | - if (fan != bios_cfg->cmd.cmd_off) |
---|
| 289 | + if (fan != ctrl_cfg.cmd.cmd_off) |
---|
290 | 290 | *state = ACERHDF_FAN_AUTO; |
---|
291 | 291 | else |
---|
292 | 292 | *state = ACERHDF_FAN_OFF; |
---|
.. | .. |
---|
307 | 307 | state = ACERHDF_FAN_AUTO; |
---|
308 | 308 | } |
---|
309 | 309 | |
---|
310 | | - cmd = (state == ACERHDF_FAN_OFF) ? bios_cfg->cmd.cmd_off |
---|
311 | | - : bios_cfg->cmd.cmd_auto; |
---|
| 310 | + cmd = (state == ACERHDF_FAN_OFF) ? ctrl_cfg.cmd.cmd_off |
---|
| 311 | + : ctrl_cfg.cmd.cmd_auto; |
---|
312 | 312 | fanstate = state; |
---|
313 | 313 | |
---|
314 | | - ec_write(bios_cfg->fanreg, cmd); |
---|
| 314 | + ec_write(ctrl_cfg.fanreg, cmd); |
---|
315 | 315 | |
---|
316 | | - if (bios_cfg->mcmd_enable && state == ACERHDF_FAN_OFF) { |
---|
| 316 | + if (ctrl_cfg.mcmd_enable && state == ACERHDF_FAN_OFF) { |
---|
317 | 317 | if (verbose) |
---|
318 | 318 | pr_notice("turning off fan manually\n"); |
---|
319 | 319 | ec_write(mcmd.mreg, mcmd.moff); |
---|
.. | .. |
---|
397 | 397 | { |
---|
398 | 398 | acerhdf_change_fanstate(ACERHDF_FAN_AUTO); |
---|
399 | 399 | kernelmode = 0; |
---|
400 | | - if (thz_dev) |
---|
401 | | - thz_dev->polling_delay = 0; |
---|
| 400 | + |
---|
402 | 401 | pr_notice("kernel mode fan control OFF\n"); |
---|
403 | 402 | } |
---|
404 | 403 | static inline void acerhdf_enable_kernelmode(void) |
---|
405 | 404 | { |
---|
406 | 405 | kernelmode = 1; |
---|
407 | 406 | |
---|
408 | | - thz_dev->polling_delay = interval*1000; |
---|
409 | | - thermal_zone_device_update(thz_dev, THERMAL_EVENT_UNSPECIFIED); |
---|
410 | 407 | pr_notice("kernel mode fan control ON\n"); |
---|
411 | | -} |
---|
412 | | - |
---|
413 | | -static int acerhdf_get_mode(struct thermal_zone_device *thermal, |
---|
414 | | - enum thermal_device_mode *mode) |
---|
415 | | -{ |
---|
416 | | - if (verbose) |
---|
417 | | - pr_notice("kernel mode fan control %d\n", kernelmode); |
---|
418 | | - |
---|
419 | | - *mode = (kernelmode) ? THERMAL_DEVICE_ENABLED |
---|
420 | | - : THERMAL_DEVICE_DISABLED; |
---|
421 | | - |
---|
422 | | - return 0; |
---|
423 | 408 | } |
---|
424 | 409 | |
---|
425 | 410 | /* |
---|
.. | .. |
---|
428 | 413 | * the temperature and the fan. |
---|
429 | 414 | * disabled: the BIOS takes control of the fan. |
---|
430 | 415 | */ |
---|
431 | | -static int acerhdf_set_mode(struct thermal_zone_device *thermal, |
---|
432 | | - enum thermal_device_mode mode) |
---|
| 416 | +static int acerhdf_change_mode(struct thermal_zone_device *thermal, |
---|
| 417 | + enum thermal_device_mode mode) |
---|
433 | 418 | { |
---|
434 | 419 | if (mode == THERMAL_DEVICE_DISABLED && kernelmode) |
---|
435 | 420 | acerhdf_revert_to_bios_mode(); |
---|
.. | .. |
---|
488 | 473 | .bind = acerhdf_bind, |
---|
489 | 474 | .unbind = acerhdf_unbind, |
---|
490 | 475 | .get_temp = acerhdf_get_ec_temp, |
---|
491 | | - .get_mode = acerhdf_get_mode, |
---|
492 | | - .set_mode = acerhdf_set_mode, |
---|
| 476 | + .change_mode = acerhdf_change_mode, |
---|
493 | 477 | .get_trip_type = acerhdf_get_trip_type, |
---|
494 | 478 | .get_trip_hyst = acerhdf_get_trip_hyst, |
---|
495 | 479 | .get_trip_temp = acerhdf_get_trip_temp, |
---|
.. | .. |
---|
616 | 600 | } |
---|
617 | 601 | |
---|
618 | 602 | /* check hardware */ |
---|
619 | | -static int acerhdf_check_hardware(void) |
---|
| 603 | +static int __init acerhdf_check_hardware(void) |
---|
620 | 604 | { |
---|
621 | 605 | char const *vendor, *version, *product; |
---|
622 | 606 | const struct bios_settings *bt = NULL; |
---|
| 607 | + int found = 0; |
---|
623 | 608 | |
---|
624 | 609 | /* get BIOS data */ |
---|
625 | 610 | vendor = dmi_get_system_info(DMI_SYS_VENDOR); |
---|
.. | .. |
---|
632 | 617 | } |
---|
633 | 618 | |
---|
634 | 619 | pr_info("Acer Aspire One Fan driver, v.%s\n", DRV_VER); |
---|
| 620 | + |
---|
| 621 | + if (list_supported) { |
---|
| 622 | + pr_info("List of supported Manufacturer/Model/BIOS:\n"); |
---|
| 623 | + pr_info("---------------------------------------------------\n"); |
---|
| 624 | + for (bt = bios_tbl; bt->vendor[0]; bt++) { |
---|
| 625 | + pr_info("%-13s | %-17s | %-10s\n", bt->vendor, |
---|
| 626 | + bt->product, bt->version); |
---|
| 627 | + } |
---|
| 628 | + pr_info("---------------------------------------------------\n"); |
---|
| 629 | + return -ECANCELED; |
---|
| 630 | + } |
---|
635 | 631 | |
---|
636 | 632 | if (force_bios[0]) { |
---|
637 | 633 | version = force_bios; |
---|
.. | .. |
---|
658 | 654 | if (str_starts_with(vendor, bt->vendor) && |
---|
659 | 655 | str_starts_with(product, bt->product) && |
---|
660 | 656 | str_starts_with(version, bt->version)) { |
---|
661 | | - bios_cfg = bt; |
---|
| 657 | + found = 1; |
---|
662 | 658 | break; |
---|
663 | 659 | } |
---|
664 | 660 | } |
---|
665 | 661 | |
---|
666 | | - if (!bios_cfg) { |
---|
| 662 | + if (!found) { |
---|
667 | 663 | pr_err("unknown (unsupported) BIOS version %s/%s/%s, please report, aborting!\n", |
---|
668 | 664 | vendor, product, version); |
---|
669 | 665 | return -EINVAL; |
---|
670 | 666 | } |
---|
| 667 | + |
---|
| 668 | + /* Copy control settings from BIOS table before we free it. */ |
---|
| 669 | + ctrl_cfg.fanreg = bt->fanreg; |
---|
| 670 | + ctrl_cfg.tempreg = bt->tempreg; |
---|
| 671 | + memcpy(&ctrl_cfg.cmd, &bt->cmd, sizeof(struct fancmd)); |
---|
| 672 | + ctrl_cfg.mcmd_enable = bt->mcmd_enable; |
---|
671 | 673 | |
---|
672 | 674 | /* |
---|
673 | 675 | * if started with kernel mode off, prevent the kernel from switching |
---|
.. | .. |
---|
675 | 677 | */ |
---|
676 | 678 | if (!kernelmode) { |
---|
677 | 679 | pr_notice("Fan control off, to enable do:\n"); |
---|
678 | | - pr_notice("echo -n \"enabled\" > /sys/class/thermal/thermal_zone0/mode\n"); |
---|
| 680 | + pr_notice("echo -n \"enabled\" > /sys/class/thermal/thermal_zoneN/mode # N=0,1,2...\n"); |
---|
679 | 681 | } |
---|
680 | 682 | |
---|
681 | 683 | return 0; |
---|
682 | 684 | } |
---|
683 | 685 | |
---|
684 | | -static int acerhdf_register_platform(void) |
---|
| 686 | +static int __init acerhdf_register_platform(void) |
---|
685 | 687 | { |
---|
686 | 688 | int err = 0; |
---|
687 | 689 | |
---|
.. | .. |
---|
713 | 715 | platform_driver_unregister(&acerhdf_driver); |
---|
714 | 716 | } |
---|
715 | 717 | |
---|
716 | | -static int acerhdf_register_thermal(void) |
---|
| 718 | +static int __init acerhdf_register_thermal(void) |
---|
717 | 719 | { |
---|
| 720 | + int ret; |
---|
| 721 | + |
---|
718 | 722 | cl_dev = thermal_cooling_device_register("acerhdf-fan", NULL, |
---|
719 | 723 | &acerhdf_cooling_ops); |
---|
720 | 724 | |
---|
.. | .. |
---|
727 | 731 | (kernelmode) ? interval*1000 : 0); |
---|
728 | 732 | if (IS_ERR(thz_dev)) |
---|
729 | 733 | return -EINVAL; |
---|
| 734 | + |
---|
| 735 | + if (kernelmode) |
---|
| 736 | + ret = thermal_zone_device_enable(thz_dev); |
---|
| 737 | + else |
---|
| 738 | + ret = thermal_zone_device_disable(thz_dev); |
---|
| 739 | + if (ret) |
---|
| 740 | + return ret; |
---|
730 | 741 | |
---|
731 | 742 | if (strcmp(thz_dev->governor->name, |
---|
732 | 743 | acerhdf_zone_params.governor_name)) { |
---|
.. | .. |
---|
785 | 796 | } |
---|
786 | 797 | |
---|
787 | 798 | MODULE_LICENSE("GPL"); |
---|
788 | | -MODULE_AUTHOR("Peter Feuerer"); |
---|
| 799 | +MODULE_AUTHOR("Peter Kaestle"); |
---|
789 | 800 | MODULE_DESCRIPTION("Aspire One temperature and fan driver"); |
---|
790 | 801 | MODULE_ALIAS("dmi:*:*Acer*:pnAOA*:"); |
---|
791 | 802 | MODULE_ALIAS("dmi:*:*Acer*:pnAO751h*:"); |
---|
.. | .. |
---|
799 | 810 | MODULE_ALIAS("dmi:*:*Acer*:pnAspire*One*753:"); |
---|
800 | 811 | MODULE_ALIAS("dmi:*:*Acer*:pnAspire*5315:"); |
---|
801 | 812 | MODULE_ALIAS("dmi:*:*Acer*:TravelMate*7730G:"); |
---|
| 813 | +MODULE_ALIAS("dmi:*:*Acer*:pnAspire*7551:"); |
---|
802 | 814 | MODULE_ALIAS("dmi:*:*Acer*:TM8573T:"); |
---|
803 | 815 | MODULE_ALIAS("dmi:*:*Gateway*:pnAOA*:"); |
---|
804 | 816 | MODULE_ALIAS("dmi:*:*Gateway*:pnLT31*:"); |
---|
.. | .. |
---|
808 | 820 | MODULE_ALIAS("dmi:*:*Packard*Bell*:pnENBFT*:"); |
---|
809 | 821 | MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTMA*:"); |
---|
810 | 822 | MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTVR46*:"); |
---|
811 | | -MODULE_ALIAS("dmi:*:*Acer*:pnExtensa 5420*:"); |
---|
| 823 | +MODULE_ALIAS("dmi:*:*Acer*:pnExtensa*5420*:"); |
---|
812 | 824 | |
---|
813 | 825 | module_init(acerhdf_init); |
---|
814 | 826 | module_exit(acerhdf_exit); |
---|