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 1527046a3fSJuergen Gross #include <linux/cpu.h> 16a5afba16SPali Rohár #include <linux/delay.h> 171492fa21SArmin Wolf #include <linux/err.h> 18a5afba16SPali Rohár #include <linux/module.h> 191492fa21SArmin Wolf #include <linux/platform_device.h> 20a5afba16SPali Rohár #include <linux/types.h> 21a5afba16SPali Rohár #include <linux/init.h> 22a5afba16SPali Rohár #include <linux/proc_fs.h> 23a5afba16SPali Rohár #include <linux/seq_file.h> 24a5afba16SPali Rohár #include <linux/dmi.h> 25a5afba16SPali Rohár #include <linux/capability.h> 26a5afba16SPali Rohár #include <linux/mutex.h> 27a5afba16SPali Rohár #include <linux/hwmon.h> 28a5afba16SPali Rohár #include <linux/uaccess.h> 29a5afba16SPali Rohár #include <linux/io.h> 30a5afba16SPali Rohár #include <linux/sched.h> 31053ea640SPali Rohár #include <linux/ctype.h> 3227046a3fSJuergen Gross #include <linux/smp.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 62*deeba244SArmin Wolf #define DELL_SMM_NO_TEMP 10 63*deeba244SArmin Wolf #define DELL_SMM_NO_FANS 3 64*deeba244SArmin 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; 76*deeba244SArmin Wolf int temp_type[DELL_SMM_NO_TEMP]; 77*deeba244SArmin Wolf bool fan[DELL_SMM_NO_FANS]; 78*deeba244SArmin Wolf int fan_type[DELL_SMM_NO_FANS]; 79ba04d73cSArmin Wolf }; 80a5afba16SPali Rohár 81a5afba16SPali Rohár MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)"); 82149ed3d4SPali Rohár MODULE_AUTHOR("Pali Rohár <pali@kernel.org>"); 83039ae585SPali Rohár MODULE_DESCRIPTION("Dell laptop SMM BIOS hwmon driver"); 84a5afba16SPali Rohár MODULE_LICENSE("GPL"); 85a5afba16SPali Rohár MODULE_ALIAS("i8k"); 86a5afba16SPali Rohár 87a5afba16SPali Rohár static bool force; 88a5afba16SPali Rohár module_param(force, bool, 0); 89a5afba16SPali Rohár MODULE_PARM_DESC(force, "Force loading without checking for supported models"); 90a5afba16SPali Rohár 91a5afba16SPali Rohár static bool ignore_dmi; 92a5afba16SPali Rohár module_param(ignore_dmi, bool, 0); 93a5afba16SPali Rohár MODULE_PARM_DESC(ignore_dmi, "Continue probing hardware even if DMI data does not match"); 94a5afba16SPali Rohár 95039ae585SPali Rohár #if IS_ENABLED(CONFIG_I8K) 967613663cSPali Rohár static bool restricted = true; 97a5afba16SPali Rohár module_param(restricted, bool, 0); 987613663cSPali Rohár MODULE_PARM_DESC(restricted, "Restrict fan control and serial number to CAP_SYS_ADMIN (default: 1)"); 99a5afba16SPali Rohár 100a5afba16SPali Rohár static bool power_status; 101a5afba16SPali Rohár module_param(power_status, bool, 0600); 1027613663cSPali Rohár MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k (default: 0)"); 103039ae585SPali Rohár #endif 104a5afba16SPali Rohár 105a5afba16SPali Rohár static uint fan_mult; 106a5afba16SPali Rohár module_param(fan_mult, uint, 0); 107a5afba16SPali Rohár MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with (default: autodetect)"); 108a5afba16SPali Rohár 109a5afba16SPali Rohár static uint fan_max; 110a5afba16SPali Rohár module_param(fan_max, uint, 0); 111a5afba16SPali Rohár MODULE_PARM_DESC(fan_max, "Maximum configurable fan speed (default: autodetect)"); 112a5afba16SPali Rohár 113a5afba16SPali Rohár struct smm_regs { 114a5afba16SPali Rohár unsigned int eax; 115a5afba16SPali Rohár unsigned int ebx __packed; 116a5afba16SPali Rohár unsigned int ecx __packed; 117a5afba16SPali Rohár unsigned int edx __packed; 118a5afba16SPali Rohár unsigned int esi __packed; 119a5afba16SPali Rohár unsigned int edi __packed; 120a5afba16SPali Rohár }; 121a5afba16SPali Rohár 122*deeba244SArmin Wolf static const char * const temp_labels[] = { 123*deeba244SArmin Wolf "CPU", 124*deeba244SArmin Wolf "GPU", 125*deeba244SArmin Wolf "SODIMM", 126*deeba244SArmin Wolf "Other", 127*deeba244SArmin Wolf "Ambient", 128*deeba244SArmin Wolf "Other", 129*deeba244SArmin Wolf }; 130*deeba244SArmin Wolf 131*deeba244SArmin Wolf static const char * const fan_labels[] = { 132*deeba244SArmin Wolf "Processor Fan", 133*deeba244SArmin Wolf "Motherboard Fan", 134*deeba244SArmin Wolf "Video Fan", 135*deeba244SArmin Wolf "Power Supply Fan", 136*deeba244SArmin Wolf "Chipset Fan", 137*deeba244SArmin Wolf "Other Fan", 138*deeba244SArmin Wolf }; 139*deeba244SArmin Wolf 140*deeba244SArmin Wolf static const char * const docking_labels[] = { 141*deeba244SArmin Wolf "Docking Processor Fan", 142*deeba244SArmin Wolf "Docking Motherboard Fan", 143*deeba244SArmin Wolf "Docking Video Fan", 144*deeba244SArmin Wolf "Docking Power Supply Fan", 145*deeba244SArmin Wolf "Docking Chipset Fan", 146*deeba244SArmin Wolf "Docking Other Fan", 147*deeba244SArmin Wolf }; 148*deeba244SArmin Wolf 149c9363cdfSArmin Wolf static inline const char __init *i8k_get_dmi_data(int field) 150a5afba16SPali Rohár { 151a5afba16SPali Rohár const char *dmi_data = dmi_get_system_info(field); 152a5afba16SPali Rohár 153a5afba16SPali Rohár return dmi_data && *dmi_data ? dmi_data : "?"; 154a5afba16SPali Rohár } 155a5afba16SPali Rohár 156a5afba16SPali Rohár /* 157a5afba16SPali Rohár * Call the System Management Mode BIOS. Code provided by Jonathan Buzzard. 158a5afba16SPali Rohár */ 15927046a3fSJuergen Gross static int i8k_smm_func(void *par) 160a5afba16SPali Rohár { 161a5afba16SPali Rohár int rc; 16227046a3fSJuergen Gross struct smm_regs *regs = par; 163a5afba16SPali Rohár int eax = regs->eax; 164a5afba16SPali Rohár 1659d58bec0SPali Rohár #ifdef DEBUG 1669d58bec0SPali Rohár int ebx = regs->ebx; 1679d58bec0SPali Rohár unsigned long duration; 1689d58bec0SPali Rohár ktime_t calltime, delta, rettime; 1699d58bec0SPali Rohár 1709d58bec0SPali Rohár calltime = ktime_get(); 1719d58bec0SPali Rohár #endif 1729d58bec0SPali Rohár 173a5afba16SPali Rohár /* SMM requires CPU 0 */ 17427046a3fSJuergen Gross if (smp_processor_id() != 0) 17527046a3fSJuergen Gross return -EBUSY; 176a5afba16SPali Rohár 177a5afba16SPali Rohár #if defined(CONFIG_X86_64) 178a5afba16SPali Rohár asm volatile("pushq %%rax\n\t" 179a5afba16SPali Rohár "movl 0(%%rax),%%edx\n\t" 180a5afba16SPali Rohár "pushq %%rdx\n\t" 181a5afba16SPali Rohár "movl 4(%%rax),%%ebx\n\t" 182a5afba16SPali Rohár "movl 8(%%rax),%%ecx\n\t" 183a5afba16SPali Rohár "movl 12(%%rax),%%edx\n\t" 184a5afba16SPali Rohár "movl 16(%%rax),%%esi\n\t" 185a5afba16SPali Rohár "movl 20(%%rax),%%edi\n\t" 186a5afba16SPali Rohár "popq %%rax\n\t" 187a5afba16SPali Rohár "out %%al,$0xb2\n\t" 188a5afba16SPali Rohár "out %%al,$0x84\n\t" 189a5afba16SPali Rohár "xchgq %%rax,(%%rsp)\n\t" 190a5afba16SPali Rohár "movl %%ebx,4(%%rax)\n\t" 191a5afba16SPali Rohár "movl %%ecx,8(%%rax)\n\t" 192a5afba16SPali Rohár "movl %%edx,12(%%rax)\n\t" 193a5afba16SPali Rohár "movl %%esi,16(%%rax)\n\t" 194a5afba16SPali Rohár "movl %%edi,20(%%rax)\n\t" 195a5afba16SPali Rohár "popq %%rdx\n\t" 196a5afba16SPali Rohár "movl %%edx,0(%%rax)\n\t" 197a5afba16SPali Rohár "pushfq\n\t" 198a5afba16SPali Rohár "popq %%rax\n\t" 199a5afba16SPali Rohár "andl $1,%%eax\n" 200a5afba16SPali Rohár : "=a"(rc) 201a5afba16SPali Rohár : "a"(regs) 202a5afba16SPali Rohár : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); 203a5afba16SPali Rohár #else 204a5afba16SPali Rohár asm volatile("pushl %%eax\n\t" 205a5afba16SPali Rohár "movl 0(%%eax),%%edx\n\t" 206a5afba16SPali Rohár "push %%edx\n\t" 207a5afba16SPali Rohár "movl 4(%%eax),%%ebx\n\t" 208a5afba16SPali Rohár "movl 8(%%eax),%%ecx\n\t" 209a5afba16SPali Rohár "movl 12(%%eax),%%edx\n\t" 210a5afba16SPali Rohár "movl 16(%%eax),%%esi\n\t" 211a5afba16SPali Rohár "movl 20(%%eax),%%edi\n\t" 212a5afba16SPali Rohár "popl %%eax\n\t" 213a5afba16SPali Rohár "out %%al,$0xb2\n\t" 214a5afba16SPali Rohár "out %%al,$0x84\n\t" 215a5afba16SPali Rohár "xchgl %%eax,(%%esp)\n\t" 216a5afba16SPali Rohár "movl %%ebx,4(%%eax)\n\t" 217a5afba16SPali Rohár "movl %%ecx,8(%%eax)\n\t" 218a5afba16SPali Rohár "movl %%edx,12(%%eax)\n\t" 219a5afba16SPali Rohár "movl %%esi,16(%%eax)\n\t" 220a5afba16SPali Rohár "movl %%edi,20(%%eax)\n\t" 221a5afba16SPali Rohár "popl %%edx\n\t" 222a5afba16SPali Rohár "movl %%edx,0(%%eax)\n\t" 223a5afba16SPali Rohár "lahf\n\t" 224a5afba16SPali Rohár "shrl $8,%%eax\n\t" 225a5afba16SPali Rohár "andl $1,%%eax\n" 226a5afba16SPali Rohár : "=a"(rc) 227a5afba16SPali Rohár : "a"(regs) 228a5afba16SPali Rohár : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); 229a5afba16SPali Rohár #endif 230a5afba16SPali Rohár if (rc != 0 || (regs->eax & 0xffff) == 0xffff || regs->eax == eax) 231a5afba16SPali Rohár rc = -EINVAL; 232a5afba16SPali Rohár 2339d58bec0SPali Rohár #ifdef DEBUG 2349d58bec0SPali Rohár rettime = ktime_get(); 2359d58bec0SPali Rohár delta = ktime_sub(rettime, calltime); 2369d58bec0SPali Rohár duration = ktime_to_ns(delta) >> 10; 2379d58bec0SPali Rohár pr_debug("smm(0x%.4x 0x%.4x) = 0x%.4x (took %7lu usecs)\n", eax, ebx, 2389d58bec0SPali Rohár (rc ? 0xffff : regs->eax & 0xffff), duration); 2399d58bec0SPali Rohár #endif 2409d58bec0SPali Rohár 241a5afba16SPali Rohár return rc; 242a5afba16SPali Rohár } 243a5afba16SPali Rohár 244a5afba16SPali Rohár /* 24527046a3fSJuergen Gross * Call the System Management Mode BIOS. 24627046a3fSJuergen Gross */ 24727046a3fSJuergen Gross static int i8k_smm(struct smm_regs *regs) 24827046a3fSJuergen Gross { 24927046a3fSJuergen Gross int ret; 25027046a3fSJuergen Gross 25127046a3fSJuergen Gross get_online_cpus(); 25227046a3fSJuergen Gross ret = smp_call_on_cpu(0, i8k_smm_func, regs, true); 25327046a3fSJuergen Gross put_online_cpus(); 25427046a3fSJuergen Gross 25527046a3fSJuergen Gross return ret; 25627046a3fSJuergen Gross } 25727046a3fSJuergen Gross 25827046a3fSJuergen Gross /* 259a5afba16SPali Rohár * Read the fan status. 260a5afba16SPali Rohár */ 261ba04d73cSArmin Wolf static int i8k_get_fan_status(const struct dell_smm_data *data, int fan) 262a5afba16SPali Rohár { 263a5afba16SPali Rohár struct smm_regs regs = { .eax = I8K_SMM_GET_FAN, }; 264a5afba16SPali Rohár 265ba04d73cSArmin Wolf if (data->disallow_fan_support) 266f480ea90SPali Rohár return -EINVAL; 267f480ea90SPali Rohár 268a5afba16SPali Rohár regs.ebx = fan & 0xff; 269a5afba16SPali Rohár return i8k_smm(®s) ? : regs.eax & 0xff; 270a5afba16SPali Rohár } 271a5afba16SPali Rohár 272a5afba16SPali Rohár /* 273a5afba16SPali Rohár * Read the fan speed in RPM. 274a5afba16SPali Rohár */ 275ba04d73cSArmin Wolf static int i8k_get_fan_speed(const struct dell_smm_data *data, int fan) 276a5afba16SPali Rohár { 277a5afba16SPali Rohár struct smm_regs regs = { .eax = I8K_SMM_GET_SPEED, }; 278a5afba16SPali Rohár 279ba04d73cSArmin Wolf if (data->disallow_fan_support) 280f480ea90SPali Rohár return -EINVAL; 281f480ea90SPali Rohár 282a5afba16SPali Rohár regs.ebx = fan & 0xff; 283ba04d73cSArmin Wolf return i8k_smm(®s) ? : (regs.eax & 0xffff) * data->i8k_fan_mult; 284a5afba16SPali Rohár } 285a5afba16SPali Rohár 286a5afba16SPali Rohár /* 287a5afba16SPali Rohár * Read the fan type. 288a5afba16SPali Rohár */ 289ba04d73cSArmin Wolf static int _i8k_get_fan_type(const struct dell_smm_data *data, int fan) 290a5afba16SPali Rohár { 291a5afba16SPali Rohár struct smm_regs regs = { .eax = I8K_SMM_GET_FAN_TYPE, }; 292a5afba16SPali Rohár 293ba04d73cSArmin Wolf if (data->disallow_fan_support || data->disallow_fan_type_call) 2942744d2fdSPali Rohár return -EINVAL; 2952744d2fdSPali Rohár 296a5afba16SPali Rohár regs.ebx = fan & 0xff; 297a5afba16SPali Rohár return i8k_smm(®s) ? : regs.eax & 0xff; 298a5afba16SPali Rohár } 299a5afba16SPali Rohár 300ba04d73cSArmin Wolf static int i8k_get_fan_type(struct dell_smm_data *data, int fan) 3015ce91714SPali Rohár { 3025ce91714SPali Rohár /* I8K_SMM_GET_FAN_TYPE SMM call is expensive, so cache values */ 303*deeba244SArmin Wolf if (data->fan_type[fan] == INT_MIN) 304*deeba244SArmin Wolf data->fan_type[fan] = _i8k_get_fan_type(data, fan); 3055ce91714SPali Rohár 306*deeba244SArmin Wolf return data->fan_type[fan]; 3075ce91714SPali Rohár } 3085ce91714SPali Rohár 309a5afba16SPali Rohár /* 310a5afba16SPali Rohár * Read the fan nominal rpm for specific fan speed. 311a5afba16SPali Rohár */ 312ba04d73cSArmin Wolf static int i8k_get_fan_nominal_speed(const struct dell_smm_data *data, int fan, int speed) 313a5afba16SPali Rohár { 314a5afba16SPali Rohár struct smm_regs regs = { .eax = I8K_SMM_GET_NOM_SPEED, }; 315a5afba16SPali Rohár 316ba04d73cSArmin Wolf if (data->disallow_fan_support) 317f480ea90SPali Rohár return -EINVAL; 318f480ea90SPali Rohár 319a5afba16SPali Rohár regs.ebx = (fan & 0xff) | (speed << 8); 320ba04d73cSArmin Wolf return i8k_smm(®s) ? : (regs.eax & 0xffff) * data->i8k_fan_mult; 321a5afba16SPali Rohár } 322a5afba16SPali Rohár 323a5afba16SPali Rohár /* 324afe45277SGiovanni Mascellani * Enable or disable automatic BIOS fan control support 325afe45277SGiovanni Mascellani */ 326ba04d73cSArmin Wolf static int i8k_enable_fan_auto_mode(const struct dell_smm_data *data, bool enable) 327afe45277SGiovanni Mascellani { 328afe45277SGiovanni Mascellani struct smm_regs regs = { }; 329afe45277SGiovanni Mascellani 330ba04d73cSArmin Wolf if (data->disallow_fan_support) 331afe45277SGiovanni Mascellani return -EINVAL; 332afe45277SGiovanni Mascellani 333ba04d73cSArmin Wolf regs.eax = enable ? data->auto_fan : data->manual_fan; 334afe45277SGiovanni Mascellani return i8k_smm(®s); 335afe45277SGiovanni Mascellani } 336afe45277SGiovanni Mascellani 337afe45277SGiovanni Mascellani /* 338a5afba16SPali Rohár * Set the fan speed (off, low, high). Returns the new fan status. 339a5afba16SPali Rohár */ 340ba04d73cSArmin Wolf static int i8k_set_fan(const struct dell_smm_data *data, int fan, int speed) 341a5afba16SPali Rohár { 342a5afba16SPali Rohár struct smm_regs regs = { .eax = I8K_SMM_SET_FAN, }; 343a5afba16SPali Rohár 344ba04d73cSArmin Wolf if (data->disallow_fan_support) 345f480ea90SPali Rohár return -EINVAL; 346f480ea90SPali Rohár 347ba04d73cSArmin Wolf speed = (speed < 0) ? 0 : ((speed > data->i8k_fan_max) ? data->i8k_fan_max : speed); 348a5afba16SPali Rohár regs.ebx = (fan & 0xff) | (speed << 8); 349a5afba16SPali Rohár 350ba04d73cSArmin Wolf return i8k_smm(®s) ? : i8k_get_fan_status(data, fan); 351a5afba16SPali Rohár } 352a5afba16SPali Rohár 353*deeba244SArmin Wolf static int __init i8k_get_temp_type(int sensor) 354a5afba16SPali Rohár { 355a5afba16SPali Rohár struct smm_regs regs = { .eax = I8K_SMM_GET_TEMP_TYPE, }; 356a5afba16SPali Rohár 357a5afba16SPali Rohár regs.ebx = sensor & 0xff; 358a5afba16SPali Rohár return i8k_smm(®s) ? : regs.eax & 0xff; 359a5afba16SPali Rohár } 360a5afba16SPali Rohár 361a5afba16SPali Rohár /* 362a5afba16SPali Rohár * Read the cpu temperature. 363a5afba16SPali Rohár */ 364a5afba16SPali Rohár static int _i8k_get_temp(int sensor) 365a5afba16SPali Rohár { 366a5afba16SPali Rohár struct smm_regs regs = { 367a5afba16SPali Rohár .eax = I8K_SMM_GET_TEMP, 368a5afba16SPali Rohár .ebx = sensor & 0xff, 369a5afba16SPali Rohár }; 370a5afba16SPali Rohár 371a5afba16SPali Rohár return i8k_smm(®s) ? : regs.eax & 0xff; 372a5afba16SPali Rohár } 373a5afba16SPali Rohár 374a5afba16SPali Rohár static int i8k_get_temp(int sensor) 375a5afba16SPali Rohár { 376a5afba16SPali Rohár int temp = _i8k_get_temp(sensor); 377a5afba16SPali Rohár 378a5afba16SPali Rohár /* 379a5afba16SPali Rohár * Sometimes the temperature sensor returns 0x99, which is out of range. 380a5afba16SPali Rohár * In this case we retry (once) before returning an error. 381a5afba16SPali Rohár # 1003655137 00000058 00005a4b 382a5afba16SPali Rohár # 1003655138 00000099 00003a80 <--- 0x99 = 153 degrees 383a5afba16SPali Rohár # 1003655139 00000054 00005c52 384a5afba16SPali Rohár */ 385a5afba16SPali Rohár if (temp == 0x99) { 386a5afba16SPali Rohár msleep(100); 387a5afba16SPali Rohár temp = _i8k_get_temp(sensor); 388a5afba16SPali Rohár } 389a5afba16SPali Rohár /* 390a5afba16SPali Rohár * Return -ENODATA for all invalid temperatures. 391a5afba16SPali Rohár * 392a5afba16SPali Rohár * Known instances are the 0x99 value as seen above as well as 393a5afba16SPali Rohár * 0xc1 (193), which may be returned when trying to read the GPU 394a5afba16SPali Rohár * temperature if the system supports a GPU and it is currently 395a5afba16SPali Rohár * turned off. 396a5afba16SPali Rohár */ 397a5afba16SPali Rohár if (temp > I8K_MAX_TEMP) 398a5afba16SPali Rohár return -ENODATA; 399a5afba16SPali Rohár 400a5afba16SPali Rohár return temp; 401a5afba16SPali Rohár } 402a5afba16SPali Rohár 403c9363cdfSArmin Wolf static int __init i8k_get_dell_signature(int req_fn) 404a5afba16SPali Rohár { 405a5afba16SPali Rohár struct smm_regs regs = { .eax = req_fn, }; 406a5afba16SPali Rohár int rc; 407a5afba16SPali Rohár 408a5afba16SPali Rohár rc = i8k_smm(®s); 409a5afba16SPali Rohár if (rc < 0) 410a5afba16SPali Rohár return rc; 411a5afba16SPali Rohár 412a5afba16SPali Rohár return regs.eax == 1145651527 && regs.edx == 1145392204 ? 0 : -1; 413a5afba16SPali Rohár } 414a5afba16SPali Rohár 415039ae585SPali Rohár #if IS_ENABLED(CONFIG_I8K) 416039ae585SPali Rohár 417039ae585SPali Rohár /* 418039ae585SPali Rohár * Read the Fn key status. 419039ae585SPali Rohár */ 420039ae585SPali Rohár static int i8k_get_fn_status(void) 421039ae585SPali Rohár { 422039ae585SPali Rohár struct smm_regs regs = { .eax = I8K_SMM_FN_STATUS, }; 423039ae585SPali Rohár int rc; 424039ae585SPali Rohár 425039ae585SPali Rohár rc = i8k_smm(®s); 426039ae585SPali Rohár if (rc < 0) 427039ae585SPali Rohár return rc; 428039ae585SPali Rohár 429039ae585SPali Rohár switch ((regs.eax >> I8K_FN_SHIFT) & I8K_FN_MASK) { 430039ae585SPali Rohár case I8K_FN_UP: 431039ae585SPali Rohár return I8K_VOL_UP; 432039ae585SPali Rohár case I8K_FN_DOWN: 433039ae585SPali Rohár return I8K_VOL_DOWN; 434039ae585SPali Rohár case I8K_FN_MUTE: 435039ae585SPali Rohár return I8K_VOL_MUTE; 436039ae585SPali Rohár default: 437039ae585SPali Rohár return 0; 438039ae585SPali Rohár } 439039ae585SPali Rohár } 440039ae585SPali Rohár 441039ae585SPali Rohár /* 442039ae585SPali Rohár * Read the power status. 443039ae585SPali Rohár */ 444039ae585SPali Rohár static int i8k_get_power_status(void) 445039ae585SPali Rohár { 446039ae585SPali Rohár struct smm_regs regs = { .eax = I8K_SMM_POWER_STATUS, }; 447039ae585SPali Rohár int rc; 448039ae585SPali Rohár 449039ae585SPali Rohár rc = i8k_smm(®s); 450039ae585SPali Rohár if (rc < 0) 451039ae585SPali Rohár return rc; 452039ae585SPali Rohár 453039ae585SPali Rohár return (regs.eax & 0xff) == I8K_POWER_AC ? I8K_AC : I8K_BATTERY; 454039ae585SPali Rohár } 455039ae585SPali Rohár 456039ae585SPali Rohár /* 457039ae585SPali Rohár * Procfs interface 458039ae585SPali Rohár */ 459039ae585SPali Rohár 460a5afba16SPali Rohár static int 461ba04d73cSArmin Wolf i8k_ioctl_unlocked(struct file *fp, struct dell_smm_data *data, unsigned int cmd, unsigned long arg) 462a5afba16SPali Rohár { 463a5afba16SPali Rohár int val = 0; 464a5afba16SPali Rohár int speed; 465a5afba16SPali Rohár unsigned char buff[16]; 466a5afba16SPali Rohár int __user *argp = (int __user *)arg; 467a5afba16SPali Rohár 468a5afba16SPali Rohár if (!argp) 469a5afba16SPali Rohár return -EINVAL; 470a5afba16SPali Rohár 471a5afba16SPali Rohár switch (cmd) { 472a5afba16SPali Rohár case I8K_BIOS_VERSION: 473ba04d73cSArmin Wolf if (!isdigit(data->bios_version[0]) || !isdigit(data->bios_version[1]) || 474ba04d73cSArmin Wolf !isdigit(data->bios_version[2])) 475053ea640SPali Rohár return -EINVAL; 476053ea640SPali Rohár 477ba04d73cSArmin Wolf val = (data->bios_version[0] << 16) | 478ba04d73cSArmin Wolf (data->bios_version[1] << 8) | data->bios_version[2]; 479a5afba16SPali Rohár break; 480a5afba16SPali Rohár 481a5afba16SPali Rohár case I8K_MACHINE_ID: 4827613663cSPali Rohár if (restricted && !capable(CAP_SYS_ADMIN)) 4837613663cSPali Rohár return -EPERM; 4847613663cSPali Rohár 4857613663cSPali Rohár memset(buff, 0, sizeof(buff)); 486ba04d73cSArmin Wolf strscpy(buff, data->bios_machineid, sizeof(buff)); 487a5afba16SPali Rohár break; 488a5afba16SPali Rohár 489a5afba16SPali Rohár case I8K_FN_STATUS: 490a5afba16SPali Rohár val = i8k_get_fn_status(); 491a5afba16SPali Rohár break; 492a5afba16SPali Rohár 493a5afba16SPali Rohár case I8K_POWER_STATUS: 494a5afba16SPali Rohár val = i8k_get_power_status(); 495a5afba16SPali Rohár break; 496a5afba16SPali Rohár 497a5afba16SPali Rohár case I8K_GET_TEMP: 498a5afba16SPali Rohár val = i8k_get_temp(0); 499a5afba16SPali Rohár break; 500a5afba16SPali Rohár 501a5afba16SPali Rohár case I8K_GET_SPEED: 502a5afba16SPali Rohár if (copy_from_user(&val, argp, sizeof(int))) 503a5afba16SPali Rohár return -EFAULT; 504a5afba16SPali Rohár 505ba04d73cSArmin Wolf val = i8k_get_fan_speed(data, val); 506a5afba16SPali Rohár break; 507a5afba16SPali Rohár 508a5afba16SPali Rohár case I8K_GET_FAN: 509a5afba16SPali Rohár if (copy_from_user(&val, argp, sizeof(int))) 510a5afba16SPali Rohár return -EFAULT; 511a5afba16SPali Rohár 512ba04d73cSArmin Wolf val = i8k_get_fan_status(data, val); 513a5afba16SPali Rohár break; 514a5afba16SPali Rohár 515a5afba16SPali Rohár case I8K_SET_FAN: 516a5afba16SPali Rohár if (restricted && !capable(CAP_SYS_ADMIN)) 517a5afba16SPali Rohár return -EPERM; 518a5afba16SPali Rohár 519a5afba16SPali Rohár if (copy_from_user(&val, argp, sizeof(int))) 520a5afba16SPali Rohár return -EFAULT; 521a5afba16SPali Rohár 522a5afba16SPali Rohár if (copy_from_user(&speed, argp + 1, sizeof(int))) 523a5afba16SPali Rohár return -EFAULT; 524a5afba16SPali Rohár 525ba04d73cSArmin Wolf val = i8k_set_fan(data, val, speed); 526a5afba16SPali Rohár break; 527a5afba16SPali Rohár 528a5afba16SPali Rohár default: 529a5afba16SPali Rohár return -EINVAL; 530a5afba16SPali Rohár } 531a5afba16SPali Rohár 532a5afba16SPali Rohár if (val < 0) 533a5afba16SPali Rohár return val; 534a5afba16SPali Rohár 535a5afba16SPali Rohár switch (cmd) { 536a5afba16SPali Rohár case I8K_BIOS_VERSION: 537a5afba16SPali Rohár if (copy_to_user(argp, &val, 4)) 538a5afba16SPali Rohár return -EFAULT; 539a5afba16SPali Rohár 540a5afba16SPali Rohár break; 541a5afba16SPali Rohár case I8K_MACHINE_ID: 542a5afba16SPali Rohár if (copy_to_user(argp, buff, 16)) 543a5afba16SPali Rohár return -EFAULT; 544a5afba16SPali Rohár 545a5afba16SPali Rohár break; 546a5afba16SPali Rohár default: 547a5afba16SPali Rohár if (copy_to_user(argp, &val, sizeof(int))) 548a5afba16SPali Rohár return -EFAULT; 549a5afba16SPali Rohár 550a5afba16SPali Rohár break; 551a5afba16SPali Rohár } 552a5afba16SPali Rohár 553a5afba16SPali Rohár return 0; 554a5afba16SPali Rohár } 555a5afba16SPali Rohár 556a5afba16SPali Rohár static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) 557a5afba16SPali Rohár { 558ba04d73cSArmin Wolf struct dell_smm_data *data = PDE_DATA(file_inode(fp)); 559a5afba16SPali Rohár long ret; 560a5afba16SPali Rohár 561ba04d73cSArmin Wolf mutex_lock(&data->i8k_mutex); 562ba04d73cSArmin Wolf ret = i8k_ioctl_unlocked(fp, data, cmd, arg); 563ba04d73cSArmin Wolf mutex_unlock(&data->i8k_mutex); 564a5afba16SPali Rohár 565a5afba16SPali Rohár return ret; 566a5afba16SPali Rohár } 567a5afba16SPali Rohár 568a5afba16SPali Rohár /* 569a5afba16SPali Rohár * Print the information for /proc/i8k. 570a5afba16SPali Rohár */ 571a5afba16SPali Rohár static int i8k_proc_show(struct seq_file *seq, void *offset) 572a5afba16SPali Rohár { 573ba04d73cSArmin Wolf struct dell_smm_data *data = seq->private; 574a5afba16SPali Rohár int fn_key, cpu_temp, ac_power; 575a5afba16SPali Rohár int left_fan, right_fan, left_speed, right_speed; 576a5afba16SPali Rohár 577a5afba16SPali Rohár cpu_temp = i8k_get_temp(0); /* 11100 µs */ 578ba04d73cSArmin Wolf left_fan = i8k_get_fan_status(data, I8K_FAN_LEFT); /* 580 µs */ 579ba04d73cSArmin Wolf right_fan = i8k_get_fan_status(data, I8K_FAN_RIGHT); /* 580 µs */ 580ba04d73cSArmin Wolf left_speed = i8k_get_fan_speed(data, I8K_FAN_LEFT); /* 580 µs */ 581ba04d73cSArmin Wolf right_speed = i8k_get_fan_speed(data, I8K_FAN_RIGHT); /* 580 µs */ 582a5afba16SPali Rohár fn_key = i8k_get_fn_status(); /* 750 µs */ 583a5afba16SPali Rohár if (power_status) 584a5afba16SPali Rohár ac_power = i8k_get_power_status(); /* 14700 µs */ 585a5afba16SPali Rohár else 586a5afba16SPali Rohár ac_power = -1; 587a5afba16SPali Rohár 588a5afba16SPali Rohár /* 589a5afba16SPali Rohár * Info: 590a5afba16SPali Rohár * 591a5afba16SPali Rohár * 1) Format version (this will change if format changes) 592a5afba16SPali Rohár * 2) BIOS version 593a5afba16SPali Rohár * 3) BIOS machine ID 594a5afba16SPali Rohár * 4) Cpu temperature 595a5afba16SPali Rohár * 5) Left fan status 596a5afba16SPali Rohár * 6) Right fan status 597a5afba16SPali Rohár * 7) Left fan speed 598a5afba16SPali Rohár * 8) Right fan speed 599a5afba16SPali Rohár * 9) AC power 600a5afba16SPali Rohár * 10) Fn Key status 601a5afba16SPali Rohár */ 602a5afba16SPali Rohár seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n", 603a5afba16SPali Rohár I8K_PROC_FMT, 604ba04d73cSArmin Wolf data->bios_version, 605ba04d73cSArmin Wolf (restricted && !capable(CAP_SYS_ADMIN)) ? "-1" : data->bios_machineid, 606a5afba16SPali Rohár cpu_temp, 607a5afba16SPali Rohár left_fan, right_fan, left_speed, right_speed, 608a5afba16SPali Rohár ac_power, fn_key); 609a5afba16SPali Rohár 610a5afba16SPali Rohár return 0; 611a5afba16SPali Rohár } 612a5afba16SPali Rohár 613a5afba16SPali Rohár static int i8k_open_fs(struct inode *inode, struct file *file) 614a5afba16SPali Rohár { 615ba04d73cSArmin Wolf return single_open(file, i8k_proc_show, PDE_DATA(inode)); 616a5afba16SPali Rohár } 617a5afba16SPali Rohár 61897a32539SAlexey Dobriyan static const struct proc_ops i8k_proc_ops = { 61997a32539SAlexey Dobriyan .proc_open = i8k_open_fs, 62097a32539SAlexey Dobriyan .proc_read = seq_read, 62197a32539SAlexey Dobriyan .proc_lseek = seq_lseek, 62297a32539SAlexey Dobriyan .proc_release = single_release, 62397a32539SAlexey Dobriyan .proc_ioctl = i8k_ioctl, 624039ae585SPali Rohár }; 625039ae585SPali Rohár 626a2cb66b4SArmin Wolf static void i8k_exit_procfs(void *param) 627039ae585SPali Rohár { 628039ae585SPali Rohár remove_proc_entry("i8k", NULL); 629039ae585SPali Rohár } 630039ae585SPali Rohár 631a2cb66b4SArmin Wolf static void __init i8k_init_procfs(struct device *dev) 632039ae585SPali Rohár { 633ba04d73cSArmin Wolf struct dell_smm_data *data = dev_get_drvdata(dev); 634ba04d73cSArmin Wolf 635a2cb66b4SArmin Wolf /* Register the proc entry */ 636ba04d73cSArmin Wolf proc_create_data("i8k", 0, NULL, &i8k_proc_ops, data); 637a2cb66b4SArmin Wolf 638a2cb66b4SArmin Wolf devm_add_action_or_reset(dev, i8k_exit_procfs, NULL); 639039ae585SPali Rohár } 640039ae585SPali Rohár 641a2cb66b4SArmin Wolf #else 642a2cb66b4SArmin Wolf 643a2cb66b4SArmin Wolf static void __init i8k_init_procfs(struct device *dev) 644039ae585SPali Rohár { 645039ae585SPali Rohár } 646039ae585SPali Rohár 647039ae585SPali Rohár #endif 648a5afba16SPali Rohár 649a5afba16SPali Rohár /* 650a5afba16SPali Rohár * Hwmon interface 651a5afba16SPali Rohár */ 652a5afba16SPali Rohár 653*deeba244SArmin Wolf static umode_t dell_smm_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr, 654*deeba244SArmin Wolf int channel) 655a5afba16SPali Rohár { 656*deeba244SArmin Wolf const struct dell_smm_data *data = drvdata; 657a5afba16SPali Rohár 658*deeba244SArmin Wolf switch (type) { 659*deeba244SArmin Wolf case hwmon_temp: 660*deeba244SArmin Wolf switch (attr) { 661*deeba244SArmin Wolf case hwmon_temp_input: 662*deeba244SArmin Wolf case hwmon_temp_label: 663*deeba244SArmin Wolf if (data->temp_type[channel] >= 0) 664*deeba244SArmin Wolf return 0444; 665*deeba244SArmin Wolf 666*deeba244SArmin Wolf break; 667*deeba244SArmin Wolf default: 668*deeba244SArmin Wolf break; 669*deeba244SArmin Wolf } 670*deeba244SArmin Wolf break; 671*deeba244SArmin Wolf case hwmon_fan: 672*deeba244SArmin Wolf if (data->disallow_fan_support) 673*deeba244SArmin Wolf break; 674*deeba244SArmin Wolf 675*deeba244SArmin Wolf switch (attr) { 676*deeba244SArmin Wolf case hwmon_fan_input: 677*deeba244SArmin Wolf if (data->fan[channel]) 678*deeba244SArmin Wolf return 0444; 679*deeba244SArmin Wolf 680*deeba244SArmin Wolf break; 681*deeba244SArmin Wolf case hwmon_fan_label: 682*deeba244SArmin Wolf if (data->fan[channel] && !data->disallow_fan_type_call) 683*deeba244SArmin Wolf return 0444; 684*deeba244SArmin Wolf 685*deeba244SArmin Wolf break; 686*deeba244SArmin Wolf default: 687*deeba244SArmin Wolf break; 688*deeba244SArmin Wolf } 689*deeba244SArmin Wolf break; 690*deeba244SArmin Wolf case hwmon_pwm: 691*deeba244SArmin Wolf if (data->disallow_fan_support) 692*deeba244SArmin Wolf break; 693*deeba244SArmin Wolf 694*deeba244SArmin Wolf switch (attr) { 695*deeba244SArmin Wolf case hwmon_pwm_input: 696*deeba244SArmin Wolf if (data->fan[channel]) 697*deeba244SArmin Wolf return 0644; 698*deeba244SArmin Wolf 699*deeba244SArmin Wolf break; 700*deeba244SArmin Wolf case hwmon_pwm_enable: 701*deeba244SArmin Wolf if (data->auto_fan) 702*deeba244SArmin Wolf /* 703*deeba244SArmin Wolf * There is no command for retrieve the current status 704*deeba244SArmin Wolf * from BIOS, and userspace/firmware itself can change 705*deeba244SArmin Wolf * it. 706*deeba244SArmin Wolf * Thus we can only provide write-only access for now. 707*deeba244SArmin Wolf */ 708*deeba244SArmin Wolf return 0200; 709*deeba244SArmin Wolf 710*deeba244SArmin Wolf break; 711*deeba244SArmin Wolf default: 712*deeba244SArmin Wolf break; 713*deeba244SArmin Wolf } 714*deeba244SArmin Wolf break; 715*deeba244SArmin Wolf default: 716*deeba244SArmin Wolf break; 717a5afba16SPali Rohár } 718a5afba16SPali Rohár 719*deeba244SArmin Wolf return 0; 720a5afba16SPali Rohár } 721a5afba16SPali Rohár 722*deeba244SArmin Wolf static int dell_smm_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, 723*deeba244SArmin Wolf long *val) 724a5afba16SPali Rohár { 725ba04d73cSArmin Wolf struct dell_smm_data *data = dev_get_drvdata(dev); 726*deeba244SArmin Wolf int ret; 727a5afba16SPali Rohár 728*deeba244SArmin Wolf switch (type) { 729*deeba244SArmin Wolf case hwmon_temp: 730*deeba244SArmin Wolf switch (attr) { 731*deeba244SArmin Wolf case hwmon_temp_input: 732*deeba244SArmin Wolf ret = i8k_get_temp(channel); 733*deeba244SArmin Wolf if (ret < 0) 734*deeba244SArmin Wolf return ret; 735*deeba244SArmin Wolf 736*deeba244SArmin Wolf *val = ret * 1000; 737*deeba244SArmin Wolf 738*deeba244SArmin Wolf return 0; 739*deeba244SArmin Wolf default: 740*deeba244SArmin Wolf break; 741*deeba244SArmin Wolf } 742*deeba244SArmin Wolf break; 743*deeba244SArmin Wolf case hwmon_fan: 744*deeba244SArmin Wolf switch (attr) { 745*deeba244SArmin Wolf case hwmon_fan_input: 746*deeba244SArmin Wolf ret = i8k_get_fan_speed(data, channel); 747*deeba244SArmin Wolf if (ret < 0) 748*deeba244SArmin Wolf return ret; 749*deeba244SArmin Wolf 750*deeba244SArmin Wolf *val = ret; 751*deeba244SArmin Wolf 752*deeba244SArmin Wolf return 0; 753*deeba244SArmin Wolf default: 754*deeba244SArmin Wolf break; 755*deeba244SArmin Wolf } 756*deeba244SArmin Wolf break; 757*deeba244SArmin Wolf case hwmon_pwm: 758*deeba244SArmin Wolf switch (attr) { 759*deeba244SArmin Wolf case hwmon_pwm_input: 760*deeba244SArmin Wolf ret = i8k_get_fan_status(data, channel); 761*deeba244SArmin Wolf if (ret < 0) 762*deeba244SArmin Wolf return ret; 763*deeba244SArmin Wolf 764*deeba244SArmin Wolf *val = clamp_val(ret * data->i8k_pwm_mult, 0, 255); 765*deeba244SArmin Wolf 766*deeba244SArmin Wolf return 0; 767*deeba244SArmin Wolf default: 768*deeba244SArmin Wolf break; 769*deeba244SArmin Wolf } 770*deeba244SArmin Wolf break; 771*deeba244SArmin Wolf default: 772*deeba244SArmin Wolf break; 773*deeba244SArmin Wolf } 774*deeba244SArmin Wolf 775*deeba244SArmin Wolf return -EOPNOTSUPP; 776*deeba244SArmin Wolf } 777*deeba244SArmin Wolf 778*deeba244SArmin Wolf static const char *dell_smm_fan_label(struct dell_smm_data *data, int channel) 779*deeba244SArmin Wolf { 780*deeba244SArmin Wolf bool dock = false; 781*deeba244SArmin Wolf int type = i8k_get_fan_type(data, channel); 782*deeba244SArmin Wolf 783a5afba16SPali Rohár if (type < 0) 784*deeba244SArmin Wolf return ERR_PTR(type); 785a5afba16SPali Rohár 786a5afba16SPali Rohár if (type & 0x10) { 787a5afba16SPali Rohár dock = true; 788a5afba16SPali Rohár type &= 0x0F; 789a5afba16SPali Rohár } 790a5afba16SPali Rohár 791*deeba244SArmin Wolf if (type >= ARRAY_SIZE(fan_labels)) 792*deeba244SArmin Wolf type = ARRAY_SIZE(fan_labels) - 1; 793a5afba16SPali Rohár 794*deeba244SArmin Wolf return dock ? docking_labels[type] : fan_labels[type]; 795a5afba16SPali Rohár } 796a5afba16SPali Rohár 797*deeba244SArmin Wolf static int dell_smm_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, 798*deeba244SArmin Wolf int channel, const char **str) 799a5afba16SPali Rohár { 800ba04d73cSArmin Wolf struct dell_smm_data *data = dev_get_drvdata(dev); 801a5afba16SPali Rohár 802*deeba244SArmin Wolf switch (type) { 803*deeba244SArmin Wolf case hwmon_temp: 804*deeba244SArmin Wolf switch (attr) { 805*deeba244SArmin Wolf case hwmon_temp_label: 806*deeba244SArmin Wolf *str = temp_labels[data->temp_type[channel]]; 807*deeba244SArmin Wolf return 0; 808*deeba244SArmin Wolf default: 809*deeba244SArmin Wolf break; 810*deeba244SArmin Wolf } 811*deeba244SArmin Wolf break; 812*deeba244SArmin Wolf case hwmon_fan: 813*deeba244SArmin Wolf switch (attr) { 814*deeba244SArmin Wolf case hwmon_fan_label: 815*deeba244SArmin Wolf *str = dell_smm_fan_label(data, channel); 816*deeba244SArmin Wolf return PTR_ERR_OR_ZERO(*str); 817*deeba244SArmin Wolf default: 818*deeba244SArmin Wolf break; 819*deeba244SArmin Wolf } 820*deeba244SArmin Wolf break; 821*deeba244SArmin Wolf default: 822*deeba244SArmin Wolf break; 823a5afba16SPali Rohár } 824a5afba16SPali Rohár 825*deeba244SArmin Wolf return -EOPNOTSUPP; 826a5afba16SPali Rohár } 827a5afba16SPali Rohár 828*deeba244SArmin Wolf static int dell_smm_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, 829*deeba244SArmin Wolf long val) 830a5afba16SPali Rohár { 831ba04d73cSArmin Wolf struct dell_smm_data *data = dev_get_drvdata(dev); 832*deeba244SArmin Wolf unsigned long pwm; 833*deeba244SArmin Wolf bool enable; 834a5afba16SPali Rohár int err; 835a5afba16SPali Rohár 836*deeba244SArmin Wolf switch (type) { 837*deeba244SArmin Wolf case hwmon_pwm: 838*deeba244SArmin Wolf switch (attr) { 839*deeba244SArmin Wolf case hwmon_pwm_input: 840*deeba244SArmin Wolf pwm = clamp_val(DIV_ROUND_CLOSEST(val, data->i8k_pwm_mult), 0, 841*deeba244SArmin Wolf data->i8k_fan_max); 842a5afba16SPali Rohár 843ba04d73cSArmin Wolf mutex_lock(&data->i8k_mutex); 844*deeba244SArmin Wolf err = i8k_set_fan(data, channel, pwm); 845ba04d73cSArmin Wolf mutex_unlock(&data->i8k_mutex); 846a5afba16SPali Rohár 847*deeba244SArmin Wolf if (err < 0) 848afe45277SGiovanni Mascellani return err; 849afe45277SGiovanni Mascellani 850*deeba244SArmin Wolf return 0; 851*deeba244SArmin Wolf case hwmon_pwm_enable: 852*deeba244SArmin Wolf if (!val) 853*deeba244SArmin Wolf return -EINVAL; 854*deeba244SArmin Wolf 855afe45277SGiovanni Mascellani if (val == 1) 856afe45277SGiovanni Mascellani enable = false; 857afe45277SGiovanni Mascellani else 858*deeba244SArmin Wolf enable = true; 859afe45277SGiovanni Mascellani 860ba04d73cSArmin Wolf mutex_lock(&data->i8k_mutex); 861ba04d73cSArmin Wolf err = i8k_enable_fan_auto_mode(data, enable); 862ba04d73cSArmin Wolf mutex_unlock(&data->i8k_mutex); 863afe45277SGiovanni Mascellani 864*deeba244SArmin Wolf if (err < 0) 865*deeba244SArmin Wolf return err; 866*deeba244SArmin Wolf 867*deeba244SArmin Wolf return 0; 868*deeba244SArmin Wolf default: 869*deeba244SArmin Wolf break; 870*deeba244SArmin Wolf } 871*deeba244SArmin Wolf break; 872*deeba244SArmin Wolf default: 873*deeba244SArmin Wolf break; 874afe45277SGiovanni Mascellani } 875afe45277SGiovanni Mascellani 876*deeba244SArmin Wolf return -EOPNOTSUPP; 877*deeba244SArmin Wolf } 878a5afba16SPali Rohár 879*deeba244SArmin Wolf static const struct hwmon_ops dell_smm_ops = { 880*deeba244SArmin Wolf .is_visible = dell_smm_is_visible, 881*deeba244SArmin Wolf .read = dell_smm_read, 882*deeba244SArmin Wolf .read_string = dell_smm_read_string, 883*deeba244SArmin Wolf .write = dell_smm_write, 884*deeba244SArmin Wolf }; 885*deeba244SArmin Wolf 886*deeba244SArmin Wolf static const struct hwmon_channel_info *dell_smm_info[] = { 887*deeba244SArmin Wolf HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), 888*deeba244SArmin Wolf HWMON_CHANNEL_INFO(temp, 889*deeba244SArmin Wolf HWMON_T_INPUT | HWMON_T_LABEL, 890*deeba244SArmin Wolf HWMON_T_INPUT | HWMON_T_LABEL, 891*deeba244SArmin Wolf HWMON_T_INPUT | HWMON_T_LABEL, 892*deeba244SArmin Wolf HWMON_T_INPUT | HWMON_T_LABEL, 893*deeba244SArmin Wolf HWMON_T_INPUT | HWMON_T_LABEL, 894*deeba244SArmin Wolf HWMON_T_INPUT | HWMON_T_LABEL, 895*deeba244SArmin Wolf HWMON_T_INPUT | HWMON_T_LABEL, 896*deeba244SArmin Wolf HWMON_T_INPUT | HWMON_T_LABEL, 897*deeba244SArmin Wolf HWMON_T_INPUT | HWMON_T_LABEL, 898*deeba244SArmin Wolf HWMON_T_INPUT | HWMON_T_LABEL 899*deeba244SArmin Wolf ), 900*deeba244SArmin Wolf HWMON_CHANNEL_INFO(fan, 901*deeba244SArmin Wolf HWMON_F_INPUT | HWMON_F_LABEL, 902*deeba244SArmin Wolf HWMON_F_INPUT | HWMON_F_LABEL, 903*deeba244SArmin Wolf HWMON_F_INPUT | HWMON_F_LABEL 904*deeba244SArmin Wolf ), 905*deeba244SArmin Wolf HWMON_CHANNEL_INFO(pwm, 906*deeba244SArmin Wolf HWMON_PWM_INPUT | HWMON_PWM_ENABLE, 907*deeba244SArmin Wolf HWMON_PWM_INPUT, 908*deeba244SArmin Wolf HWMON_PWM_INPUT 909*deeba244SArmin Wolf ), 910a5afba16SPali Rohár NULL 911a5afba16SPali Rohár }; 912a5afba16SPali Rohár 913*deeba244SArmin Wolf static const struct hwmon_chip_info dell_smm_chip_info = { 914*deeba244SArmin Wolf .ops = &dell_smm_ops, 915*deeba244SArmin Wolf .info = dell_smm_info, 916a5afba16SPali Rohár }; 917a5afba16SPali Rohár 9181492fa21SArmin Wolf static int __init dell_smm_init_hwmon(struct device *dev) 919a5afba16SPali Rohár { 920ba04d73cSArmin Wolf struct dell_smm_data *data = dev_get_drvdata(dev); 921*deeba244SArmin Wolf struct device *dell_smm_hwmon_dev; 922*deeba244SArmin Wolf int i, err; 923a5afba16SPali Rohár 924*deeba244SArmin Wolf for (i = 0; i < DELL_SMM_NO_TEMP; i++) { 925*deeba244SArmin Wolf data->temp_type[i] = i8k_get_temp_type(i); 926*deeba244SArmin Wolf if (data->temp_type[i] < 0) 927*deeba244SArmin Wolf continue; 928a5afba16SPali Rohár 929*deeba244SArmin Wolf if (data->temp_type[i] >= ARRAY_SIZE(temp_labels)) 930*deeba244SArmin Wolf data->temp_type[i] = ARRAY_SIZE(temp_labels) - 1; 931*deeba244SArmin Wolf } 932*deeba244SArmin Wolf 933*deeba244SArmin Wolf for (i = 0; i < DELL_SMM_NO_FANS; i++) { 934*deeba244SArmin Wolf data->fan_type[i] = INT_MIN; 935*deeba244SArmin Wolf err = i8k_get_fan_status(data, i); 9365ce91714SPali Rohár if (err < 0) 937*deeba244SArmin Wolf err = i8k_get_fan_type(data, i); 938a5afba16SPali Rohár if (err >= 0) 939*deeba244SArmin Wolf data->fan[i] = true; 940*deeba244SArmin Wolf } 941a5afba16SPali Rohár 942*deeba244SArmin Wolf dell_smm_hwmon_dev = devm_hwmon_device_register_with_info(dev, "dell_smm", data, 943*deeba244SArmin Wolf &dell_smm_chip_info, NULL); 944a5afba16SPali Rohár 945*deeba244SArmin Wolf return PTR_ERR_OR_ZERO(dell_smm_hwmon_dev); 946a5afba16SPali Rohár } 947a5afba16SPali Rohár 948a5afba16SPali Rohár struct i8k_config_data { 949a5afba16SPali Rohár uint fan_mult; 950a5afba16SPali Rohár uint fan_max; 951a5afba16SPali Rohár }; 952a5afba16SPali Rohár 953a5afba16SPali Rohár enum i8k_configs { 954a5afba16SPali Rohár DELL_LATITUDE_D520, 955a5afba16SPali Rohár DELL_PRECISION_490, 956a5afba16SPali Rohár DELL_STUDIO, 957a5afba16SPali Rohár DELL_XPS, 958a5afba16SPali Rohár }; 959a5afba16SPali Rohár 960a5afba16SPali Rohár static const struct i8k_config_data i8k_config_data[] = { 961a5afba16SPali Rohár [DELL_LATITUDE_D520] = { 962a5afba16SPali Rohár .fan_mult = 1, 963a5afba16SPali Rohár .fan_max = I8K_FAN_TURBO, 964a5afba16SPali Rohár }, 965a5afba16SPali Rohár [DELL_PRECISION_490] = { 966a5afba16SPali Rohár .fan_mult = 1, 967a5afba16SPali Rohár .fan_max = I8K_FAN_TURBO, 968a5afba16SPali Rohár }, 969a5afba16SPali Rohár [DELL_STUDIO] = { 970a5afba16SPali Rohár .fan_mult = 1, 971a5afba16SPali Rohár .fan_max = I8K_FAN_HIGH, 972a5afba16SPali Rohár }, 973a5afba16SPali Rohár [DELL_XPS] = { 974a5afba16SPali Rohár .fan_mult = 1, 975a5afba16SPali Rohár .fan_max = I8K_FAN_HIGH, 976a5afba16SPali Rohár }, 977a5afba16SPali Rohár }; 978a5afba16SPali Rohár 9796faadbbbSChristoph Hellwig static const struct dmi_system_id i8k_dmi_table[] __initconst = { 980a5afba16SPali Rohár { 981a5afba16SPali Rohár .ident = "Dell Inspiron", 982a5afba16SPali Rohár .matches = { 983a5afba16SPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"), 984a5afba16SPali Rohár DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"), 985a5afba16SPali Rohár }, 986a5afba16SPali Rohár }, 987a5afba16SPali Rohár { 988a5afba16SPali Rohár .ident = "Dell Latitude", 989a5afba16SPali Rohár .matches = { 990a5afba16SPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"), 991a5afba16SPali Rohár DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"), 992a5afba16SPali Rohár }, 993a5afba16SPali Rohár }, 994a5afba16SPali Rohár { 995a5afba16SPali Rohár .ident = "Dell Inspiron 2", 996a5afba16SPali Rohár .matches = { 997a5afba16SPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 998a5afba16SPali Rohár DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"), 999a5afba16SPali Rohár }, 1000a5afba16SPali Rohár }, 1001a5afba16SPali Rohár { 1002a5afba16SPali Rohár .ident = "Dell Latitude D520", 1003a5afba16SPali Rohár .matches = { 1004a5afba16SPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1005a5afba16SPali Rohár DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D520"), 1006a5afba16SPali Rohár }, 1007a5afba16SPali Rohár .driver_data = (void *)&i8k_config_data[DELL_LATITUDE_D520], 1008a5afba16SPali Rohár }, 1009a5afba16SPali Rohár { 1010a5afba16SPali Rohár .ident = "Dell Latitude 2", 1011a5afba16SPali Rohár .matches = { 1012a5afba16SPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1013a5afba16SPali Rohár DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"), 1014a5afba16SPali Rohár }, 1015a5afba16SPali Rohár }, 1016a5afba16SPali Rohár { /* UK Inspiron 6400 */ 1017a5afba16SPali Rohár .ident = "Dell Inspiron 3", 1018a5afba16SPali Rohár .matches = { 1019a5afba16SPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1020a5afba16SPali Rohár DMI_MATCH(DMI_PRODUCT_NAME, "MM061"), 1021a5afba16SPali Rohár }, 1022a5afba16SPali Rohár }, 1023a5afba16SPali Rohár { 1024a5afba16SPali Rohár .ident = "Dell Inspiron 3", 1025a5afba16SPali Rohár .matches = { 1026a5afba16SPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1027a5afba16SPali Rohár DMI_MATCH(DMI_PRODUCT_NAME, "MP061"), 1028a5afba16SPali Rohár }, 1029a5afba16SPali Rohár }, 1030a5afba16SPali Rohár { 1031a5afba16SPali Rohár .ident = "Dell Precision 490", 1032a5afba16SPali Rohár .matches = { 1033a5afba16SPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1034a5afba16SPali Rohár DMI_MATCH(DMI_PRODUCT_NAME, 1035a5afba16SPali Rohár "Precision WorkStation 490"), 1036a5afba16SPali Rohár }, 1037a5afba16SPali Rohár .driver_data = (void *)&i8k_config_data[DELL_PRECISION_490], 1038a5afba16SPali Rohár }, 1039a5afba16SPali Rohár { 1040a5afba16SPali Rohár .ident = "Dell Precision", 1041a5afba16SPali Rohár .matches = { 1042a5afba16SPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1043a5afba16SPali Rohár DMI_MATCH(DMI_PRODUCT_NAME, "Precision"), 1044a5afba16SPali Rohár }, 1045a5afba16SPali Rohár }, 1046a5afba16SPali Rohár { 1047a5afba16SPali Rohár .ident = "Dell Vostro", 1048a5afba16SPali Rohár .matches = { 1049a5afba16SPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1050a5afba16SPali Rohár DMI_MATCH(DMI_PRODUCT_NAME, "Vostro"), 1051a5afba16SPali Rohár }, 1052a5afba16SPali Rohár }, 1053a5afba16SPali Rohár { 1054a5afba16SPali Rohár .ident = "Dell Studio", 1055a5afba16SPali Rohár .matches = { 1056a5afba16SPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1057a5afba16SPali Rohár DMI_MATCH(DMI_PRODUCT_NAME, "Studio"), 1058a5afba16SPali Rohár }, 1059a5afba16SPali Rohár .driver_data = (void *)&i8k_config_data[DELL_STUDIO], 1060a5afba16SPali Rohár }, 1061a5afba16SPali Rohár { 1062a5afba16SPali Rohár .ident = "Dell XPS M140", 1063a5afba16SPali Rohár .matches = { 1064a5afba16SPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1065a5afba16SPali Rohár DMI_MATCH(DMI_PRODUCT_NAME, "MXC051"), 1066a5afba16SPali Rohár }, 1067a5afba16SPali Rohár .driver_data = (void *)&i8k_config_data[DELL_XPS], 1068a5afba16SPali Rohár }, 1069a4811b6cSPali Rohár { 1070b8a13e5eSThomas Hebb .ident = "Dell XPS", 1071a4811b6cSPali Rohár .matches = { 1072a4811b6cSPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1073b8a13e5eSThomas Hebb DMI_MATCH(DMI_PRODUCT_NAME, "XPS"), 1074162372b0SMichele Sorcinelli }, 1075162372b0SMichele Sorcinelli }, 1076a5afba16SPali Rohár { } 1077a5afba16SPali Rohár }; 1078a5afba16SPali Rohár 1079a5afba16SPali Rohár MODULE_DEVICE_TABLE(dmi, i8k_dmi_table); 1080a5afba16SPali Rohár 1081a4b45b25SPali Rohár /* 10822744d2fdSPali Rohár * On some machines once I8K_SMM_GET_FAN_TYPE is issued then CPU fan speed 10832744d2fdSPali Rohár * randomly going up and down due to bug in Dell SMM or BIOS. Here is blacklist 10842744d2fdSPali Rohár * of affected Dell machines for which we disallow I8K_SMM_GET_FAN_TYPE call. 10852744d2fdSPali Rohár * See bug: https://bugzilla.kernel.org/show_bug.cgi?id=100121 10866220f4ebSThorsten Leemhuis */ 10876faadbbbSChristoph Hellwig static const struct dmi_system_id i8k_blacklist_fan_type_dmi_table[] __initconst = { 10882744d2fdSPali Rohár { 10896220f4ebSThorsten Leemhuis .ident = "Dell Studio XPS 8000", 10906220f4ebSThorsten Leemhuis .matches = { 10916220f4ebSThorsten Leemhuis DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 10926220f4ebSThorsten Leemhuis DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Studio XPS 8000"), 10936220f4ebSThorsten Leemhuis }, 10946220f4ebSThorsten Leemhuis }, 10956220f4ebSThorsten Leemhuis { 1096a4b45b25SPali Rohár .ident = "Dell Studio XPS 8100", 1097a4b45b25SPali Rohár .matches = { 1098a4b45b25SPali Rohár DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1099a4b45b25SPali Rohár DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Studio XPS 8100"), 1100a4b45b25SPali Rohár }, 1101a4b45b25SPali Rohár }, 11022744d2fdSPali Rohár { 11032744d2fdSPali Rohár .ident = "Dell Inspiron 580", 11042744d2fdSPali Rohár .matches = { 11052744d2fdSPali Rohár DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 11062744d2fdSPali Rohár DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Inspiron 580 "), 11072744d2fdSPali Rohár }, 11082744d2fdSPali Rohár }, 1109a4b45b25SPali Rohár { } 1110a4b45b25SPali Rohár }; 1111a4b45b25SPali Rohár 1112a5afba16SPali Rohár /* 1113f480ea90SPali Rohár * On some machines all fan related SMM functions implemented by Dell BIOS 1114f480ea90SPali Rohár * firmware freeze kernel for about 500ms. Until Dell fixes these problems fan 1115f480ea90SPali Rohár * support for affected blacklisted Dell machines stay disabled. 1116f480ea90SPali Rohár * See bug: https://bugzilla.kernel.org/show_bug.cgi?id=195751 1117f480ea90SPali Rohár */ 1118f480ea90SPali Rohár static struct dmi_system_id i8k_blacklist_fan_support_dmi_table[] __initdata = { 1119f480ea90SPali Rohár { 1120f480ea90SPali Rohár .ident = "Dell Inspiron 7720", 1121f480ea90SPali Rohár .matches = { 1122f480ea90SPali Rohár DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1123f480ea90SPali Rohár DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Inspiron 7720"), 1124f480ea90SPali Rohár }, 1125f480ea90SPali Rohár }, 11266fbc4232SOleksandr Natalenko { 11276fbc4232SOleksandr Natalenko .ident = "Dell Vostro 3360", 11286fbc4232SOleksandr Natalenko .matches = { 11296fbc4232SOleksandr Natalenko DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 11306fbc4232SOleksandr Natalenko DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Vostro 3360"), 11316fbc4232SOleksandr Natalenko }, 11326fbc4232SOleksandr Natalenko }, 1133536e0019SHelge Eichelberg { 1134536e0019SHelge Eichelberg .ident = "Dell XPS13 9333", 1135536e0019SHelge Eichelberg .matches = { 1136536e0019SHelge Eichelberg DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1137536e0019SHelge Eichelberg DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS13 9333"), 1138536e0019SHelge Eichelberg }, 1139536e0019SHelge Eichelberg }, 11404008bc7dSThomas Hebb { 11414008bc7dSThomas Hebb .ident = "Dell XPS 15 L502X", 11424008bc7dSThomas Hebb .matches = { 11434008bc7dSThomas Hebb DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 11444008bc7dSThomas Hebb DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Dell System XPS L502X"), 11454008bc7dSThomas Hebb }, 11464008bc7dSThomas Hebb }, 1147f480ea90SPali Rohár { } 1148f480ea90SPali Rohár }; 1149f480ea90SPali Rohár 1150afe45277SGiovanni Mascellani struct i8k_fan_control_data { 1151afe45277SGiovanni Mascellani unsigned int manual_fan; 1152afe45277SGiovanni Mascellani unsigned int auto_fan; 1153afe45277SGiovanni Mascellani }; 1154afe45277SGiovanni Mascellani 1155afe45277SGiovanni Mascellani enum i8k_fan_controls { 1156afe45277SGiovanni Mascellani I8K_FAN_34A3_35A3, 1157afe45277SGiovanni Mascellani }; 1158afe45277SGiovanni Mascellani 1159afe45277SGiovanni Mascellani static const struct i8k_fan_control_data i8k_fan_control_data[] = { 1160afe45277SGiovanni Mascellani [I8K_FAN_34A3_35A3] = { 1161afe45277SGiovanni Mascellani .manual_fan = 0x34a3, 1162afe45277SGiovanni Mascellani .auto_fan = 0x35a3, 1163afe45277SGiovanni Mascellani }, 1164afe45277SGiovanni Mascellani }; 1165afe45277SGiovanni Mascellani 1166afe45277SGiovanni Mascellani static struct dmi_system_id i8k_whitelist_fan_control[] __initdata = { 1167afe45277SGiovanni Mascellani { 1168afe45277SGiovanni Mascellani .ident = "Dell Precision 5530", 1169afe45277SGiovanni Mascellani .matches = { 1170afe45277SGiovanni Mascellani DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1171afe45277SGiovanni Mascellani DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Precision 5530"), 1172afe45277SGiovanni Mascellani }, 1173afe45277SGiovanni Mascellani .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], 1174afe45277SGiovanni Mascellani }, 1175afe45277SGiovanni Mascellani { 11760ca8bb2cSJeffrey Lin .ident = "Dell Latitude 5480", 11770ca8bb2cSJeffrey Lin .matches = { 11780ca8bb2cSJeffrey Lin DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 11790ca8bb2cSJeffrey Lin DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude 5480"), 11800ca8bb2cSJeffrey Lin }, 11810ca8bb2cSJeffrey Lin .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], 11820ca8bb2cSJeffrey Lin }, 11830ca8bb2cSJeffrey Lin { 1184afe45277SGiovanni Mascellani .ident = "Dell Latitude E6440", 1185afe45277SGiovanni Mascellani .matches = { 1186afe45277SGiovanni Mascellani DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1187afe45277SGiovanni Mascellani DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude E6440"), 1188afe45277SGiovanni Mascellani }, 1189afe45277SGiovanni Mascellani .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], 1190afe45277SGiovanni Mascellani }, 1191807b8c29SSebastian Oechsle { 1192807b8c29SSebastian Oechsle .ident = "Dell Latitude E7440", 1193807b8c29SSebastian Oechsle .matches = { 1194807b8c29SSebastian Oechsle DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1195807b8c29SSebastian Oechsle DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude E7440"), 1196807b8c29SSebastian Oechsle }, 1197807b8c29SSebastian Oechsle .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], 1198807b8c29SSebastian Oechsle }, 1199afe45277SGiovanni Mascellani { } 1200afe45277SGiovanni Mascellani }; 1201afe45277SGiovanni Mascellani 12021492fa21SArmin Wolf static int __init dell_smm_probe(struct platform_device *pdev) 1203a5afba16SPali Rohár { 1204ba04d73cSArmin Wolf struct dell_smm_data *data; 1205afe45277SGiovanni Mascellani const struct dmi_system_id *id, *fan_control; 1206a5afba16SPali Rohár int fan, ret; 1207a5afba16SPali Rohár 1208ba04d73cSArmin Wolf data = devm_kzalloc(&pdev->dev, sizeof(struct dell_smm_data), GFP_KERNEL); 1209ba04d73cSArmin Wolf if (!data) 1210ba04d73cSArmin Wolf return -ENOMEM; 1211ba04d73cSArmin Wolf 1212ba04d73cSArmin Wolf mutex_init(&data->i8k_mutex); 1213ba04d73cSArmin Wolf data->i8k_fan_mult = I8K_FAN_MULT; 1214ba04d73cSArmin Wolf data->i8k_fan_max = I8K_FAN_HIGH; 1215ba04d73cSArmin Wolf platform_set_drvdata(pdev, data); 1216ba04d73cSArmin Wolf 1217f480ea90SPali Rohár if (dmi_check_system(i8k_blacklist_fan_support_dmi_table)) { 1218ba04d73cSArmin Wolf dev_warn(&pdev->dev, "broken Dell BIOS detected, disallow fan support\n"); 1219f480ea90SPali Rohár if (!force) 1220ba04d73cSArmin Wolf data->disallow_fan_support = true; 1221f480ea90SPali Rohár } 1222f480ea90SPali Rohár 1223836ad112SPali Rohár if (dmi_check_system(i8k_blacklist_fan_type_dmi_table)) { 1224ba04d73cSArmin Wolf dev_warn(&pdev->dev, "broken Dell BIOS detected, disallow fan type call\n"); 1225836ad112SPali Rohár if (!force) 1226ba04d73cSArmin Wolf data->disallow_fan_type_call = true; 1227836ad112SPali Rohár } 12282744d2fdSPali Rohár 1229ba04d73cSArmin Wolf strscpy(data->bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION), 1230ba04d73cSArmin Wolf sizeof(data->bios_version)); 1231ba04d73cSArmin Wolf strscpy(data->bios_machineid, i8k_get_dmi_data(DMI_PRODUCT_SERIAL), 1232ba04d73cSArmin Wolf sizeof(data->bios_machineid)); 1233a5afba16SPali Rohár 1234a5afba16SPali Rohár /* 1235a5afba16SPali Rohár * Set fan multiplier and maximal fan speed from dmi config 1236a5afba16SPali Rohár * Values specified in module parameters override values from dmi 1237a5afba16SPali Rohár */ 1238a5afba16SPali Rohár id = dmi_first_match(i8k_dmi_table); 1239a5afba16SPali Rohár if (id && id->driver_data) { 1240a5afba16SPali Rohár const struct i8k_config_data *conf = id->driver_data; 12411492fa21SArmin Wolf 1242a5afba16SPali Rohár if (!fan_mult && conf->fan_mult) 1243a5afba16SPali Rohár fan_mult = conf->fan_mult; 1244ba04d73cSArmin Wolf 1245a5afba16SPali Rohár if (!fan_max && conf->fan_max) 1246a5afba16SPali Rohár fan_max = conf->fan_max; 1247a5afba16SPali Rohár } 1248a5afba16SPali Rohár 1249ba04d73cSArmin Wolf data->i8k_fan_max = fan_max ? : I8K_FAN_HIGH; /* Must not be 0 */ 1250ba04d73cSArmin Wolf data->i8k_pwm_mult = DIV_ROUND_UP(255, data->i8k_fan_max); 1251a5afba16SPali Rohár 1252afe45277SGiovanni Mascellani fan_control = dmi_first_match(i8k_whitelist_fan_control); 1253afe45277SGiovanni Mascellani if (fan_control && fan_control->driver_data) { 1254ba04d73cSArmin Wolf const struct i8k_fan_control_data *control = fan_control->driver_data; 1255afe45277SGiovanni Mascellani 1256ba04d73cSArmin Wolf data->manual_fan = control->manual_fan; 1257ba04d73cSArmin Wolf data->auto_fan = control->auto_fan; 1258ba04d73cSArmin Wolf dev_info(&pdev->dev, "enabling support for setting automatic/manual fan control\n"); 1259afe45277SGiovanni Mascellani } 1260afe45277SGiovanni Mascellani 1261a5afba16SPali Rohár if (!fan_mult) { 1262a5afba16SPali Rohár /* 1263a5afba16SPali Rohár * Autodetect fan multiplier based on nominal rpm 1264a5afba16SPali Rohár * If fan reports rpm value too high then set multiplier to 1 1265a5afba16SPali Rohár */ 1266a5afba16SPali Rohár for (fan = 0; fan < 2; ++fan) { 1267ba04d73cSArmin Wolf ret = i8k_get_fan_nominal_speed(data, fan, data->i8k_fan_max); 1268a5afba16SPali Rohár if (ret < 0) 1269a5afba16SPali Rohár continue; 1270ba04d73cSArmin Wolf 1271a5afba16SPali Rohár if (ret > I8K_FAN_MAX_RPM) 1272ba04d73cSArmin Wolf data->i8k_fan_mult = 1; 1273a5afba16SPali Rohár break; 1274a5afba16SPali Rohár } 1275a5afba16SPali Rohár } else { 1276a5afba16SPali Rohár /* Fan multiplier was specified in module param or in dmi */ 1277ba04d73cSArmin Wolf data->i8k_fan_mult = fan_mult; 1278a5afba16SPali Rohár } 1279a5afba16SPali Rohár 12801492fa21SArmin Wolf ret = dell_smm_init_hwmon(&pdev->dev); 12811492fa21SArmin Wolf if (ret) 12821492fa21SArmin Wolf return ret; 12831492fa21SArmin Wolf 1284a2cb66b4SArmin Wolf i8k_init_procfs(&pdev->dev); 12851492fa21SArmin Wolf 12861492fa21SArmin Wolf return 0; 12871492fa21SArmin Wolf } 12881492fa21SArmin Wolf 12891492fa21SArmin Wolf static struct platform_driver dell_smm_driver = { 12901492fa21SArmin Wolf .driver = { 12911492fa21SArmin Wolf .name = KBUILD_MODNAME, 12921492fa21SArmin Wolf }, 12931492fa21SArmin Wolf }; 12941492fa21SArmin Wolf 12951492fa21SArmin Wolf static struct platform_device *dell_smm_device; 12961492fa21SArmin Wolf 12971492fa21SArmin Wolf /* 12981492fa21SArmin Wolf * Probe for the presence of a supported laptop. 12991492fa21SArmin Wolf */ 1300a5afba16SPali Rohár static int __init i8k_init(void) 1301a5afba16SPali Rohár { 13021492fa21SArmin Wolf /* 13031492fa21SArmin Wolf * Get DMI information 13041492fa21SArmin Wolf */ 13051492fa21SArmin Wolf if (!dmi_check_system(i8k_dmi_table)) { 13061492fa21SArmin Wolf if (!ignore_dmi && !force) 1307a5afba16SPali Rohár return -ENODEV; 1308a5afba16SPali Rohár 13091492fa21SArmin Wolf pr_info("not running on a supported Dell system.\n"); 13101492fa21SArmin Wolf pr_info("vendor=%s, model=%s, version=%s\n", 13111492fa21SArmin Wolf i8k_get_dmi_data(DMI_SYS_VENDOR), 13121492fa21SArmin Wolf i8k_get_dmi_data(DMI_PRODUCT_NAME), 13131492fa21SArmin Wolf i8k_get_dmi_data(DMI_BIOS_VERSION)); 13141492fa21SArmin Wolf } 1315039ae585SPali Rohár 13161492fa21SArmin Wolf /* 13171492fa21SArmin Wolf * Get SMM Dell signature 13181492fa21SArmin Wolf */ 13191492fa21SArmin Wolf if (i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG1) && 13201492fa21SArmin Wolf i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG2)) { 13211492fa21SArmin Wolf pr_err("unable to get SMM Dell signature\n"); 13221492fa21SArmin Wolf if (!force) 13231492fa21SArmin Wolf return -ENODEV; 13241492fa21SArmin Wolf } 13251492fa21SArmin Wolf 13261492fa21SArmin Wolf dell_smm_device = platform_create_bundle(&dell_smm_driver, dell_smm_probe, NULL, 0, NULL, 13271492fa21SArmin Wolf 0); 13281492fa21SArmin Wolf 13291492fa21SArmin Wolf return PTR_ERR_OR_ZERO(dell_smm_device); 1330a5afba16SPali Rohár } 1331a5afba16SPali Rohár 1332a5afba16SPali Rohár static void __exit i8k_exit(void) 1333a5afba16SPali Rohár { 13341492fa21SArmin Wolf platform_device_unregister(dell_smm_device); 13351492fa21SArmin Wolf platform_driver_unregister(&dell_smm_driver); 1336a5afba16SPali Rohár } 1337a5afba16SPali Rohár 1338a5afba16SPali Rohár module_init(i8k_init); 1339a5afba16SPali Rohár module_exit(i8k_exit); 1340