18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * dell-smm-hwmon.c -- Linux driver for accessing the SMM BIOS on Dell laptops. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2001 Massimo Dal Zotto <dz@debian.org> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Hwmon integration: 88c2ecf20Sopenharmony_ci * Copyright (C) 2011 Jean Delvare <jdelvare@suse.de> 98c2ecf20Sopenharmony_ci * Copyright (C) 2013, 2014 Guenter Roeck <linux@roeck-us.net> 108c2ecf20Sopenharmony_ci * Copyright (C) 2014, 2015 Pali Rohár <pali@kernel.org> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/cpu.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/types.h> 198c2ecf20Sopenharmony_ci#include <linux/init.h> 208c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 218c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 228c2ecf20Sopenharmony_ci#include <linux/dmi.h> 238c2ecf20Sopenharmony_ci#include <linux/capability.h> 248c2ecf20Sopenharmony_ci#include <linux/mutex.h> 258c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 268c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 278c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 288c2ecf20Sopenharmony_ci#include <linux/io.h> 298c2ecf20Sopenharmony_ci#include <linux/sched.h> 308c2ecf20Sopenharmony_ci#include <linux/ctype.h> 318c2ecf20Sopenharmony_ci#include <linux/smp.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include <linux/i8k.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define I8K_SMM_FN_STATUS 0x0025 368c2ecf20Sopenharmony_ci#define I8K_SMM_POWER_STATUS 0x0069 378c2ecf20Sopenharmony_ci#define I8K_SMM_SET_FAN 0x01a3 388c2ecf20Sopenharmony_ci#define I8K_SMM_GET_FAN 0x00a3 398c2ecf20Sopenharmony_ci#define I8K_SMM_GET_SPEED 0x02a3 408c2ecf20Sopenharmony_ci#define I8K_SMM_GET_FAN_TYPE 0x03a3 418c2ecf20Sopenharmony_ci#define I8K_SMM_GET_NOM_SPEED 0x04a3 428c2ecf20Sopenharmony_ci#define I8K_SMM_GET_TEMP 0x10a3 438c2ecf20Sopenharmony_ci#define I8K_SMM_GET_TEMP_TYPE 0x11a3 448c2ecf20Sopenharmony_ci#define I8K_SMM_GET_DELL_SIG1 0xfea3 458c2ecf20Sopenharmony_ci#define I8K_SMM_GET_DELL_SIG2 0xffa3 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define I8K_FAN_MULT 30 488c2ecf20Sopenharmony_ci#define I8K_FAN_MAX_RPM 30000 498c2ecf20Sopenharmony_ci#define I8K_MAX_TEMP 127 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define I8K_FN_NONE 0x00 528c2ecf20Sopenharmony_ci#define I8K_FN_UP 0x01 538c2ecf20Sopenharmony_ci#define I8K_FN_DOWN 0x02 548c2ecf20Sopenharmony_ci#define I8K_FN_MUTE 0x04 558c2ecf20Sopenharmony_ci#define I8K_FN_MASK 0x07 568c2ecf20Sopenharmony_ci#define I8K_FN_SHIFT 8 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define I8K_POWER_AC 0x05 598c2ecf20Sopenharmony_ci#define I8K_POWER_BATTERY 0x01 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(i8k_mutex); 628c2ecf20Sopenharmony_cistatic char bios_version[4]; 638c2ecf20Sopenharmony_cistatic char bios_machineid[16]; 648c2ecf20Sopenharmony_cistatic struct device *i8k_hwmon_dev; 658c2ecf20Sopenharmony_cistatic u32 i8k_hwmon_flags; 668c2ecf20Sopenharmony_cistatic uint i8k_fan_mult = I8K_FAN_MULT; 678c2ecf20Sopenharmony_cistatic uint i8k_pwm_mult; 688c2ecf20Sopenharmony_cistatic uint i8k_fan_max = I8K_FAN_HIGH; 698c2ecf20Sopenharmony_cistatic bool disallow_fan_type_call; 708c2ecf20Sopenharmony_cistatic bool disallow_fan_support; 718c2ecf20Sopenharmony_cistatic unsigned int manual_fan; 728c2ecf20Sopenharmony_cistatic unsigned int auto_fan; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#define I8K_HWMON_HAVE_TEMP1 (1 << 0) 758c2ecf20Sopenharmony_ci#define I8K_HWMON_HAVE_TEMP2 (1 << 1) 768c2ecf20Sopenharmony_ci#define I8K_HWMON_HAVE_TEMP3 (1 << 2) 778c2ecf20Sopenharmony_ci#define I8K_HWMON_HAVE_TEMP4 (1 << 3) 788c2ecf20Sopenharmony_ci#define I8K_HWMON_HAVE_TEMP5 (1 << 4) 798c2ecf20Sopenharmony_ci#define I8K_HWMON_HAVE_TEMP6 (1 << 5) 808c2ecf20Sopenharmony_ci#define I8K_HWMON_HAVE_TEMP7 (1 << 6) 818c2ecf20Sopenharmony_ci#define I8K_HWMON_HAVE_TEMP8 (1 << 7) 828c2ecf20Sopenharmony_ci#define I8K_HWMON_HAVE_TEMP9 (1 << 8) 838c2ecf20Sopenharmony_ci#define I8K_HWMON_HAVE_TEMP10 (1 << 9) 848c2ecf20Sopenharmony_ci#define I8K_HWMON_HAVE_FAN1 (1 << 10) 858c2ecf20Sopenharmony_ci#define I8K_HWMON_HAVE_FAN2 (1 << 11) 868c2ecf20Sopenharmony_ci#define I8K_HWMON_HAVE_FAN3 (1 << 12) 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ciMODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)"); 898c2ecf20Sopenharmony_ciMODULE_AUTHOR("Pali Rohár <pali@kernel.org>"); 908c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Dell laptop SMM BIOS hwmon driver"); 918c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 928c2ecf20Sopenharmony_ciMODULE_ALIAS("i8k"); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic bool force; 958c2ecf20Sopenharmony_cimodule_param(force, bool, 0); 968c2ecf20Sopenharmony_ciMODULE_PARM_DESC(force, "Force loading without checking for supported models"); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic bool ignore_dmi; 998c2ecf20Sopenharmony_cimodule_param(ignore_dmi, bool, 0); 1008c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ignore_dmi, "Continue probing hardware even if DMI data does not match"); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_I8K) 1038c2ecf20Sopenharmony_cistatic bool restricted = true; 1048c2ecf20Sopenharmony_cimodule_param(restricted, bool, 0); 1058c2ecf20Sopenharmony_ciMODULE_PARM_DESC(restricted, "Restrict fan control and serial number to CAP_SYS_ADMIN (default: 1)"); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic bool power_status; 1088c2ecf20Sopenharmony_cimodule_param(power_status, bool, 0600); 1098c2ecf20Sopenharmony_ciMODULE_PARM_DESC(power_status, "Report power status in /proc/i8k (default: 0)"); 1108c2ecf20Sopenharmony_ci#endif 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic uint fan_mult; 1138c2ecf20Sopenharmony_cimodule_param(fan_mult, uint, 0); 1148c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with (default: autodetect)"); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic uint fan_max; 1178c2ecf20Sopenharmony_cimodule_param(fan_max, uint, 0); 1188c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fan_max, "Maximum configurable fan speed (default: autodetect)"); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistruct smm_regs { 1218c2ecf20Sopenharmony_ci unsigned int eax; 1228c2ecf20Sopenharmony_ci unsigned int ebx __packed; 1238c2ecf20Sopenharmony_ci unsigned int ecx __packed; 1248c2ecf20Sopenharmony_ci unsigned int edx __packed; 1258c2ecf20Sopenharmony_ci unsigned int esi __packed; 1268c2ecf20Sopenharmony_ci unsigned int edi __packed; 1278c2ecf20Sopenharmony_ci}; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic inline const char *i8k_get_dmi_data(int field) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci const char *dmi_data = dmi_get_system_info(field); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci return dmi_data && *dmi_data ? dmi_data : "?"; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci/* 1378c2ecf20Sopenharmony_ci * Call the System Management Mode BIOS. Code provided by Jonathan Buzzard. 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_cistatic int i8k_smm_func(void *par) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci int rc; 1428c2ecf20Sopenharmony_ci struct smm_regs *regs = par; 1438c2ecf20Sopenharmony_ci int eax = regs->eax; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci#ifdef DEBUG 1468c2ecf20Sopenharmony_ci int ebx = regs->ebx; 1478c2ecf20Sopenharmony_ci unsigned long duration; 1488c2ecf20Sopenharmony_ci ktime_t calltime, delta, rettime; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci calltime = ktime_get(); 1518c2ecf20Sopenharmony_ci#endif 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* SMM requires CPU 0 */ 1548c2ecf20Sopenharmony_ci if (smp_processor_id() != 0) 1558c2ecf20Sopenharmony_ci return -EBUSY; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci#if defined(CONFIG_X86_64) 1588c2ecf20Sopenharmony_ci asm volatile("pushq %%rax\n\t" 1598c2ecf20Sopenharmony_ci "movl 0(%%rax),%%edx\n\t" 1608c2ecf20Sopenharmony_ci "pushq %%rdx\n\t" 1618c2ecf20Sopenharmony_ci "movl 4(%%rax),%%ebx\n\t" 1628c2ecf20Sopenharmony_ci "movl 8(%%rax),%%ecx\n\t" 1638c2ecf20Sopenharmony_ci "movl 12(%%rax),%%edx\n\t" 1648c2ecf20Sopenharmony_ci "movl 16(%%rax),%%esi\n\t" 1658c2ecf20Sopenharmony_ci "movl 20(%%rax),%%edi\n\t" 1668c2ecf20Sopenharmony_ci "popq %%rax\n\t" 1678c2ecf20Sopenharmony_ci "out %%al,$0xb2\n\t" 1688c2ecf20Sopenharmony_ci "out %%al,$0x84\n\t" 1698c2ecf20Sopenharmony_ci "xchgq %%rax,(%%rsp)\n\t" 1708c2ecf20Sopenharmony_ci "movl %%ebx,4(%%rax)\n\t" 1718c2ecf20Sopenharmony_ci "movl %%ecx,8(%%rax)\n\t" 1728c2ecf20Sopenharmony_ci "movl %%edx,12(%%rax)\n\t" 1738c2ecf20Sopenharmony_ci "movl %%esi,16(%%rax)\n\t" 1748c2ecf20Sopenharmony_ci "movl %%edi,20(%%rax)\n\t" 1758c2ecf20Sopenharmony_ci "popq %%rdx\n\t" 1768c2ecf20Sopenharmony_ci "movl %%edx,0(%%rax)\n\t" 1778c2ecf20Sopenharmony_ci "pushfq\n\t" 1788c2ecf20Sopenharmony_ci "popq %%rax\n\t" 1798c2ecf20Sopenharmony_ci "andl $1,%%eax\n" 1808c2ecf20Sopenharmony_ci : "=a"(rc) 1818c2ecf20Sopenharmony_ci : "a"(regs) 1828c2ecf20Sopenharmony_ci : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); 1838c2ecf20Sopenharmony_ci#else 1848c2ecf20Sopenharmony_ci asm volatile("pushl %%eax\n\t" 1858c2ecf20Sopenharmony_ci "movl 0(%%eax),%%edx\n\t" 1868c2ecf20Sopenharmony_ci "push %%edx\n\t" 1878c2ecf20Sopenharmony_ci "movl 4(%%eax),%%ebx\n\t" 1888c2ecf20Sopenharmony_ci "movl 8(%%eax),%%ecx\n\t" 1898c2ecf20Sopenharmony_ci "movl 12(%%eax),%%edx\n\t" 1908c2ecf20Sopenharmony_ci "movl 16(%%eax),%%esi\n\t" 1918c2ecf20Sopenharmony_ci "movl 20(%%eax),%%edi\n\t" 1928c2ecf20Sopenharmony_ci "popl %%eax\n\t" 1938c2ecf20Sopenharmony_ci "out %%al,$0xb2\n\t" 1948c2ecf20Sopenharmony_ci "out %%al,$0x84\n\t" 1958c2ecf20Sopenharmony_ci "xchgl %%eax,(%%esp)\n\t" 1968c2ecf20Sopenharmony_ci "movl %%ebx,4(%%eax)\n\t" 1978c2ecf20Sopenharmony_ci "movl %%ecx,8(%%eax)\n\t" 1988c2ecf20Sopenharmony_ci "movl %%edx,12(%%eax)\n\t" 1998c2ecf20Sopenharmony_ci "movl %%esi,16(%%eax)\n\t" 2008c2ecf20Sopenharmony_ci "movl %%edi,20(%%eax)\n\t" 2018c2ecf20Sopenharmony_ci "popl %%edx\n\t" 2028c2ecf20Sopenharmony_ci "movl %%edx,0(%%eax)\n\t" 2038c2ecf20Sopenharmony_ci "lahf\n\t" 2048c2ecf20Sopenharmony_ci "shrl $8,%%eax\n\t" 2058c2ecf20Sopenharmony_ci "andl $1,%%eax\n" 2068c2ecf20Sopenharmony_ci : "=a"(rc) 2078c2ecf20Sopenharmony_ci : "a"(regs) 2088c2ecf20Sopenharmony_ci : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); 2098c2ecf20Sopenharmony_ci#endif 2108c2ecf20Sopenharmony_ci if (rc != 0 || (regs->eax & 0xffff) == 0xffff || regs->eax == eax) 2118c2ecf20Sopenharmony_ci rc = -EINVAL; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci#ifdef DEBUG 2148c2ecf20Sopenharmony_ci rettime = ktime_get(); 2158c2ecf20Sopenharmony_ci delta = ktime_sub(rettime, calltime); 2168c2ecf20Sopenharmony_ci duration = ktime_to_ns(delta) >> 10; 2178c2ecf20Sopenharmony_ci pr_debug("smm(0x%.4x 0x%.4x) = 0x%.4x (took %7lu usecs)\n", eax, ebx, 2188c2ecf20Sopenharmony_ci (rc ? 0xffff : regs->eax & 0xffff), duration); 2198c2ecf20Sopenharmony_ci#endif 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return rc; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci/* 2258c2ecf20Sopenharmony_ci * Call the System Management Mode BIOS. 2268c2ecf20Sopenharmony_ci */ 2278c2ecf20Sopenharmony_cistatic int i8k_smm(struct smm_regs *regs) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci int ret; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci get_online_cpus(); 2328c2ecf20Sopenharmony_ci ret = smp_call_on_cpu(0, i8k_smm_func, regs, true); 2338c2ecf20Sopenharmony_ci put_online_cpus(); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return ret; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci/* 2398c2ecf20Sopenharmony_ci * Read the fan status. 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_cistatic int i8k_get_fan_status(int fan) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct smm_regs regs = { .eax = I8K_SMM_GET_FAN, }; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (disallow_fan_support) 2468c2ecf20Sopenharmony_ci return -EINVAL; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci regs.ebx = fan & 0xff; 2498c2ecf20Sopenharmony_ci return i8k_smm(®s) ? : regs.eax & 0xff; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci/* 2538c2ecf20Sopenharmony_ci * Read the fan speed in RPM. 2548c2ecf20Sopenharmony_ci */ 2558c2ecf20Sopenharmony_cistatic int i8k_get_fan_speed(int fan) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct smm_regs regs = { .eax = I8K_SMM_GET_SPEED, }; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (disallow_fan_support) 2608c2ecf20Sopenharmony_ci return -EINVAL; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci regs.ebx = fan & 0xff; 2638c2ecf20Sopenharmony_ci return i8k_smm(®s) ? : (regs.eax & 0xffff) * i8k_fan_mult; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci/* 2678c2ecf20Sopenharmony_ci * Read the fan type. 2688c2ecf20Sopenharmony_ci */ 2698c2ecf20Sopenharmony_cistatic int _i8k_get_fan_type(int fan) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct smm_regs regs = { .eax = I8K_SMM_GET_FAN_TYPE, }; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci if (disallow_fan_support || disallow_fan_type_call) 2748c2ecf20Sopenharmony_ci return -EINVAL; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci regs.ebx = fan & 0xff; 2778c2ecf20Sopenharmony_ci return i8k_smm(®s) ? : regs.eax & 0xff; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic int i8k_get_fan_type(int fan) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci /* I8K_SMM_GET_FAN_TYPE SMM call is expensive, so cache values */ 2838c2ecf20Sopenharmony_ci static int types[3] = { INT_MIN, INT_MIN, INT_MIN }; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (types[fan] == INT_MIN) 2868c2ecf20Sopenharmony_ci types[fan] = _i8k_get_fan_type(fan); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return types[fan]; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci/* 2928c2ecf20Sopenharmony_ci * Read the fan nominal rpm for specific fan speed. 2938c2ecf20Sopenharmony_ci */ 2948c2ecf20Sopenharmony_cistatic int i8k_get_fan_nominal_speed(int fan, int speed) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci struct smm_regs regs = { .eax = I8K_SMM_GET_NOM_SPEED, }; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (disallow_fan_support) 2998c2ecf20Sopenharmony_ci return -EINVAL; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci regs.ebx = (fan & 0xff) | (speed << 8); 3028c2ecf20Sopenharmony_ci return i8k_smm(®s) ? : (regs.eax & 0xffff) * i8k_fan_mult; 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci/* 3068c2ecf20Sopenharmony_ci * Enable or disable automatic BIOS fan control support 3078c2ecf20Sopenharmony_ci */ 3088c2ecf20Sopenharmony_cistatic int i8k_enable_fan_auto_mode(bool enable) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci struct smm_regs regs = { }; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (disallow_fan_support) 3138c2ecf20Sopenharmony_ci return -EINVAL; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci regs.eax = enable ? auto_fan : manual_fan; 3168c2ecf20Sopenharmony_ci return i8k_smm(®s); 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci/* 3208c2ecf20Sopenharmony_ci * Set the fan speed (off, low, high, ...). 3218c2ecf20Sopenharmony_ci */ 3228c2ecf20Sopenharmony_cistatic int i8k_set_fan(int fan, int speed) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci struct smm_regs regs = { .eax = I8K_SMM_SET_FAN, }; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (disallow_fan_support) 3278c2ecf20Sopenharmony_ci return -EINVAL; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci speed = (speed < 0) ? 0 : ((speed > i8k_fan_max) ? i8k_fan_max : speed); 3308c2ecf20Sopenharmony_ci regs.ebx = (fan & 0xff) | (speed << 8); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci return i8k_smm(®s); 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic int i8k_get_temp_type(int sensor) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci struct smm_regs regs = { .eax = I8K_SMM_GET_TEMP_TYPE, }; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci regs.ebx = sensor & 0xff; 3408c2ecf20Sopenharmony_ci return i8k_smm(®s) ? : regs.eax & 0xff; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci/* 3448c2ecf20Sopenharmony_ci * Read the cpu temperature. 3458c2ecf20Sopenharmony_ci */ 3468c2ecf20Sopenharmony_cistatic int _i8k_get_temp(int sensor) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci struct smm_regs regs = { 3498c2ecf20Sopenharmony_ci .eax = I8K_SMM_GET_TEMP, 3508c2ecf20Sopenharmony_ci .ebx = sensor & 0xff, 3518c2ecf20Sopenharmony_ci }; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci return i8k_smm(®s) ? : regs.eax & 0xff; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic int i8k_get_temp(int sensor) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci int temp = _i8k_get_temp(sensor); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci /* 3618c2ecf20Sopenharmony_ci * Sometimes the temperature sensor returns 0x99, which is out of range. 3628c2ecf20Sopenharmony_ci * In this case we retry (once) before returning an error. 3638c2ecf20Sopenharmony_ci # 1003655137 00000058 00005a4b 3648c2ecf20Sopenharmony_ci # 1003655138 00000099 00003a80 <--- 0x99 = 153 degrees 3658c2ecf20Sopenharmony_ci # 1003655139 00000054 00005c52 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_ci if (temp == 0x99) { 3688c2ecf20Sopenharmony_ci msleep(100); 3698c2ecf20Sopenharmony_ci temp = _i8k_get_temp(sensor); 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci /* 3728c2ecf20Sopenharmony_ci * Return -ENODATA for all invalid temperatures. 3738c2ecf20Sopenharmony_ci * 3748c2ecf20Sopenharmony_ci * Known instances are the 0x99 value as seen above as well as 3758c2ecf20Sopenharmony_ci * 0xc1 (193), which may be returned when trying to read the GPU 3768c2ecf20Sopenharmony_ci * temperature if the system supports a GPU and it is currently 3778c2ecf20Sopenharmony_ci * turned off. 3788c2ecf20Sopenharmony_ci */ 3798c2ecf20Sopenharmony_ci if (temp > I8K_MAX_TEMP) 3808c2ecf20Sopenharmony_ci return -ENODATA; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci return temp; 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic int i8k_get_dell_signature(int req_fn) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci struct smm_regs regs = { .eax = req_fn, }; 3888c2ecf20Sopenharmony_ci int rc; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci rc = i8k_smm(®s); 3918c2ecf20Sopenharmony_ci if (rc < 0) 3928c2ecf20Sopenharmony_ci return rc; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci return regs.eax == 1145651527 && regs.edx == 1145392204 ? 0 : -1; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_I8K) 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci/* 4008c2ecf20Sopenharmony_ci * Read the Fn key status. 4018c2ecf20Sopenharmony_ci */ 4028c2ecf20Sopenharmony_cistatic int i8k_get_fn_status(void) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci struct smm_regs regs = { .eax = I8K_SMM_FN_STATUS, }; 4058c2ecf20Sopenharmony_ci int rc; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci rc = i8k_smm(®s); 4088c2ecf20Sopenharmony_ci if (rc < 0) 4098c2ecf20Sopenharmony_ci return rc; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci switch ((regs.eax >> I8K_FN_SHIFT) & I8K_FN_MASK) { 4128c2ecf20Sopenharmony_ci case I8K_FN_UP: 4138c2ecf20Sopenharmony_ci return I8K_VOL_UP; 4148c2ecf20Sopenharmony_ci case I8K_FN_DOWN: 4158c2ecf20Sopenharmony_ci return I8K_VOL_DOWN; 4168c2ecf20Sopenharmony_ci case I8K_FN_MUTE: 4178c2ecf20Sopenharmony_ci return I8K_VOL_MUTE; 4188c2ecf20Sopenharmony_ci default: 4198c2ecf20Sopenharmony_ci return 0; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci/* 4248c2ecf20Sopenharmony_ci * Read the power status. 4258c2ecf20Sopenharmony_ci */ 4268c2ecf20Sopenharmony_cistatic int i8k_get_power_status(void) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci struct smm_regs regs = { .eax = I8K_SMM_POWER_STATUS, }; 4298c2ecf20Sopenharmony_ci int rc; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci rc = i8k_smm(®s); 4328c2ecf20Sopenharmony_ci if (rc < 0) 4338c2ecf20Sopenharmony_ci return rc; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci return (regs.eax & 0xff) == I8K_POWER_AC ? I8K_AC : I8K_BATTERY; 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci/* 4398c2ecf20Sopenharmony_ci * Procfs interface 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic int 4438c2ecf20Sopenharmony_cii8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci int val = 0; 4468c2ecf20Sopenharmony_ci int speed, err; 4478c2ecf20Sopenharmony_ci unsigned char buff[16]; 4488c2ecf20Sopenharmony_ci int __user *argp = (int __user *)arg; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (!argp) 4518c2ecf20Sopenharmony_ci return -EINVAL; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci switch (cmd) { 4548c2ecf20Sopenharmony_ci case I8K_BIOS_VERSION: 4558c2ecf20Sopenharmony_ci if (!isdigit(bios_version[0]) || !isdigit(bios_version[1]) || 4568c2ecf20Sopenharmony_ci !isdigit(bios_version[2])) 4578c2ecf20Sopenharmony_ci return -EINVAL; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci val = (bios_version[0] << 16) | 4608c2ecf20Sopenharmony_ci (bios_version[1] << 8) | bios_version[2]; 4618c2ecf20Sopenharmony_ci break; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci case I8K_MACHINE_ID: 4648c2ecf20Sopenharmony_ci if (restricted && !capable(CAP_SYS_ADMIN)) 4658c2ecf20Sopenharmony_ci return -EPERM; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci memset(buff, 0, sizeof(buff)); 4688c2ecf20Sopenharmony_ci strlcpy(buff, bios_machineid, sizeof(buff)); 4698c2ecf20Sopenharmony_ci break; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci case I8K_FN_STATUS: 4728c2ecf20Sopenharmony_ci val = i8k_get_fn_status(); 4738c2ecf20Sopenharmony_ci break; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci case I8K_POWER_STATUS: 4768c2ecf20Sopenharmony_ci val = i8k_get_power_status(); 4778c2ecf20Sopenharmony_ci break; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci case I8K_GET_TEMP: 4808c2ecf20Sopenharmony_ci val = i8k_get_temp(0); 4818c2ecf20Sopenharmony_ci break; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci case I8K_GET_SPEED: 4848c2ecf20Sopenharmony_ci if (copy_from_user(&val, argp, sizeof(int))) 4858c2ecf20Sopenharmony_ci return -EFAULT; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci val = i8k_get_fan_speed(val); 4888c2ecf20Sopenharmony_ci break; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci case I8K_GET_FAN: 4918c2ecf20Sopenharmony_ci if (copy_from_user(&val, argp, sizeof(int))) 4928c2ecf20Sopenharmony_ci return -EFAULT; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci val = i8k_get_fan_status(val); 4958c2ecf20Sopenharmony_ci break; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci case I8K_SET_FAN: 4988c2ecf20Sopenharmony_ci if (restricted && !capable(CAP_SYS_ADMIN)) 4998c2ecf20Sopenharmony_ci return -EPERM; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci if (copy_from_user(&val, argp, sizeof(int))) 5028c2ecf20Sopenharmony_ci return -EFAULT; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (copy_from_user(&speed, argp + 1, sizeof(int))) 5058c2ecf20Sopenharmony_ci return -EFAULT; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci err = i8k_set_fan(val, speed); 5088c2ecf20Sopenharmony_ci if (err < 0) 5098c2ecf20Sopenharmony_ci return err; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci val = i8k_get_fan_status(val); 5128c2ecf20Sopenharmony_ci break; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci default: 5158c2ecf20Sopenharmony_ci return -EINVAL; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci if (val < 0) 5198c2ecf20Sopenharmony_ci return val; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci switch (cmd) { 5228c2ecf20Sopenharmony_ci case I8K_BIOS_VERSION: 5238c2ecf20Sopenharmony_ci if (copy_to_user(argp, &val, 4)) 5248c2ecf20Sopenharmony_ci return -EFAULT; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci break; 5278c2ecf20Sopenharmony_ci case I8K_MACHINE_ID: 5288c2ecf20Sopenharmony_ci if (copy_to_user(argp, buff, 16)) 5298c2ecf20Sopenharmony_ci return -EFAULT; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci break; 5328c2ecf20Sopenharmony_ci default: 5338c2ecf20Sopenharmony_ci if (copy_to_user(argp, &val, sizeof(int))) 5348c2ecf20Sopenharmony_ci return -EFAULT; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci break; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci return 0; 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci long ret; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci mutex_lock(&i8k_mutex); 5478c2ecf20Sopenharmony_ci ret = i8k_ioctl_unlocked(fp, cmd, arg); 5488c2ecf20Sopenharmony_ci mutex_unlock(&i8k_mutex); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci return ret; 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci/* 5548c2ecf20Sopenharmony_ci * Print the information for /proc/i8k. 5558c2ecf20Sopenharmony_ci */ 5568c2ecf20Sopenharmony_cistatic int i8k_proc_show(struct seq_file *seq, void *offset) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci int fn_key, cpu_temp, ac_power; 5598c2ecf20Sopenharmony_ci int left_fan, right_fan, left_speed, right_speed; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci cpu_temp = i8k_get_temp(0); /* 11100 µs */ 5628c2ecf20Sopenharmony_ci left_fan = i8k_get_fan_status(I8K_FAN_LEFT); /* 580 µs */ 5638c2ecf20Sopenharmony_ci right_fan = i8k_get_fan_status(I8K_FAN_RIGHT); /* 580 µs */ 5648c2ecf20Sopenharmony_ci left_speed = i8k_get_fan_speed(I8K_FAN_LEFT); /* 580 µs */ 5658c2ecf20Sopenharmony_ci right_speed = i8k_get_fan_speed(I8K_FAN_RIGHT); /* 580 µs */ 5668c2ecf20Sopenharmony_ci fn_key = i8k_get_fn_status(); /* 750 µs */ 5678c2ecf20Sopenharmony_ci if (power_status) 5688c2ecf20Sopenharmony_ci ac_power = i8k_get_power_status(); /* 14700 µs */ 5698c2ecf20Sopenharmony_ci else 5708c2ecf20Sopenharmony_ci ac_power = -1; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci /* 5738c2ecf20Sopenharmony_ci * Info: 5748c2ecf20Sopenharmony_ci * 5758c2ecf20Sopenharmony_ci * 1) Format version (this will change if format changes) 5768c2ecf20Sopenharmony_ci * 2) BIOS version 5778c2ecf20Sopenharmony_ci * 3) BIOS machine ID 5788c2ecf20Sopenharmony_ci * 4) Cpu temperature 5798c2ecf20Sopenharmony_ci * 5) Left fan status 5808c2ecf20Sopenharmony_ci * 6) Right fan status 5818c2ecf20Sopenharmony_ci * 7) Left fan speed 5828c2ecf20Sopenharmony_ci * 8) Right fan speed 5838c2ecf20Sopenharmony_ci * 9) AC power 5848c2ecf20Sopenharmony_ci * 10) Fn Key status 5858c2ecf20Sopenharmony_ci */ 5868c2ecf20Sopenharmony_ci seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n", 5878c2ecf20Sopenharmony_ci I8K_PROC_FMT, 5888c2ecf20Sopenharmony_ci bios_version, 5898c2ecf20Sopenharmony_ci (restricted && !capable(CAP_SYS_ADMIN)) ? "-1" : bios_machineid, 5908c2ecf20Sopenharmony_ci cpu_temp, 5918c2ecf20Sopenharmony_ci left_fan, right_fan, left_speed, right_speed, 5928c2ecf20Sopenharmony_ci ac_power, fn_key); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci return 0; 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_cistatic int i8k_open_fs(struct inode *inode, struct file *file) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci return single_open(file, i8k_proc_show, NULL); 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic const struct proc_ops i8k_proc_ops = { 6038c2ecf20Sopenharmony_ci .proc_open = i8k_open_fs, 6048c2ecf20Sopenharmony_ci .proc_read = seq_read, 6058c2ecf20Sopenharmony_ci .proc_lseek = seq_lseek, 6068c2ecf20Sopenharmony_ci .proc_release = single_release, 6078c2ecf20Sopenharmony_ci .proc_ioctl = i8k_ioctl, 6088c2ecf20Sopenharmony_ci}; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_cistatic struct proc_dir_entry *entry; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_cistatic void __init i8k_init_procfs(void) 6138c2ecf20Sopenharmony_ci{ 6148c2ecf20Sopenharmony_ci /* Register the proc entry */ 6158c2ecf20Sopenharmony_ci entry = proc_create("i8k", 0, NULL, &i8k_proc_ops); 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_cistatic void __exit i8k_exit_procfs(void) 6198c2ecf20Sopenharmony_ci{ 6208c2ecf20Sopenharmony_ci if (entry) 6218c2ecf20Sopenharmony_ci remove_proc_entry("i8k", NULL); 6228c2ecf20Sopenharmony_ci} 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci#else 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_cistatic inline void __init i8k_init_procfs(void) 6278c2ecf20Sopenharmony_ci{ 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic inline void __exit i8k_exit_procfs(void) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci} 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci#endif 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci/* 6378c2ecf20Sopenharmony_ci * Hwmon interface 6388c2ecf20Sopenharmony_ci */ 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_cistatic ssize_t i8k_hwmon_temp_label_show(struct device *dev, 6418c2ecf20Sopenharmony_ci struct device_attribute *devattr, 6428c2ecf20Sopenharmony_ci char *buf) 6438c2ecf20Sopenharmony_ci{ 6448c2ecf20Sopenharmony_ci static const char * const labels[] = { 6458c2ecf20Sopenharmony_ci "CPU", 6468c2ecf20Sopenharmony_ci "GPU", 6478c2ecf20Sopenharmony_ci "SODIMM", 6488c2ecf20Sopenharmony_ci "Other", 6498c2ecf20Sopenharmony_ci "Ambient", 6508c2ecf20Sopenharmony_ci "Other", 6518c2ecf20Sopenharmony_ci }; 6528c2ecf20Sopenharmony_ci int index = to_sensor_dev_attr(devattr)->index; 6538c2ecf20Sopenharmony_ci int type; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci type = i8k_get_temp_type(index); 6568c2ecf20Sopenharmony_ci if (type < 0) 6578c2ecf20Sopenharmony_ci return type; 6588c2ecf20Sopenharmony_ci if (type >= ARRAY_SIZE(labels)) 6598c2ecf20Sopenharmony_ci type = ARRAY_SIZE(labels) - 1; 6608c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", labels[type]); 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cistatic ssize_t i8k_hwmon_temp_show(struct device *dev, 6648c2ecf20Sopenharmony_ci struct device_attribute *devattr, 6658c2ecf20Sopenharmony_ci char *buf) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci int index = to_sensor_dev_attr(devattr)->index; 6688c2ecf20Sopenharmony_ci int temp; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci temp = i8k_get_temp(index); 6718c2ecf20Sopenharmony_ci if (temp < 0) 6728c2ecf20Sopenharmony_ci return temp; 6738c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", temp * 1000); 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cistatic ssize_t i8k_hwmon_fan_label_show(struct device *dev, 6778c2ecf20Sopenharmony_ci struct device_attribute *devattr, 6788c2ecf20Sopenharmony_ci char *buf) 6798c2ecf20Sopenharmony_ci{ 6808c2ecf20Sopenharmony_ci static const char * const labels[] = { 6818c2ecf20Sopenharmony_ci "Processor Fan", 6828c2ecf20Sopenharmony_ci "Motherboard Fan", 6838c2ecf20Sopenharmony_ci "Video Fan", 6848c2ecf20Sopenharmony_ci "Power Supply Fan", 6858c2ecf20Sopenharmony_ci "Chipset Fan", 6868c2ecf20Sopenharmony_ci "Other Fan", 6878c2ecf20Sopenharmony_ci }; 6888c2ecf20Sopenharmony_ci int index = to_sensor_dev_attr(devattr)->index; 6898c2ecf20Sopenharmony_ci bool dock = false; 6908c2ecf20Sopenharmony_ci int type; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci type = i8k_get_fan_type(index); 6938c2ecf20Sopenharmony_ci if (type < 0) 6948c2ecf20Sopenharmony_ci return type; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci if (type & 0x10) { 6978c2ecf20Sopenharmony_ci dock = true; 6988c2ecf20Sopenharmony_ci type &= 0x0F; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci if (type >= ARRAY_SIZE(labels)) 7028c2ecf20Sopenharmony_ci type = (ARRAY_SIZE(labels) - 1); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci return sprintf(buf, "%s%s\n", (dock ? "Docking " : ""), labels[type]); 7058c2ecf20Sopenharmony_ci} 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistatic ssize_t i8k_hwmon_fan_show(struct device *dev, 7088c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci int index = to_sensor_dev_attr(devattr)->index; 7118c2ecf20Sopenharmony_ci int fan_speed; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci fan_speed = i8k_get_fan_speed(index); 7148c2ecf20Sopenharmony_ci if (fan_speed < 0) 7158c2ecf20Sopenharmony_ci return fan_speed; 7168c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", fan_speed); 7178c2ecf20Sopenharmony_ci} 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_cistatic ssize_t i8k_hwmon_pwm_show(struct device *dev, 7208c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 7218c2ecf20Sopenharmony_ci{ 7228c2ecf20Sopenharmony_ci int index = to_sensor_dev_attr(devattr)->index; 7238c2ecf20Sopenharmony_ci int status; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci status = i8k_get_fan_status(index); 7268c2ecf20Sopenharmony_ci if (status < 0) 7278c2ecf20Sopenharmony_ci return -EIO; 7288c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", clamp_val(status * i8k_pwm_mult, 0, 255)); 7298c2ecf20Sopenharmony_ci} 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_cistatic ssize_t i8k_hwmon_pwm_store(struct device *dev, 7328c2ecf20Sopenharmony_ci struct device_attribute *attr, 7338c2ecf20Sopenharmony_ci const char *buf, size_t count) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci int index = to_sensor_dev_attr(attr)->index; 7368c2ecf20Sopenharmony_ci unsigned long val; 7378c2ecf20Sopenharmony_ci int err; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 7408c2ecf20Sopenharmony_ci if (err) 7418c2ecf20Sopenharmony_ci return err; 7428c2ecf20Sopenharmony_ci val = clamp_val(DIV_ROUND_CLOSEST(val, i8k_pwm_mult), 0, i8k_fan_max); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci mutex_lock(&i8k_mutex); 7458c2ecf20Sopenharmony_ci err = i8k_set_fan(index, val); 7468c2ecf20Sopenharmony_ci mutex_unlock(&i8k_mutex); 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci return err < 0 ? -EIO : count; 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_cistatic ssize_t i8k_hwmon_pwm_enable_store(struct device *dev, 7528c2ecf20Sopenharmony_ci struct device_attribute *attr, 7538c2ecf20Sopenharmony_ci const char *buf, size_t count) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci int err; 7568c2ecf20Sopenharmony_ci bool enable; 7578c2ecf20Sopenharmony_ci unsigned long val; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci if (!auto_fan) 7608c2ecf20Sopenharmony_ci return -ENODEV; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 7638c2ecf20Sopenharmony_ci if (err) 7648c2ecf20Sopenharmony_ci return err; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci if (val == 1) 7678c2ecf20Sopenharmony_ci enable = false; 7688c2ecf20Sopenharmony_ci else if (val == 2) 7698c2ecf20Sopenharmony_ci enable = true; 7708c2ecf20Sopenharmony_ci else 7718c2ecf20Sopenharmony_ci return -EINVAL; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci mutex_lock(&i8k_mutex); 7748c2ecf20Sopenharmony_ci err = i8k_enable_fan_auto_mode(enable); 7758c2ecf20Sopenharmony_ci mutex_unlock(&i8k_mutex); 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci return err ? err : count; 7788c2ecf20Sopenharmony_ci} 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_input, i8k_hwmon_temp, 0); 7818c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_label, i8k_hwmon_temp_label, 0); 7828c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_input, i8k_hwmon_temp, 1); 7838c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_label, i8k_hwmon_temp_label, 1); 7848c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp3_input, i8k_hwmon_temp, 2); 7858c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp3_label, i8k_hwmon_temp_label, 2); 7868c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp4_input, i8k_hwmon_temp, 3); 7878c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp4_label, i8k_hwmon_temp_label, 3); 7888c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp5_input, i8k_hwmon_temp, 4); 7898c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp5_label, i8k_hwmon_temp_label, 4); 7908c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp6_input, i8k_hwmon_temp, 5); 7918c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp6_label, i8k_hwmon_temp_label, 5); 7928c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp7_input, i8k_hwmon_temp, 6); 7938c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp7_label, i8k_hwmon_temp_label, 6); 7948c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp8_input, i8k_hwmon_temp, 7); 7958c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp8_label, i8k_hwmon_temp_label, 7); 7968c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp9_input, i8k_hwmon_temp, 8); 7978c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp9_label, i8k_hwmon_temp_label, 8); 7988c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp10_input, i8k_hwmon_temp, 9); 7998c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp10_label, i8k_hwmon_temp_label, 9); 8008c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan1_input, i8k_hwmon_fan, 0); 8018c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan1_label, i8k_hwmon_fan_label, 0); 8028c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(pwm1, i8k_hwmon_pwm, 0); 8038c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_WO(pwm1_enable, i8k_hwmon_pwm_enable, 0); 8048c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan2_input, i8k_hwmon_fan, 1); 8058c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan2_label, i8k_hwmon_fan_label, 1); 8068c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(pwm2, i8k_hwmon_pwm, 1); 8078c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan3_input, i8k_hwmon_fan, 2); 8088c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan3_label, i8k_hwmon_fan_label, 2); 8098c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(pwm3, i8k_hwmon_pwm, 2); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_cistatic struct attribute *i8k_attrs[] = { 8128c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_input.dev_attr.attr, /* 0 */ 8138c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_label.dev_attr.attr, /* 1 */ 8148c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_input.dev_attr.attr, /* 2 */ 8158c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_label.dev_attr.attr, /* 3 */ 8168c2ecf20Sopenharmony_ci &sensor_dev_attr_temp3_input.dev_attr.attr, /* 4 */ 8178c2ecf20Sopenharmony_ci &sensor_dev_attr_temp3_label.dev_attr.attr, /* 5 */ 8188c2ecf20Sopenharmony_ci &sensor_dev_attr_temp4_input.dev_attr.attr, /* 6 */ 8198c2ecf20Sopenharmony_ci &sensor_dev_attr_temp4_label.dev_attr.attr, /* 7 */ 8208c2ecf20Sopenharmony_ci &sensor_dev_attr_temp5_input.dev_attr.attr, /* 8 */ 8218c2ecf20Sopenharmony_ci &sensor_dev_attr_temp5_label.dev_attr.attr, /* 9 */ 8228c2ecf20Sopenharmony_ci &sensor_dev_attr_temp6_input.dev_attr.attr, /* 10 */ 8238c2ecf20Sopenharmony_ci &sensor_dev_attr_temp6_label.dev_attr.attr, /* 11 */ 8248c2ecf20Sopenharmony_ci &sensor_dev_attr_temp7_input.dev_attr.attr, /* 12 */ 8258c2ecf20Sopenharmony_ci &sensor_dev_attr_temp7_label.dev_attr.attr, /* 13 */ 8268c2ecf20Sopenharmony_ci &sensor_dev_attr_temp8_input.dev_attr.attr, /* 14 */ 8278c2ecf20Sopenharmony_ci &sensor_dev_attr_temp8_label.dev_attr.attr, /* 15 */ 8288c2ecf20Sopenharmony_ci &sensor_dev_attr_temp9_input.dev_attr.attr, /* 16 */ 8298c2ecf20Sopenharmony_ci &sensor_dev_attr_temp9_label.dev_attr.attr, /* 17 */ 8308c2ecf20Sopenharmony_ci &sensor_dev_attr_temp10_input.dev_attr.attr, /* 18 */ 8318c2ecf20Sopenharmony_ci &sensor_dev_attr_temp10_label.dev_attr.attr, /* 19 */ 8328c2ecf20Sopenharmony_ci &sensor_dev_attr_fan1_input.dev_attr.attr, /* 20 */ 8338c2ecf20Sopenharmony_ci &sensor_dev_attr_fan1_label.dev_attr.attr, /* 21 */ 8348c2ecf20Sopenharmony_ci &sensor_dev_attr_pwm1.dev_attr.attr, /* 22 */ 8358c2ecf20Sopenharmony_ci &sensor_dev_attr_pwm1_enable.dev_attr.attr, /* 23 */ 8368c2ecf20Sopenharmony_ci &sensor_dev_attr_fan2_input.dev_attr.attr, /* 24 */ 8378c2ecf20Sopenharmony_ci &sensor_dev_attr_fan2_label.dev_attr.attr, /* 25 */ 8388c2ecf20Sopenharmony_ci &sensor_dev_attr_pwm2.dev_attr.attr, /* 26 */ 8398c2ecf20Sopenharmony_ci &sensor_dev_attr_fan3_input.dev_attr.attr, /* 27 */ 8408c2ecf20Sopenharmony_ci &sensor_dev_attr_fan3_label.dev_attr.attr, /* 28 */ 8418c2ecf20Sopenharmony_ci &sensor_dev_attr_pwm3.dev_attr.attr, /* 29 */ 8428c2ecf20Sopenharmony_ci NULL 8438c2ecf20Sopenharmony_ci}; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_cistatic umode_t i8k_is_visible(struct kobject *kobj, struct attribute *attr, 8468c2ecf20Sopenharmony_ci int index) 8478c2ecf20Sopenharmony_ci{ 8488c2ecf20Sopenharmony_ci if (disallow_fan_support && index >= 20) 8498c2ecf20Sopenharmony_ci return 0; 8508c2ecf20Sopenharmony_ci if (disallow_fan_type_call && 8518c2ecf20Sopenharmony_ci (index == 21 || index == 25 || index == 28)) 8528c2ecf20Sopenharmony_ci return 0; 8538c2ecf20Sopenharmony_ci if (index >= 0 && index <= 1 && 8548c2ecf20Sopenharmony_ci !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP1)) 8558c2ecf20Sopenharmony_ci return 0; 8568c2ecf20Sopenharmony_ci if (index >= 2 && index <= 3 && 8578c2ecf20Sopenharmony_ci !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP2)) 8588c2ecf20Sopenharmony_ci return 0; 8598c2ecf20Sopenharmony_ci if (index >= 4 && index <= 5 && 8608c2ecf20Sopenharmony_ci !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP3)) 8618c2ecf20Sopenharmony_ci return 0; 8628c2ecf20Sopenharmony_ci if (index >= 6 && index <= 7 && 8638c2ecf20Sopenharmony_ci !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP4)) 8648c2ecf20Sopenharmony_ci return 0; 8658c2ecf20Sopenharmony_ci if (index >= 8 && index <= 9 && 8668c2ecf20Sopenharmony_ci !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP5)) 8678c2ecf20Sopenharmony_ci return 0; 8688c2ecf20Sopenharmony_ci if (index >= 10 && index <= 11 && 8698c2ecf20Sopenharmony_ci !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP6)) 8708c2ecf20Sopenharmony_ci return 0; 8718c2ecf20Sopenharmony_ci if (index >= 12 && index <= 13 && 8728c2ecf20Sopenharmony_ci !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP7)) 8738c2ecf20Sopenharmony_ci return 0; 8748c2ecf20Sopenharmony_ci if (index >= 14 && index <= 15 && 8758c2ecf20Sopenharmony_ci !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP8)) 8768c2ecf20Sopenharmony_ci return 0; 8778c2ecf20Sopenharmony_ci if (index >= 16 && index <= 17 && 8788c2ecf20Sopenharmony_ci !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP9)) 8798c2ecf20Sopenharmony_ci return 0; 8808c2ecf20Sopenharmony_ci if (index >= 18 && index <= 19 && 8818c2ecf20Sopenharmony_ci !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP10)) 8828c2ecf20Sopenharmony_ci return 0; 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci if (index >= 20 && index <= 23 && 8858c2ecf20Sopenharmony_ci !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN1)) 8868c2ecf20Sopenharmony_ci return 0; 8878c2ecf20Sopenharmony_ci if (index >= 24 && index <= 26 && 8888c2ecf20Sopenharmony_ci !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN2)) 8898c2ecf20Sopenharmony_ci return 0; 8908c2ecf20Sopenharmony_ci if (index >= 27 && index <= 29 && 8918c2ecf20Sopenharmony_ci !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN3)) 8928c2ecf20Sopenharmony_ci return 0; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci if (index == 23 && !auto_fan) 8958c2ecf20Sopenharmony_ci return 0; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci return attr->mode; 8988c2ecf20Sopenharmony_ci} 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_cistatic const struct attribute_group i8k_group = { 9018c2ecf20Sopenharmony_ci .attrs = i8k_attrs, 9028c2ecf20Sopenharmony_ci .is_visible = i8k_is_visible, 9038c2ecf20Sopenharmony_ci}; 9048c2ecf20Sopenharmony_ci__ATTRIBUTE_GROUPS(i8k); 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_cistatic int __init i8k_init_hwmon(void) 9078c2ecf20Sopenharmony_ci{ 9088c2ecf20Sopenharmony_ci int err; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci i8k_hwmon_flags = 0; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci /* CPU temperature attributes, if temperature type is OK */ 9138c2ecf20Sopenharmony_ci err = i8k_get_temp_type(0); 9148c2ecf20Sopenharmony_ci if (err >= 0) 9158c2ecf20Sopenharmony_ci i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP1; 9168c2ecf20Sopenharmony_ci /* check for additional temperature sensors */ 9178c2ecf20Sopenharmony_ci err = i8k_get_temp_type(1); 9188c2ecf20Sopenharmony_ci if (err >= 0) 9198c2ecf20Sopenharmony_ci i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP2; 9208c2ecf20Sopenharmony_ci err = i8k_get_temp_type(2); 9218c2ecf20Sopenharmony_ci if (err >= 0) 9228c2ecf20Sopenharmony_ci i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP3; 9238c2ecf20Sopenharmony_ci err = i8k_get_temp_type(3); 9248c2ecf20Sopenharmony_ci if (err >= 0) 9258c2ecf20Sopenharmony_ci i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP4; 9268c2ecf20Sopenharmony_ci err = i8k_get_temp_type(4); 9278c2ecf20Sopenharmony_ci if (err >= 0) 9288c2ecf20Sopenharmony_ci i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP5; 9298c2ecf20Sopenharmony_ci err = i8k_get_temp_type(5); 9308c2ecf20Sopenharmony_ci if (err >= 0) 9318c2ecf20Sopenharmony_ci i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP6; 9328c2ecf20Sopenharmony_ci err = i8k_get_temp_type(6); 9338c2ecf20Sopenharmony_ci if (err >= 0) 9348c2ecf20Sopenharmony_ci i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP7; 9358c2ecf20Sopenharmony_ci err = i8k_get_temp_type(7); 9368c2ecf20Sopenharmony_ci if (err >= 0) 9378c2ecf20Sopenharmony_ci i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP8; 9388c2ecf20Sopenharmony_ci err = i8k_get_temp_type(8); 9398c2ecf20Sopenharmony_ci if (err >= 0) 9408c2ecf20Sopenharmony_ci i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP9; 9418c2ecf20Sopenharmony_ci err = i8k_get_temp_type(9); 9428c2ecf20Sopenharmony_ci if (err >= 0) 9438c2ecf20Sopenharmony_ci i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP10; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci /* First fan attributes, if fan status or type is OK */ 9468c2ecf20Sopenharmony_ci err = i8k_get_fan_status(0); 9478c2ecf20Sopenharmony_ci if (err < 0) 9488c2ecf20Sopenharmony_ci err = i8k_get_fan_type(0); 9498c2ecf20Sopenharmony_ci if (err >= 0) 9508c2ecf20Sopenharmony_ci i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN1; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci /* Second fan attributes, if fan status or type is OK */ 9538c2ecf20Sopenharmony_ci err = i8k_get_fan_status(1); 9548c2ecf20Sopenharmony_ci if (err < 0) 9558c2ecf20Sopenharmony_ci err = i8k_get_fan_type(1); 9568c2ecf20Sopenharmony_ci if (err >= 0) 9578c2ecf20Sopenharmony_ci i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN2; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci /* Third fan attributes, if fan status or type is OK */ 9608c2ecf20Sopenharmony_ci err = i8k_get_fan_status(2); 9618c2ecf20Sopenharmony_ci if (err < 0) 9628c2ecf20Sopenharmony_ci err = i8k_get_fan_type(2); 9638c2ecf20Sopenharmony_ci if (err >= 0) 9648c2ecf20Sopenharmony_ci i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN3; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci i8k_hwmon_dev = hwmon_device_register_with_groups(NULL, "dell_smm", 9678c2ecf20Sopenharmony_ci NULL, i8k_groups); 9688c2ecf20Sopenharmony_ci if (IS_ERR(i8k_hwmon_dev)) { 9698c2ecf20Sopenharmony_ci err = PTR_ERR(i8k_hwmon_dev); 9708c2ecf20Sopenharmony_ci i8k_hwmon_dev = NULL; 9718c2ecf20Sopenharmony_ci pr_err("hwmon registration failed (%d)\n", err); 9728c2ecf20Sopenharmony_ci return err; 9738c2ecf20Sopenharmony_ci } 9748c2ecf20Sopenharmony_ci return 0; 9758c2ecf20Sopenharmony_ci} 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_cistruct i8k_config_data { 9788c2ecf20Sopenharmony_ci uint fan_mult; 9798c2ecf20Sopenharmony_ci uint fan_max; 9808c2ecf20Sopenharmony_ci}; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_cienum i8k_configs { 9838c2ecf20Sopenharmony_ci DELL_LATITUDE_D520, 9848c2ecf20Sopenharmony_ci DELL_PRECISION_490, 9858c2ecf20Sopenharmony_ci DELL_STUDIO, 9868c2ecf20Sopenharmony_ci DELL_XPS, 9878c2ecf20Sopenharmony_ci}; 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_cistatic const struct i8k_config_data i8k_config_data[] = { 9908c2ecf20Sopenharmony_ci [DELL_LATITUDE_D520] = { 9918c2ecf20Sopenharmony_ci .fan_mult = 1, 9928c2ecf20Sopenharmony_ci .fan_max = I8K_FAN_TURBO, 9938c2ecf20Sopenharmony_ci }, 9948c2ecf20Sopenharmony_ci [DELL_PRECISION_490] = { 9958c2ecf20Sopenharmony_ci .fan_mult = 1, 9968c2ecf20Sopenharmony_ci .fan_max = I8K_FAN_TURBO, 9978c2ecf20Sopenharmony_ci }, 9988c2ecf20Sopenharmony_ci [DELL_STUDIO] = { 9998c2ecf20Sopenharmony_ci .fan_mult = 1, 10008c2ecf20Sopenharmony_ci .fan_max = I8K_FAN_HIGH, 10018c2ecf20Sopenharmony_ci }, 10028c2ecf20Sopenharmony_ci [DELL_XPS] = { 10038c2ecf20Sopenharmony_ci .fan_mult = 1, 10048c2ecf20Sopenharmony_ci .fan_max = I8K_FAN_HIGH, 10058c2ecf20Sopenharmony_ci }, 10068c2ecf20Sopenharmony_ci}; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_cistatic const struct dmi_system_id i8k_dmi_table[] __initconst = { 10098c2ecf20Sopenharmony_ci { 10108c2ecf20Sopenharmony_ci .ident = "Dell Inspiron", 10118c2ecf20Sopenharmony_ci .matches = { 10128c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"), 10138c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"), 10148c2ecf20Sopenharmony_ci }, 10158c2ecf20Sopenharmony_ci }, 10168c2ecf20Sopenharmony_ci { 10178c2ecf20Sopenharmony_ci .ident = "Dell Latitude", 10188c2ecf20Sopenharmony_ci .matches = { 10198c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"), 10208c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"), 10218c2ecf20Sopenharmony_ci }, 10228c2ecf20Sopenharmony_ci }, 10238c2ecf20Sopenharmony_ci { 10248c2ecf20Sopenharmony_ci .ident = "Dell Inspiron 2", 10258c2ecf20Sopenharmony_ci .matches = { 10268c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 10278c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"), 10288c2ecf20Sopenharmony_ci }, 10298c2ecf20Sopenharmony_ci }, 10308c2ecf20Sopenharmony_ci { 10318c2ecf20Sopenharmony_ci .ident = "Dell Latitude D520", 10328c2ecf20Sopenharmony_ci .matches = { 10338c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 10348c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D520"), 10358c2ecf20Sopenharmony_ci }, 10368c2ecf20Sopenharmony_ci .driver_data = (void *)&i8k_config_data[DELL_LATITUDE_D520], 10378c2ecf20Sopenharmony_ci }, 10388c2ecf20Sopenharmony_ci { 10398c2ecf20Sopenharmony_ci .ident = "Dell Latitude 2", 10408c2ecf20Sopenharmony_ci .matches = { 10418c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 10428c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"), 10438c2ecf20Sopenharmony_ci }, 10448c2ecf20Sopenharmony_ci }, 10458c2ecf20Sopenharmony_ci { /* UK Inspiron 6400 */ 10468c2ecf20Sopenharmony_ci .ident = "Dell Inspiron 3", 10478c2ecf20Sopenharmony_ci .matches = { 10488c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 10498c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "MM061"), 10508c2ecf20Sopenharmony_ci }, 10518c2ecf20Sopenharmony_ci }, 10528c2ecf20Sopenharmony_ci { 10538c2ecf20Sopenharmony_ci .ident = "Dell Inspiron 3", 10548c2ecf20Sopenharmony_ci .matches = { 10558c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 10568c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "MP061"), 10578c2ecf20Sopenharmony_ci }, 10588c2ecf20Sopenharmony_ci }, 10598c2ecf20Sopenharmony_ci { 10608c2ecf20Sopenharmony_ci .ident = "Dell Precision 490", 10618c2ecf20Sopenharmony_ci .matches = { 10628c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 10638c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, 10648c2ecf20Sopenharmony_ci "Precision WorkStation 490"), 10658c2ecf20Sopenharmony_ci }, 10668c2ecf20Sopenharmony_ci .driver_data = (void *)&i8k_config_data[DELL_PRECISION_490], 10678c2ecf20Sopenharmony_ci }, 10688c2ecf20Sopenharmony_ci { 10698c2ecf20Sopenharmony_ci .ident = "Dell Precision", 10708c2ecf20Sopenharmony_ci .matches = { 10718c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 10728c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Precision"), 10738c2ecf20Sopenharmony_ci }, 10748c2ecf20Sopenharmony_ci }, 10758c2ecf20Sopenharmony_ci { 10768c2ecf20Sopenharmony_ci .ident = "Dell Vostro", 10778c2ecf20Sopenharmony_ci .matches = { 10788c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 10798c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Vostro"), 10808c2ecf20Sopenharmony_ci }, 10818c2ecf20Sopenharmony_ci }, 10828c2ecf20Sopenharmony_ci { 10838c2ecf20Sopenharmony_ci .ident = "Dell Studio", 10848c2ecf20Sopenharmony_ci .matches = { 10858c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 10868c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Studio"), 10878c2ecf20Sopenharmony_ci }, 10888c2ecf20Sopenharmony_ci .driver_data = (void *)&i8k_config_data[DELL_STUDIO], 10898c2ecf20Sopenharmony_ci }, 10908c2ecf20Sopenharmony_ci { 10918c2ecf20Sopenharmony_ci .ident = "Dell XPS M140", 10928c2ecf20Sopenharmony_ci .matches = { 10938c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 10948c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "MXC051"), 10958c2ecf20Sopenharmony_ci }, 10968c2ecf20Sopenharmony_ci .driver_data = (void *)&i8k_config_data[DELL_XPS], 10978c2ecf20Sopenharmony_ci }, 10988c2ecf20Sopenharmony_ci { 10998c2ecf20Sopenharmony_ci .ident = "Dell XPS", 11008c2ecf20Sopenharmony_ci .matches = { 11018c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 11028c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "XPS"), 11038c2ecf20Sopenharmony_ci }, 11048c2ecf20Sopenharmony_ci }, 11058c2ecf20Sopenharmony_ci { } 11068c2ecf20Sopenharmony_ci}; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(dmi, i8k_dmi_table); 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci/* 11118c2ecf20Sopenharmony_ci * On some machines once I8K_SMM_GET_FAN_TYPE is issued then CPU fan speed 11128c2ecf20Sopenharmony_ci * randomly going up and down due to bug in Dell SMM or BIOS. Here is blacklist 11138c2ecf20Sopenharmony_ci * of affected Dell machines for which we disallow I8K_SMM_GET_FAN_TYPE call. 11148c2ecf20Sopenharmony_ci * See bug: https://bugzilla.kernel.org/show_bug.cgi?id=100121 11158c2ecf20Sopenharmony_ci */ 11168c2ecf20Sopenharmony_cistatic const struct dmi_system_id i8k_blacklist_fan_type_dmi_table[] __initconst = { 11178c2ecf20Sopenharmony_ci { 11188c2ecf20Sopenharmony_ci .ident = "Dell Studio XPS 8000", 11198c2ecf20Sopenharmony_ci .matches = { 11208c2ecf20Sopenharmony_ci DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 11218c2ecf20Sopenharmony_ci DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Studio XPS 8000"), 11228c2ecf20Sopenharmony_ci }, 11238c2ecf20Sopenharmony_ci }, 11248c2ecf20Sopenharmony_ci { 11258c2ecf20Sopenharmony_ci .ident = "Dell Studio XPS 8100", 11268c2ecf20Sopenharmony_ci .matches = { 11278c2ecf20Sopenharmony_ci DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 11288c2ecf20Sopenharmony_ci DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Studio XPS 8100"), 11298c2ecf20Sopenharmony_ci }, 11308c2ecf20Sopenharmony_ci }, 11318c2ecf20Sopenharmony_ci { 11328c2ecf20Sopenharmony_ci .ident = "Dell Inspiron 580", 11338c2ecf20Sopenharmony_ci .matches = { 11348c2ecf20Sopenharmony_ci DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 11358c2ecf20Sopenharmony_ci DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Inspiron 580 "), 11368c2ecf20Sopenharmony_ci }, 11378c2ecf20Sopenharmony_ci }, 11388c2ecf20Sopenharmony_ci { } 11398c2ecf20Sopenharmony_ci}; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci/* 11428c2ecf20Sopenharmony_ci * On some machines all fan related SMM functions implemented by Dell BIOS 11438c2ecf20Sopenharmony_ci * firmware freeze kernel for about 500ms. Until Dell fixes these problems fan 11448c2ecf20Sopenharmony_ci * support for affected blacklisted Dell machines stay disabled. 11458c2ecf20Sopenharmony_ci * See bug: https://bugzilla.kernel.org/show_bug.cgi?id=195751 11468c2ecf20Sopenharmony_ci */ 11478c2ecf20Sopenharmony_cistatic struct dmi_system_id i8k_blacklist_fan_support_dmi_table[] __initdata = { 11488c2ecf20Sopenharmony_ci { 11498c2ecf20Sopenharmony_ci .ident = "Dell Inspiron 7720", 11508c2ecf20Sopenharmony_ci .matches = { 11518c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 11528c2ecf20Sopenharmony_ci DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Inspiron 7720"), 11538c2ecf20Sopenharmony_ci }, 11548c2ecf20Sopenharmony_ci }, 11558c2ecf20Sopenharmony_ci { 11568c2ecf20Sopenharmony_ci .ident = "Dell Vostro 3360", 11578c2ecf20Sopenharmony_ci .matches = { 11588c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 11598c2ecf20Sopenharmony_ci DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Vostro 3360"), 11608c2ecf20Sopenharmony_ci }, 11618c2ecf20Sopenharmony_ci }, 11628c2ecf20Sopenharmony_ci { 11638c2ecf20Sopenharmony_ci .ident = "Dell XPS13 9333", 11648c2ecf20Sopenharmony_ci .matches = { 11658c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 11668c2ecf20Sopenharmony_ci DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS13 9333"), 11678c2ecf20Sopenharmony_ci }, 11688c2ecf20Sopenharmony_ci }, 11698c2ecf20Sopenharmony_ci { 11708c2ecf20Sopenharmony_ci .ident = "Dell XPS 15 L502X", 11718c2ecf20Sopenharmony_ci .matches = { 11728c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 11738c2ecf20Sopenharmony_ci DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Dell System XPS L502X"), 11748c2ecf20Sopenharmony_ci }, 11758c2ecf20Sopenharmony_ci }, 11768c2ecf20Sopenharmony_ci { } 11778c2ecf20Sopenharmony_ci}; 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_cistruct i8k_fan_control_data { 11808c2ecf20Sopenharmony_ci unsigned int manual_fan; 11818c2ecf20Sopenharmony_ci unsigned int auto_fan; 11828c2ecf20Sopenharmony_ci}; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_cienum i8k_fan_controls { 11858c2ecf20Sopenharmony_ci I8K_FAN_34A3_35A3, 11868c2ecf20Sopenharmony_ci}; 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_cistatic const struct i8k_fan_control_data i8k_fan_control_data[] = { 11898c2ecf20Sopenharmony_ci [I8K_FAN_34A3_35A3] = { 11908c2ecf20Sopenharmony_ci .manual_fan = 0x34a3, 11918c2ecf20Sopenharmony_ci .auto_fan = 0x35a3, 11928c2ecf20Sopenharmony_ci }, 11938c2ecf20Sopenharmony_ci}; 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_cistatic struct dmi_system_id i8k_whitelist_fan_control[] __initdata = { 11968c2ecf20Sopenharmony_ci { 11978c2ecf20Sopenharmony_ci .ident = "Dell Precision 5530", 11988c2ecf20Sopenharmony_ci .matches = { 11998c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 12008c2ecf20Sopenharmony_ci DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Precision 5530"), 12018c2ecf20Sopenharmony_ci }, 12028c2ecf20Sopenharmony_ci .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], 12038c2ecf20Sopenharmony_ci }, 12048c2ecf20Sopenharmony_ci { 12058c2ecf20Sopenharmony_ci .ident = "Dell Latitude 5480", 12068c2ecf20Sopenharmony_ci .matches = { 12078c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 12088c2ecf20Sopenharmony_ci DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude 5480"), 12098c2ecf20Sopenharmony_ci }, 12108c2ecf20Sopenharmony_ci .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], 12118c2ecf20Sopenharmony_ci }, 12128c2ecf20Sopenharmony_ci { 12138c2ecf20Sopenharmony_ci .ident = "Dell Latitude E6440", 12148c2ecf20Sopenharmony_ci .matches = { 12158c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 12168c2ecf20Sopenharmony_ci DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude E6440"), 12178c2ecf20Sopenharmony_ci }, 12188c2ecf20Sopenharmony_ci .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], 12198c2ecf20Sopenharmony_ci }, 12208c2ecf20Sopenharmony_ci { } 12218c2ecf20Sopenharmony_ci}; 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci/* 12248c2ecf20Sopenharmony_ci * Probe for the presence of a supported laptop. 12258c2ecf20Sopenharmony_ci */ 12268c2ecf20Sopenharmony_cistatic int __init i8k_probe(void) 12278c2ecf20Sopenharmony_ci{ 12288c2ecf20Sopenharmony_ci const struct dmi_system_id *id, *fan_control; 12298c2ecf20Sopenharmony_ci int fan, ret; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci /* 12328c2ecf20Sopenharmony_ci * Get DMI information 12338c2ecf20Sopenharmony_ci */ 12348c2ecf20Sopenharmony_ci if (!dmi_check_system(i8k_dmi_table)) { 12358c2ecf20Sopenharmony_ci if (!ignore_dmi && !force) 12368c2ecf20Sopenharmony_ci return -ENODEV; 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci pr_info("not running on a supported Dell system.\n"); 12398c2ecf20Sopenharmony_ci pr_info("vendor=%s, model=%s, version=%s\n", 12408c2ecf20Sopenharmony_ci i8k_get_dmi_data(DMI_SYS_VENDOR), 12418c2ecf20Sopenharmony_ci i8k_get_dmi_data(DMI_PRODUCT_NAME), 12428c2ecf20Sopenharmony_ci i8k_get_dmi_data(DMI_BIOS_VERSION)); 12438c2ecf20Sopenharmony_ci } 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci if (dmi_check_system(i8k_blacklist_fan_support_dmi_table)) { 12468c2ecf20Sopenharmony_ci pr_warn("broken Dell BIOS detected, disallow fan support\n"); 12478c2ecf20Sopenharmony_ci if (!force) 12488c2ecf20Sopenharmony_ci disallow_fan_support = true; 12498c2ecf20Sopenharmony_ci } 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci if (dmi_check_system(i8k_blacklist_fan_type_dmi_table)) { 12528c2ecf20Sopenharmony_ci pr_warn("broken Dell BIOS detected, disallow fan type call\n"); 12538c2ecf20Sopenharmony_ci if (!force) 12548c2ecf20Sopenharmony_ci disallow_fan_type_call = true; 12558c2ecf20Sopenharmony_ci } 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci strlcpy(bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION), 12588c2ecf20Sopenharmony_ci sizeof(bios_version)); 12598c2ecf20Sopenharmony_ci strlcpy(bios_machineid, i8k_get_dmi_data(DMI_PRODUCT_SERIAL), 12608c2ecf20Sopenharmony_ci sizeof(bios_machineid)); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci /* 12638c2ecf20Sopenharmony_ci * Get SMM Dell signature 12648c2ecf20Sopenharmony_ci */ 12658c2ecf20Sopenharmony_ci if (i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG1) && 12668c2ecf20Sopenharmony_ci i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG2)) { 12678c2ecf20Sopenharmony_ci pr_err("unable to get SMM Dell signature\n"); 12688c2ecf20Sopenharmony_ci if (!force) 12698c2ecf20Sopenharmony_ci return -ENODEV; 12708c2ecf20Sopenharmony_ci } 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci /* 12738c2ecf20Sopenharmony_ci * Set fan multiplier and maximal fan speed from dmi config 12748c2ecf20Sopenharmony_ci * Values specified in module parameters override values from dmi 12758c2ecf20Sopenharmony_ci */ 12768c2ecf20Sopenharmony_ci id = dmi_first_match(i8k_dmi_table); 12778c2ecf20Sopenharmony_ci if (id && id->driver_data) { 12788c2ecf20Sopenharmony_ci const struct i8k_config_data *conf = id->driver_data; 12798c2ecf20Sopenharmony_ci if (!fan_mult && conf->fan_mult) 12808c2ecf20Sopenharmony_ci fan_mult = conf->fan_mult; 12818c2ecf20Sopenharmony_ci if (!fan_max && conf->fan_max) 12828c2ecf20Sopenharmony_ci fan_max = conf->fan_max; 12838c2ecf20Sopenharmony_ci } 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci i8k_fan_max = fan_max ? : I8K_FAN_HIGH; /* Must not be 0 */ 12868c2ecf20Sopenharmony_ci i8k_pwm_mult = DIV_ROUND_UP(255, i8k_fan_max); 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci fan_control = dmi_first_match(i8k_whitelist_fan_control); 12898c2ecf20Sopenharmony_ci if (fan_control && fan_control->driver_data) { 12908c2ecf20Sopenharmony_ci const struct i8k_fan_control_data *data = fan_control->driver_data; 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci manual_fan = data->manual_fan; 12938c2ecf20Sopenharmony_ci auto_fan = data->auto_fan; 12948c2ecf20Sopenharmony_ci pr_info("enabling support for setting automatic/manual fan control\n"); 12958c2ecf20Sopenharmony_ci } 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci if (!fan_mult) { 12988c2ecf20Sopenharmony_ci /* 12998c2ecf20Sopenharmony_ci * Autodetect fan multiplier based on nominal rpm 13008c2ecf20Sopenharmony_ci * If fan reports rpm value too high then set multiplier to 1 13018c2ecf20Sopenharmony_ci */ 13028c2ecf20Sopenharmony_ci for (fan = 0; fan < 2; ++fan) { 13038c2ecf20Sopenharmony_ci ret = i8k_get_fan_nominal_speed(fan, i8k_fan_max); 13048c2ecf20Sopenharmony_ci if (ret < 0) 13058c2ecf20Sopenharmony_ci continue; 13068c2ecf20Sopenharmony_ci if (ret > I8K_FAN_MAX_RPM) 13078c2ecf20Sopenharmony_ci i8k_fan_mult = 1; 13088c2ecf20Sopenharmony_ci break; 13098c2ecf20Sopenharmony_ci } 13108c2ecf20Sopenharmony_ci } else { 13118c2ecf20Sopenharmony_ci /* Fan multiplier was specified in module param or in dmi */ 13128c2ecf20Sopenharmony_ci i8k_fan_mult = fan_mult; 13138c2ecf20Sopenharmony_ci } 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci return 0; 13168c2ecf20Sopenharmony_ci} 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_cistatic int __init i8k_init(void) 13198c2ecf20Sopenharmony_ci{ 13208c2ecf20Sopenharmony_ci int err; 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci /* Are we running on an supported laptop? */ 13238c2ecf20Sopenharmony_ci if (i8k_probe()) 13248c2ecf20Sopenharmony_ci return -ENODEV; 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci err = i8k_init_hwmon(); 13278c2ecf20Sopenharmony_ci if (err) 13288c2ecf20Sopenharmony_ci return err; 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci i8k_init_procfs(); 13318c2ecf20Sopenharmony_ci return 0; 13328c2ecf20Sopenharmony_ci} 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_cistatic void __exit i8k_exit(void) 13358c2ecf20Sopenharmony_ci{ 13368c2ecf20Sopenharmony_ci hwmon_device_unregister(i8k_hwmon_dev); 13378c2ecf20Sopenharmony_ci i8k_exit_procfs(); 13388c2ecf20Sopenharmony_ci} 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_cimodule_init(i8k_init); 13418c2ecf20Sopenharmony_cimodule_exit(i8k_exit); 1342