^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * dell-smm-hwmon.c -- Linux driver for accessing the SMM BIOS on Dell laptops.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2001 Massimo Dal Zotto <dz@debian.org>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Hwmon integration:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * Copyright (C) 2011 Jean Delvare <jdelvare@suse.de>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * Copyright (C) 2013, 2014 Guenter Roeck <linux@roeck-us.net>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * Copyright (C) 2014, 2015 Pali Rohár <pali@kernel.org>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/cpu.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/types.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/proc_fs.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <linux/seq_file.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #include <linux/dmi.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #include <linux/capability.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #include <linux/mutex.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #include <linux/hwmon.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #include <linux/hwmon-sysfs.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #include <linux/uaccess.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) #include <linux/sched.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) #include <linux/ctype.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) #include <linux/smp.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) #include <linux/i8k.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) #define I8K_SMM_FN_STATUS 0x0025
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) #define I8K_SMM_POWER_STATUS 0x0069
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) #define I8K_SMM_SET_FAN 0x01a3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) #define I8K_SMM_GET_FAN 0x00a3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) #define I8K_SMM_GET_SPEED 0x02a3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) #define I8K_SMM_GET_FAN_TYPE 0x03a3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) #define I8K_SMM_GET_NOM_SPEED 0x04a3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) #define I8K_SMM_GET_TEMP 0x10a3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) #define I8K_SMM_GET_TEMP_TYPE 0x11a3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) #define I8K_SMM_GET_DELL_SIG1 0xfea3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) #define I8K_SMM_GET_DELL_SIG2 0xffa3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) #define I8K_FAN_MULT 30
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) #define I8K_FAN_MAX_RPM 30000
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) #define I8K_MAX_TEMP 127
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) #define I8K_FN_NONE 0x00
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) #define I8K_FN_UP 0x01
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) #define I8K_FN_DOWN 0x02
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) #define I8K_FN_MUTE 0x04
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) #define I8K_FN_MASK 0x07
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) #define I8K_FN_SHIFT 8
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) #define I8K_POWER_AC 0x05
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) #define I8K_POWER_BATTERY 0x01
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) static DEFINE_MUTEX(i8k_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) static char bios_version[4];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) static char bios_machineid[16];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) static struct device *i8k_hwmon_dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) static u32 i8k_hwmon_flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) static uint i8k_fan_mult = I8K_FAN_MULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) static uint i8k_pwm_mult;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) static uint i8k_fan_max = I8K_FAN_HIGH;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) static bool disallow_fan_type_call;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) static bool disallow_fan_support;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) static unsigned int manual_fan;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) static unsigned int auto_fan;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) #define I8K_HWMON_HAVE_TEMP1 (1 << 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) #define I8K_HWMON_HAVE_TEMP2 (1 << 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) #define I8K_HWMON_HAVE_TEMP3 (1 << 2)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) #define I8K_HWMON_HAVE_TEMP4 (1 << 3)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) #define I8K_HWMON_HAVE_TEMP5 (1 << 4)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) #define I8K_HWMON_HAVE_TEMP6 (1 << 5)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) #define I8K_HWMON_HAVE_TEMP7 (1 << 6)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) #define I8K_HWMON_HAVE_TEMP8 (1 << 7)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) #define I8K_HWMON_HAVE_TEMP9 (1 << 8)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) #define I8K_HWMON_HAVE_TEMP10 (1 << 9)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) #define I8K_HWMON_HAVE_FAN1 (1 << 10)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) #define I8K_HWMON_HAVE_FAN2 (1 << 11)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) #define I8K_HWMON_HAVE_FAN3 (1 << 12)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) MODULE_DESCRIPTION("Dell laptop SMM BIOS hwmon driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) MODULE_LICENSE("GPL");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) MODULE_ALIAS("i8k");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) static bool force;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) module_param(force, bool, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) MODULE_PARM_DESC(force, "Force loading without checking for supported models");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) static bool ignore_dmi;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) module_param(ignore_dmi, bool, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) MODULE_PARM_DESC(ignore_dmi, "Continue probing hardware even if DMI data does not match");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) #if IS_ENABLED(CONFIG_I8K)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) static bool restricted = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) module_param(restricted, bool, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) MODULE_PARM_DESC(restricted, "Restrict fan control and serial number to CAP_SYS_ADMIN (default: 1)");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) static bool power_status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) module_param(power_status, bool, 0600);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k (default: 0)");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) static uint fan_mult;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) module_param(fan_mult, uint, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with (default: autodetect)");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) static uint fan_max;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) module_param(fan_max, uint, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) MODULE_PARM_DESC(fan_max, "Maximum configurable fan speed (default: autodetect)");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) struct smm_regs {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) unsigned int eax;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) unsigned int ebx __packed;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) unsigned int ecx __packed;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) unsigned int edx __packed;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) unsigned int esi __packed;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) unsigned int edi __packed;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) static inline const char *i8k_get_dmi_data(int field)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) const char *dmi_data = dmi_get_system_info(field);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) return dmi_data && *dmi_data ? dmi_data : "?";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) * Call the System Management Mode BIOS. Code provided by Jonathan Buzzard.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) static int i8k_smm_func(void *par)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) int rc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) struct smm_regs *regs = par;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) int eax = regs->eax;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) #ifdef DEBUG
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) int ebx = regs->ebx;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) unsigned long duration;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) ktime_t calltime, delta, rettime;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) calltime = ktime_get();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) /* SMM requires CPU 0 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) if (smp_processor_id() != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) return -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) #if defined(CONFIG_X86_64)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) asm volatile("pushq %%rax\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) "movl 0(%%rax),%%edx\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) "pushq %%rdx\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) "movl 4(%%rax),%%ebx\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) "movl 8(%%rax),%%ecx\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) "movl 12(%%rax),%%edx\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) "movl 16(%%rax),%%esi\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) "movl 20(%%rax),%%edi\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) "popq %%rax\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) "out %%al,$0xb2\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) "out %%al,$0x84\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) "xchgq %%rax,(%%rsp)\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) "movl %%ebx,4(%%rax)\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) "movl %%ecx,8(%%rax)\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) "movl %%edx,12(%%rax)\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) "movl %%esi,16(%%rax)\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) "movl %%edi,20(%%rax)\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) "popq %%rdx\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) "movl %%edx,0(%%rax)\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) "pushfq\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) "popq %%rax\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) "andl $1,%%eax\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) : "=a"(rc)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) : "a"(regs)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) #else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) asm volatile("pushl %%eax\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) "movl 0(%%eax),%%edx\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) "push %%edx\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) "movl 4(%%eax),%%ebx\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) "movl 8(%%eax),%%ecx\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) "movl 12(%%eax),%%edx\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) "movl 16(%%eax),%%esi\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) "movl 20(%%eax),%%edi\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) "popl %%eax\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) "out %%al,$0xb2\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) "out %%al,$0x84\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) "xchgl %%eax,(%%esp)\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) "movl %%ebx,4(%%eax)\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) "movl %%ecx,8(%%eax)\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) "movl %%edx,12(%%eax)\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) "movl %%esi,16(%%eax)\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) "movl %%edi,20(%%eax)\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) "popl %%edx\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) "movl %%edx,0(%%eax)\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) "lahf\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) "shrl $8,%%eax\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) "andl $1,%%eax\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) : "=a"(rc)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) : "a"(regs)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) if (rc != 0 || (regs->eax & 0xffff) == 0xffff || regs->eax == eax)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) rc = -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) #ifdef DEBUG
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) rettime = ktime_get();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) delta = ktime_sub(rettime, calltime);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) duration = ktime_to_ns(delta) >> 10;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) pr_debug("smm(0x%.4x 0x%.4x) = 0x%.4x (took %7lu usecs)\n", eax, ebx,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) (rc ? 0xffff : regs->eax & 0xffff), duration);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) return rc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) * Call the System Management Mode BIOS.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) static int i8k_smm(struct smm_regs *regs)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) get_online_cpus();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) ret = smp_call_on_cpu(0, i8k_smm_func, regs, true);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) put_online_cpus();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) * Read the fan status.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) static int i8k_get_fan_status(int fan)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) struct smm_regs regs = { .eax = I8K_SMM_GET_FAN, };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) if (disallow_fan_support)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) regs.ebx = fan & 0xff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) return i8k_smm(®s) ? : regs.eax & 0xff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) * Read the fan speed in RPM.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) static int i8k_get_fan_speed(int fan)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) struct smm_regs regs = { .eax = I8K_SMM_GET_SPEED, };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) if (disallow_fan_support)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) regs.ebx = fan & 0xff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) return i8k_smm(®s) ? : (regs.eax & 0xffff) * i8k_fan_mult;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) * Read the fan type.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) static int _i8k_get_fan_type(int fan)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) struct smm_regs regs = { .eax = I8K_SMM_GET_FAN_TYPE, };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) if (disallow_fan_support || disallow_fan_type_call)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) regs.ebx = fan & 0xff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) return i8k_smm(®s) ? : regs.eax & 0xff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) static int i8k_get_fan_type(int fan)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) /* I8K_SMM_GET_FAN_TYPE SMM call is expensive, so cache values */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) static int types[3] = { INT_MIN, INT_MIN, INT_MIN };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) if (types[fan] == INT_MIN)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) types[fan] = _i8k_get_fan_type(fan);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) return types[fan];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) * Read the fan nominal rpm for specific fan speed.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) static int i8k_get_fan_nominal_speed(int fan, int speed)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) struct smm_regs regs = { .eax = I8K_SMM_GET_NOM_SPEED, };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) if (disallow_fan_support)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) regs.ebx = (fan & 0xff) | (speed << 8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) return i8k_smm(®s) ? : (regs.eax & 0xffff) * i8k_fan_mult;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) * Enable or disable automatic BIOS fan control support
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) static int i8k_enable_fan_auto_mode(bool enable)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) struct smm_regs regs = { };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) if (disallow_fan_support)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) regs.eax = enable ? auto_fan : manual_fan;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) return i8k_smm(®s);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) * Set the fan speed (off, low, high, ...).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) static int i8k_set_fan(int fan, int speed)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) struct smm_regs regs = { .eax = I8K_SMM_SET_FAN, };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) if (disallow_fan_support)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) speed = (speed < 0) ? 0 : ((speed > i8k_fan_max) ? i8k_fan_max : speed);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) regs.ebx = (fan & 0xff) | (speed << 8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) return i8k_smm(®s);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) static int i8k_get_temp_type(int sensor)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) struct smm_regs regs = { .eax = I8K_SMM_GET_TEMP_TYPE, };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) regs.ebx = sensor & 0xff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) return i8k_smm(®s) ? : regs.eax & 0xff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) * Read the cpu temperature.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) static int _i8k_get_temp(int sensor)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) struct smm_regs regs = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) .eax = I8K_SMM_GET_TEMP,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) .ebx = sensor & 0xff,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) return i8k_smm(®s) ? : regs.eax & 0xff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) static int i8k_get_temp(int sensor)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) int temp = _i8k_get_temp(sensor);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) * Sometimes the temperature sensor returns 0x99, which is out of range.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) * In this case we retry (once) before returning an error.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) # 1003655137 00000058 00005a4b
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) # 1003655138 00000099 00003a80 <--- 0x99 = 153 degrees
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) # 1003655139 00000054 00005c52
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) if (temp == 0x99) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) msleep(100);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) temp = _i8k_get_temp(sensor);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) * Return -ENODATA for all invalid temperatures.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) * Known instances are the 0x99 value as seen above as well as
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) * 0xc1 (193), which may be returned when trying to read the GPU
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) * temperature if the system supports a GPU and it is currently
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) * turned off.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) if (temp > I8K_MAX_TEMP)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) return -ENODATA;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) return temp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) static int i8k_get_dell_signature(int req_fn)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) struct smm_regs regs = { .eax = req_fn, };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) int rc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) rc = i8k_smm(®s);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) if (rc < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) return rc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) return regs.eax == 1145651527 && regs.edx == 1145392204 ? 0 : -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) #if IS_ENABLED(CONFIG_I8K)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) * Read the Fn key status.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) static int i8k_get_fn_status(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) struct smm_regs regs = { .eax = I8K_SMM_FN_STATUS, };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) int rc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) rc = i8k_smm(®s);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) if (rc < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) return rc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) switch ((regs.eax >> I8K_FN_SHIFT) & I8K_FN_MASK) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) case I8K_FN_UP:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) return I8K_VOL_UP;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) case I8K_FN_DOWN:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) return I8K_VOL_DOWN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) case I8K_FN_MUTE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) return I8K_VOL_MUTE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424) * Read the power status.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) static int i8k_get_power_status(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) struct smm_regs regs = { .eax = I8K_SMM_POWER_STATUS, };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429) int rc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) rc = i8k_smm(®s);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) if (rc < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433) return rc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) return (regs.eax & 0xff) == I8K_POWER_AC ? I8K_AC : I8K_BATTERY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) * Procfs interface
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) static int
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445) int val = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) int speed, err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447) unsigned char buff[16];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) int __user *argp = (int __user *)arg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450) if (!argp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) switch (cmd) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454) case I8K_BIOS_VERSION:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455) if (!isdigit(bios_version[0]) || !isdigit(bios_version[1]) ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456) !isdigit(bios_version[2]))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459) val = (bios_version[0] << 16) |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460) (bios_version[1] << 8) | bios_version[2];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 462)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 463) case I8K_MACHINE_ID:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464) if (restricted && !capable(CAP_SYS_ADMIN))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465) return -EPERM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 466)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 467) memset(buff, 0, sizeof(buff));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 468) strlcpy(buff, bios_machineid, sizeof(buff));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 469) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 470)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 471) case I8K_FN_STATUS:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 472) val = i8k_get_fn_status();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 473) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 474)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 475) case I8K_POWER_STATUS:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 476) val = i8k_get_power_status();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 477) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 478)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 479) case I8K_GET_TEMP:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 480) val = i8k_get_temp(0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 481) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 482)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 483) case I8K_GET_SPEED:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 484) if (copy_from_user(&val, argp, sizeof(int)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 485) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 486)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 487) val = i8k_get_fan_speed(val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 488) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 489)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 490) case I8K_GET_FAN:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 491) if (copy_from_user(&val, argp, sizeof(int)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 492) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 493)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 494) val = i8k_get_fan_status(val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 495) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 496)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 497) case I8K_SET_FAN:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 498) if (restricted && !capable(CAP_SYS_ADMIN))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 499) return -EPERM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 500)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 501) if (copy_from_user(&val, argp, sizeof(int)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 502) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 503)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 504) if (copy_from_user(&speed, argp + 1, sizeof(int)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 505) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 506)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 507) err = i8k_set_fan(val, speed);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 508) if (err < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 509) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 510)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 511) val = i8k_get_fan_status(val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 512) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 513)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 514) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 515) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 516) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 517)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 518) if (val < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 519) return val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 520)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 521) switch (cmd) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 522) case I8K_BIOS_VERSION:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 523) if (copy_to_user(argp, &val, 4))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 524) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 525)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 526) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 527) case I8K_MACHINE_ID:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 528) if (copy_to_user(argp, buff, 16))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 529) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 530)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 531) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 532) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 533) if (copy_to_user(argp, &val, sizeof(int)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 534) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 535)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 536) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 537) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 538)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 539) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 540) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 541)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 542) static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 543) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 544) long ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 545)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 546) mutex_lock(&i8k_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 547) ret = i8k_ioctl_unlocked(fp, cmd, arg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 548) mutex_unlock(&i8k_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 549)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 550) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 551) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 552)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 553) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 554) * Print the information for /proc/i8k.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 555) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 556) static int i8k_proc_show(struct seq_file *seq, void *offset)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 557) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 558) int fn_key, cpu_temp, ac_power;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 559) int left_fan, right_fan, left_speed, right_speed;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 560)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 561) cpu_temp = i8k_get_temp(0); /* 11100 µs */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 562) left_fan = i8k_get_fan_status(I8K_FAN_LEFT); /* 580 µs */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 563) right_fan = i8k_get_fan_status(I8K_FAN_RIGHT); /* 580 µs */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 564) left_speed = i8k_get_fan_speed(I8K_FAN_LEFT); /* 580 µs */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 565) right_speed = i8k_get_fan_speed(I8K_FAN_RIGHT); /* 580 µs */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 566) fn_key = i8k_get_fn_status(); /* 750 µs */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 567) if (power_status)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 568) ac_power = i8k_get_power_status(); /* 14700 µs */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 569) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 570) ac_power = -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 571)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 572) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 573) * Info:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 574) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 575) * 1) Format version (this will change if format changes)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 576) * 2) BIOS version
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 577) * 3) BIOS machine ID
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 578) * 4) Cpu temperature
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 579) * 5) Left fan status
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 580) * 6) Right fan status
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 581) * 7) Left fan speed
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 582) * 8) Right fan speed
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 583) * 9) AC power
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 584) * 10) Fn Key status
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 585) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 586) seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 587) I8K_PROC_FMT,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 588) bios_version,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 589) (restricted && !capable(CAP_SYS_ADMIN)) ? "-1" : bios_machineid,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 590) cpu_temp,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 591) left_fan, right_fan, left_speed, right_speed,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 592) ac_power, fn_key);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 593)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 594) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 595) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 596)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 597) static int i8k_open_fs(struct inode *inode, struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 598) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 599) return single_open(file, i8k_proc_show, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 600) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 601)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 602) static const struct proc_ops i8k_proc_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 603) .proc_open = i8k_open_fs,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 604) .proc_read = seq_read,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 605) .proc_lseek = seq_lseek,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 606) .proc_release = single_release,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 607) .proc_ioctl = i8k_ioctl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 608) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 609)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 610) static struct proc_dir_entry *entry;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 611)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 612) static void __init i8k_init_procfs(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 613) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 614) /* Register the proc entry */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 615) entry = proc_create("i8k", 0, NULL, &i8k_proc_ops);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 616) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 617)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 618) static void __exit i8k_exit_procfs(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 619) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 620) if (entry)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 621) remove_proc_entry("i8k", NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 622) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 623)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 624) #else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 625)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 626) static inline void __init i8k_init_procfs(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 627) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 628) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 629)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 630) static inline void __exit i8k_exit_procfs(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 631) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 632) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 633)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 634) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 635)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 636) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 637) * Hwmon interface
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 638) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 639)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 640) static ssize_t i8k_hwmon_temp_label_show(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 641) struct device_attribute *devattr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 642) char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 643) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 644) static const char * const labels[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 645) "CPU",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 646) "GPU",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 647) "SODIMM",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 648) "Other",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 649) "Ambient",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 650) "Other",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 651) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 652) int index = to_sensor_dev_attr(devattr)->index;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 653) int type;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 654)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 655) type = i8k_get_temp_type(index);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 656) if (type < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 657) return type;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 658) if (type >= ARRAY_SIZE(labels))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 659) type = ARRAY_SIZE(labels) - 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 660) return sprintf(buf, "%s\n", labels[type]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 661) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 662)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 663) static ssize_t i8k_hwmon_temp_show(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 664) struct device_attribute *devattr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 665) char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 666) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 667) int index = to_sensor_dev_attr(devattr)->index;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 668) int temp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 669)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 670) temp = i8k_get_temp(index);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 671) if (temp < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 672) return temp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 673) return sprintf(buf, "%d\n", temp * 1000);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 674) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 675)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 676) static ssize_t i8k_hwmon_fan_label_show(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 677) struct device_attribute *devattr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 678) char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 679) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 680) static const char * const labels[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 681) "Processor Fan",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 682) "Motherboard Fan",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 683) "Video Fan",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 684) "Power Supply Fan",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 685) "Chipset Fan",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 686) "Other Fan",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 687) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 688) int index = to_sensor_dev_attr(devattr)->index;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 689) bool dock = false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 690) int type;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 691)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 692) type = i8k_get_fan_type(index);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 693) if (type < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 694) return type;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 695)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 696) if (type & 0x10) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 697) dock = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 698) type &= 0x0F;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 699) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 700)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 701) if (type >= ARRAY_SIZE(labels))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 702) type = (ARRAY_SIZE(labels) - 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 703)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 704) return sprintf(buf, "%s%s\n", (dock ? "Docking " : ""), labels[type]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 705) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 706)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 707) static ssize_t i8k_hwmon_fan_show(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 708) struct device_attribute *devattr, char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 709) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 710) int index = to_sensor_dev_attr(devattr)->index;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 711) int fan_speed;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 712)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 713) fan_speed = i8k_get_fan_speed(index);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 714) if (fan_speed < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 715) return fan_speed;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 716) return sprintf(buf, "%d\n", fan_speed);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 717) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 718)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 719) static ssize_t i8k_hwmon_pwm_show(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 720) struct device_attribute *devattr, char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 721) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 722) int index = to_sensor_dev_attr(devattr)->index;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 723) int status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 724)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 725) status = i8k_get_fan_status(index);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 726) if (status < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 727) return -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 728) return sprintf(buf, "%d\n", clamp_val(status * i8k_pwm_mult, 0, 255));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 729) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 730)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 731) static ssize_t i8k_hwmon_pwm_store(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 732) struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 733) const char *buf, size_t count)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 734) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 735) int index = to_sensor_dev_attr(attr)->index;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 736) unsigned long val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 737) int err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 738)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 739) err = kstrtoul(buf, 10, &val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 740) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 741) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 742) val = clamp_val(DIV_ROUND_CLOSEST(val, i8k_pwm_mult), 0, i8k_fan_max);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 743)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 744) mutex_lock(&i8k_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 745) err = i8k_set_fan(index, val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 746) mutex_unlock(&i8k_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 747)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 748) return err < 0 ? -EIO : count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 749) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 750)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 751) static ssize_t i8k_hwmon_pwm_enable_store(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 752) struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 753) const char *buf, size_t count)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 754) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 755) int err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 756) bool enable;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 757) unsigned long val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 758)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 759) if (!auto_fan)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 760) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 761)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 762) err = kstrtoul(buf, 10, &val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 763) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 764) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 765)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 766) if (val == 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 767) enable = false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 768) else if (val == 2)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 769) enable = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 770) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 771) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 772)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 773) mutex_lock(&i8k_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 774) err = i8k_enable_fan_auto_mode(enable);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 775) mutex_unlock(&i8k_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 776)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 777) return err ? err : count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 778) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 779)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 780) static SENSOR_DEVICE_ATTR_RO(temp1_input, i8k_hwmon_temp, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 781) static SENSOR_DEVICE_ATTR_RO(temp1_label, i8k_hwmon_temp_label, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 782) static SENSOR_DEVICE_ATTR_RO(temp2_input, i8k_hwmon_temp, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 783) static SENSOR_DEVICE_ATTR_RO(temp2_label, i8k_hwmon_temp_label, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 784) static SENSOR_DEVICE_ATTR_RO(temp3_input, i8k_hwmon_temp, 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 785) static SENSOR_DEVICE_ATTR_RO(temp3_label, i8k_hwmon_temp_label, 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 786) static SENSOR_DEVICE_ATTR_RO(temp4_input, i8k_hwmon_temp, 3);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 787) static SENSOR_DEVICE_ATTR_RO(temp4_label, i8k_hwmon_temp_label, 3);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 788) static SENSOR_DEVICE_ATTR_RO(temp5_input, i8k_hwmon_temp, 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 789) static SENSOR_DEVICE_ATTR_RO(temp5_label, i8k_hwmon_temp_label, 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 790) static SENSOR_DEVICE_ATTR_RO(temp6_input, i8k_hwmon_temp, 5);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 791) static SENSOR_DEVICE_ATTR_RO(temp6_label, i8k_hwmon_temp_label, 5);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 792) static SENSOR_DEVICE_ATTR_RO(temp7_input, i8k_hwmon_temp, 6);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 793) static SENSOR_DEVICE_ATTR_RO(temp7_label, i8k_hwmon_temp_label, 6);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 794) static SENSOR_DEVICE_ATTR_RO(temp8_input, i8k_hwmon_temp, 7);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 795) static SENSOR_DEVICE_ATTR_RO(temp8_label, i8k_hwmon_temp_label, 7);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 796) static SENSOR_DEVICE_ATTR_RO(temp9_input, i8k_hwmon_temp, 8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 797) static SENSOR_DEVICE_ATTR_RO(temp9_label, i8k_hwmon_temp_label, 8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 798) static SENSOR_DEVICE_ATTR_RO(temp10_input, i8k_hwmon_temp, 9);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 799) static SENSOR_DEVICE_ATTR_RO(temp10_label, i8k_hwmon_temp_label, 9);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 800) static SENSOR_DEVICE_ATTR_RO(fan1_input, i8k_hwmon_fan, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 801) static SENSOR_DEVICE_ATTR_RO(fan1_label, i8k_hwmon_fan_label, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 802) static SENSOR_DEVICE_ATTR_RW(pwm1, i8k_hwmon_pwm, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 803) static SENSOR_DEVICE_ATTR_WO(pwm1_enable, i8k_hwmon_pwm_enable, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 804) static SENSOR_DEVICE_ATTR_RO(fan2_input, i8k_hwmon_fan, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 805) static SENSOR_DEVICE_ATTR_RO(fan2_label, i8k_hwmon_fan_label, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 806) static SENSOR_DEVICE_ATTR_RW(pwm2, i8k_hwmon_pwm, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 807) static SENSOR_DEVICE_ATTR_RO(fan3_input, i8k_hwmon_fan, 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 808) static SENSOR_DEVICE_ATTR_RO(fan3_label, i8k_hwmon_fan_label, 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 809) static SENSOR_DEVICE_ATTR_RW(pwm3, i8k_hwmon_pwm, 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 810)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 811) static struct attribute *i8k_attrs[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 812) &sensor_dev_attr_temp1_input.dev_attr.attr, /* 0 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 813) &sensor_dev_attr_temp1_label.dev_attr.attr, /* 1 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 814) &sensor_dev_attr_temp2_input.dev_attr.attr, /* 2 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 815) &sensor_dev_attr_temp2_label.dev_attr.attr, /* 3 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 816) &sensor_dev_attr_temp3_input.dev_attr.attr, /* 4 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 817) &sensor_dev_attr_temp3_label.dev_attr.attr, /* 5 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 818) &sensor_dev_attr_temp4_input.dev_attr.attr, /* 6 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 819) &sensor_dev_attr_temp4_label.dev_attr.attr, /* 7 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 820) &sensor_dev_attr_temp5_input.dev_attr.attr, /* 8 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 821) &sensor_dev_attr_temp5_label.dev_attr.attr, /* 9 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 822) &sensor_dev_attr_temp6_input.dev_attr.attr, /* 10 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 823) &sensor_dev_attr_temp6_label.dev_attr.attr, /* 11 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 824) &sensor_dev_attr_temp7_input.dev_attr.attr, /* 12 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 825) &sensor_dev_attr_temp7_label.dev_attr.attr, /* 13 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 826) &sensor_dev_attr_temp8_input.dev_attr.attr, /* 14 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 827) &sensor_dev_attr_temp8_label.dev_attr.attr, /* 15 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 828) &sensor_dev_attr_temp9_input.dev_attr.attr, /* 16 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 829) &sensor_dev_attr_temp9_label.dev_attr.attr, /* 17 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 830) &sensor_dev_attr_temp10_input.dev_attr.attr, /* 18 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 831) &sensor_dev_attr_temp10_label.dev_attr.attr, /* 19 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 832) &sensor_dev_attr_fan1_input.dev_attr.attr, /* 20 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 833) &sensor_dev_attr_fan1_label.dev_attr.attr, /* 21 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 834) &sensor_dev_attr_pwm1.dev_attr.attr, /* 22 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 835) &sensor_dev_attr_pwm1_enable.dev_attr.attr, /* 23 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 836) &sensor_dev_attr_fan2_input.dev_attr.attr, /* 24 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 837) &sensor_dev_attr_fan2_label.dev_attr.attr, /* 25 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 838) &sensor_dev_attr_pwm2.dev_attr.attr, /* 26 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 839) &sensor_dev_attr_fan3_input.dev_attr.attr, /* 27 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 840) &sensor_dev_attr_fan3_label.dev_attr.attr, /* 28 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 841) &sensor_dev_attr_pwm3.dev_attr.attr, /* 29 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 842) NULL
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 843) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 844)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 845) static umode_t i8k_is_visible(struct kobject *kobj, struct attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 846) int index)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 847) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 848) if (disallow_fan_support && index >= 20)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 849) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 850) if (disallow_fan_type_call &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 851) (index == 21 || index == 25 || index == 28))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 852) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 853) if (index >= 0 && index <= 1 &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 854) !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP1))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 855) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 856) if (index >= 2 && index <= 3 &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 857) !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP2))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 858) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 859) if (index >= 4 && index <= 5 &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 860) !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP3))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 861) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 862) if (index >= 6 && index <= 7 &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 863) !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP4))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 864) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 865) if (index >= 8 && index <= 9 &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 866) !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP5))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 867) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 868) if (index >= 10 && index <= 11 &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 869) !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP6))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 870) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 871) if (index >= 12 && index <= 13 &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 872) !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP7))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 873) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 874) if (index >= 14 && index <= 15 &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 875) !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP8))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 876) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 877) if (index >= 16 && index <= 17 &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 878) !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP9))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 879) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 880) if (index >= 18 && index <= 19 &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 881) !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP10))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 882) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 883)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 884) if (index >= 20 && index <= 23 &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 885) !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN1))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 886) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 887) if (index >= 24 && index <= 26 &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 888) !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN2))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 889) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 890) if (index >= 27 && index <= 29 &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 891) !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN3))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 892) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 893)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 894) if (index == 23 && !auto_fan)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 895) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 896)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 897) return attr->mode;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 898) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 899)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 900) static const struct attribute_group i8k_group = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 901) .attrs = i8k_attrs,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 902) .is_visible = i8k_is_visible,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 903) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 904) __ATTRIBUTE_GROUPS(i8k);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 905)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 906) static int __init i8k_init_hwmon(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 907) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 908) int err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 909)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 910) i8k_hwmon_flags = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 911)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 912) /* CPU temperature attributes, if temperature type is OK */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 913) err = i8k_get_temp_type(0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 914) if (err >= 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 915) i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 916) /* check for additional temperature sensors */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 917) err = i8k_get_temp_type(1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 918) if (err >= 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 919) i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 920) err = i8k_get_temp_type(2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 921) if (err >= 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 922) i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP3;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 923) err = i8k_get_temp_type(3);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 924) if (err >= 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 925) i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 926) err = i8k_get_temp_type(4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 927) if (err >= 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 928) i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP5;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 929) err = i8k_get_temp_type(5);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 930) if (err >= 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 931) i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP6;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 932) err = i8k_get_temp_type(6);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 933) if (err >= 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 934) i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP7;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 935) err = i8k_get_temp_type(7);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 936) if (err >= 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 937) i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP8;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 938) err = i8k_get_temp_type(8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 939) if (err >= 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 940) i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP9;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 941) err = i8k_get_temp_type(9);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 942) if (err >= 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 943) i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP10;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 944)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 945) /* First fan attributes, if fan status or type is OK */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 946) err = i8k_get_fan_status(0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 947) if (err < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 948) err = i8k_get_fan_type(0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 949) if (err >= 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 950) i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 951)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 952) /* Second fan attributes, if fan status or type is OK */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 953) err = i8k_get_fan_status(1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 954) if (err < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 955) err = i8k_get_fan_type(1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 956) if (err >= 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 957) i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 958)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 959) /* Third fan attributes, if fan status or type is OK */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 960) err = i8k_get_fan_status(2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 961) if (err < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 962) err = i8k_get_fan_type(2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 963) if (err >= 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 964) i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN3;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 965)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 966) i8k_hwmon_dev = hwmon_device_register_with_groups(NULL, "dell_smm",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 967) NULL, i8k_groups);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 968) if (IS_ERR(i8k_hwmon_dev)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 969) err = PTR_ERR(i8k_hwmon_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 970) i8k_hwmon_dev = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 971) pr_err("hwmon registration failed (%d)\n", err);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 972) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 973) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 974) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 975) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 976)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 977) struct i8k_config_data {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 978) uint fan_mult;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 979) uint fan_max;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 980) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 981)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 982) enum i8k_configs {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 983) DELL_LATITUDE_D520,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 984) DELL_PRECISION_490,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 985) DELL_STUDIO,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 986) DELL_XPS,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 987) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 988)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 989) static const struct i8k_config_data i8k_config_data[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 990) [DELL_LATITUDE_D520] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 991) .fan_mult = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 992) .fan_max = I8K_FAN_TURBO,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 993) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 994) [DELL_PRECISION_490] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 995) .fan_mult = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 996) .fan_max = I8K_FAN_TURBO,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 997) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 998) [DELL_STUDIO] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 999) .fan_mult = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1000) .fan_max = I8K_FAN_HIGH,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1001) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1002) [DELL_XPS] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1003) .fan_mult = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1004) .fan_max = I8K_FAN_HIGH,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1005) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1006) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1007)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1008) static const struct dmi_system_id i8k_dmi_table[] __initconst = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1009) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1010) .ident = "Dell Inspiron",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1011) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1012) DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1013) DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1014) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1015) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1016) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1017) .ident = "Dell Latitude",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1018) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1019) DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1020) DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1021) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1022) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1023) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1024) .ident = "Dell Inspiron 2",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1025) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1026) DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1027) DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1028) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1029) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1030) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1031) .ident = "Dell Latitude D520",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1032) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1033) DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1034) DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D520"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1035) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1036) .driver_data = (void *)&i8k_config_data[DELL_LATITUDE_D520],
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1037) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1038) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1039) .ident = "Dell Latitude 2",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1040) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1041) DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1042) DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1043) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1044) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1045) { /* UK Inspiron 6400 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1046) .ident = "Dell Inspiron 3",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1047) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1048) DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1049) DMI_MATCH(DMI_PRODUCT_NAME, "MM061"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1050) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1051) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1052) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1053) .ident = "Dell Inspiron 3",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1054) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1055) DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1056) DMI_MATCH(DMI_PRODUCT_NAME, "MP061"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1057) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1058) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1059) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1060) .ident = "Dell Precision 490",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1061) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1062) DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1063) DMI_MATCH(DMI_PRODUCT_NAME,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1064) "Precision WorkStation 490"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1065) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1066) .driver_data = (void *)&i8k_config_data[DELL_PRECISION_490],
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1067) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1068) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1069) .ident = "Dell Precision",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1070) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1071) DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1072) DMI_MATCH(DMI_PRODUCT_NAME, "Precision"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1073) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1074) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1075) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1076) .ident = "Dell Vostro",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1077) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1078) DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1079) DMI_MATCH(DMI_PRODUCT_NAME, "Vostro"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1080) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1081) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1082) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1083) .ident = "Dell Studio",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1084) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1085) DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1086) DMI_MATCH(DMI_PRODUCT_NAME, "Studio"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1087) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1088) .driver_data = (void *)&i8k_config_data[DELL_STUDIO],
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1089) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1090) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1091) .ident = "Dell XPS M140",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1092) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1093) DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1094) DMI_MATCH(DMI_PRODUCT_NAME, "MXC051"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1095) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1096) .driver_data = (void *)&i8k_config_data[DELL_XPS],
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1097) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1098) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1099) .ident = "Dell XPS",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1100) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1101) DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1102) DMI_MATCH(DMI_PRODUCT_NAME, "XPS"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1103) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1104) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1105) { }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1106) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1107)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1108) MODULE_DEVICE_TABLE(dmi, i8k_dmi_table);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1109)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1110) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1111) * On some machines once I8K_SMM_GET_FAN_TYPE is issued then CPU fan speed
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1112) * randomly going up and down due to bug in Dell SMM or BIOS. Here is blacklist
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1113) * of affected Dell machines for which we disallow I8K_SMM_GET_FAN_TYPE call.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1114) * See bug: https://bugzilla.kernel.org/show_bug.cgi?id=100121
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1115) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1116) static const struct dmi_system_id i8k_blacklist_fan_type_dmi_table[] __initconst = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1117) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1118) .ident = "Dell Studio XPS 8000",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1119) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1120) DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1121) DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Studio XPS 8000"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1122) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1123) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1124) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1125) .ident = "Dell Studio XPS 8100",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1126) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1127) DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1128) DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Studio XPS 8100"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1129) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1130) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1131) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1132) .ident = "Dell Inspiron 580",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1133) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1134) DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1135) DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Inspiron 580 "),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1136) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1137) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1138) { }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1139) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1140)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1141) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1142) * On some machines all fan related SMM functions implemented by Dell BIOS
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1143) * firmware freeze kernel for about 500ms. Until Dell fixes these problems fan
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1144) * support for affected blacklisted Dell machines stay disabled.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1145) * See bug: https://bugzilla.kernel.org/show_bug.cgi?id=195751
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1146) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1147) static struct dmi_system_id i8k_blacklist_fan_support_dmi_table[] __initdata = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1148) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1149) .ident = "Dell Inspiron 7720",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1150) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1151) DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1152) DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Inspiron 7720"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1153) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1154) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1155) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1156) .ident = "Dell Vostro 3360",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1157) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1158) DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1159) DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Vostro 3360"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1160) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1161) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1162) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1163) .ident = "Dell XPS13 9333",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1164) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1165) DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1166) DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS13 9333"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1167) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1168) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1169) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1170) .ident = "Dell XPS 15 L502X",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1171) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1172) DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1173) DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Dell System XPS L502X"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1174) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1175) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1176) { }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1177) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1178)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1179) struct i8k_fan_control_data {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1180) unsigned int manual_fan;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1181) unsigned int auto_fan;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1182) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1183)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1184) enum i8k_fan_controls {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1185) I8K_FAN_34A3_35A3,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1186) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1187)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1188) static const struct i8k_fan_control_data i8k_fan_control_data[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1189) [I8K_FAN_34A3_35A3] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1190) .manual_fan = 0x34a3,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1191) .auto_fan = 0x35a3,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1192) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1193) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1194)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1195) static struct dmi_system_id i8k_whitelist_fan_control[] __initdata = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1196) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1197) .ident = "Dell Precision 5530",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1198) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1199) DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1200) DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Precision 5530"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1201) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1202) .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1203) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1204) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1205) .ident = "Dell Latitude 5480",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1206) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1207) DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1208) DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude 5480"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1209) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1210) .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1211) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1212) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1213) .ident = "Dell Latitude E6440",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1214) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1215) DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1216) DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude E6440"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1217) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1218) .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1219) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1220) { }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1221) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1222)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1223) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1224) * Probe for the presence of a supported laptop.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1225) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1226) static int __init i8k_probe(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1227) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1228) const struct dmi_system_id *id, *fan_control;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1229) int fan, ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1230)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1231) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1232) * Get DMI information
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1233) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1234) if (!dmi_check_system(i8k_dmi_table)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1235) if (!ignore_dmi && !force)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1236) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1237)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1238) pr_info("not running on a supported Dell system.\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1239) pr_info("vendor=%s, model=%s, version=%s\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1240) i8k_get_dmi_data(DMI_SYS_VENDOR),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1241) i8k_get_dmi_data(DMI_PRODUCT_NAME),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1242) i8k_get_dmi_data(DMI_BIOS_VERSION));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1243) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1244)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1245) if (dmi_check_system(i8k_blacklist_fan_support_dmi_table)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1246) pr_warn("broken Dell BIOS detected, disallow fan support\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1247) if (!force)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1248) disallow_fan_support = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1249) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1250)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1251) if (dmi_check_system(i8k_blacklist_fan_type_dmi_table)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1252) pr_warn("broken Dell BIOS detected, disallow fan type call\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1253) if (!force)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1254) disallow_fan_type_call = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1255) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1256)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1257) strlcpy(bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1258) sizeof(bios_version));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1259) strlcpy(bios_machineid, i8k_get_dmi_data(DMI_PRODUCT_SERIAL),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1260) sizeof(bios_machineid));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1261)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1262) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1263) * Get SMM Dell signature
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1264) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1265) if (i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG1) &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1266) i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG2)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1267) pr_err("unable to get SMM Dell signature\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1268) if (!force)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1269) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1270) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1271)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1272) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1273) * Set fan multiplier and maximal fan speed from dmi config
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1274) * Values specified in module parameters override values from dmi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1275) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1276) id = dmi_first_match(i8k_dmi_table);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1277) if (id && id->driver_data) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1278) const struct i8k_config_data *conf = id->driver_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1279) if (!fan_mult && conf->fan_mult)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1280) fan_mult = conf->fan_mult;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1281) if (!fan_max && conf->fan_max)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1282) fan_max = conf->fan_max;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1283) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1284)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1285) i8k_fan_max = fan_max ? : I8K_FAN_HIGH; /* Must not be 0 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1286) i8k_pwm_mult = DIV_ROUND_UP(255, i8k_fan_max);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1287)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1288) fan_control = dmi_first_match(i8k_whitelist_fan_control);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1289) if (fan_control && fan_control->driver_data) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1290) const struct i8k_fan_control_data *data = fan_control->driver_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1291)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1292) manual_fan = data->manual_fan;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1293) auto_fan = data->auto_fan;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1294) pr_info("enabling support for setting automatic/manual fan control\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1295) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1296)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1297) if (!fan_mult) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1298) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1299) * Autodetect fan multiplier based on nominal rpm
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1300) * If fan reports rpm value too high then set multiplier to 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1301) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1302) for (fan = 0; fan < 2; ++fan) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1303) ret = i8k_get_fan_nominal_speed(fan, i8k_fan_max);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1304) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1305) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1306) if (ret > I8K_FAN_MAX_RPM)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1307) i8k_fan_mult = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1308) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1309) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1310) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1311) /* Fan multiplier was specified in module param or in dmi */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1312) i8k_fan_mult = fan_mult;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1313) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1314)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1315) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1316) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1317)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1318) static int __init i8k_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1319) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1320) int err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1321)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1322) /* Are we running on an supported laptop? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1323) if (i8k_probe())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1324) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1325)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1326) err = i8k_init_hwmon();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1327) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1328) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1329)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1330) i8k_init_procfs();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1331) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1332) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1333)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1334) static void __exit i8k_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1335) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1336) hwmon_device_unregister(i8k_hwmon_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1337) i8k_exit_procfs();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1338) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1339)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1340) module_init(i8k_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1341) module_exit(i8k_exit);