162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * acerhdf - A driver which monitors the temperature 462306a36Sopenharmony_ci * of the aspire one netbook, turns on/off the fan 562306a36Sopenharmony_ci * as soon as the upper/lower threshold is reached. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * (C) 2009 - Peter Kaestle peter (a) piie.net 862306a36Sopenharmony_ci * https://piie.net 962306a36Sopenharmony_ci * 2009 Borislav Petkov bp (a) alien8.de 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Inspired by and many thanks to: 1262306a36Sopenharmony_ci * o acerfand - Rachel Greenham 1362306a36Sopenharmony_ci * o acer_ec.pl - Michael Kurz michi.kurz (at) googlemail.com 1462306a36Sopenharmony_ci * - Petr Tomasek tomasek (#) etf,cuni,cz 1562306a36Sopenharmony_ci * - Carlos Corbacho cathectic (at) gmail.com 1662306a36Sopenharmony_ci * o lkml - Matthew Garrett 1762306a36Sopenharmony_ci * - Borislav Petkov 1862306a36Sopenharmony_ci * - Andreas Mohr 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define pr_fmt(fmt) "acerhdf: " fmt 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <linux/kernel.h> 2462306a36Sopenharmony_ci#include <linux/module.h> 2562306a36Sopenharmony_ci#include <linux/dmi.h> 2662306a36Sopenharmony_ci#include <linux/acpi.h> 2762306a36Sopenharmony_ci#include <linux/thermal.h> 2862306a36Sopenharmony_ci#include <linux/platform_device.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* 3162306a36Sopenharmony_ci * The driver is started with "kernel mode off" by default. That means, the BIOS 3262306a36Sopenharmony_ci * is still in control of the fan. In this mode the driver allows to read the 3362306a36Sopenharmony_ci * temperature of the cpu and a userspace tool may take over control of the fan. 3462306a36Sopenharmony_ci * If the driver is switched to "kernel mode" (e.g. via module parameter) the 3562306a36Sopenharmony_ci * driver is in full control of the fan. If you want the module to be started in 3662306a36Sopenharmony_ci * kernel mode by default, define the following: 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_ci#undef START_IN_KERNEL_MODE 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define DRV_VER "0.7.0" 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* 4362306a36Sopenharmony_ci * According to the Atom N270 datasheet, 4462306a36Sopenharmony_ci * (http://download.intel.com/design/processor/datashts/320032.pdf) the 4562306a36Sopenharmony_ci * CPU's optimal operating limits denoted in junction temperature as 4662306a36Sopenharmony_ci * measured by the on-die thermal monitor are within 0 <= Tj <= 90. So, 4762306a36Sopenharmony_ci * assume 89°C is critical temperature. 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_ci#define ACERHDF_DEFAULT_TEMP_FANON 60000 5062306a36Sopenharmony_ci#define ACERHDF_DEFAULT_TEMP_FANOFF 53000 5162306a36Sopenharmony_ci#define ACERHDF_TEMP_CRIT 89000 5262306a36Sopenharmony_ci#define ACERHDF_FAN_OFF 0 5362306a36Sopenharmony_ci#define ACERHDF_FAN_AUTO 1 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* 5662306a36Sopenharmony_ci * No matter what value the user puts into the fanon variable, turn on the fan 5762306a36Sopenharmony_ci * at 80 degree Celsius to prevent hardware damage 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_ci#define ACERHDF_MAX_FANON 80000 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* 6262306a36Sopenharmony_ci * Maximum interval between two temperature checks is 15 seconds, as the die 6362306a36Sopenharmony_ci * can get hot really fast under heavy load (plus we shouldn't forget about 6462306a36Sopenharmony_ci * possible impact of _external_ aggressive sources such as heaters, sun etc.) 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_ci#define ACERHDF_MAX_INTERVAL 15 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#ifdef START_IN_KERNEL_MODE 6962306a36Sopenharmony_cistatic int kernelmode = 1; 7062306a36Sopenharmony_ci#else 7162306a36Sopenharmony_cistatic int kernelmode; 7262306a36Sopenharmony_ci#endif 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic unsigned int interval = 10; 7562306a36Sopenharmony_cistatic unsigned int fanon = ACERHDF_DEFAULT_TEMP_FANON; 7662306a36Sopenharmony_cistatic unsigned int fanoff = ACERHDF_DEFAULT_TEMP_FANOFF; 7762306a36Sopenharmony_cistatic unsigned int verbose; 7862306a36Sopenharmony_cistatic unsigned int list_supported; 7962306a36Sopenharmony_cistatic unsigned int fanstate = ACERHDF_FAN_AUTO; 8062306a36Sopenharmony_cistatic char force_bios[16]; 8162306a36Sopenharmony_cistatic char force_product[16]; 8262306a36Sopenharmony_cistatic struct thermal_zone_device *thz_dev; 8362306a36Sopenharmony_cistatic struct thermal_cooling_device *cl_dev; 8462306a36Sopenharmony_cistatic struct platform_device *acerhdf_dev; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cimodule_param(kernelmode, uint, 0); 8762306a36Sopenharmony_ciMODULE_PARM_DESC(kernelmode, "Kernel mode fan control on / off"); 8862306a36Sopenharmony_cimodule_param(fanon, uint, 0600); 8962306a36Sopenharmony_ciMODULE_PARM_DESC(fanon, "Turn the fan on above this temperature"); 9062306a36Sopenharmony_cimodule_param(fanoff, uint, 0600); 9162306a36Sopenharmony_ciMODULE_PARM_DESC(fanoff, "Turn the fan off below this temperature"); 9262306a36Sopenharmony_cimodule_param(verbose, uint, 0600); 9362306a36Sopenharmony_ciMODULE_PARM_DESC(verbose, "Enable verbose dmesg output"); 9462306a36Sopenharmony_cimodule_param(list_supported, uint, 0600); 9562306a36Sopenharmony_ciMODULE_PARM_DESC(list_supported, "List supported models and BIOS versions"); 9662306a36Sopenharmony_cimodule_param_string(force_bios, force_bios, 16, 0); 9762306a36Sopenharmony_ciMODULE_PARM_DESC(force_bios, "Pretend system has this known supported BIOS version"); 9862306a36Sopenharmony_cimodule_param_string(force_product, force_product, 16, 0); 9962306a36Sopenharmony_ciMODULE_PARM_DESC(force_product, "Pretend system is this known supported model"); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* 10262306a36Sopenharmony_ci * cmd_off: to switch the fan completely off and check if the fan is off 10362306a36Sopenharmony_ci * cmd_auto: to set the BIOS in control of the fan. The BIOS regulates then 10462306a36Sopenharmony_ci * the fan speed depending on the temperature 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_cistruct fancmd { 10762306a36Sopenharmony_ci u8 cmd_off; 10862306a36Sopenharmony_ci u8 cmd_auto; 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistruct manualcmd { 11262306a36Sopenharmony_ci u8 mreg; 11362306a36Sopenharmony_ci u8 moff; 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/* default register and command to disable fan in manual mode */ 11762306a36Sopenharmony_cistatic const struct manualcmd mcmd = { 11862306a36Sopenharmony_ci .mreg = 0x94, 11962306a36Sopenharmony_ci .moff = 0xff, 12062306a36Sopenharmony_ci}; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/* BIOS settings - only used during probe */ 12362306a36Sopenharmony_cistruct bios_settings { 12462306a36Sopenharmony_ci const char *vendor; 12562306a36Sopenharmony_ci const char *product; 12662306a36Sopenharmony_ci const char *version; 12762306a36Sopenharmony_ci u8 fanreg; 12862306a36Sopenharmony_ci u8 tempreg; 12962306a36Sopenharmony_ci struct fancmd cmd; 13062306a36Sopenharmony_ci int mcmd_enable; 13162306a36Sopenharmony_ci}; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/* This could be a daughter struct in the above, but not worth the redirect */ 13462306a36Sopenharmony_cistruct ctrl_settings { 13562306a36Sopenharmony_ci u8 fanreg; 13662306a36Sopenharmony_ci u8 tempreg; 13762306a36Sopenharmony_ci struct fancmd cmd; 13862306a36Sopenharmony_ci int mcmd_enable; 13962306a36Sopenharmony_ci}; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic struct thermal_trip trips[] = { 14262306a36Sopenharmony_ci [0] = { .temperature = ACERHDF_DEFAULT_TEMP_FANON, 14362306a36Sopenharmony_ci .hysteresis = ACERHDF_DEFAULT_TEMP_FANON - ACERHDF_DEFAULT_TEMP_FANOFF, 14462306a36Sopenharmony_ci .type = THERMAL_TRIP_ACTIVE }, 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci [1] = { .temperature = ACERHDF_TEMP_CRIT, 14762306a36Sopenharmony_ci .type = THERMAL_TRIP_CRITICAL } 14862306a36Sopenharmony_ci}; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic struct ctrl_settings ctrl_cfg __read_mostly; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci/* Register addresses and values for different BIOS versions */ 15362306a36Sopenharmony_cistatic const struct bios_settings bios_tbl[] __initconst = { 15462306a36Sopenharmony_ci /* AOA110 */ 15562306a36Sopenharmony_ci {"Acer", "AOA110", "v0.3109", 0x55, 0x58, {0x1f, 0x00}, 0}, 15662306a36Sopenharmony_ci {"Acer", "AOA110", "v0.3114", 0x55, 0x58, {0x1f, 0x00}, 0}, 15762306a36Sopenharmony_ci {"Acer", "AOA110", "v0.3301", 0x55, 0x58, {0xaf, 0x00}, 0}, 15862306a36Sopenharmony_ci {"Acer", "AOA110", "v0.3304", 0x55, 0x58, {0xaf, 0x00}, 0}, 15962306a36Sopenharmony_ci {"Acer", "AOA110", "v0.3305", 0x55, 0x58, {0xaf, 0x00}, 0}, 16062306a36Sopenharmony_ci {"Acer", "AOA110", "v0.3307", 0x55, 0x58, {0xaf, 0x00}, 0}, 16162306a36Sopenharmony_ci {"Acer", "AOA110", "v0.3308", 0x55, 0x58, {0x21, 0x00}, 0}, 16262306a36Sopenharmony_ci {"Acer", "AOA110", "v0.3309", 0x55, 0x58, {0x21, 0x00}, 0}, 16362306a36Sopenharmony_ci {"Acer", "AOA110", "v0.3310", 0x55, 0x58, {0x21, 0x00}, 0}, 16462306a36Sopenharmony_ci /* AOA150 */ 16562306a36Sopenharmony_ci {"Acer", "AOA150", "v0.3114", 0x55, 0x58, {0x1f, 0x00}, 0}, 16662306a36Sopenharmony_ci {"Acer", "AOA150", "v0.3301", 0x55, 0x58, {0x20, 0x00}, 0}, 16762306a36Sopenharmony_ci {"Acer", "AOA150", "v0.3304", 0x55, 0x58, {0x20, 0x00}, 0}, 16862306a36Sopenharmony_ci {"Acer", "AOA150", "v0.3305", 0x55, 0x58, {0x20, 0x00}, 0}, 16962306a36Sopenharmony_ci {"Acer", "AOA150", "v0.3307", 0x55, 0x58, {0x20, 0x00}, 0}, 17062306a36Sopenharmony_ci {"Acer", "AOA150", "v0.3308", 0x55, 0x58, {0x20, 0x00}, 0}, 17162306a36Sopenharmony_ci {"Acer", "AOA150", "v0.3309", 0x55, 0x58, {0x20, 0x00}, 0}, 17262306a36Sopenharmony_ci {"Acer", "AOA150", "v0.3310", 0x55, 0x58, {0x20, 0x00}, 0}, 17362306a36Sopenharmony_ci /* LT1005u */ 17462306a36Sopenharmony_ci {"Acer", "LT-10Q", "v0.3310", 0x55, 0x58, {0x20, 0x00}, 0}, 17562306a36Sopenharmony_ci /* Acer 1410 */ 17662306a36Sopenharmony_ci {"Acer", "Aspire 1410", "v0.3108", 0x55, 0x58, {0x9e, 0x00}, 0}, 17762306a36Sopenharmony_ci {"Acer", "Aspire 1410", "v0.3113", 0x55, 0x58, {0x9e, 0x00}, 0}, 17862306a36Sopenharmony_ci {"Acer", "Aspire 1410", "v0.3115", 0x55, 0x58, {0x9e, 0x00}, 0}, 17962306a36Sopenharmony_ci {"Acer", "Aspire 1410", "v0.3117", 0x55, 0x58, {0x9e, 0x00}, 0}, 18062306a36Sopenharmony_ci {"Acer", "Aspire 1410", "v0.3119", 0x55, 0x58, {0x9e, 0x00}, 0}, 18162306a36Sopenharmony_ci {"Acer", "Aspire 1410", "v0.3120", 0x55, 0x58, {0x9e, 0x00}, 0}, 18262306a36Sopenharmony_ci {"Acer", "Aspire 1410", "v1.3204", 0x55, 0x58, {0x9e, 0x00}, 0}, 18362306a36Sopenharmony_ci {"Acer", "Aspire 1410", "v1.3303", 0x55, 0x58, {0x9e, 0x00}, 0}, 18462306a36Sopenharmony_ci {"Acer", "Aspire 1410", "v1.3308", 0x55, 0x58, {0x9e, 0x00}, 0}, 18562306a36Sopenharmony_ci {"Acer", "Aspire 1410", "v1.3310", 0x55, 0x58, {0x9e, 0x00}, 0}, 18662306a36Sopenharmony_ci {"Acer", "Aspire 1410", "v1.3314", 0x55, 0x58, {0x9e, 0x00}, 0}, 18762306a36Sopenharmony_ci /* Acer 1810xx */ 18862306a36Sopenharmony_ci {"Acer", "Aspire 1810TZ", "v0.3108", 0x55, 0x58, {0x9e, 0x00}, 0}, 18962306a36Sopenharmony_ci {"Acer", "Aspire 1810T", "v0.3108", 0x55, 0x58, {0x9e, 0x00}, 0}, 19062306a36Sopenharmony_ci {"Acer", "Aspire 1810TZ", "v0.3113", 0x55, 0x58, {0x9e, 0x00}, 0}, 19162306a36Sopenharmony_ci {"Acer", "Aspire 1810T", "v0.3113", 0x55, 0x58, {0x9e, 0x00}, 0}, 19262306a36Sopenharmony_ci {"Acer", "Aspire 1810TZ", "v0.3115", 0x55, 0x58, {0x9e, 0x00}, 0}, 19362306a36Sopenharmony_ci {"Acer", "Aspire 1810T", "v0.3115", 0x55, 0x58, {0x9e, 0x00}, 0}, 19462306a36Sopenharmony_ci {"Acer", "Aspire 1810TZ", "v0.3117", 0x55, 0x58, {0x9e, 0x00}, 0}, 19562306a36Sopenharmony_ci {"Acer", "Aspire 1810T", "v0.3117", 0x55, 0x58, {0x9e, 0x00}, 0}, 19662306a36Sopenharmony_ci {"Acer", "Aspire 1810TZ", "v0.3119", 0x55, 0x58, {0x9e, 0x00}, 0}, 19762306a36Sopenharmony_ci {"Acer", "Aspire 1810T", "v0.3119", 0x55, 0x58, {0x9e, 0x00}, 0}, 19862306a36Sopenharmony_ci {"Acer", "Aspire 1810TZ", "v0.3120", 0x55, 0x58, {0x9e, 0x00}, 0}, 19962306a36Sopenharmony_ci {"Acer", "Aspire 1810T", "v0.3120", 0x55, 0x58, {0x9e, 0x00}, 0}, 20062306a36Sopenharmony_ci {"Acer", "Aspire 1810TZ", "v1.3204", 0x55, 0x58, {0x9e, 0x00}, 0}, 20162306a36Sopenharmony_ci {"Acer", "Aspire 1810T", "v1.3204", 0x55, 0x58, {0x9e, 0x00}, 0}, 20262306a36Sopenharmony_ci {"Acer", "Aspire 1810TZ", "v1.3303", 0x55, 0x58, {0x9e, 0x00}, 0}, 20362306a36Sopenharmony_ci {"Acer", "Aspire 1810T", "v1.3303", 0x55, 0x58, {0x9e, 0x00}, 0}, 20462306a36Sopenharmony_ci {"Acer", "Aspire 1810TZ", "v1.3308", 0x55, 0x58, {0x9e, 0x00}, 0}, 20562306a36Sopenharmony_ci {"Acer", "Aspire 1810T", "v1.3308", 0x55, 0x58, {0x9e, 0x00}, 0}, 20662306a36Sopenharmony_ci {"Acer", "Aspire 1810TZ", "v1.3310", 0x55, 0x58, {0x9e, 0x00}, 0}, 20762306a36Sopenharmony_ci {"Acer", "Aspire 1810T", "v1.3310", 0x55, 0x58, {0x9e, 0x00}, 0}, 20862306a36Sopenharmony_ci {"Acer", "Aspire 1810TZ", "v1.3314", 0x55, 0x58, {0x9e, 0x00}, 0}, 20962306a36Sopenharmony_ci {"Acer", "Aspire 1810T", "v1.3314", 0x55, 0x58, {0x9e, 0x00}, 0}, 21062306a36Sopenharmony_ci /* Acer 5755G */ 21162306a36Sopenharmony_ci {"Acer", "Aspire 5755G", "V1.20", 0xab, 0xb4, {0x00, 0x08}, 0}, 21262306a36Sopenharmony_ci {"Acer", "Aspire 5755G", "V1.21", 0xab, 0xb3, {0x00, 0x08}, 0}, 21362306a36Sopenharmony_ci /* Acer 521 */ 21462306a36Sopenharmony_ci {"Acer", "AO521", "V1.11", 0x55, 0x58, {0x1f, 0x00}, 0}, 21562306a36Sopenharmony_ci /* Acer 531 */ 21662306a36Sopenharmony_ci {"Acer", "AO531h", "v0.3104", 0x55, 0x58, {0x20, 0x00}, 0}, 21762306a36Sopenharmony_ci {"Acer", "AO531h", "v0.3201", 0x55, 0x58, {0x20, 0x00}, 0}, 21862306a36Sopenharmony_ci {"Acer", "AO531h", "v0.3304", 0x55, 0x58, {0x20, 0x00}, 0}, 21962306a36Sopenharmony_ci /* Acer 751 */ 22062306a36Sopenharmony_ci {"Acer", "AO751h", "V0.3206", 0x55, 0x58, {0x21, 0x00}, 0}, 22162306a36Sopenharmony_ci {"Acer", "AO751h", "V0.3212", 0x55, 0x58, {0x21, 0x00}, 0}, 22262306a36Sopenharmony_ci /* Acer 753 */ 22362306a36Sopenharmony_ci {"Acer", "Aspire One 753", "V1.24", 0x93, 0xac, {0x14, 0x04}, 1}, 22462306a36Sopenharmony_ci /* Acer 1825 */ 22562306a36Sopenharmony_ci {"Acer", "Aspire 1825PTZ", "V1.3118", 0x55, 0x58, {0x9e, 0x00}, 0}, 22662306a36Sopenharmony_ci {"Acer", "Aspire 1825PTZ", "V1.3127", 0x55, 0x58, {0x9e, 0x00}, 0}, 22762306a36Sopenharmony_ci /* Acer Extensa 5420 */ 22862306a36Sopenharmony_ci {"Acer", "Extensa 5420", "V1.17", 0x93, 0xac, {0x14, 0x04}, 1}, 22962306a36Sopenharmony_ci /* Acer Aspire 5315 */ 23062306a36Sopenharmony_ci {"Acer", "Aspire 5315", "V1.19", 0x93, 0xac, {0x14, 0x04}, 1}, 23162306a36Sopenharmony_ci /* Acer Aspire 5739 */ 23262306a36Sopenharmony_ci {"Acer", "Aspire 5739G", "V1.3311", 0x55, 0x58, {0x20, 0x00}, 0}, 23362306a36Sopenharmony_ci /* Acer TravelMate 7730 */ 23462306a36Sopenharmony_ci {"Acer", "TravelMate 7730G", "v0.3509", 0x55, 0x58, {0xaf, 0x00}, 0}, 23562306a36Sopenharmony_ci /* Acer Aspire 7551 */ 23662306a36Sopenharmony_ci {"Acer", "Aspire 7551", "V1.18", 0x93, 0xa8, {0x14, 0x04}, 1}, 23762306a36Sopenharmony_ci /* Acer TravelMate TM8573T */ 23862306a36Sopenharmony_ci {"Acer", "TM8573T", "V1.13", 0x93, 0xa8, {0x14, 0x04}, 1}, 23962306a36Sopenharmony_ci /* Gateway */ 24062306a36Sopenharmony_ci {"Gateway", "AOA110", "v0.3103", 0x55, 0x58, {0x21, 0x00}, 0}, 24162306a36Sopenharmony_ci {"Gateway", "AOA150", "v0.3103", 0x55, 0x58, {0x20, 0x00}, 0}, 24262306a36Sopenharmony_ci {"Gateway", "LT31", "v1.3103", 0x55, 0x58, {0x9e, 0x00}, 0}, 24362306a36Sopenharmony_ci {"Gateway", "LT31", "v1.3201", 0x55, 0x58, {0x9e, 0x00}, 0}, 24462306a36Sopenharmony_ci {"Gateway", "LT31", "v1.3302", 0x55, 0x58, {0x9e, 0x00}, 0}, 24562306a36Sopenharmony_ci {"Gateway", "LT31", "v1.3303t", 0x55, 0x58, {0x9e, 0x00}, 0}, 24662306a36Sopenharmony_ci {"Gateway", "LT31", "v1.3307", 0x55, 0x58, {0x9e, 0x00}, 0}, 24762306a36Sopenharmony_ci /* Packard Bell */ 24862306a36Sopenharmony_ci {"Packard Bell", "DOA150", "v0.3104", 0x55, 0x58, {0x21, 0x00}, 0}, 24962306a36Sopenharmony_ci {"Packard Bell", "DOA150", "v0.3105", 0x55, 0x58, {0x20, 0x00}, 0}, 25062306a36Sopenharmony_ci {"Packard Bell", "AOA110", "v0.3105", 0x55, 0x58, {0x21, 0x00}, 0}, 25162306a36Sopenharmony_ci {"Packard Bell", "AOA150", "v0.3105", 0x55, 0x58, {0x20, 0x00}, 0}, 25262306a36Sopenharmony_ci {"Packard Bell", "ENBFT", "V1.3118", 0x55, 0x58, {0x9e, 0x00}, 0}, 25362306a36Sopenharmony_ci {"Packard Bell", "ENBFT", "V1.3127", 0x55, 0x58, {0x9e, 0x00}, 0}, 25462306a36Sopenharmony_ci {"Packard Bell", "DOTMU", "v1.3303", 0x55, 0x58, {0x9e, 0x00}, 0}, 25562306a36Sopenharmony_ci {"Packard Bell", "DOTMU", "v0.3120", 0x55, 0x58, {0x9e, 0x00}, 0}, 25662306a36Sopenharmony_ci {"Packard Bell", "DOTMU", "v0.3108", 0x55, 0x58, {0x9e, 0x00}, 0}, 25762306a36Sopenharmony_ci {"Packard Bell", "DOTMU", "v0.3113", 0x55, 0x58, {0x9e, 0x00}, 0}, 25862306a36Sopenharmony_ci {"Packard Bell", "DOTMU", "v0.3115", 0x55, 0x58, {0x9e, 0x00}, 0}, 25962306a36Sopenharmony_ci {"Packard Bell", "DOTMU", "v0.3117", 0x55, 0x58, {0x9e, 0x00}, 0}, 26062306a36Sopenharmony_ci {"Packard Bell", "DOTMU", "v0.3119", 0x55, 0x58, {0x9e, 0x00}, 0}, 26162306a36Sopenharmony_ci {"Packard Bell", "DOTMU", "v1.3204", 0x55, 0x58, {0x9e, 0x00}, 0}, 26262306a36Sopenharmony_ci {"Packard Bell", "DOTMA", "v1.3201", 0x55, 0x58, {0x9e, 0x00}, 0}, 26362306a36Sopenharmony_ci {"Packard Bell", "DOTMA", "v1.3302", 0x55, 0x58, {0x9e, 0x00}, 0}, 26462306a36Sopenharmony_ci {"Packard Bell", "DOTMA", "v1.3303t", 0x55, 0x58, {0x9e, 0x00}, 0}, 26562306a36Sopenharmony_ci {"Packard Bell", "DOTVR46", "v1.3308", 0x55, 0x58, {0x9e, 0x00}, 0}, 26662306a36Sopenharmony_ci /* pewpew-terminator */ 26762306a36Sopenharmony_ci {"", "", "", 0, 0, {0, 0}, 0} 26862306a36Sopenharmony_ci}; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci/* 27162306a36Sopenharmony_ci * this struct is used to instruct thermal layer to use bang_bang instead of 27262306a36Sopenharmony_ci * default governor for acerhdf 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_cistatic struct thermal_zone_params acerhdf_zone_params = { 27562306a36Sopenharmony_ci .governor_name = "bang_bang", 27662306a36Sopenharmony_ci}; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic int acerhdf_get_temp(int *temp) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci u8 read_temp; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (ec_read(ctrl_cfg.tempreg, &read_temp)) 28362306a36Sopenharmony_ci return -EINVAL; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci *temp = read_temp * 1000; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci return 0; 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic int acerhdf_get_fanstate(int *state) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci u8 fan; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (ec_read(ctrl_cfg.fanreg, &fan)) 29562306a36Sopenharmony_ci return -EINVAL; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (fan != ctrl_cfg.cmd.cmd_off) 29862306a36Sopenharmony_ci *state = ACERHDF_FAN_AUTO; 29962306a36Sopenharmony_ci else 30062306a36Sopenharmony_ci *state = ACERHDF_FAN_OFF; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci return 0; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic void acerhdf_change_fanstate(int state) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci unsigned char cmd; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (verbose) 31062306a36Sopenharmony_ci pr_notice("fan %s\n", state == ACERHDF_FAN_OFF ? "OFF" : "ON"); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if ((state != ACERHDF_FAN_OFF) && (state != ACERHDF_FAN_AUTO)) { 31362306a36Sopenharmony_ci pr_err("invalid fan state %d requested, setting to auto!\n", 31462306a36Sopenharmony_ci state); 31562306a36Sopenharmony_ci state = ACERHDF_FAN_AUTO; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci cmd = (state == ACERHDF_FAN_OFF) ? ctrl_cfg.cmd.cmd_off 31962306a36Sopenharmony_ci : ctrl_cfg.cmd.cmd_auto; 32062306a36Sopenharmony_ci fanstate = state; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci ec_write(ctrl_cfg.fanreg, cmd); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (ctrl_cfg.mcmd_enable && state == ACERHDF_FAN_OFF) { 32562306a36Sopenharmony_ci if (verbose) 32662306a36Sopenharmony_ci pr_notice("turning off fan manually\n"); 32762306a36Sopenharmony_ci ec_write(mcmd.mreg, mcmd.moff); 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic void acerhdf_check_param(struct thermal_zone_device *thermal) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci if (fanon > ACERHDF_MAX_FANON) { 33462306a36Sopenharmony_ci pr_err("fanon temperature too high, set to %d\n", 33562306a36Sopenharmony_ci ACERHDF_MAX_FANON); 33662306a36Sopenharmony_ci fanon = ACERHDF_MAX_FANON; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (fanon < fanoff) { 34062306a36Sopenharmony_ci pr_err("fanoff temperature (%d) is above fanon temperature (%d), clamping to %d\n", 34162306a36Sopenharmony_ci fanoff, fanon, fanon); 34262306a36Sopenharmony_ci fanoff = fanon; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci trips[0].temperature = fanon; 34662306a36Sopenharmony_ci trips[0].hysteresis = fanon - fanoff; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (kernelmode) { 34962306a36Sopenharmony_ci if (interval > ACERHDF_MAX_INTERVAL) { 35062306a36Sopenharmony_ci pr_err("interval too high, set to %d\n", 35162306a36Sopenharmony_ci ACERHDF_MAX_INTERVAL); 35262306a36Sopenharmony_ci interval = ACERHDF_MAX_INTERVAL; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (verbose) 35662306a36Sopenharmony_ci pr_notice("interval changed to: %d\n", interval); 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci/* 36162306a36Sopenharmony_ci * This is the thermal zone callback which does the delayed polling of the fan 36262306a36Sopenharmony_ci * state. We do check /sysfs-originating settings here in acerhdf_check_param() 36362306a36Sopenharmony_ci * as late as the polling interval is since we can't do that in the respective 36462306a36Sopenharmony_ci * accessors of the module parameters. 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_cistatic int acerhdf_get_ec_temp(struct thermal_zone_device *thermal, int *t) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci int temp, err = 0; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci err = acerhdf_get_temp(&temp); 37162306a36Sopenharmony_ci if (err) 37262306a36Sopenharmony_ci return err; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (verbose) 37562306a36Sopenharmony_ci pr_notice("temp %d\n", temp); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci *t = temp; 37862306a36Sopenharmony_ci return 0; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic int acerhdf_bind(struct thermal_zone_device *thermal, 38262306a36Sopenharmony_ci struct thermal_cooling_device *cdev) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci /* if the cooling device is the one from acerhdf bind it */ 38562306a36Sopenharmony_ci if (cdev != cl_dev) 38662306a36Sopenharmony_ci return 0; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (thermal_zone_bind_cooling_device(thermal, 0, cdev, 38962306a36Sopenharmony_ci THERMAL_NO_LIMIT, THERMAL_NO_LIMIT, 39062306a36Sopenharmony_ci THERMAL_WEIGHT_DEFAULT)) { 39162306a36Sopenharmony_ci pr_err("error binding cooling dev\n"); 39262306a36Sopenharmony_ci return -EINVAL; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci return 0; 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic int acerhdf_unbind(struct thermal_zone_device *thermal, 39862306a36Sopenharmony_ci struct thermal_cooling_device *cdev) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci if (cdev != cl_dev) 40162306a36Sopenharmony_ci return 0; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) { 40462306a36Sopenharmony_ci pr_err("error unbinding cooling dev\n"); 40562306a36Sopenharmony_ci return -EINVAL; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci return 0; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic inline void acerhdf_revert_to_bios_mode(void) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci acerhdf_change_fanstate(ACERHDF_FAN_AUTO); 41362306a36Sopenharmony_ci kernelmode = 0; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci pr_notice("kernel mode fan control OFF\n"); 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_cistatic inline void acerhdf_enable_kernelmode(void) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci kernelmode = 1; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci pr_notice("kernel mode fan control ON\n"); 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci/* 42562306a36Sopenharmony_ci * set operation mode; 42662306a36Sopenharmony_ci * enabled: the thermal layer of the kernel takes care about 42762306a36Sopenharmony_ci * the temperature and the fan. 42862306a36Sopenharmony_ci * disabled: the BIOS takes control of the fan. 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_cistatic int acerhdf_change_mode(struct thermal_zone_device *thermal, 43162306a36Sopenharmony_ci enum thermal_device_mode mode) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci if (mode == THERMAL_DEVICE_DISABLED && kernelmode) 43462306a36Sopenharmony_ci acerhdf_revert_to_bios_mode(); 43562306a36Sopenharmony_ci else if (mode == THERMAL_DEVICE_ENABLED && !kernelmode) 43662306a36Sopenharmony_ci acerhdf_enable_kernelmode(); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci return 0; 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_cistatic int acerhdf_get_crit_temp(struct thermal_zone_device *thermal, 44262306a36Sopenharmony_ci int *temperature) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci *temperature = ACERHDF_TEMP_CRIT; 44562306a36Sopenharmony_ci return 0; 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci/* bind callback functions to thermalzone */ 44962306a36Sopenharmony_cistatic struct thermal_zone_device_ops acerhdf_dev_ops = { 45062306a36Sopenharmony_ci .bind = acerhdf_bind, 45162306a36Sopenharmony_ci .unbind = acerhdf_unbind, 45262306a36Sopenharmony_ci .get_temp = acerhdf_get_ec_temp, 45362306a36Sopenharmony_ci .change_mode = acerhdf_change_mode, 45462306a36Sopenharmony_ci .get_crit_temp = acerhdf_get_crit_temp, 45562306a36Sopenharmony_ci}; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci/* 45862306a36Sopenharmony_ci * cooling device callback functions 45962306a36Sopenharmony_ci * get maximal fan cooling state 46062306a36Sopenharmony_ci */ 46162306a36Sopenharmony_cistatic int acerhdf_get_max_state(struct thermal_cooling_device *cdev, 46262306a36Sopenharmony_ci unsigned long *state) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci *state = 1; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci return 0; 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic int acerhdf_get_cur_state(struct thermal_cooling_device *cdev, 47062306a36Sopenharmony_ci unsigned long *state) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci int err = 0, tmp; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci err = acerhdf_get_fanstate(&tmp); 47562306a36Sopenharmony_ci if (err) 47662306a36Sopenharmony_ci return err; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci *state = (tmp == ACERHDF_FAN_AUTO) ? 1 : 0; 47962306a36Sopenharmony_ci return 0; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci/* change current fan state - is overwritten when running in kernel mode */ 48362306a36Sopenharmony_cistatic int acerhdf_set_cur_state(struct thermal_cooling_device *cdev, 48462306a36Sopenharmony_ci unsigned long state) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci int cur_temp, cur_state, err = 0; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (!kernelmode) 48962306a36Sopenharmony_ci return 0; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci err = acerhdf_get_temp(&cur_temp); 49262306a36Sopenharmony_ci if (err) { 49362306a36Sopenharmony_ci pr_err("error reading temperature, hand off control to BIOS\n"); 49462306a36Sopenharmony_ci goto err_out; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci err = acerhdf_get_fanstate(&cur_state); 49862306a36Sopenharmony_ci if (err) { 49962306a36Sopenharmony_ci pr_err("error reading fan state, hand off control to BIOS\n"); 50062306a36Sopenharmony_ci goto err_out; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (state == 0) { 50462306a36Sopenharmony_ci if (cur_state == ACERHDF_FAN_AUTO) 50562306a36Sopenharmony_ci acerhdf_change_fanstate(ACERHDF_FAN_OFF); 50662306a36Sopenharmony_ci } else { 50762306a36Sopenharmony_ci if (cur_state == ACERHDF_FAN_OFF) 50862306a36Sopenharmony_ci acerhdf_change_fanstate(ACERHDF_FAN_AUTO); 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci return 0; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_cierr_out: 51362306a36Sopenharmony_ci acerhdf_revert_to_bios_mode(); 51462306a36Sopenharmony_ci return -EINVAL; 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci/* bind fan callbacks to fan device */ 51862306a36Sopenharmony_cistatic const struct thermal_cooling_device_ops acerhdf_cooling_ops = { 51962306a36Sopenharmony_ci .get_max_state = acerhdf_get_max_state, 52062306a36Sopenharmony_ci .get_cur_state = acerhdf_get_cur_state, 52162306a36Sopenharmony_ci .set_cur_state = acerhdf_set_cur_state, 52262306a36Sopenharmony_ci}; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci/* suspend / resume functionality */ 52562306a36Sopenharmony_cistatic int acerhdf_suspend(struct device *dev) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci if (kernelmode) 52862306a36Sopenharmony_ci acerhdf_change_fanstate(ACERHDF_FAN_AUTO); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (verbose) 53162306a36Sopenharmony_ci pr_notice("going suspend\n"); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci return 0; 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic int acerhdf_probe(struct platform_device *device) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci return 0; 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic const struct dev_pm_ops acerhdf_pm_ops = { 54262306a36Sopenharmony_ci .suspend = acerhdf_suspend, 54362306a36Sopenharmony_ci .freeze = acerhdf_suspend, 54462306a36Sopenharmony_ci}; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cistatic struct platform_driver acerhdf_driver = { 54762306a36Sopenharmony_ci .driver = { 54862306a36Sopenharmony_ci .name = "acerhdf", 54962306a36Sopenharmony_ci .pm = &acerhdf_pm_ops, 55062306a36Sopenharmony_ci }, 55162306a36Sopenharmony_ci .probe = acerhdf_probe, 55262306a36Sopenharmony_ci}; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci/* check hardware */ 55562306a36Sopenharmony_cistatic int __init acerhdf_check_hardware(void) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci char const *vendor, *version, *product; 55862306a36Sopenharmony_ci const struct bios_settings *bt = NULL; 55962306a36Sopenharmony_ci int found = 0; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci /* get BIOS data */ 56262306a36Sopenharmony_ci vendor = dmi_get_system_info(DMI_SYS_VENDOR); 56362306a36Sopenharmony_ci version = dmi_get_system_info(DMI_BIOS_VERSION); 56462306a36Sopenharmony_ci product = dmi_get_system_info(DMI_PRODUCT_NAME); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (!vendor || !version || !product) { 56762306a36Sopenharmony_ci pr_err("error getting hardware information\n"); 56862306a36Sopenharmony_ci return -EINVAL; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci pr_info("Acer Aspire One Fan driver, v.%s\n", DRV_VER); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (list_supported) { 57462306a36Sopenharmony_ci pr_info("List of supported Manufacturer/Model/BIOS:\n"); 57562306a36Sopenharmony_ci pr_info("---------------------------------------------------\n"); 57662306a36Sopenharmony_ci for (bt = bios_tbl; bt->vendor[0]; bt++) { 57762306a36Sopenharmony_ci pr_info("%-13s | %-17s | %-10s\n", bt->vendor, 57862306a36Sopenharmony_ci bt->product, bt->version); 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci pr_info("---------------------------------------------------\n"); 58162306a36Sopenharmony_ci return -ECANCELED; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci if (force_bios[0]) { 58562306a36Sopenharmony_ci version = force_bios; 58662306a36Sopenharmony_ci pr_info("forcing BIOS version: %s\n", version); 58762306a36Sopenharmony_ci kernelmode = 0; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (force_product[0]) { 59162306a36Sopenharmony_ci product = force_product; 59262306a36Sopenharmony_ci pr_info("forcing BIOS product: %s\n", product); 59362306a36Sopenharmony_ci kernelmode = 0; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if (verbose) 59762306a36Sopenharmony_ci pr_info("BIOS info: %s %s, product: %s\n", 59862306a36Sopenharmony_ci vendor, version, product); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci /* search BIOS version and vendor in BIOS settings table */ 60162306a36Sopenharmony_ci for (bt = bios_tbl; bt->vendor[0]; bt++) { 60262306a36Sopenharmony_ci /* 60362306a36Sopenharmony_ci * check if actual hardware BIOS vendor, product and version 60462306a36Sopenharmony_ci * IDs start with the strings of BIOS table entry 60562306a36Sopenharmony_ci */ 60662306a36Sopenharmony_ci if (strstarts(vendor, bt->vendor) && 60762306a36Sopenharmony_ci strstarts(product, bt->product) && 60862306a36Sopenharmony_ci strstarts(version, bt->version)) { 60962306a36Sopenharmony_ci found = 1; 61062306a36Sopenharmony_ci break; 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (!found) { 61562306a36Sopenharmony_ci pr_err("unknown (unsupported) BIOS version %s/%s/%s, please report, aborting!\n", 61662306a36Sopenharmony_ci vendor, product, version); 61762306a36Sopenharmony_ci return -EINVAL; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci /* Copy control settings from BIOS table before we free it. */ 62162306a36Sopenharmony_ci ctrl_cfg.fanreg = bt->fanreg; 62262306a36Sopenharmony_ci ctrl_cfg.tempreg = bt->tempreg; 62362306a36Sopenharmony_ci memcpy(&ctrl_cfg.cmd, &bt->cmd, sizeof(struct fancmd)); 62462306a36Sopenharmony_ci ctrl_cfg.mcmd_enable = bt->mcmd_enable; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci /* 62762306a36Sopenharmony_ci * if started with kernel mode off, prevent the kernel from switching 62862306a36Sopenharmony_ci * off the fan 62962306a36Sopenharmony_ci */ 63062306a36Sopenharmony_ci if (!kernelmode) { 63162306a36Sopenharmony_ci pr_notice("Fan control off, to enable do:\n"); 63262306a36Sopenharmony_ci pr_notice("echo -n \"enabled\" > /sys/class/thermal/thermal_zoneN/mode # N=0,1,2...\n"); 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci return 0; 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cistatic int __init acerhdf_register_platform(void) 63962306a36Sopenharmony_ci{ 64062306a36Sopenharmony_ci int err = 0; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci err = platform_driver_register(&acerhdf_driver); 64362306a36Sopenharmony_ci if (err) 64462306a36Sopenharmony_ci return err; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci acerhdf_dev = platform_device_alloc("acerhdf", PLATFORM_DEVID_NONE); 64762306a36Sopenharmony_ci if (!acerhdf_dev) { 64862306a36Sopenharmony_ci err = -ENOMEM; 64962306a36Sopenharmony_ci goto err_device_alloc; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci err = platform_device_add(acerhdf_dev); 65262306a36Sopenharmony_ci if (err) 65362306a36Sopenharmony_ci goto err_device_add; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci return 0; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_cierr_device_add: 65862306a36Sopenharmony_ci platform_device_put(acerhdf_dev); 65962306a36Sopenharmony_cierr_device_alloc: 66062306a36Sopenharmony_ci platform_driver_unregister(&acerhdf_driver); 66162306a36Sopenharmony_ci return err; 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_cistatic void acerhdf_unregister_platform(void) 66562306a36Sopenharmony_ci{ 66662306a36Sopenharmony_ci platform_device_unregister(acerhdf_dev); 66762306a36Sopenharmony_ci platform_driver_unregister(&acerhdf_driver); 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_cistatic int __init acerhdf_register_thermal(void) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci int ret; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci cl_dev = thermal_cooling_device_register("acerhdf-fan", NULL, 67562306a36Sopenharmony_ci &acerhdf_cooling_ops); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci if (IS_ERR(cl_dev)) 67862306a36Sopenharmony_ci return -EINVAL; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci thz_dev = thermal_zone_device_register_with_trips("acerhdf", trips, ARRAY_SIZE(trips), 68162306a36Sopenharmony_ci 0, NULL, &acerhdf_dev_ops, 68262306a36Sopenharmony_ci &acerhdf_zone_params, 0, 68362306a36Sopenharmony_ci (kernelmode) ? interval*1000 : 0); 68462306a36Sopenharmony_ci if (IS_ERR(thz_dev)) 68562306a36Sopenharmony_ci return -EINVAL; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (kernelmode) 68862306a36Sopenharmony_ci ret = thermal_zone_device_enable(thz_dev); 68962306a36Sopenharmony_ci else 69062306a36Sopenharmony_ci ret = thermal_zone_device_disable(thz_dev); 69162306a36Sopenharmony_ci if (ret) 69262306a36Sopenharmony_ci return ret; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci return 0; 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cistatic void acerhdf_unregister_thermal(void) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci if (cl_dev) { 70062306a36Sopenharmony_ci thermal_cooling_device_unregister(cl_dev); 70162306a36Sopenharmony_ci cl_dev = NULL; 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci if (thz_dev) { 70562306a36Sopenharmony_ci thermal_zone_device_unregister(thz_dev); 70662306a36Sopenharmony_ci thz_dev = NULL; 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_cistatic int __init acerhdf_init(void) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci int err = 0; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci err = acerhdf_check_hardware(); 71562306a36Sopenharmony_ci if (err) 71662306a36Sopenharmony_ci goto out_err; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci err = acerhdf_register_platform(); 71962306a36Sopenharmony_ci if (err) 72062306a36Sopenharmony_ci goto out_err; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci err = acerhdf_register_thermal(); 72362306a36Sopenharmony_ci if (err) 72462306a36Sopenharmony_ci goto err_unreg; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci return 0; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_cierr_unreg: 72962306a36Sopenharmony_ci acerhdf_unregister_thermal(); 73062306a36Sopenharmony_ci acerhdf_unregister_platform(); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ciout_err: 73362306a36Sopenharmony_ci return err; 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_cistatic void __exit acerhdf_exit(void) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci acerhdf_change_fanstate(ACERHDF_FAN_AUTO); 73962306a36Sopenharmony_ci acerhdf_unregister_thermal(); 74062306a36Sopenharmony_ci acerhdf_unregister_platform(); 74162306a36Sopenharmony_ci} 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 74462306a36Sopenharmony_ciMODULE_AUTHOR("Peter Kaestle"); 74562306a36Sopenharmony_ciMODULE_DESCRIPTION("Aspire One temperature and fan driver"); 74662306a36Sopenharmony_ciMODULE_ALIAS("dmi:*:*Acer*:pnAOA*:"); 74762306a36Sopenharmony_ciMODULE_ALIAS("dmi:*:*Acer*:pnAO751h*:"); 74862306a36Sopenharmony_ciMODULE_ALIAS("dmi:*:*Acer*:pnAspire*1410*:"); 74962306a36Sopenharmony_ciMODULE_ALIAS("dmi:*:*Acer*:pnAspire*1810*:"); 75062306a36Sopenharmony_ciMODULE_ALIAS("dmi:*:*Acer*:pnAspire*5755G:"); 75162306a36Sopenharmony_ciMODULE_ALIAS("dmi:*:*Acer*:pnAspire*1825PTZ:"); 75262306a36Sopenharmony_ciMODULE_ALIAS("dmi:*:*Acer*:pnAO521*:"); 75362306a36Sopenharmony_ciMODULE_ALIAS("dmi:*:*Acer*:pnAO531*:"); 75462306a36Sopenharmony_ciMODULE_ALIAS("dmi:*:*Acer*:pnAspire*5739G:"); 75562306a36Sopenharmony_ciMODULE_ALIAS("dmi:*:*Acer*:pnAspire*One*753:"); 75662306a36Sopenharmony_ciMODULE_ALIAS("dmi:*:*Acer*:pnAspire*5315:"); 75762306a36Sopenharmony_ciMODULE_ALIAS("dmi:*:*Acer*:TravelMate*7730G:"); 75862306a36Sopenharmony_ciMODULE_ALIAS("dmi:*:*Acer*:pnAspire*7551:"); 75962306a36Sopenharmony_ciMODULE_ALIAS("dmi:*:*Acer*:TM8573T:"); 76062306a36Sopenharmony_ciMODULE_ALIAS("dmi:*:*Gateway*:pnAOA*:"); 76162306a36Sopenharmony_ciMODULE_ALIAS("dmi:*:*Gateway*:pnLT31*:"); 76262306a36Sopenharmony_ciMODULE_ALIAS("dmi:*:*Packard*Bell*:pnAOA*:"); 76362306a36Sopenharmony_ciMODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOA*:"); 76462306a36Sopenharmony_ciMODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTMU*:"); 76562306a36Sopenharmony_ciMODULE_ALIAS("dmi:*:*Packard*Bell*:pnENBFT*:"); 76662306a36Sopenharmony_ciMODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTMA*:"); 76762306a36Sopenharmony_ciMODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTVR46*:"); 76862306a36Sopenharmony_ciMODULE_ALIAS("dmi:*:*Acer*:pnExtensa*5420*:"); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_cimodule_init(acerhdf_init); 77162306a36Sopenharmony_cimodule_exit(acerhdf_exit); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cistatic int interval_set_uint(const char *val, const struct kernel_param *kp) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci int ret; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci ret = param_set_uint(val, kp); 77862306a36Sopenharmony_ci if (ret) 77962306a36Sopenharmony_ci return ret; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci acerhdf_check_param(thz_dev); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci return 0; 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cistatic const struct kernel_param_ops interval_ops = { 78762306a36Sopenharmony_ci .set = interval_set_uint, 78862306a36Sopenharmony_ci .get = param_get_uint, 78962306a36Sopenharmony_ci}; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_cimodule_param_cb(interval, &interval_ops, &interval, 0000); 79262306a36Sopenharmony_ciMODULE_PARM_DESC(interval, "Polling interval of temperature check"); 793