13e0a4e85SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2a5afba16SPali Rohár /* 3a5afba16SPali Rohár * dell-smm-hwmon.c -- Linux driver for accessing the SMM BIOS on Dell laptops. 4a5afba16SPali Rohár * 5a5afba16SPali Rohár * Copyright (C) 2001 Massimo Dal Zotto <dz@debian.org> 6a5afba16SPali Rohár * 7a5afba16SPali Rohár * Hwmon integration: 8a5afba16SPali Rohár * Copyright (C) 2011 Jean Delvare <jdelvare@suse.de> 9a5afba16SPali Rohár * Copyright (C) 2013, 2014 Guenter Roeck <linux@roeck-us.net> 10149ed3d4SPali Rohár * Copyright (C) 2014, 2015 Pali Rohár <pali@kernel.org> 11a5afba16SPali Rohár */ 12a5afba16SPali Rohár 13a5afba16SPali Rohár #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 14a5afba16SPali Rohár 156105870fSArmin Wolf #include <linux/capability.h> 1627046a3fSJuergen Gross #include <linux/cpu.h> 176105870fSArmin Wolf #include <linux/ctype.h> 18a5afba16SPali Rohár #include <linux/delay.h> 196105870fSArmin Wolf #include <linux/dmi.h> 201492fa21SArmin Wolf #include <linux/err.h> 21e64325e8SArmin Wolf #include <linux/errno.h> 226105870fSArmin Wolf #include <linux/hwmon.h> 23a5afba16SPali Rohár #include <linux/init.h> 246105870fSArmin Wolf #include <linux/module.h> 256105870fSArmin Wolf #include <linux/mutex.h> 266105870fSArmin Wolf #include <linux/platform_device.h> 27a5afba16SPali Rohár #include <linux/proc_fs.h> 28a5afba16SPali Rohár #include <linux/seq_file.h> 2938c5b0ddSArmin Wolf #include <linux/string.h> 3027046a3fSJuergen Gross #include <linux/smp.h> 316105870fSArmin Wolf #include <linux/types.h> 326105870fSArmin Wolf #include <linux/uaccess.h> 33a5afba16SPali Rohár 34a5afba16SPali Rohár #include <linux/i8k.h> 35a5afba16SPali Rohár 36a5afba16SPali Rohár #define I8K_SMM_FN_STATUS 0x0025 37a5afba16SPali Rohár #define I8K_SMM_POWER_STATUS 0x0069 38a5afba16SPali Rohár #define I8K_SMM_SET_FAN 0x01a3 39a5afba16SPali Rohár #define I8K_SMM_GET_FAN 0x00a3 40a5afba16SPali Rohár #define I8K_SMM_GET_SPEED 0x02a3 41a5afba16SPali Rohár #define I8K_SMM_GET_FAN_TYPE 0x03a3 42a5afba16SPali Rohár #define I8K_SMM_GET_NOM_SPEED 0x04a3 43a5afba16SPali Rohár #define I8K_SMM_GET_TEMP 0x10a3 44a5afba16SPali Rohár #define I8K_SMM_GET_TEMP_TYPE 0x11a3 45a5afba16SPali Rohár #define I8K_SMM_GET_DELL_SIG1 0xfea3 46a5afba16SPali Rohár #define I8K_SMM_GET_DELL_SIG2 0xffa3 47a5afba16SPali Rohár 48a5afba16SPali Rohár #define I8K_FAN_MULT 30 49a5afba16SPali Rohár #define I8K_FAN_MAX_RPM 30000 50a5afba16SPali Rohár #define I8K_MAX_TEMP 127 51a5afba16SPali Rohár 52a5afba16SPali Rohár #define I8K_FN_NONE 0x00 53a5afba16SPali Rohár #define I8K_FN_UP 0x01 54a5afba16SPali Rohár #define I8K_FN_DOWN 0x02 55a5afba16SPali Rohár #define I8K_FN_MUTE 0x04 56a5afba16SPali Rohár #define I8K_FN_MASK 0x07 57a5afba16SPali Rohár #define I8K_FN_SHIFT 8 58a5afba16SPali Rohár 59a5afba16SPali Rohár #define I8K_POWER_AC 0x05 60a5afba16SPali Rohár #define I8K_POWER_BATTERY 0x01 61a5afba16SPali Rohár 62deeba244SArmin Wolf #define DELL_SMM_NO_TEMP 10 63deeba244SArmin Wolf #define DELL_SMM_NO_FANS 3 64deeba244SArmin Wolf 65ba04d73cSArmin Wolf struct dell_smm_data { 66ba04d73cSArmin Wolf struct mutex i8k_mutex; /* lock for sensors writes */ 67ba04d73cSArmin Wolf char bios_version[4]; 68ba04d73cSArmin Wolf char bios_machineid[16]; 69ba04d73cSArmin Wolf uint i8k_fan_mult; 70ba04d73cSArmin Wolf uint i8k_pwm_mult; 71ba04d73cSArmin Wolf uint i8k_fan_max; 72ba04d73cSArmin Wolf bool disallow_fan_type_call; 73ba04d73cSArmin Wolf bool disallow_fan_support; 74ba04d73cSArmin Wolf unsigned int manual_fan; 75ba04d73cSArmin Wolf unsigned int auto_fan; 76deeba244SArmin Wolf int temp_type[DELL_SMM_NO_TEMP]; 77deeba244SArmin Wolf bool fan[DELL_SMM_NO_FANS]; 78deeba244SArmin Wolf int fan_type[DELL_SMM_NO_FANS]; 79b1986c8eSArmin Wolf int *fan_nominal_speed[DELL_SMM_NO_FANS]; 80ba04d73cSArmin Wolf }; 81a5afba16SPali Rohár 82a5afba16SPali Rohár MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)"); 83149ed3d4SPali Rohár MODULE_AUTHOR("Pali Rohár <pali@kernel.org>"); 84039ae585SPali Rohár MODULE_DESCRIPTION("Dell laptop SMM BIOS hwmon driver"); 85a5afba16SPali Rohár MODULE_LICENSE("GPL"); 86a5afba16SPali Rohár MODULE_ALIAS("i8k"); 87a5afba16SPali Rohár 88a5afba16SPali Rohár static bool force; 89a5afba16SPali Rohár module_param(force, bool, 0); 90a5afba16SPali Rohár MODULE_PARM_DESC(force, "Force loading without checking for supported models"); 91a5afba16SPali Rohár 92a5afba16SPali Rohár static bool ignore_dmi; 93a5afba16SPali Rohár module_param(ignore_dmi, bool, 0); 94a5afba16SPali Rohár MODULE_PARM_DESC(ignore_dmi, "Continue probing hardware even if DMI data does not match"); 95a5afba16SPali Rohár 96039ae585SPali Rohár #if IS_ENABLED(CONFIG_I8K) 977613663cSPali Rohár static bool restricted = true; 98a5afba16SPali Rohár module_param(restricted, bool, 0); 997613663cSPali Rohár MODULE_PARM_DESC(restricted, "Restrict fan control and serial number to CAP_SYS_ADMIN (default: 1)"); 100a5afba16SPali Rohár 101a5afba16SPali Rohár static bool power_status; 102a5afba16SPali Rohár module_param(power_status, bool, 0600); 1037613663cSPali Rohár MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k (default: 0)"); 104039ae585SPali Rohár #endif 105a5afba16SPali Rohár 106a5afba16SPali Rohár static uint fan_mult; 107a5afba16SPali Rohár module_param(fan_mult, uint, 0); 108a5afba16SPali Rohár MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with (default: autodetect)"); 109a5afba16SPali Rohár 110a5afba16SPali Rohár static uint fan_max; 111a5afba16SPali Rohár module_param(fan_max, uint, 0); 112a5afba16SPali Rohár MODULE_PARM_DESC(fan_max, "Maximum configurable fan speed (default: autodetect)"); 113a5afba16SPali Rohár 114a5afba16SPali Rohár struct smm_regs { 115a5afba16SPali Rohár unsigned int eax; 116565210c7SArmin Wolf unsigned int ebx; 117565210c7SArmin Wolf unsigned int ecx; 118565210c7SArmin Wolf unsigned int edx; 119565210c7SArmin Wolf unsigned int esi; 120565210c7SArmin Wolf unsigned int edi; 121565210c7SArmin Wolf } __packed; 122a5afba16SPali Rohár 123deeba244SArmin Wolf static const char * const temp_labels[] = { 124deeba244SArmin Wolf "CPU", 125deeba244SArmin Wolf "GPU", 126deeba244SArmin Wolf "SODIMM", 127deeba244SArmin Wolf "Other", 128deeba244SArmin Wolf "Ambient", 129deeba244SArmin Wolf "Other", 130deeba244SArmin Wolf }; 131deeba244SArmin Wolf 132deeba244SArmin Wolf static const char * const fan_labels[] = { 133deeba244SArmin Wolf "Processor Fan", 134deeba244SArmin Wolf "Motherboard Fan", 135deeba244SArmin Wolf "Video Fan", 136deeba244SArmin Wolf "Power Supply Fan", 137deeba244SArmin Wolf "Chipset Fan", 138deeba244SArmin Wolf "Other Fan", 139deeba244SArmin Wolf }; 140deeba244SArmin Wolf 141deeba244SArmin Wolf static const char * const docking_labels[] = { 142deeba244SArmin Wolf "Docking Processor Fan", 143deeba244SArmin Wolf "Docking Motherboard Fan", 144deeba244SArmin Wolf "Docking Video Fan", 145deeba244SArmin Wolf "Docking Power Supply Fan", 146deeba244SArmin Wolf "Docking Chipset Fan", 147deeba244SArmin Wolf "Docking Other Fan", 148deeba244SArmin Wolf }; 149deeba244SArmin Wolf 150c9363cdfSArmin Wolf static inline const char __init *i8k_get_dmi_data(int field) 151a5afba16SPali Rohár { 152a5afba16SPali Rohár const char *dmi_data = dmi_get_system_info(field); 153a5afba16SPali Rohár 154a5afba16SPali Rohár return dmi_data && *dmi_data ? dmi_data : "?"; 155a5afba16SPali Rohár } 156a5afba16SPali Rohár 157a5afba16SPali Rohár /* 158a5afba16SPali Rohár * Call the System Management Mode BIOS. Code provided by Jonathan Buzzard. 159a5afba16SPali Rohár */ 16027046a3fSJuergen Gross static int i8k_smm_func(void *par) 161a5afba16SPali Rohár { 1628713b4a4SArmin Wolf ktime_t calltime = ktime_get(); 16327046a3fSJuergen Gross struct smm_regs *regs = par; 164a5afba16SPali Rohár int eax = regs->eax; 1659d58bec0SPali Rohár int ebx = regs->ebx; 1668713b4a4SArmin Wolf long long duration; 1678713b4a4SArmin Wolf int rc; 1689d58bec0SPali Rohár 169a5afba16SPali Rohár /* SMM requires CPU 0 */ 17027046a3fSJuergen Gross if (smp_processor_id() != 0) 17127046a3fSJuergen Gross return -EBUSY; 172a5afba16SPali Rohár 173a5afba16SPali Rohár #if defined(CONFIG_X86_64) 174a5afba16SPali Rohár asm volatile("pushq %%rax\n\t" 175a5afba16SPali Rohár "movl 0(%%rax),%%edx\n\t" 176a5afba16SPali Rohár "pushq %%rdx\n\t" 177a5afba16SPali Rohár "movl 4(%%rax),%%ebx\n\t" 178a5afba16SPali Rohár "movl 8(%%rax),%%ecx\n\t" 179a5afba16SPali Rohár "movl 12(%%rax),%%edx\n\t" 180a5afba16SPali Rohár "movl 16(%%rax),%%esi\n\t" 181a5afba16SPali Rohár "movl 20(%%rax),%%edi\n\t" 182a5afba16SPali Rohár "popq %%rax\n\t" 183a5afba16SPali Rohár "out %%al,$0xb2\n\t" 184a5afba16SPali Rohár "out %%al,$0x84\n\t" 185a5afba16SPali Rohár "xchgq %%rax,(%%rsp)\n\t" 186a5afba16SPali Rohár "movl %%ebx,4(%%rax)\n\t" 187a5afba16SPali Rohár "movl %%ecx,8(%%rax)\n\t" 188a5afba16SPali Rohár "movl %%edx,12(%%rax)\n\t" 189a5afba16SPali Rohár "movl %%esi,16(%%rax)\n\t" 190a5afba16SPali Rohár "movl %%edi,20(%%rax)\n\t" 191a5afba16SPali Rohár "popq %%rdx\n\t" 192a5afba16SPali Rohár "movl %%edx,0(%%rax)\n\t" 193a5afba16SPali Rohár "pushfq\n\t" 194a5afba16SPali Rohár "popq %%rax\n\t" 195a5afba16SPali Rohár "andl $1,%%eax\n" 196a5afba16SPali Rohár : "=a"(rc) 197a5afba16SPali Rohár : "a"(regs) 198a5afba16SPali Rohár : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); 199a5afba16SPali Rohár #else 200a5afba16SPali Rohár asm volatile("pushl %%eax\n\t" 201a5afba16SPali Rohár "movl 0(%%eax),%%edx\n\t" 202a5afba16SPali Rohár "push %%edx\n\t" 203a5afba16SPali Rohár "movl 4(%%eax),%%ebx\n\t" 204a5afba16SPali Rohár "movl 8(%%eax),%%ecx\n\t" 205a5afba16SPali Rohár "movl 12(%%eax),%%edx\n\t" 206a5afba16SPali Rohár "movl 16(%%eax),%%esi\n\t" 207a5afba16SPali Rohár "movl 20(%%eax),%%edi\n\t" 208a5afba16SPali Rohár "popl %%eax\n\t" 209a5afba16SPali Rohár "out %%al,$0xb2\n\t" 210a5afba16SPali Rohár "out %%al,$0x84\n\t" 211a5afba16SPali Rohár "xchgl %%eax,(%%esp)\n\t" 212a5afba16SPali Rohár "movl %%ebx,4(%%eax)\n\t" 213a5afba16SPali Rohár "movl %%ecx,8(%%eax)\n\t" 214a5afba16SPali Rohár "movl %%edx,12(%%eax)\n\t" 215a5afba16SPali Rohár "movl %%esi,16(%%eax)\n\t" 216a5afba16SPali Rohár "movl %%edi,20(%%eax)\n\t" 217a5afba16SPali Rohár "popl %%edx\n\t" 218a5afba16SPali Rohár "movl %%edx,0(%%eax)\n\t" 219a5afba16SPali Rohár "lahf\n\t" 220a5afba16SPali Rohár "shrl $8,%%eax\n\t" 221a5afba16SPali Rohár "andl $1,%%eax\n" 222a5afba16SPali Rohár : "=a"(rc) 223a5afba16SPali Rohár : "a"(regs) 224a5afba16SPali Rohár : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); 225a5afba16SPali Rohár #endif 226a5afba16SPali Rohár if (rc != 0 || (regs->eax & 0xffff) == 0xffff || regs->eax == eax) 227a5afba16SPali Rohár rc = -EINVAL; 228a5afba16SPali Rohár 2298713b4a4SArmin Wolf duration = ktime_us_delta(ktime_get(), calltime); 2308713b4a4SArmin Wolf pr_debug("smm(0x%.4x 0x%.4x) = 0x%.4x (took %7lld usecs)\n", eax, ebx, 2319d58bec0SPali Rohár (rc ? 0xffff : regs->eax & 0xffff), duration); 2329d58bec0SPali Rohár 233a5afba16SPali Rohár return rc; 234a5afba16SPali Rohár } 235a5afba16SPali Rohár 236a5afba16SPali Rohár /* 23727046a3fSJuergen Gross * Call the System Management Mode BIOS. 23827046a3fSJuergen Gross */ 23927046a3fSJuergen Gross static int i8k_smm(struct smm_regs *regs) 24027046a3fSJuergen Gross { 24127046a3fSJuergen Gross int ret; 24227046a3fSJuergen Gross 243e104d530SSebastian Andrzej Siewior cpus_read_lock(); 24427046a3fSJuergen Gross ret = smp_call_on_cpu(0, i8k_smm_func, regs, true); 245e104d530SSebastian Andrzej Siewior cpus_read_unlock(); 24627046a3fSJuergen Gross 24727046a3fSJuergen Gross return ret; 24827046a3fSJuergen Gross } 24927046a3fSJuergen Gross 25027046a3fSJuergen Gross /* 251a5afba16SPali Rohár * Read the fan status. 252a5afba16SPali Rohár */ 253ba04d73cSArmin Wolf static int i8k_get_fan_status(const struct dell_smm_data *data, int fan) 254a5afba16SPali Rohár { 255a5afba16SPali Rohár struct smm_regs regs = { .eax = I8K_SMM_GET_FAN, }; 256a5afba16SPali Rohár 257ba04d73cSArmin Wolf if (data->disallow_fan_support) 258f480ea90SPali Rohár return -EINVAL; 259f480ea90SPali Rohár 260a5afba16SPali Rohár regs.ebx = fan & 0xff; 261a5afba16SPali Rohár return i8k_smm(®s) ? : regs.eax & 0xff; 262a5afba16SPali Rohár } 263a5afba16SPali Rohár 264a5afba16SPali Rohár /* 265a5afba16SPali Rohár * Read the fan speed in RPM. 266a5afba16SPali Rohár */ 267ba04d73cSArmin Wolf static int i8k_get_fan_speed(const struct dell_smm_data *data, int fan) 268a5afba16SPali Rohár { 269a5afba16SPali Rohár struct smm_regs regs = { .eax = I8K_SMM_GET_SPEED, }; 270a5afba16SPali Rohár 271ba04d73cSArmin Wolf if (data->disallow_fan_support) 272f480ea90SPali Rohár return -EINVAL; 273f480ea90SPali Rohár 274a5afba16SPali Rohár regs.ebx = fan & 0xff; 275ba04d73cSArmin Wolf return i8k_smm(®s) ? : (regs.eax & 0xffff) * data->i8k_fan_mult; 276a5afba16SPali Rohár } 277a5afba16SPali Rohár 278a5afba16SPali Rohár /* 279a5afba16SPali Rohár * Read the fan type. 280a5afba16SPali Rohár */ 281ba04d73cSArmin Wolf static int _i8k_get_fan_type(const struct dell_smm_data *data, int fan) 282a5afba16SPali Rohár { 283a5afba16SPali Rohár struct smm_regs regs = { .eax = I8K_SMM_GET_FAN_TYPE, }; 284a5afba16SPali Rohár 285ba04d73cSArmin Wolf if (data->disallow_fan_support || data->disallow_fan_type_call) 2862744d2fdSPali Rohár return -EINVAL; 2872744d2fdSPali Rohár 288a5afba16SPali Rohár regs.ebx = fan & 0xff; 289a5afba16SPali Rohár return i8k_smm(®s) ? : regs.eax & 0xff; 290a5afba16SPali Rohár } 291a5afba16SPali Rohár 292ba04d73cSArmin Wolf static int i8k_get_fan_type(struct dell_smm_data *data, int fan) 2935ce91714SPali Rohár { 2945ce91714SPali Rohár /* I8K_SMM_GET_FAN_TYPE SMM call is expensive, so cache values */ 295deeba244SArmin Wolf if (data->fan_type[fan] == INT_MIN) 296deeba244SArmin Wolf data->fan_type[fan] = _i8k_get_fan_type(data, fan); 2975ce91714SPali Rohár 298deeba244SArmin Wolf return data->fan_type[fan]; 2995ce91714SPali Rohár } 3005ce91714SPali Rohár 301a5afba16SPali Rohár /* 302a5afba16SPali Rohár * Read the fan nominal rpm for specific fan speed. 303a5afba16SPali Rohár */ 304782a99c1SArmin Wolf static int __init i8k_get_fan_nominal_speed(const struct dell_smm_data *data, int fan, int speed) 305a5afba16SPali Rohár { 306a5afba16SPali Rohár struct smm_regs regs = { .eax = I8K_SMM_GET_NOM_SPEED, }; 307a5afba16SPali Rohár 308ba04d73cSArmin Wolf if (data->disallow_fan_support) 309f480ea90SPali Rohár return -EINVAL; 310f480ea90SPali Rohár 311a5afba16SPali Rohár regs.ebx = (fan & 0xff) | (speed << 8); 312ba04d73cSArmin Wolf return i8k_smm(®s) ? : (regs.eax & 0xffff) * data->i8k_fan_mult; 313a5afba16SPali Rohár } 314a5afba16SPali Rohár 315a5afba16SPali Rohár /* 316afe45277SGiovanni Mascellani * Enable or disable automatic BIOS fan control support 317afe45277SGiovanni Mascellani */ 318ba04d73cSArmin Wolf static int i8k_enable_fan_auto_mode(const struct dell_smm_data *data, bool enable) 319afe45277SGiovanni Mascellani { 320afe45277SGiovanni Mascellani struct smm_regs regs = { }; 321afe45277SGiovanni Mascellani 322ba04d73cSArmin Wolf if (data->disallow_fan_support) 323afe45277SGiovanni Mascellani return -EINVAL; 324afe45277SGiovanni Mascellani 325ba04d73cSArmin Wolf regs.eax = enable ? data->auto_fan : data->manual_fan; 326afe45277SGiovanni Mascellani return i8k_smm(®s); 327afe45277SGiovanni Mascellani } 328afe45277SGiovanni Mascellani 329afe45277SGiovanni Mascellani /* 330c0d79987SArmin Wolf * Set the fan speed (off, low, high, ...). 331a5afba16SPali Rohár */ 332ba04d73cSArmin Wolf static int i8k_set_fan(const struct dell_smm_data *data, int fan, int speed) 333a5afba16SPali Rohár { 334a5afba16SPali Rohár struct smm_regs regs = { .eax = I8K_SMM_SET_FAN, }; 335a5afba16SPali Rohár 336ba04d73cSArmin Wolf if (data->disallow_fan_support) 337f480ea90SPali Rohár return -EINVAL; 338f480ea90SPali Rohár 339ba04d73cSArmin Wolf speed = (speed < 0) ? 0 : ((speed > data->i8k_fan_max) ? data->i8k_fan_max : speed); 340a5afba16SPali Rohár regs.ebx = (fan & 0xff) | (speed << 8); 341a5afba16SPali Rohár 342c0d79987SArmin Wolf return i8k_smm(®s); 343a5afba16SPali Rohár } 344a5afba16SPali Rohár 345deeba244SArmin Wolf static int __init i8k_get_temp_type(int sensor) 346a5afba16SPali Rohár { 347a5afba16SPali Rohár struct smm_regs regs = { .eax = I8K_SMM_GET_TEMP_TYPE, }; 348a5afba16SPali Rohár 349a5afba16SPali Rohár regs.ebx = sensor & 0xff; 350a5afba16SPali Rohár return i8k_smm(®s) ? : regs.eax & 0xff; 351a5afba16SPali Rohár } 352a5afba16SPali Rohár 353a5afba16SPali Rohár /* 354a5afba16SPali Rohár * Read the cpu temperature. 355a5afba16SPali Rohár */ 356a5afba16SPali Rohár static int _i8k_get_temp(int sensor) 357a5afba16SPali Rohár { 358a5afba16SPali Rohár struct smm_regs regs = { 359a5afba16SPali Rohár .eax = I8K_SMM_GET_TEMP, 360a5afba16SPali Rohár .ebx = sensor & 0xff, 361a5afba16SPali Rohár }; 362a5afba16SPali Rohár 363a5afba16SPali Rohár return i8k_smm(®s) ? : regs.eax & 0xff; 364a5afba16SPali Rohár } 365a5afba16SPali Rohár 366a5afba16SPali Rohár static int i8k_get_temp(int sensor) 367a5afba16SPali Rohár { 368a5afba16SPali Rohár int temp = _i8k_get_temp(sensor); 369a5afba16SPali Rohár 370a5afba16SPali Rohár /* 371a5afba16SPali Rohár * Sometimes the temperature sensor returns 0x99, which is out of range. 372a5afba16SPali Rohár * In this case we retry (once) before returning an error. 373a5afba16SPali Rohár # 1003655137 00000058 00005a4b 374a5afba16SPali Rohár # 1003655138 00000099 00003a80 <--- 0x99 = 153 degrees 375a5afba16SPali Rohár # 1003655139 00000054 00005c52 376a5afba16SPali Rohár */ 377a5afba16SPali Rohár if (temp == 0x99) { 378a5afba16SPali Rohár msleep(100); 379a5afba16SPali Rohár temp = _i8k_get_temp(sensor); 380a5afba16SPali Rohár } 381a5afba16SPali Rohár /* 382a5afba16SPali Rohár * Return -ENODATA for all invalid temperatures. 383a5afba16SPali Rohár * 384a5afba16SPali Rohár * Known instances are the 0x99 value as seen above as well as 385a5afba16SPali Rohár * 0xc1 (193), which may be returned when trying to read the GPU 386a5afba16SPali Rohár * temperature if the system supports a GPU and it is currently 387a5afba16SPali Rohár * turned off. 388a5afba16SPali Rohár */ 389a5afba16SPali Rohár if (temp > I8K_MAX_TEMP) 390a5afba16SPali Rohár return -ENODATA; 391a5afba16SPali Rohár 392a5afba16SPali Rohár return temp; 393a5afba16SPali Rohár } 394a5afba16SPali Rohár 395c9363cdfSArmin Wolf static int __init i8k_get_dell_signature(int req_fn) 396a5afba16SPali Rohár { 397a5afba16SPali Rohár struct smm_regs regs = { .eax = req_fn, }; 398a5afba16SPali Rohár int rc; 399a5afba16SPali Rohár 400a5afba16SPali Rohár rc = i8k_smm(®s); 401a5afba16SPali Rohár if (rc < 0) 402a5afba16SPali Rohár return rc; 403a5afba16SPali Rohár 404a5afba16SPali Rohár return regs.eax == 1145651527 && regs.edx == 1145392204 ? 0 : -1; 405a5afba16SPali Rohár } 406a5afba16SPali Rohár 407039ae585SPali Rohár #if IS_ENABLED(CONFIG_I8K) 408039ae585SPali Rohár 409039ae585SPali Rohár /* 410039ae585SPali Rohár * Read the Fn key status. 411039ae585SPali Rohár */ 412039ae585SPali Rohár static int i8k_get_fn_status(void) 413039ae585SPali Rohár { 414039ae585SPali Rohár struct smm_regs regs = { .eax = I8K_SMM_FN_STATUS, }; 415039ae585SPali Rohár int rc; 416039ae585SPali Rohár 417039ae585SPali Rohár rc = i8k_smm(®s); 418039ae585SPali Rohár if (rc < 0) 419039ae585SPali Rohár return rc; 420039ae585SPali Rohár 421039ae585SPali Rohár switch ((regs.eax >> I8K_FN_SHIFT) & I8K_FN_MASK) { 422039ae585SPali Rohár case I8K_FN_UP: 423039ae585SPali Rohár return I8K_VOL_UP; 424039ae585SPali Rohár case I8K_FN_DOWN: 425039ae585SPali Rohár return I8K_VOL_DOWN; 426039ae585SPali Rohár case I8K_FN_MUTE: 427039ae585SPali Rohár return I8K_VOL_MUTE; 428039ae585SPali Rohár default: 429039ae585SPali Rohár return 0; 430039ae585SPali Rohár } 431039ae585SPali Rohár } 432039ae585SPali Rohár 433039ae585SPali Rohár /* 434039ae585SPali Rohár * Read the power status. 435039ae585SPali Rohár */ 436039ae585SPali Rohár static int i8k_get_power_status(void) 437039ae585SPali Rohár { 438039ae585SPali Rohár struct smm_regs regs = { .eax = I8K_SMM_POWER_STATUS, }; 439039ae585SPali Rohár int rc; 440039ae585SPali Rohár 441039ae585SPali Rohár rc = i8k_smm(®s); 442039ae585SPali Rohár if (rc < 0) 443039ae585SPali Rohár return rc; 444039ae585SPali Rohár 445039ae585SPali Rohár return (regs.eax & 0xff) == I8K_POWER_AC ? I8K_AC : I8K_BATTERY; 446039ae585SPali Rohár } 447039ae585SPali Rohár 448039ae585SPali Rohár /* 449039ae585SPali Rohár * Procfs interface 450039ae585SPali Rohár */ 451039ae585SPali Rohár 45287b93329SArmin Wolf static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) 453a5afba16SPali Rohár { 454*359745d7SMuchun Song struct dell_smm_data *data = pde_data(file_inode(fp)); 455a5afba16SPali Rohár int __user *argp = (int __user *)arg; 45687b93329SArmin Wolf int speed, err; 45787b93329SArmin Wolf int val = 0; 458a5afba16SPali Rohár 459a5afba16SPali Rohár if (!argp) 460a5afba16SPali Rohár return -EINVAL; 461a5afba16SPali Rohár 462a5afba16SPali Rohár switch (cmd) { 463a5afba16SPali Rohár case I8K_BIOS_VERSION: 464ba04d73cSArmin Wolf if (!isdigit(data->bios_version[0]) || !isdigit(data->bios_version[1]) || 465ba04d73cSArmin Wolf !isdigit(data->bios_version[2])) 466053ea640SPali Rohár return -EINVAL; 467053ea640SPali Rohár 468ba04d73cSArmin Wolf val = (data->bios_version[0] << 16) | 469ba04d73cSArmin Wolf (data->bios_version[1] << 8) | data->bios_version[2]; 470a5afba16SPali Rohár 47102405387SArmin Wolf if (copy_to_user(argp, &val, sizeof(val))) 47202405387SArmin Wolf return -EFAULT; 47302405387SArmin Wolf 47402405387SArmin Wolf return 0; 475a5afba16SPali Rohár case I8K_MACHINE_ID: 4767613663cSPali Rohár if (restricted && !capable(CAP_SYS_ADMIN)) 4777613663cSPali Rohár return -EPERM; 4787613663cSPali Rohár 47902405387SArmin Wolf if (copy_to_user(argp, data->bios_machineid, sizeof(data->bios_machineid))) 48002405387SArmin Wolf return -EFAULT; 481a5afba16SPali Rohár 48202405387SArmin Wolf return 0; 483a5afba16SPali Rohár case I8K_FN_STATUS: 484a5afba16SPali Rohár val = i8k_get_fn_status(); 485a5afba16SPali Rohár break; 486a5afba16SPali Rohár 487a5afba16SPali Rohár case I8K_POWER_STATUS: 488a5afba16SPali Rohár val = i8k_get_power_status(); 489a5afba16SPali Rohár break; 490a5afba16SPali Rohár 491a5afba16SPali Rohár case I8K_GET_TEMP: 492a5afba16SPali Rohár val = i8k_get_temp(0); 493a5afba16SPali Rohár break; 494a5afba16SPali Rohár 495a5afba16SPali Rohár case I8K_GET_SPEED: 496a5afba16SPali Rohár if (copy_from_user(&val, argp, sizeof(int))) 497a5afba16SPali Rohár return -EFAULT; 498a5afba16SPali Rohár 499ba04d73cSArmin Wolf val = i8k_get_fan_speed(data, val); 500a5afba16SPali Rohár break; 501a5afba16SPali Rohár 502a5afba16SPali Rohár case I8K_GET_FAN: 503a5afba16SPali Rohár if (copy_from_user(&val, argp, sizeof(int))) 504a5afba16SPali Rohár return -EFAULT; 505a5afba16SPali Rohár 506ba04d73cSArmin Wolf val = i8k_get_fan_status(data, val); 507a5afba16SPali Rohár break; 508a5afba16SPali Rohár 509a5afba16SPali Rohár case I8K_SET_FAN: 510a5afba16SPali Rohár if (restricted && !capable(CAP_SYS_ADMIN)) 511a5afba16SPali Rohár return -EPERM; 512a5afba16SPali Rohár 513a5afba16SPali Rohár if (copy_from_user(&val, argp, sizeof(int))) 514a5afba16SPali Rohár return -EFAULT; 515a5afba16SPali Rohár 516a5afba16SPali Rohár if (copy_from_user(&speed, argp + 1, sizeof(int))) 517a5afba16SPali Rohár return -EFAULT; 518a5afba16SPali Rohár 51987b93329SArmin Wolf mutex_lock(&data->i8k_mutex); 520c0d79987SArmin Wolf err = i8k_set_fan(data, val, speed); 521c0d79987SArmin Wolf if (err < 0) 52287b93329SArmin Wolf val = err; 52387b93329SArmin Wolf else 524c0d79987SArmin Wolf val = i8k_get_fan_status(data, val); 52587b93329SArmin Wolf mutex_unlock(&data->i8k_mutex); 526a5afba16SPali Rohár break; 527a5afba16SPali Rohár 528a5afba16SPali Rohár default: 529e64325e8SArmin Wolf return -ENOIOCTLCMD; 530a5afba16SPali Rohár } 531a5afba16SPali Rohár 532a5afba16SPali Rohár if (val < 0) 533a5afba16SPali Rohár return val; 534a5afba16SPali Rohár 535a5afba16SPali Rohár if (copy_to_user(argp, &val, sizeof(int))) 536a5afba16SPali Rohár return -EFAULT; 537a5afba16SPali Rohár 538a5afba16SPali Rohár return 0; 539a5afba16SPali Rohár } 540a5afba16SPali Rohár 541a5afba16SPali Rohár /* 542a5afba16SPali Rohár * Print the information for /proc/i8k. 543a5afba16SPali Rohár */ 544a5afba16SPali Rohár static int i8k_proc_show(struct seq_file *seq, void *offset) 545a5afba16SPali Rohár { 546ba04d73cSArmin Wolf struct dell_smm_data *data = seq->private; 547a5afba16SPali Rohár int fn_key, cpu_temp, ac_power; 548a5afba16SPali Rohár int left_fan, right_fan, left_speed, right_speed; 549a5afba16SPali Rohár 550a5afba16SPali Rohár cpu_temp = i8k_get_temp(0); /* 11100 µs */ 551ba04d73cSArmin Wolf left_fan = i8k_get_fan_status(data, I8K_FAN_LEFT); /* 580 µs */ 552ba04d73cSArmin Wolf right_fan = i8k_get_fan_status(data, I8K_FAN_RIGHT); /* 580 µs */ 553ba04d73cSArmin Wolf left_speed = i8k_get_fan_speed(data, I8K_FAN_LEFT); /* 580 µs */ 554ba04d73cSArmin Wolf right_speed = i8k_get_fan_speed(data, I8K_FAN_RIGHT); /* 580 µs */ 555a5afba16SPali Rohár fn_key = i8k_get_fn_status(); /* 750 µs */ 556a5afba16SPali Rohár if (power_status) 557a5afba16SPali Rohár ac_power = i8k_get_power_status(); /* 14700 µs */ 558a5afba16SPali Rohár else 559a5afba16SPali Rohár ac_power = -1; 560a5afba16SPali Rohár 561a5afba16SPali Rohár /* 562a5afba16SPali Rohár * Info: 563a5afba16SPali Rohár * 564a5afba16SPali Rohár * 1) Format version (this will change if format changes) 565a5afba16SPali Rohár * 2) BIOS version 566a5afba16SPali Rohár * 3) BIOS machine ID 567a5afba16SPali Rohár * 4) Cpu temperature 568a5afba16SPali Rohár * 5) Left fan status 569a5afba16SPali Rohár * 6) Right fan status 570a5afba16SPali Rohár * 7) Left fan speed 571a5afba16SPali Rohár * 8) Right fan speed 572a5afba16SPali Rohár * 9) AC power 573a5afba16SPali Rohár * 10) Fn Key status 574a5afba16SPali Rohár */ 575a5afba16SPali Rohár seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n", 576a5afba16SPali Rohár I8K_PROC_FMT, 577ba04d73cSArmin Wolf data->bios_version, 578ba04d73cSArmin Wolf (restricted && !capable(CAP_SYS_ADMIN)) ? "-1" : data->bios_machineid, 579a5afba16SPali Rohár cpu_temp, 580a5afba16SPali Rohár left_fan, right_fan, left_speed, right_speed, 581a5afba16SPali Rohár ac_power, fn_key); 582a5afba16SPali Rohár 583a5afba16SPali Rohár return 0; 584a5afba16SPali Rohár } 585a5afba16SPali Rohár 586a5afba16SPali Rohár static int i8k_open_fs(struct inode *inode, struct file *file) 587a5afba16SPali Rohár { 588*359745d7SMuchun Song return single_open(file, i8k_proc_show, pde_data(inode)); 589a5afba16SPali Rohár } 590a5afba16SPali Rohár 59197a32539SAlexey Dobriyan static const struct proc_ops i8k_proc_ops = { 59297a32539SAlexey Dobriyan .proc_open = i8k_open_fs, 59397a32539SAlexey Dobriyan .proc_read = seq_read, 59497a32539SAlexey Dobriyan .proc_lseek = seq_lseek, 59597a32539SAlexey Dobriyan .proc_release = single_release, 59697a32539SAlexey Dobriyan .proc_ioctl = i8k_ioctl, 597039ae585SPali Rohár }; 598039ae585SPali Rohár 599a2cb66b4SArmin Wolf static void i8k_exit_procfs(void *param) 600039ae585SPali Rohár { 601039ae585SPali Rohár remove_proc_entry("i8k", NULL); 602039ae585SPali Rohár } 603039ae585SPali Rohár 604a2cb66b4SArmin Wolf static void __init i8k_init_procfs(struct device *dev) 605039ae585SPali Rohár { 606ba04d73cSArmin Wolf struct dell_smm_data *data = dev_get_drvdata(dev); 607ba04d73cSArmin Wolf 608dbd3e6eaSArmin Wolf /* Only register exit function if creation was successful */ 609dbd3e6eaSArmin Wolf if (proc_create_data("i8k", 0, NULL, &i8k_proc_ops, data)) 610a2cb66b4SArmin Wolf devm_add_action_or_reset(dev, i8k_exit_procfs, NULL); 611039ae585SPali Rohár } 612039ae585SPali Rohár 613a2cb66b4SArmin Wolf #else 614a2cb66b4SArmin Wolf 615a2cb66b4SArmin Wolf static void __init i8k_init_procfs(struct device *dev) 616039ae585SPali Rohár { 617039ae585SPali Rohár } 618039ae585SPali Rohár 619039ae585SPali Rohár #endif 620a5afba16SPali Rohár 621a5afba16SPali Rohár /* 622a5afba16SPali Rohár * Hwmon interface 623a5afba16SPali Rohár */ 624a5afba16SPali Rohár 625deeba244SArmin Wolf static umode_t dell_smm_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr, 626deeba244SArmin Wolf int channel) 627a5afba16SPali Rohár { 628deeba244SArmin Wolf const struct dell_smm_data *data = drvdata; 629a5afba16SPali Rohár 630deeba244SArmin Wolf switch (type) { 631deeba244SArmin Wolf case hwmon_temp: 632deeba244SArmin Wolf switch (attr) { 633deeba244SArmin Wolf case hwmon_temp_input: 634deeba244SArmin Wolf case hwmon_temp_label: 635deeba244SArmin Wolf if (data->temp_type[channel] >= 0) 636deeba244SArmin Wolf return 0444; 637deeba244SArmin Wolf 638deeba244SArmin Wolf break; 639deeba244SArmin Wolf default: 640deeba244SArmin Wolf break; 641deeba244SArmin Wolf } 642deeba244SArmin Wolf break; 643deeba244SArmin Wolf case hwmon_fan: 644deeba244SArmin Wolf if (data->disallow_fan_support) 645deeba244SArmin Wolf break; 646deeba244SArmin Wolf 647deeba244SArmin Wolf switch (attr) { 648deeba244SArmin Wolf case hwmon_fan_input: 649deeba244SArmin Wolf if (data->fan[channel]) 650deeba244SArmin Wolf return 0444; 651deeba244SArmin Wolf 652deeba244SArmin Wolf break; 653deeba244SArmin Wolf case hwmon_fan_label: 654deeba244SArmin Wolf if (data->fan[channel] && !data->disallow_fan_type_call) 655deeba244SArmin Wolf return 0444; 656deeba244SArmin Wolf 657deeba244SArmin Wolf break; 658b1986c8eSArmin Wolf case hwmon_fan_min: 659b1986c8eSArmin Wolf case hwmon_fan_max: 660b1986c8eSArmin Wolf case hwmon_fan_target: 661b1986c8eSArmin Wolf if (data->fan_nominal_speed[channel]) 662b1986c8eSArmin Wolf return 0444; 663b1986c8eSArmin Wolf 664b1986c8eSArmin Wolf break; 665deeba244SArmin Wolf default: 666deeba244SArmin Wolf break; 667deeba244SArmin Wolf } 668deeba244SArmin Wolf break; 669deeba244SArmin Wolf case hwmon_pwm: 670deeba244SArmin Wolf if (data->disallow_fan_support) 671deeba244SArmin Wolf break; 672deeba244SArmin Wolf 673deeba244SArmin Wolf switch (attr) { 674deeba244SArmin Wolf case hwmon_pwm_input: 675deeba244SArmin Wolf if (data->fan[channel]) 676deeba244SArmin Wolf return 0644; 677deeba244SArmin Wolf 678deeba244SArmin Wolf break; 679deeba244SArmin Wolf case hwmon_pwm_enable: 680deeba244SArmin Wolf if (data->auto_fan) 681deeba244SArmin Wolf /* 682deeba244SArmin Wolf * There is no command for retrieve the current status 683deeba244SArmin Wolf * from BIOS, and userspace/firmware itself can change 684deeba244SArmin Wolf * it. 685deeba244SArmin Wolf * Thus we can only provide write-only access for now. 686deeba244SArmin Wolf */ 687deeba244SArmin Wolf return 0200; 688deeba244SArmin Wolf 689deeba244SArmin Wolf break; 690deeba244SArmin Wolf default: 691deeba244SArmin Wolf break; 692deeba244SArmin Wolf } 693deeba244SArmin Wolf break; 694deeba244SArmin Wolf default: 695deeba244SArmin Wolf break; 696a5afba16SPali Rohár } 697a5afba16SPali Rohár 698deeba244SArmin Wolf return 0; 699a5afba16SPali Rohár } 700a5afba16SPali Rohár 701deeba244SArmin Wolf static int dell_smm_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, 702deeba244SArmin Wolf long *val) 703a5afba16SPali Rohár { 704ba04d73cSArmin Wolf struct dell_smm_data *data = dev_get_drvdata(dev); 705deeba244SArmin Wolf int ret; 706a5afba16SPali Rohár 707deeba244SArmin Wolf switch (type) { 708deeba244SArmin Wolf case hwmon_temp: 709deeba244SArmin Wolf switch (attr) { 710deeba244SArmin Wolf case hwmon_temp_input: 711deeba244SArmin Wolf ret = i8k_get_temp(channel); 712deeba244SArmin Wolf if (ret < 0) 713deeba244SArmin Wolf return ret; 714deeba244SArmin Wolf 715deeba244SArmin Wolf *val = ret * 1000; 716deeba244SArmin Wolf 717deeba244SArmin Wolf return 0; 718deeba244SArmin Wolf default: 719deeba244SArmin Wolf break; 720deeba244SArmin Wolf } 721deeba244SArmin Wolf break; 722deeba244SArmin Wolf case hwmon_fan: 723deeba244SArmin Wolf switch (attr) { 724deeba244SArmin Wolf case hwmon_fan_input: 725deeba244SArmin Wolf ret = i8k_get_fan_speed(data, channel); 726deeba244SArmin Wolf if (ret < 0) 727deeba244SArmin Wolf return ret; 728deeba244SArmin Wolf 729deeba244SArmin Wolf *val = ret; 730deeba244SArmin Wolf 731deeba244SArmin Wolf return 0; 732b1986c8eSArmin Wolf case hwmon_fan_min: 733b1986c8eSArmin Wolf *val = data->fan_nominal_speed[channel][0]; 734b1986c8eSArmin Wolf 735b1986c8eSArmin Wolf return 0; 736b1986c8eSArmin Wolf case hwmon_fan_max: 737b1986c8eSArmin Wolf *val = data->fan_nominal_speed[channel][data->i8k_fan_max]; 738b1986c8eSArmin Wolf 739b1986c8eSArmin Wolf return 0; 740b1986c8eSArmin Wolf case hwmon_fan_target: 741b1986c8eSArmin Wolf ret = i8k_get_fan_status(data, channel); 742b1986c8eSArmin Wolf if (ret < 0) 743b1986c8eSArmin Wolf return ret; 744b1986c8eSArmin Wolf 745b1986c8eSArmin Wolf if (ret > data->i8k_fan_max) 746b1986c8eSArmin Wolf ret = data->i8k_fan_max; 747b1986c8eSArmin Wolf 748b1986c8eSArmin Wolf *val = data->fan_nominal_speed[channel][ret]; 749b1986c8eSArmin Wolf 750b1986c8eSArmin Wolf return 0; 751deeba244SArmin Wolf default: 752deeba244SArmin Wolf break; 753deeba244SArmin Wolf } 754deeba244SArmin Wolf break; 755deeba244SArmin Wolf case hwmon_pwm: 756deeba244SArmin Wolf switch (attr) { 757deeba244SArmin Wolf case hwmon_pwm_input: 758deeba244SArmin Wolf ret = i8k_get_fan_status(data, channel); 759deeba244SArmin Wolf if (ret < 0) 760deeba244SArmin Wolf return ret; 761deeba244SArmin Wolf 762deeba244SArmin Wolf *val = clamp_val(ret * data->i8k_pwm_mult, 0, 255); 763deeba244SArmin Wolf 764deeba244SArmin Wolf return 0; 765deeba244SArmin Wolf default: 766deeba244SArmin Wolf break; 767deeba244SArmin Wolf } 768deeba244SArmin Wolf break; 769deeba244SArmin Wolf default: 770deeba244SArmin Wolf break; 771deeba244SArmin Wolf } 772deeba244SArmin Wolf 773deeba244SArmin Wolf return -EOPNOTSUPP; 774deeba244SArmin Wolf } 775deeba244SArmin Wolf 776deeba244SArmin Wolf static const char *dell_smm_fan_label(struct dell_smm_data *data, int channel) 777deeba244SArmin Wolf { 778deeba244SArmin Wolf bool dock = false; 779deeba244SArmin Wolf int type = i8k_get_fan_type(data, channel); 780deeba244SArmin Wolf 781a5afba16SPali Rohár if (type < 0) 782deeba244SArmin Wolf return ERR_PTR(type); 783a5afba16SPali Rohár 784a5afba16SPali Rohár if (type & 0x10) { 785a5afba16SPali Rohár dock = true; 786a5afba16SPali Rohár type &= 0x0F; 787a5afba16SPali Rohár } 788a5afba16SPali Rohár 789deeba244SArmin Wolf if (type >= ARRAY_SIZE(fan_labels)) 790deeba244SArmin Wolf type = ARRAY_SIZE(fan_labels) - 1; 791a5afba16SPali Rohár 792deeba244SArmin Wolf return dock ? docking_labels[type] : fan_labels[type]; 793a5afba16SPali Rohár } 794a5afba16SPali Rohár 795deeba244SArmin Wolf static int dell_smm_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, 796deeba244SArmin Wolf int channel, const char **str) 797a5afba16SPali Rohár { 798ba04d73cSArmin Wolf struct dell_smm_data *data = dev_get_drvdata(dev); 799a5afba16SPali Rohár 800deeba244SArmin Wolf switch (type) { 801deeba244SArmin Wolf case hwmon_temp: 802deeba244SArmin Wolf switch (attr) { 803deeba244SArmin Wolf case hwmon_temp_label: 804deeba244SArmin Wolf *str = temp_labels[data->temp_type[channel]]; 805deeba244SArmin Wolf return 0; 806deeba244SArmin Wolf default: 807deeba244SArmin Wolf break; 808deeba244SArmin Wolf } 809deeba244SArmin Wolf break; 810deeba244SArmin Wolf case hwmon_fan: 811deeba244SArmin Wolf switch (attr) { 812deeba244SArmin Wolf case hwmon_fan_label: 813deeba244SArmin Wolf *str = dell_smm_fan_label(data, channel); 814deeba244SArmin Wolf return PTR_ERR_OR_ZERO(*str); 815deeba244SArmin Wolf default: 816deeba244SArmin Wolf break; 817deeba244SArmin Wolf } 818deeba244SArmin Wolf break; 819deeba244SArmin Wolf default: 820deeba244SArmin Wolf break; 821a5afba16SPali Rohár } 822a5afba16SPali Rohár 823deeba244SArmin Wolf return -EOPNOTSUPP; 824a5afba16SPali Rohár } 825a5afba16SPali Rohár 826deeba244SArmin Wolf static int dell_smm_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, 827deeba244SArmin Wolf long val) 828a5afba16SPali Rohár { 829ba04d73cSArmin Wolf struct dell_smm_data *data = dev_get_drvdata(dev); 830deeba244SArmin Wolf unsigned long pwm; 831deeba244SArmin Wolf bool enable; 832a5afba16SPali Rohár int err; 833a5afba16SPali Rohár 834deeba244SArmin Wolf switch (type) { 835deeba244SArmin Wolf case hwmon_pwm: 836deeba244SArmin Wolf switch (attr) { 837deeba244SArmin Wolf case hwmon_pwm_input: 838deeba244SArmin Wolf pwm = clamp_val(DIV_ROUND_CLOSEST(val, data->i8k_pwm_mult), 0, 839deeba244SArmin Wolf data->i8k_fan_max); 840a5afba16SPali Rohár 841ba04d73cSArmin Wolf mutex_lock(&data->i8k_mutex); 842deeba244SArmin Wolf err = i8k_set_fan(data, channel, pwm); 843ba04d73cSArmin Wolf mutex_unlock(&data->i8k_mutex); 844a5afba16SPali Rohár 845deeba244SArmin Wolf if (err < 0) 846afe45277SGiovanni Mascellani return err; 847afe45277SGiovanni Mascellani 848deeba244SArmin Wolf return 0; 849deeba244SArmin Wolf case hwmon_pwm_enable: 850deeba244SArmin Wolf if (!val) 851deeba244SArmin Wolf return -EINVAL; 852deeba244SArmin Wolf 853afe45277SGiovanni Mascellani if (val == 1) 854afe45277SGiovanni Mascellani enable = false; 855afe45277SGiovanni Mascellani else 856deeba244SArmin Wolf enable = true; 857afe45277SGiovanni Mascellani 858ba04d73cSArmin Wolf mutex_lock(&data->i8k_mutex); 859ba04d73cSArmin Wolf err = i8k_enable_fan_auto_mode(data, enable); 860ba04d73cSArmin Wolf mutex_unlock(&data->i8k_mutex); 861afe45277SGiovanni Mascellani 862deeba244SArmin Wolf if (err < 0) 863deeba244SArmin Wolf return err; 864deeba244SArmin Wolf 865deeba244SArmin Wolf return 0; 866deeba244SArmin Wolf default: 867deeba244SArmin Wolf break; 868deeba244SArmin Wolf } 869deeba244SArmin Wolf break; 870deeba244SArmin Wolf default: 871deeba244SArmin Wolf break; 872afe45277SGiovanni Mascellani } 873afe45277SGiovanni Mascellani 874deeba244SArmin Wolf return -EOPNOTSUPP; 875deeba244SArmin Wolf } 876a5afba16SPali Rohár 877deeba244SArmin Wolf static const struct hwmon_ops dell_smm_ops = { 878deeba244SArmin Wolf .is_visible = dell_smm_is_visible, 879deeba244SArmin Wolf .read = dell_smm_read, 880deeba244SArmin Wolf .read_string = dell_smm_read_string, 881deeba244SArmin Wolf .write = dell_smm_write, 882deeba244SArmin Wolf }; 883deeba244SArmin Wolf 884deeba244SArmin Wolf static const struct hwmon_channel_info *dell_smm_info[] = { 885deeba244SArmin Wolf HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), 886deeba244SArmin Wolf HWMON_CHANNEL_INFO(temp, 887deeba244SArmin Wolf HWMON_T_INPUT | HWMON_T_LABEL, 888deeba244SArmin Wolf HWMON_T_INPUT | HWMON_T_LABEL, 889deeba244SArmin Wolf HWMON_T_INPUT | HWMON_T_LABEL, 890deeba244SArmin Wolf HWMON_T_INPUT | HWMON_T_LABEL, 891deeba244SArmin Wolf HWMON_T_INPUT | HWMON_T_LABEL, 892deeba244SArmin Wolf HWMON_T_INPUT | HWMON_T_LABEL, 893deeba244SArmin Wolf HWMON_T_INPUT | HWMON_T_LABEL, 894deeba244SArmin Wolf HWMON_T_INPUT | HWMON_T_LABEL, 895deeba244SArmin Wolf HWMON_T_INPUT | HWMON_T_LABEL, 896deeba244SArmin Wolf HWMON_T_INPUT | HWMON_T_LABEL 897deeba244SArmin Wolf ), 898deeba244SArmin Wolf HWMON_CHANNEL_INFO(fan, 899b1986c8eSArmin Wolf HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX | 900b1986c8eSArmin Wolf HWMON_F_TARGET, 901b1986c8eSArmin Wolf HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX | 902b1986c8eSArmin Wolf HWMON_F_TARGET, 903b1986c8eSArmin Wolf HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX | 904b1986c8eSArmin Wolf HWMON_F_TARGET 905deeba244SArmin Wolf ), 906deeba244SArmin Wolf HWMON_CHANNEL_INFO(pwm, 907deeba244SArmin Wolf HWMON_PWM_INPUT | HWMON_PWM_ENABLE, 908deeba244SArmin Wolf HWMON_PWM_INPUT, 909deeba244SArmin Wolf HWMON_PWM_INPUT 910deeba244SArmin Wolf ), 911a5afba16SPali Rohár NULL 912a5afba16SPali Rohár }; 913a5afba16SPali Rohár 914deeba244SArmin Wolf static const struct hwmon_chip_info dell_smm_chip_info = { 915deeba244SArmin Wolf .ops = &dell_smm_ops, 916deeba244SArmin Wolf .info = dell_smm_info, 917a5afba16SPali Rohár }; 918a5afba16SPali Rohár 9191492fa21SArmin Wolf static int __init dell_smm_init_hwmon(struct device *dev) 920a5afba16SPali Rohár { 921ba04d73cSArmin Wolf struct dell_smm_data *data = dev_get_drvdata(dev); 922deeba244SArmin Wolf struct device *dell_smm_hwmon_dev; 923b1986c8eSArmin Wolf int i, state, err; 924a5afba16SPali Rohár 925deeba244SArmin Wolf for (i = 0; i < DELL_SMM_NO_TEMP; i++) { 926deeba244SArmin Wolf data->temp_type[i] = i8k_get_temp_type(i); 927deeba244SArmin Wolf if (data->temp_type[i] < 0) 928deeba244SArmin Wolf continue; 929a5afba16SPali Rohár 930deeba244SArmin Wolf if (data->temp_type[i] >= ARRAY_SIZE(temp_labels)) 931deeba244SArmin Wolf data->temp_type[i] = ARRAY_SIZE(temp_labels) - 1; 932deeba244SArmin Wolf } 933deeba244SArmin Wolf 934deeba244SArmin Wolf for (i = 0; i < DELL_SMM_NO_FANS; i++) { 935deeba244SArmin Wolf data->fan_type[i] = INT_MIN; 936deeba244SArmin Wolf err = i8k_get_fan_status(data, i); 9375ce91714SPali Rohár if (err < 0) 938deeba244SArmin Wolf err = i8k_get_fan_type(data, i); 939b1986c8eSArmin Wolf 940b1986c8eSArmin Wolf if (err < 0) 941b1986c8eSArmin Wolf continue; 942b1986c8eSArmin Wolf 943deeba244SArmin Wolf data->fan[i] = true; 944b1986c8eSArmin Wolf data->fan_nominal_speed[i] = devm_kmalloc_array(dev, data->i8k_fan_max + 1, 945b1986c8eSArmin Wolf sizeof(*data->fan_nominal_speed[i]), 946b1986c8eSArmin Wolf GFP_KERNEL); 947b1986c8eSArmin Wolf if (!data->fan_nominal_speed[i]) 948b1986c8eSArmin Wolf continue; 949b1986c8eSArmin Wolf 950b1986c8eSArmin Wolf for (state = 0; state <= data->i8k_fan_max; state++) { 951b1986c8eSArmin Wolf err = i8k_get_fan_nominal_speed(data, i, state); 952b1986c8eSArmin Wolf if (err < 0) { 953b1986c8eSArmin Wolf /* Mark nominal speed table as invalid in case of error */ 954b1986c8eSArmin Wolf devm_kfree(dev, data->fan_nominal_speed[i]); 955b1986c8eSArmin Wolf data->fan_nominal_speed[i] = NULL; 956b1986c8eSArmin Wolf break; 957b1986c8eSArmin Wolf } 958b1986c8eSArmin Wolf data->fan_nominal_speed[i][state] = err; 959b1986c8eSArmin Wolf } 960deeba244SArmin Wolf } 961a5afba16SPali Rohár 962deeba244SArmin Wolf dell_smm_hwmon_dev = devm_hwmon_device_register_with_info(dev, "dell_smm", data, 963deeba244SArmin Wolf &dell_smm_chip_info, NULL); 964a5afba16SPali Rohár 965deeba244SArmin Wolf return PTR_ERR_OR_ZERO(dell_smm_hwmon_dev); 966a5afba16SPali Rohár } 967a5afba16SPali Rohár 968a5afba16SPali Rohár struct i8k_config_data { 969a5afba16SPali Rohár uint fan_mult; 970a5afba16SPali Rohár uint fan_max; 971a5afba16SPali Rohár }; 972a5afba16SPali Rohár 973a5afba16SPali Rohár enum i8k_configs { 974a5afba16SPali Rohár DELL_LATITUDE_D520, 975a5afba16SPali Rohár DELL_PRECISION_490, 976a5afba16SPali Rohár DELL_STUDIO, 977a5afba16SPali Rohár DELL_XPS, 978a5afba16SPali Rohár }; 979a5afba16SPali Rohár 980927d89eeSArmin Wolf /* 981927d89eeSArmin Wolf * Only use for machines which need some special configuration 982927d89eeSArmin Wolf * in order to work correctly (e.g. if autoconfig fails on this machines). 983927d89eeSArmin Wolf */ 984927d89eeSArmin Wolf 985c510f6acSArmin Wolf static const struct i8k_config_data i8k_config_data[] __initconst = { 986a5afba16SPali Rohár [DELL_LATITUDE_D520] = { 987a5afba16SPali Rohár .fan_mult = 1, 988a5afba16SPali Rohár .fan_max = I8K_FAN_TURBO, 989a5afba16SPali Rohár }, 990a5afba16SPali Rohár [DELL_PRECISION_490] = { 991a5afba16SPali Rohár .fan_mult = 1, 992a5afba16SPali Rohár .fan_max = I8K_FAN_TURBO, 993a5afba16SPali Rohár }, 994a5afba16SPali Rohár [DELL_STUDIO] = { 995a5afba16SPali Rohár .fan_mult = 1, 996a5afba16SPali Rohár .fan_max = I8K_FAN_HIGH, 997a5afba16SPali Rohár }, 998a5afba16SPali Rohár [DELL_XPS] = { 999a5afba16SPali Rohár .fan_mult = 1, 1000a5afba16SPali Rohár .fan_max = I8K_FAN_HIGH, 1001a5afba16SPali Rohár }, 1002a5afba16SPali Rohár }; 1003a5afba16SPali Rohár 10046faadbbbSChristoph Hellwig static const struct dmi_system_id i8k_dmi_table[] __initconst = { 1005a5afba16SPali Rohár { 1006a5afba16SPali Rohár .ident = "Dell Inspiron", 1007a5afba16SPali Rohár .matches = { 1008a5afba16SPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"), 1009a5afba16SPali Rohár DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"), 1010a5afba16SPali Rohár }, 1011a5afba16SPali Rohár }, 1012a5afba16SPali Rohár { 1013a5afba16SPali Rohár .ident = "Dell Latitude", 1014a5afba16SPali Rohár .matches = { 1015a5afba16SPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"), 1016a5afba16SPali Rohár DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"), 1017a5afba16SPali Rohár }, 1018a5afba16SPali Rohár }, 1019a5afba16SPali Rohár { 1020a5afba16SPali Rohár .ident = "Dell Inspiron 2", 1021a5afba16SPali Rohár .matches = { 1022a5afba16SPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1023a5afba16SPali Rohár DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"), 1024a5afba16SPali Rohár }, 1025a5afba16SPali Rohár }, 1026a5afba16SPali Rohár { 1027a5afba16SPali Rohár .ident = "Dell Latitude D520", 1028a5afba16SPali Rohár .matches = { 1029a5afba16SPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1030a5afba16SPali Rohár DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D520"), 1031a5afba16SPali Rohár }, 1032a5afba16SPali Rohár .driver_data = (void *)&i8k_config_data[DELL_LATITUDE_D520], 1033a5afba16SPali Rohár }, 1034a5afba16SPali Rohár { 1035a5afba16SPali Rohár .ident = "Dell Latitude 2", 1036a5afba16SPali Rohár .matches = { 1037a5afba16SPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1038a5afba16SPali Rohár DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"), 1039a5afba16SPali Rohár }, 1040a5afba16SPali Rohár }, 1041a5afba16SPali Rohár { /* UK Inspiron 6400 */ 1042a5afba16SPali Rohár .ident = "Dell Inspiron 3", 1043a5afba16SPali Rohár .matches = { 1044a5afba16SPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1045a5afba16SPali Rohár DMI_MATCH(DMI_PRODUCT_NAME, "MM061"), 1046a5afba16SPali Rohár }, 1047a5afba16SPali Rohár }, 1048a5afba16SPali Rohár { 1049a5afba16SPali Rohár .ident = "Dell Inspiron 3", 1050a5afba16SPali Rohár .matches = { 1051a5afba16SPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1052a5afba16SPali Rohár DMI_MATCH(DMI_PRODUCT_NAME, "MP061"), 1053a5afba16SPali Rohár }, 1054a5afba16SPali Rohár }, 1055a5afba16SPali Rohár { 1056a5afba16SPali Rohár .ident = "Dell Precision 490", 1057a5afba16SPali Rohár .matches = { 1058a5afba16SPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1059a5afba16SPali Rohár DMI_MATCH(DMI_PRODUCT_NAME, 1060a5afba16SPali Rohár "Precision WorkStation 490"), 1061a5afba16SPali Rohár }, 1062a5afba16SPali Rohár .driver_data = (void *)&i8k_config_data[DELL_PRECISION_490], 1063a5afba16SPali Rohár }, 1064a5afba16SPali Rohár { 1065a5afba16SPali Rohár .ident = "Dell Precision", 1066a5afba16SPali Rohár .matches = { 1067a5afba16SPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1068a5afba16SPali Rohár DMI_MATCH(DMI_PRODUCT_NAME, "Precision"), 1069a5afba16SPali Rohár }, 1070a5afba16SPali Rohár }, 1071a5afba16SPali Rohár { 1072a5afba16SPali Rohár .ident = "Dell Vostro", 1073a5afba16SPali Rohár .matches = { 1074a5afba16SPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1075a5afba16SPali Rohár DMI_MATCH(DMI_PRODUCT_NAME, "Vostro"), 1076a5afba16SPali Rohár }, 1077a5afba16SPali Rohár }, 1078a5afba16SPali Rohár { 1079a5afba16SPali Rohár .ident = "Dell Studio", 1080a5afba16SPali Rohár .matches = { 1081a5afba16SPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1082a5afba16SPali Rohár DMI_MATCH(DMI_PRODUCT_NAME, "Studio"), 1083a5afba16SPali Rohár }, 1084a5afba16SPali Rohár .driver_data = (void *)&i8k_config_data[DELL_STUDIO], 1085a5afba16SPali Rohár }, 1086a5afba16SPali Rohár { 1087a5afba16SPali Rohár .ident = "Dell XPS M140", 1088a5afba16SPali Rohár .matches = { 1089a5afba16SPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1090a5afba16SPali Rohár DMI_MATCH(DMI_PRODUCT_NAME, "MXC051"), 1091a5afba16SPali Rohár }, 1092a5afba16SPali Rohár .driver_data = (void *)&i8k_config_data[DELL_XPS], 1093a5afba16SPali Rohár }, 1094a4811b6cSPali Rohár { 1095b8a13e5eSThomas Hebb .ident = "Dell XPS", 1096a4811b6cSPali Rohár .matches = { 1097a4811b6cSPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1098b8a13e5eSThomas Hebb DMI_MATCH(DMI_PRODUCT_NAME, "XPS"), 1099162372b0SMichele Sorcinelli }, 1100162372b0SMichele Sorcinelli }, 1101a5afba16SPali Rohár { } 1102a5afba16SPali Rohár }; 1103a5afba16SPali Rohár 1104a5afba16SPali Rohár MODULE_DEVICE_TABLE(dmi, i8k_dmi_table); 1105a5afba16SPali Rohár 1106a4b45b25SPali Rohár /* 11072744d2fdSPali Rohár * On some machines once I8K_SMM_GET_FAN_TYPE is issued then CPU fan speed 11082744d2fdSPali Rohár * randomly going up and down due to bug in Dell SMM or BIOS. Here is blacklist 11092744d2fdSPali Rohár * of affected Dell machines for which we disallow I8K_SMM_GET_FAN_TYPE call. 11102744d2fdSPali Rohár * See bug: https://bugzilla.kernel.org/show_bug.cgi?id=100121 11116220f4ebSThorsten Leemhuis */ 11126faadbbbSChristoph Hellwig static const struct dmi_system_id i8k_blacklist_fan_type_dmi_table[] __initconst = { 11132744d2fdSPali Rohár { 11146220f4ebSThorsten Leemhuis .ident = "Dell Studio XPS 8000", 11156220f4ebSThorsten Leemhuis .matches = { 11166220f4ebSThorsten Leemhuis DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 11176220f4ebSThorsten Leemhuis DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Studio XPS 8000"), 11186220f4ebSThorsten Leemhuis }, 11196220f4ebSThorsten Leemhuis }, 11206220f4ebSThorsten Leemhuis { 1121a4b45b25SPali Rohár .ident = "Dell Studio XPS 8100", 1122a4b45b25SPali Rohár .matches = { 1123a4b45b25SPali Rohár DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1124a4b45b25SPali Rohár DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Studio XPS 8100"), 1125a4b45b25SPali Rohár }, 1126a4b45b25SPali Rohár }, 11272744d2fdSPali Rohár { 11282744d2fdSPali Rohár .ident = "Dell Inspiron 580", 11292744d2fdSPali Rohár .matches = { 11302744d2fdSPali Rohár DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 11312744d2fdSPali Rohár DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Inspiron 580 "), 11322744d2fdSPali Rohár }, 11332744d2fdSPali Rohár }, 1134a4b45b25SPali Rohár { } 1135a4b45b25SPali Rohár }; 1136a4b45b25SPali Rohár 1137a5afba16SPali Rohár /* 1138f480ea90SPali Rohár * On some machines all fan related SMM functions implemented by Dell BIOS 1139f480ea90SPali Rohár * firmware freeze kernel for about 500ms. Until Dell fixes these problems fan 1140f480ea90SPali Rohár * support for affected blacklisted Dell machines stay disabled. 1141f480ea90SPali Rohár * See bug: https://bugzilla.kernel.org/show_bug.cgi?id=195751 1142f480ea90SPali Rohár */ 1143c510f6acSArmin Wolf static const struct dmi_system_id i8k_blacklist_fan_support_dmi_table[] __initconst = { 1144f480ea90SPali Rohár { 1145f480ea90SPali Rohár .ident = "Dell Inspiron 7720", 1146f480ea90SPali Rohár .matches = { 1147f480ea90SPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1148f480ea90SPali Rohár DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Inspiron 7720"), 1149f480ea90SPali Rohár }, 1150f480ea90SPali Rohár }, 11516fbc4232SOleksandr Natalenko { 11526fbc4232SOleksandr Natalenko .ident = "Dell Vostro 3360", 11536fbc4232SOleksandr Natalenko .matches = { 11546fbc4232SOleksandr Natalenko DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 11556fbc4232SOleksandr Natalenko DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Vostro 3360"), 11566fbc4232SOleksandr Natalenko }, 11576fbc4232SOleksandr Natalenko }, 1158536e0019SHelge Eichelberg { 1159536e0019SHelge Eichelberg .ident = "Dell XPS13 9333", 1160536e0019SHelge Eichelberg .matches = { 1161536e0019SHelge Eichelberg DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1162536e0019SHelge Eichelberg DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS13 9333"), 1163536e0019SHelge Eichelberg }, 1164536e0019SHelge Eichelberg }, 11654008bc7dSThomas Hebb { 11664008bc7dSThomas Hebb .ident = "Dell XPS 15 L502X", 11674008bc7dSThomas Hebb .matches = { 11684008bc7dSThomas Hebb DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 11694008bc7dSThomas Hebb DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Dell System XPS L502X"), 11704008bc7dSThomas Hebb }, 11714008bc7dSThomas Hebb }, 1172f480ea90SPali Rohár { } 1173f480ea90SPali Rohár }; 1174f480ea90SPali Rohár 1175afe45277SGiovanni Mascellani struct i8k_fan_control_data { 1176afe45277SGiovanni Mascellani unsigned int manual_fan; 1177afe45277SGiovanni Mascellani unsigned int auto_fan; 1178afe45277SGiovanni Mascellani }; 1179afe45277SGiovanni Mascellani 1180afe45277SGiovanni Mascellani enum i8k_fan_controls { 1181afe45277SGiovanni Mascellani I8K_FAN_34A3_35A3, 1182afe45277SGiovanni Mascellani }; 1183afe45277SGiovanni Mascellani 1184c510f6acSArmin Wolf static const struct i8k_fan_control_data i8k_fan_control_data[] __initconst = { 1185afe45277SGiovanni Mascellani [I8K_FAN_34A3_35A3] = { 1186afe45277SGiovanni Mascellani .manual_fan = 0x34a3, 1187afe45277SGiovanni Mascellani .auto_fan = 0x35a3, 1188afe45277SGiovanni Mascellani }, 1189afe45277SGiovanni Mascellani }; 1190afe45277SGiovanni Mascellani 1191c510f6acSArmin Wolf static const struct dmi_system_id i8k_whitelist_fan_control[] __initconst = { 1192afe45277SGiovanni Mascellani { 11930ca8bb2cSJeffrey Lin .ident = "Dell Latitude 5480", 11940ca8bb2cSJeffrey Lin .matches = { 11950ca8bb2cSJeffrey Lin DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 11960ca8bb2cSJeffrey Lin DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude 5480"), 11970ca8bb2cSJeffrey Lin }, 11980ca8bb2cSJeffrey Lin .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], 11990ca8bb2cSJeffrey Lin }, 12000ca8bb2cSJeffrey Lin { 1201afe45277SGiovanni Mascellani .ident = "Dell Latitude E6440", 1202afe45277SGiovanni Mascellani .matches = { 1203afe45277SGiovanni Mascellani DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1204afe45277SGiovanni Mascellani DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude E6440"), 1205afe45277SGiovanni Mascellani }, 1206afe45277SGiovanni Mascellani .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], 1207afe45277SGiovanni Mascellani }, 1208807b8c29SSebastian Oechsle { 1209807b8c29SSebastian Oechsle .ident = "Dell Latitude E7440", 1210807b8c29SSebastian Oechsle .matches = { 1211807b8c29SSebastian Oechsle DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1212807b8c29SSebastian Oechsle DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude E7440"), 1213807b8c29SSebastian Oechsle }, 1214807b8c29SSebastian Oechsle .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], 1215807b8c29SSebastian Oechsle }, 121695d88d05SCarlos Alberto Lopez Perez { 121795d88d05SCarlos Alberto Lopez Perez .ident = "Dell Precision 5530", 121895d88d05SCarlos Alberto Lopez Perez .matches = { 121995d88d05SCarlos Alberto Lopez Perez DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 122095d88d05SCarlos Alberto Lopez Perez DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Precision 5530"), 122195d88d05SCarlos Alberto Lopez Perez }, 122295d88d05SCarlos Alberto Lopez Perez .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], 122395d88d05SCarlos Alberto Lopez Perez }, 122495d88d05SCarlos Alberto Lopez Perez { 122595d88d05SCarlos Alberto Lopez Perez .ident = "Dell Precision 7510", 122695d88d05SCarlos Alberto Lopez Perez .matches = { 122795d88d05SCarlos Alberto Lopez Perez DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 122895d88d05SCarlos Alberto Lopez Perez DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Precision 7510"), 122995d88d05SCarlos Alberto Lopez Perez }, 123095d88d05SCarlos Alberto Lopez Perez .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], 123195d88d05SCarlos Alberto Lopez Perez }, 1232afe45277SGiovanni Mascellani { } 1233afe45277SGiovanni Mascellani }; 1234afe45277SGiovanni Mascellani 12351492fa21SArmin Wolf static int __init dell_smm_probe(struct platform_device *pdev) 1236a5afba16SPali Rohár { 1237ba04d73cSArmin Wolf struct dell_smm_data *data; 1238afe45277SGiovanni Mascellani const struct dmi_system_id *id, *fan_control; 1239a5afba16SPali Rohár int fan, ret; 1240a5afba16SPali Rohár 1241ba04d73cSArmin Wolf data = devm_kzalloc(&pdev->dev, sizeof(struct dell_smm_data), GFP_KERNEL); 1242ba04d73cSArmin Wolf if (!data) 1243ba04d73cSArmin Wolf return -ENOMEM; 1244ba04d73cSArmin Wolf 1245ba04d73cSArmin Wolf mutex_init(&data->i8k_mutex); 1246ba04d73cSArmin Wolf data->i8k_fan_mult = I8K_FAN_MULT; 1247ba04d73cSArmin Wolf data->i8k_fan_max = I8K_FAN_HIGH; 1248ba04d73cSArmin Wolf platform_set_drvdata(pdev, data); 1249ba04d73cSArmin Wolf 1250f480ea90SPali Rohár if (dmi_check_system(i8k_blacklist_fan_support_dmi_table)) { 1251ba04d73cSArmin Wolf dev_warn(&pdev->dev, "broken Dell BIOS detected, disallow fan support\n"); 1252f480ea90SPali Rohár if (!force) 1253ba04d73cSArmin Wolf data->disallow_fan_support = true; 1254f480ea90SPali Rohár } 1255f480ea90SPali Rohár 1256836ad112SPali Rohár if (dmi_check_system(i8k_blacklist_fan_type_dmi_table)) { 1257ba04d73cSArmin Wolf dev_warn(&pdev->dev, "broken Dell BIOS detected, disallow fan type call\n"); 1258836ad112SPali Rohár if (!force) 1259ba04d73cSArmin Wolf data->disallow_fan_type_call = true; 1260836ad112SPali Rohár } 12612744d2fdSPali Rohár 1262ba04d73cSArmin Wolf strscpy(data->bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION), 1263ba04d73cSArmin Wolf sizeof(data->bios_version)); 1264ba04d73cSArmin Wolf strscpy(data->bios_machineid, i8k_get_dmi_data(DMI_PRODUCT_SERIAL), 1265ba04d73cSArmin Wolf sizeof(data->bios_machineid)); 1266a5afba16SPali Rohár 1267a5afba16SPali Rohár /* 1268a5afba16SPali Rohár * Set fan multiplier and maximal fan speed from dmi config 1269a5afba16SPali Rohár * Values specified in module parameters override values from dmi 1270a5afba16SPali Rohár */ 1271a5afba16SPali Rohár id = dmi_first_match(i8k_dmi_table); 1272a5afba16SPali Rohár if (id && id->driver_data) { 1273a5afba16SPali Rohár const struct i8k_config_data *conf = id->driver_data; 12741492fa21SArmin Wolf 1275a5afba16SPali Rohár if (!fan_mult && conf->fan_mult) 1276a5afba16SPali Rohár fan_mult = conf->fan_mult; 1277ba04d73cSArmin Wolf 1278a5afba16SPali Rohár if (!fan_max && conf->fan_max) 1279a5afba16SPali Rohár fan_max = conf->fan_max; 1280a5afba16SPali Rohár } 1281a5afba16SPali Rohár 1282ba04d73cSArmin Wolf data->i8k_fan_max = fan_max ? : I8K_FAN_HIGH; /* Must not be 0 */ 1283ba04d73cSArmin Wolf data->i8k_pwm_mult = DIV_ROUND_UP(255, data->i8k_fan_max); 1284a5afba16SPali Rohár 1285afe45277SGiovanni Mascellani fan_control = dmi_first_match(i8k_whitelist_fan_control); 1286afe45277SGiovanni Mascellani if (fan_control && fan_control->driver_data) { 1287ba04d73cSArmin Wolf const struct i8k_fan_control_data *control = fan_control->driver_data; 1288afe45277SGiovanni Mascellani 1289ba04d73cSArmin Wolf data->manual_fan = control->manual_fan; 1290ba04d73cSArmin Wolf data->auto_fan = control->auto_fan; 1291ba04d73cSArmin Wolf dev_info(&pdev->dev, "enabling support for setting automatic/manual fan control\n"); 1292afe45277SGiovanni Mascellani } 1293afe45277SGiovanni Mascellani 1294a5afba16SPali Rohár if (!fan_mult) { 1295a5afba16SPali Rohár /* 1296a5afba16SPali Rohár * Autodetect fan multiplier based on nominal rpm 1297a5afba16SPali Rohár * If fan reports rpm value too high then set multiplier to 1 1298a5afba16SPali Rohár */ 12992757269aSArmin Wolf for (fan = 0; fan < DELL_SMM_NO_FANS; ++fan) { 1300ba04d73cSArmin Wolf ret = i8k_get_fan_nominal_speed(data, fan, data->i8k_fan_max); 1301a5afba16SPali Rohár if (ret < 0) 1302a5afba16SPali Rohár continue; 1303ba04d73cSArmin Wolf 1304a5afba16SPali Rohár if (ret > I8K_FAN_MAX_RPM) 1305ba04d73cSArmin Wolf data->i8k_fan_mult = 1; 1306a5afba16SPali Rohár break; 1307a5afba16SPali Rohár } 1308a5afba16SPali Rohár } else { 1309a5afba16SPali Rohár /* Fan multiplier was specified in module param or in dmi */ 1310ba04d73cSArmin Wolf data->i8k_fan_mult = fan_mult; 1311a5afba16SPali Rohár } 1312a5afba16SPali Rohár 13131492fa21SArmin Wolf ret = dell_smm_init_hwmon(&pdev->dev); 13141492fa21SArmin Wolf if (ret) 13151492fa21SArmin Wolf return ret; 13161492fa21SArmin Wolf 1317a2cb66b4SArmin Wolf i8k_init_procfs(&pdev->dev); 13181492fa21SArmin Wolf 13191492fa21SArmin Wolf return 0; 13201492fa21SArmin Wolf } 13211492fa21SArmin Wolf 13221492fa21SArmin Wolf static struct platform_driver dell_smm_driver = { 13231492fa21SArmin Wolf .driver = { 13241492fa21SArmin Wolf .name = KBUILD_MODNAME, 13251492fa21SArmin Wolf }, 13261492fa21SArmin Wolf }; 13271492fa21SArmin Wolf 13281492fa21SArmin Wolf static struct platform_device *dell_smm_device; 13291492fa21SArmin Wolf 13301492fa21SArmin Wolf /* 13311492fa21SArmin Wolf * Probe for the presence of a supported laptop. 13321492fa21SArmin Wolf */ 1333a5afba16SPali Rohár static int __init i8k_init(void) 1334a5afba16SPali Rohár { 13351492fa21SArmin Wolf /* 13361492fa21SArmin Wolf * Get DMI information 13371492fa21SArmin Wolf */ 13381492fa21SArmin Wolf if (!dmi_check_system(i8k_dmi_table)) { 13391492fa21SArmin Wolf if (!ignore_dmi && !force) 1340a5afba16SPali Rohár return -ENODEV; 1341a5afba16SPali Rohár 13421492fa21SArmin Wolf pr_info("not running on a supported Dell system.\n"); 13431492fa21SArmin Wolf pr_info("vendor=%s, model=%s, version=%s\n", 13441492fa21SArmin Wolf i8k_get_dmi_data(DMI_SYS_VENDOR), 13451492fa21SArmin Wolf i8k_get_dmi_data(DMI_PRODUCT_NAME), 13461492fa21SArmin Wolf i8k_get_dmi_data(DMI_BIOS_VERSION)); 13471492fa21SArmin Wolf } 1348039ae585SPali Rohár 13491492fa21SArmin Wolf /* 13501492fa21SArmin Wolf * Get SMM Dell signature 13511492fa21SArmin Wolf */ 13521492fa21SArmin Wolf if (i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG1) && 13531492fa21SArmin Wolf i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG2)) { 13541492fa21SArmin Wolf pr_err("unable to get SMM Dell signature\n"); 13551492fa21SArmin Wolf if (!force) 13561492fa21SArmin Wolf return -ENODEV; 13571492fa21SArmin Wolf } 13581492fa21SArmin Wolf 13591492fa21SArmin Wolf dell_smm_device = platform_create_bundle(&dell_smm_driver, dell_smm_probe, NULL, 0, NULL, 13601492fa21SArmin Wolf 0); 13611492fa21SArmin Wolf 13621492fa21SArmin Wolf return PTR_ERR_OR_ZERO(dell_smm_device); 1363a5afba16SPali Rohár } 1364a5afba16SPali Rohár 1365a5afba16SPali Rohár static void __exit i8k_exit(void) 1366a5afba16SPali Rohár { 13671492fa21SArmin Wolf platform_device_unregister(dell_smm_device); 13681492fa21SArmin Wolf platform_driver_unregister(&dell_smm_driver); 1369a5afba16SPali Rohár } 1370a5afba16SPali Rohár 1371a5afba16SPali Rohár module_init(i8k_init); 1372a5afba16SPali Rohár module_exit(i8k_exit); 1373