xref: /linux/drivers/hwmon/dell-smm-hwmon.c (revision 4fc1a51c8572179abb767f56cbfc433e00e310c1)
13e0a4e85SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2a5afba16SPali Rohár /*
3a5afba16SPali Rohár  * dell-smm-hwmon.c -- Linux driver for accessing the SMM BIOS on Dell laptops.
4a5afba16SPali Rohár  *
5a5afba16SPali Rohár  * Copyright (C) 2001  Massimo Dal Zotto <dz@debian.org>
6a5afba16SPali Rohár  *
7a5afba16SPali Rohár  * Hwmon integration:
8a5afba16SPali Rohár  * Copyright (C) 2011  Jean Delvare <jdelvare@suse.de>
9a5afba16SPali Rohár  * Copyright (C) 2013, 2014  Guenter Roeck <linux@roeck-us.net>
10149ed3d4SPali Rohár  * Copyright (C) 2014, 2015  Pali Rohár <pali@kernel.org>
11a5afba16SPali Rohár  */
12a5afba16SPali Rohár 
13a5afba16SPali Rohár #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14a5afba16SPali Rohár 
156105870fSArmin Wolf #include <linux/capability.h>
1627046a3fSJuergen Gross #include <linux/cpu.h>
176105870fSArmin Wolf #include <linux/ctype.h>
18a5afba16SPali Rohár #include <linux/delay.h>
196105870fSArmin Wolf #include <linux/dmi.h>
201492fa21SArmin Wolf #include <linux/err.h>
21e64325e8SArmin Wolf #include <linux/errno.h>
226105870fSArmin Wolf #include <linux/hwmon.h>
23a5afba16SPali Rohár #include <linux/init.h>
24e0d3f7cbSArmin Wolf #include <linux/kconfig.h>
254d9983deSArmin Wolf #include <linux/kernel.h>
266105870fSArmin Wolf #include <linux/module.h>
276105870fSArmin Wolf #include <linux/mutex.h>
286105870fSArmin Wolf #include <linux/platform_device.h>
29a5afba16SPali Rohár #include <linux/proc_fs.h>
30a5afba16SPali Rohár #include <linux/seq_file.h>
31e0d3f7cbSArmin Wolf #include <linux/slab.h>
3227046a3fSJuergen Gross #include <linux/smp.h>
33e0d3f7cbSArmin Wolf #include <linux/string.h>
34e0d3f7cbSArmin Wolf #include <linux/thermal.h>
356105870fSArmin Wolf #include <linux/types.h>
366105870fSArmin Wolf #include <linux/uaccess.h>
37a5afba16SPali Rohár 
38a5afba16SPali Rohár #include <linux/i8k.h>
39a5afba16SPali Rohár 
40a5afba16SPali Rohár #define I8K_SMM_FN_STATUS	0x0025
41a5afba16SPali Rohár #define I8K_SMM_POWER_STATUS	0x0069
42a5afba16SPali Rohár #define I8K_SMM_SET_FAN		0x01a3
43a5afba16SPali Rohár #define I8K_SMM_GET_FAN		0x00a3
44a5afba16SPali Rohár #define I8K_SMM_GET_SPEED	0x02a3
45a5afba16SPali Rohár #define I8K_SMM_GET_FAN_TYPE	0x03a3
46a5afba16SPali Rohár #define I8K_SMM_GET_NOM_SPEED	0x04a3
47a5afba16SPali Rohár #define I8K_SMM_GET_TEMP	0x10a3
48a5afba16SPali Rohár #define I8K_SMM_GET_TEMP_TYPE	0x11a3
49a5afba16SPali Rohár #define I8K_SMM_GET_DELL_SIG1	0xfea3
50a5afba16SPali Rohár #define I8K_SMM_GET_DELL_SIG2	0xffa3
51a5afba16SPali Rohár 
52a5afba16SPali Rohár #define I8K_FAN_MULT		30
53*4fc1a51cSArmin Wolf #define I8K_FAN_RPM_THRESHOLD	1000
54a5afba16SPali Rohár #define I8K_MAX_TEMP		127
55a5afba16SPali Rohár 
56a5afba16SPali Rohár #define I8K_FN_NONE		0x00
57a5afba16SPali Rohár #define I8K_FN_UP		0x01
58a5afba16SPali Rohár #define I8K_FN_DOWN		0x02
59a5afba16SPali Rohár #define I8K_FN_MUTE		0x04
60a5afba16SPali Rohár #define I8K_FN_MASK		0x07
61a5afba16SPali Rohár #define I8K_FN_SHIFT		8
62a5afba16SPali Rohár 
63a5afba16SPali Rohár #define I8K_POWER_AC		0x05
64a5afba16SPali Rohár #define I8K_POWER_BATTERY	0x01
65a5afba16SPali Rohár 
66deeba244SArmin Wolf #define DELL_SMM_NO_TEMP	10
67deeba244SArmin Wolf #define DELL_SMM_NO_FANS	3
68deeba244SArmin Wolf 
69ba04d73cSArmin Wolf struct dell_smm_data {
70ba04d73cSArmin Wolf 	struct mutex i8k_mutex; /* lock for sensors writes */
71ba04d73cSArmin Wolf 	char bios_version[4];
72ba04d73cSArmin Wolf 	char bios_machineid[16];
73ba04d73cSArmin Wolf 	uint i8k_fan_mult;
74ba04d73cSArmin Wolf 	uint i8k_pwm_mult;
75ba04d73cSArmin Wolf 	uint i8k_fan_max;
76ba04d73cSArmin Wolf 	bool disallow_fan_type_call;
77ba04d73cSArmin Wolf 	bool disallow_fan_support;
78ba04d73cSArmin Wolf 	unsigned int manual_fan;
79ba04d73cSArmin Wolf 	unsigned int auto_fan;
80deeba244SArmin Wolf 	int temp_type[DELL_SMM_NO_TEMP];
81deeba244SArmin Wolf 	bool fan[DELL_SMM_NO_FANS];
82deeba244SArmin Wolf 	int fan_type[DELL_SMM_NO_FANS];
83b1986c8eSArmin Wolf 	int *fan_nominal_speed[DELL_SMM_NO_FANS];
84ba04d73cSArmin Wolf };
85a5afba16SPali Rohár 
86e0d3f7cbSArmin Wolf struct dell_smm_cooling_data {
87e0d3f7cbSArmin Wolf 	u8 fan_num;
88e0d3f7cbSArmin Wolf 	struct dell_smm_data *data;
89e0d3f7cbSArmin Wolf };
90e0d3f7cbSArmin Wolf 
91a5afba16SPali Rohár MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)");
92149ed3d4SPali Rohár MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
93039ae585SPali Rohár MODULE_DESCRIPTION("Dell laptop SMM BIOS hwmon driver");
94a5afba16SPali Rohár MODULE_LICENSE("GPL");
95a5afba16SPali Rohár MODULE_ALIAS("i8k");
96a5afba16SPali Rohár 
97a5afba16SPali Rohár static bool force;
987cd682b0SArmin Wolf module_param_unsafe(force, bool, 0);
997cd682b0SArmin Wolf MODULE_PARM_DESC(force, "Force loading without checking for supported models and features");
100a5afba16SPali Rohár 
101a5afba16SPali Rohár static bool ignore_dmi;
102a5afba16SPali Rohár module_param(ignore_dmi, bool, 0);
103a5afba16SPali Rohár MODULE_PARM_DESC(ignore_dmi, "Continue probing hardware even if DMI data does not match");
104a5afba16SPali Rohár 
105039ae585SPali Rohár #if IS_ENABLED(CONFIG_I8K)
1067613663cSPali Rohár static bool restricted = true;
107a5afba16SPali Rohár module_param(restricted, bool, 0);
1087613663cSPali Rohár MODULE_PARM_DESC(restricted, "Restrict fan control and serial number to CAP_SYS_ADMIN (default: 1)");
109a5afba16SPali Rohár 
110a5afba16SPali Rohár static bool power_status;
111a5afba16SPali Rohár module_param(power_status, bool, 0600);
1127613663cSPali Rohár MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k (default: 0)");
113039ae585SPali Rohár #endif
114a5afba16SPali Rohár 
115a5afba16SPali Rohár static uint fan_mult;
116a5afba16SPali Rohár module_param(fan_mult, uint, 0);
117a5afba16SPali Rohár MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with (default: autodetect)");
118a5afba16SPali Rohár 
119a5afba16SPali Rohár static uint fan_max;
120a5afba16SPali Rohár module_param(fan_max, uint, 0);
121a5afba16SPali Rohár MODULE_PARM_DESC(fan_max, "Maximum configurable fan speed (default: autodetect)");
122a5afba16SPali Rohár 
123a5afba16SPali Rohár struct smm_regs {
124a5afba16SPali Rohár 	unsigned int eax;
125565210c7SArmin Wolf 	unsigned int ebx;
126565210c7SArmin Wolf 	unsigned int ecx;
127565210c7SArmin Wolf 	unsigned int edx;
128565210c7SArmin Wolf 	unsigned int esi;
129565210c7SArmin Wolf 	unsigned int edi;
130565210c7SArmin Wolf } __packed;
131a5afba16SPali Rohár 
132deeba244SArmin Wolf static const char * const temp_labels[] = {
133deeba244SArmin Wolf 	"CPU",
134deeba244SArmin Wolf 	"GPU",
135deeba244SArmin Wolf 	"SODIMM",
136deeba244SArmin Wolf 	"Other",
137deeba244SArmin Wolf 	"Ambient",
138deeba244SArmin Wolf 	"Other",
139deeba244SArmin Wolf };
140deeba244SArmin Wolf 
141deeba244SArmin Wolf static const char * const fan_labels[] = {
142deeba244SArmin Wolf 	"Processor Fan",
143deeba244SArmin Wolf 	"Motherboard Fan",
144deeba244SArmin Wolf 	"Video Fan",
145deeba244SArmin Wolf 	"Power Supply Fan",
146deeba244SArmin Wolf 	"Chipset Fan",
147deeba244SArmin Wolf 	"Other Fan",
148deeba244SArmin Wolf };
149deeba244SArmin Wolf 
150deeba244SArmin Wolf static const char * const docking_labels[] = {
151deeba244SArmin Wolf 	"Docking Processor Fan",
152deeba244SArmin Wolf 	"Docking Motherboard Fan",
153deeba244SArmin Wolf 	"Docking Video Fan",
154deeba244SArmin Wolf 	"Docking Power Supply Fan",
155deeba244SArmin Wolf 	"Docking Chipset Fan",
156deeba244SArmin Wolf 	"Docking Other Fan",
157deeba244SArmin Wolf };
158deeba244SArmin Wolf 
159c9363cdfSArmin Wolf static inline const char __init *i8k_get_dmi_data(int field)
160a5afba16SPali Rohár {
161a5afba16SPali Rohár 	const char *dmi_data = dmi_get_system_info(field);
162a5afba16SPali Rohár 
163a5afba16SPali Rohár 	return dmi_data && *dmi_data ? dmi_data : "?";
164a5afba16SPali Rohár }
165a5afba16SPali Rohár 
166a5afba16SPali Rohár /*
167a5afba16SPali Rohár  * Call the System Management Mode BIOS. Code provided by Jonathan Buzzard.
168a5afba16SPali Rohár  */
16927046a3fSJuergen Gross static int i8k_smm_func(void *par)
170a5afba16SPali Rohár {
1718713b4a4SArmin Wolf 	ktime_t calltime = ktime_get();
17227046a3fSJuergen Gross 	struct smm_regs *regs = par;
173a5afba16SPali Rohár 	int eax = regs->eax;
1749d58bec0SPali Rohár 	int ebx = regs->ebx;
1758713b4a4SArmin Wolf 	long long duration;
1768713b4a4SArmin Wolf 	int rc;
1779d58bec0SPali Rohár 
178a5afba16SPali Rohár 	/* SMM requires CPU 0 */
17927046a3fSJuergen Gross 	if (smp_processor_id() != 0)
18027046a3fSJuergen Gross 		return -EBUSY;
181a5afba16SPali Rohár 
182a5afba16SPali Rohár #if defined(CONFIG_X86_64)
183a5afba16SPali Rohár 	asm volatile("pushq %%rax\n\t"
184a5afba16SPali Rohár 		"movl 0(%%rax),%%edx\n\t"
185a5afba16SPali Rohár 		"pushq %%rdx\n\t"
186a5afba16SPali Rohár 		"movl 4(%%rax),%%ebx\n\t"
187a5afba16SPali Rohár 		"movl 8(%%rax),%%ecx\n\t"
188a5afba16SPali Rohár 		"movl 12(%%rax),%%edx\n\t"
189a5afba16SPali Rohár 		"movl 16(%%rax),%%esi\n\t"
190a5afba16SPali Rohár 		"movl 20(%%rax),%%edi\n\t"
191a5afba16SPali Rohár 		"popq %%rax\n\t"
192a5afba16SPali Rohár 		"out %%al,$0xb2\n\t"
193a5afba16SPali Rohár 		"out %%al,$0x84\n\t"
194a5afba16SPali Rohár 		"xchgq %%rax,(%%rsp)\n\t"
195a5afba16SPali Rohár 		"movl %%ebx,4(%%rax)\n\t"
196a5afba16SPali Rohár 		"movl %%ecx,8(%%rax)\n\t"
197a5afba16SPali Rohár 		"movl %%edx,12(%%rax)\n\t"
198a5afba16SPali Rohár 		"movl %%esi,16(%%rax)\n\t"
199a5afba16SPali Rohár 		"movl %%edi,20(%%rax)\n\t"
200a5afba16SPali Rohár 		"popq %%rdx\n\t"
201a5afba16SPali Rohár 		"movl %%edx,0(%%rax)\n\t"
202a5afba16SPali Rohár 		"pushfq\n\t"
203a5afba16SPali Rohár 		"popq %%rax\n\t"
204a5afba16SPali Rohár 		"andl $1,%%eax\n"
205a5afba16SPali Rohár 		: "=a"(rc)
206a5afba16SPali Rohár 		:    "a"(regs)
207a5afba16SPali Rohár 		:    "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
208a5afba16SPali Rohár #else
209a5afba16SPali Rohár 	asm volatile("pushl %%eax\n\t"
210a5afba16SPali Rohár 	    "movl 0(%%eax),%%edx\n\t"
211a5afba16SPali Rohár 	    "push %%edx\n\t"
212a5afba16SPali Rohár 	    "movl 4(%%eax),%%ebx\n\t"
213a5afba16SPali Rohár 	    "movl 8(%%eax),%%ecx\n\t"
214a5afba16SPali Rohár 	    "movl 12(%%eax),%%edx\n\t"
215a5afba16SPali Rohár 	    "movl 16(%%eax),%%esi\n\t"
216a5afba16SPali Rohár 	    "movl 20(%%eax),%%edi\n\t"
217a5afba16SPali Rohár 	    "popl %%eax\n\t"
218a5afba16SPali Rohár 	    "out %%al,$0xb2\n\t"
219a5afba16SPali Rohár 	    "out %%al,$0x84\n\t"
220a5afba16SPali Rohár 	    "xchgl %%eax,(%%esp)\n\t"
221a5afba16SPali Rohár 	    "movl %%ebx,4(%%eax)\n\t"
222a5afba16SPali Rohár 	    "movl %%ecx,8(%%eax)\n\t"
223a5afba16SPali Rohár 	    "movl %%edx,12(%%eax)\n\t"
224a5afba16SPali Rohár 	    "movl %%esi,16(%%eax)\n\t"
225a5afba16SPali Rohár 	    "movl %%edi,20(%%eax)\n\t"
226a5afba16SPali Rohár 	    "popl %%edx\n\t"
227a5afba16SPali Rohár 	    "movl %%edx,0(%%eax)\n\t"
228a5afba16SPali Rohár 	    "lahf\n\t"
229a5afba16SPali Rohár 	    "shrl $8,%%eax\n\t"
230a5afba16SPali Rohár 	    "andl $1,%%eax\n"
231a5afba16SPali Rohár 	    : "=a"(rc)
232a5afba16SPali Rohár 	    :    "a"(regs)
233a5afba16SPali Rohár 	    :    "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
234a5afba16SPali Rohár #endif
235a5afba16SPali Rohár 	if (rc != 0 || (regs->eax & 0xffff) == 0xffff || regs->eax == eax)
236a5afba16SPali Rohár 		rc = -EINVAL;
237a5afba16SPali Rohár 
2388713b4a4SArmin Wolf 	duration = ktime_us_delta(ktime_get(), calltime);
2398713b4a4SArmin Wolf 	pr_debug("smm(0x%.4x 0x%.4x) = 0x%.4x  (took %7lld usecs)\n", eax, ebx,
2409d58bec0SPali Rohár 		 (rc ? 0xffff : regs->eax & 0xffff), duration);
2419d58bec0SPali Rohár 
242a5afba16SPali Rohár 	return rc;
243a5afba16SPali Rohár }
244a5afba16SPali Rohár 
245a5afba16SPali Rohár /*
24627046a3fSJuergen Gross  * Call the System Management Mode BIOS.
24727046a3fSJuergen Gross  */
24827046a3fSJuergen Gross static int i8k_smm(struct smm_regs *regs)
24927046a3fSJuergen Gross {
25027046a3fSJuergen Gross 	int ret;
25127046a3fSJuergen Gross 
252e104d530SSebastian Andrzej Siewior 	cpus_read_lock();
25327046a3fSJuergen Gross 	ret = smp_call_on_cpu(0, i8k_smm_func, regs, true);
254e104d530SSebastian Andrzej Siewior 	cpus_read_unlock();
25527046a3fSJuergen Gross 
25627046a3fSJuergen Gross 	return ret;
25727046a3fSJuergen Gross }
25827046a3fSJuergen Gross 
25927046a3fSJuergen Gross /*
260a5afba16SPali Rohár  * Read the fan status.
261a5afba16SPali Rohár  */
2624d9983deSArmin Wolf static int i8k_get_fan_status(const struct dell_smm_data *data, u8 fan)
263a5afba16SPali Rohár {
2644d9983deSArmin Wolf 	struct smm_regs regs = {
2654d9983deSArmin Wolf 		.eax = I8K_SMM_GET_FAN,
2664d9983deSArmin Wolf 		.ebx = fan,
2674d9983deSArmin Wolf 	};
268a5afba16SPali Rohár 
269ba04d73cSArmin Wolf 	if (data->disallow_fan_support)
270f480ea90SPali Rohár 		return -EINVAL;
271f480ea90SPali Rohár 
272a5afba16SPali Rohár 	return i8k_smm(&regs) ? : regs.eax & 0xff;
273a5afba16SPali Rohár }
274a5afba16SPali Rohár 
275a5afba16SPali Rohár /*
276a5afba16SPali Rohár  * Read the fan speed in RPM.
277a5afba16SPali Rohár  */
2784d9983deSArmin Wolf static int i8k_get_fan_speed(const struct dell_smm_data *data, u8 fan)
279a5afba16SPali Rohár {
2804d9983deSArmin Wolf 	struct smm_regs regs = {
2814d9983deSArmin Wolf 		.eax = I8K_SMM_GET_SPEED,
2824d9983deSArmin Wolf 		.ebx = fan,
2834d9983deSArmin Wolf 	};
284a5afba16SPali Rohár 
285ba04d73cSArmin Wolf 	if (data->disallow_fan_support)
286f480ea90SPali Rohár 		return -EINVAL;
287f480ea90SPali Rohár 
288ba04d73cSArmin Wolf 	return i8k_smm(&regs) ? : (regs.eax & 0xffff) * data->i8k_fan_mult;
289a5afba16SPali Rohár }
290a5afba16SPali Rohár 
291a5afba16SPali Rohár /*
292a5afba16SPali Rohár  * Read the fan type.
293a5afba16SPali Rohár  */
2944d9983deSArmin Wolf static int _i8k_get_fan_type(const struct dell_smm_data *data, u8 fan)
295a5afba16SPali Rohár {
2964d9983deSArmin Wolf 	struct smm_regs regs = {
2974d9983deSArmin Wolf 		.eax = I8K_SMM_GET_FAN_TYPE,
2984d9983deSArmin Wolf 		.ebx = fan,
2994d9983deSArmin Wolf 	};
300a5afba16SPali Rohár 
301ba04d73cSArmin Wolf 	if (data->disallow_fan_support || data->disallow_fan_type_call)
3022744d2fdSPali Rohár 		return -EINVAL;
3032744d2fdSPali Rohár 
304a5afba16SPali Rohár 	return i8k_smm(&regs) ? : regs.eax & 0xff;
305a5afba16SPali Rohár }
306a5afba16SPali Rohár 
3074d9983deSArmin Wolf static int i8k_get_fan_type(struct dell_smm_data *data, u8 fan)
3085ce91714SPali Rohár {
3095ce91714SPali Rohár 	/* I8K_SMM_GET_FAN_TYPE SMM call is expensive, so cache values */
310deeba244SArmin Wolf 	if (data->fan_type[fan] == INT_MIN)
311deeba244SArmin Wolf 		data->fan_type[fan] = _i8k_get_fan_type(data, fan);
3125ce91714SPali Rohár 
313deeba244SArmin Wolf 	return data->fan_type[fan];
3145ce91714SPali Rohár }
3155ce91714SPali Rohár 
316a5afba16SPali Rohár /*
317a5afba16SPali Rohár  * Read the fan nominal rpm for specific fan speed.
318a5afba16SPali Rohár  */
3194d9983deSArmin Wolf static int __init i8k_get_fan_nominal_speed(const struct dell_smm_data *data, u8 fan, int speed)
320a5afba16SPali Rohár {
3214d9983deSArmin Wolf 	struct smm_regs regs = {
3224d9983deSArmin Wolf 		.eax = I8K_SMM_GET_NOM_SPEED,
3234d9983deSArmin Wolf 		.ebx = fan | (speed << 8),
3244d9983deSArmin Wolf 	};
325a5afba16SPali Rohár 
326ba04d73cSArmin Wolf 	if (data->disallow_fan_support)
327f480ea90SPali Rohár 		return -EINVAL;
328f480ea90SPali Rohár 
329*4fc1a51cSArmin Wolf 	return i8k_smm(&regs) ? : (regs.eax & 0xffff);
330a5afba16SPali Rohár }
331a5afba16SPali Rohár 
332a5afba16SPali Rohár /*
333afe45277SGiovanni Mascellani  * Enable or disable automatic BIOS fan control support
334afe45277SGiovanni Mascellani  */
335ba04d73cSArmin Wolf static int i8k_enable_fan_auto_mode(const struct dell_smm_data *data, bool enable)
336afe45277SGiovanni Mascellani {
337afe45277SGiovanni Mascellani 	struct smm_regs regs = { };
338afe45277SGiovanni Mascellani 
339ba04d73cSArmin Wolf 	if (data->disallow_fan_support)
340afe45277SGiovanni Mascellani 		return -EINVAL;
341afe45277SGiovanni Mascellani 
342ba04d73cSArmin Wolf 	regs.eax = enable ? data->auto_fan : data->manual_fan;
343afe45277SGiovanni Mascellani 	return i8k_smm(&regs);
344afe45277SGiovanni Mascellani }
345afe45277SGiovanni Mascellani 
346afe45277SGiovanni Mascellani /*
347c0d79987SArmin Wolf  * Set the fan speed (off, low, high, ...).
348a5afba16SPali Rohár  */
3494d9983deSArmin Wolf static int i8k_set_fan(const struct dell_smm_data *data, u8 fan, int speed)
350a5afba16SPali Rohár {
351a5afba16SPali Rohár 	struct smm_regs regs = { .eax = I8K_SMM_SET_FAN, };
352a5afba16SPali Rohár 
353ba04d73cSArmin Wolf 	if (data->disallow_fan_support)
354f480ea90SPali Rohár 		return -EINVAL;
355f480ea90SPali Rohár 
356ba04d73cSArmin Wolf 	speed = (speed < 0) ? 0 : ((speed > data->i8k_fan_max) ? data->i8k_fan_max : speed);
3574d9983deSArmin Wolf 	regs.ebx = fan | (speed << 8);
358a5afba16SPali Rohár 
359c0d79987SArmin Wolf 	return i8k_smm(&regs);
360a5afba16SPali Rohár }
361a5afba16SPali Rohár 
3624d9983deSArmin Wolf static int __init i8k_get_temp_type(u8 sensor)
363a5afba16SPali Rohár {
3644d9983deSArmin Wolf 	struct smm_regs regs = {
3654d9983deSArmin Wolf 		.eax = I8K_SMM_GET_TEMP_TYPE,
3664d9983deSArmin Wolf 		.ebx = sensor,
3674d9983deSArmin Wolf 	};
368a5afba16SPali Rohár 
369a5afba16SPali Rohár 	return i8k_smm(&regs) ? : regs.eax & 0xff;
370a5afba16SPali Rohár }
371a5afba16SPali Rohár 
372a5afba16SPali Rohár /*
373a5afba16SPali Rohár  * Read the cpu temperature.
374a5afba16SPali Rohár  */
3754d9983deSArmin Wolf static int _i8k_get_temp(u8 sensor)
376a5afba16SPali Rohár {
377a5afba16SPali Rohár 	struct smm_regs regs = {
378a5afba16SPali Rohár 		.eax = I8K_SMM_GET_TEMP,
3794d9983deSArmin Wolf 		.ebx = sensor,
380a5afba16SPali Rohár 	};
381a5afba16SPali Rohár 
382a5afba16SPali Rohár 	return i8k_smm(&regs) ? : regs.eax & 0xff;
383a5afba16SPali Rohár }
384a5afba16SPali Rohár 
3854d9983deSArmin Wolf static int i8k_get_temp(u8 sensor)
386a5afba16SPali Rohár {
387a5afba16SPali Rohár 	int temp = _i8k_get_temp(sensor);
388a5afba16SPali Rohár 
389a5afba16SPali Rohár 	/*
390a5afba16SPali Rohár 	 * Sometimes the temperature sensor returns 0x99, which is out of range.
391a5afba16SPali Rohár 	 * In this case we retry (once) before returning an error.
392a5afba16SPali Rohár 	 # 1003655137 00000058 00005a4b
393a5afba16SPali Rohár 	 # 1003655138 00000099 00003a80 <--- 0x99 = 153 degrees
394a5afba16SPali Rohár 	 # 1003655139 00000054 00005c52
395a5afba16SPali Rohár 	 */
396a5afba16SPali Rohár 	if (temp == 0x99) {
397a5afba16SPali Rohár 		msleep(100);
398a5afba16SPali Rohár 		temp = _i8k_get_temp(sensor);
399a5afba16SPali Rohár 	}
400a5afba16SPali Rohár 	/*
401a5afba16SPali Rohár 	 * Return -ENODATA for all invalid temperatures.
402a5afba16SPali Rohár 	 *
403a5afba16SPali Rohár 	 * Known instances are the 0x99 value as seen above as well as
404a5afba16SPali Rohár 	 * 0xc1 (193), which may be returned when trying to read the GPU
405a5afba16SPali Rohár 	 * temperature if the system supports a GPU and it is currently
406a5afba16SPali Rohár 	 * turned off.
407a5afba16SPali Rohár 	 */
408a5afba16SPali Rohár 	if (temp > I8K_MAX_TEMP)
409a5afba16SPali Rohár 		return -ENODATA;
410a5afba16SPali Rohár 
411a5afba16SPali Rohár 	return temp;
412a5afba16SPali Rohár }
413a5afba16SPali Rohár 
414c9363cdfSArmin Wolf static int __init i8k_get_dell_signature(int req_fn)
415a5afba16SPali Rohár {
416a5afba16SPali Rohár 	struct smm_regs regs = { .eax = req_fn, };
417a5afba16SPali Rohár 	int rc;
418a5afba16SPali Rohár 
419a5afba16SPali Rohár 	rc = i8k_smm(&regs);
420a5afba16SPali Rohár 	if (rc < 0)
421a5afba16SPali Rohár 		return rc;
422a5afba16SPali Rohár 
423a5afba16SPali Rohár 	return regs.eax == 1145651527 && regs.edx == 1145392204 ? 0 : -1;
424a5afba16SPali Rohár }
425a5afba16SPali Rohár 
426039ae585SPali Rohár #if IS_ENABLED(CONFIG_I8K)
427039ae585SPali Rohár 
428039ae585SPali Rohár /*
429039ae585SPali Rohár  * Read the Fn key status.
430039ae585SPali Rohár  */
431039ae585SPali Rohár static int i8k_get_fn_status(void)
432039ae585SPali Rohár {
433039ae585SPali Rohár 	struct smm_regs regs = { .eax = I8K_SMM_FN_STATUS, };
434039ae585SPali Rohár 	int rc;
435039ae585SPali Rohár 
436039ae585SPali Rohár 	rc = i8k_smm(&regs);
437039ae585SPali Rohár 	if (rc < 0)
438039ae585SPali Rohár 		return rc;
439039ae585SPali Rohár 
440039ae585SPali Rohár 	switch ((regs.eax >> I8K_FN_SHIFT) & I8K_FN_MASK) {
441039ae585SPali Rohár 	case I8K_FN_UP:
442039ae585SPali Rohár 		return I8K_VOL_UP;
443039ae585SPali Rohár 	case I8K_FN_DOWN:
444039ae585SPali Rohár 		return I8K_VOL_DOWN;
445039ae585SPali Rohár 	case I8K_FN_MUTE:
446039ae585SPali Rohár 		return I8K_VOL_MUTE;
447039ae585SPali Rohár 	default:
448039ae585SPali Rohár 		return 0;
449039ae585SPali Rohár 	}
450039ae585SPali Rohár }
451039ae585SPali Rohár 
452039ae585SPali Rohár /*
453039ae585SPali Rohár  * Read the power status.
454039ae585SPali Rohár  */
455039ae585SPali Rohár static int i8k_get_power_status(void)
456039ae585SPali Rohár {
457039ae585SPali Rohár 	struct smm_regs regs = { .eax = I8K_SMM_POWER_STATUS, };
458039ae585SPali Rohár 	int rc;
459039ae585SPali Rohár 
460039ae585SPali Rohár 	rc = i8k_smm(&regs);
461039ae585SPali Rohár 	if (rc < 0)
462039ae585SPali Rohár 		return rc;
463039ae585SPali Rohár 
464039ae585SPali Rohár 	return (regs.eax & 0xff) == I8K_POWER_AC ? I8K_AC : I8K_BATTERY;
465039ae585SPali Rohár }
466039ae585SPali Rohár 
467039ae585SPali Rohár /*
468039ae585SPali Rohár  * Procfs interface
469039ae585SPali Rohár  */
470039ae585SPali Rohár 
47187b93329SArmin Wolf static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
472a5afba16SPali Rohár {
473359745d7SMuchun Song 	struct dell_smm_data *data = pde_data(file_inode(fp));
474a5afba16SPali Rohár 	int __user *argp = (int __user *)arg;
47587b93329SArmin Wolf 	int speed, err;
47687b93329SArmin Wolf 	int val = 0;
477a5afba16SPali Rohár 
478a5afba16SPali Rohár 	if (!argp)
479a5afba16SPali Rohár 		return -EINVAL;
480a5afba16SPali Rohár 
481a5afba16SPali Rohár 	switch (cmd) {
482a5afba16SPali Rohár 	case I8K_BIOS_VERSION:
483ba04d73cSArmin Wolf 		if (!isdigit(data->bios_version[0]) || !isdigit(data->bios_version[1]) ||
484ba04d73cSArmin Wolf 		    !isdigit(data->bios_version[2]))
485053ea640SPali Rohár 			return -EINVAL;
486053ea640SPali Rohár 
487ba04d73cSArmin Wolf 		val = (data->bios_version[0] << 16) |
488ba04d73cSArmin Wolf 				(data->bios_version[1] << 8) | data->bios_version[2];
489a5afba16SPali Rohár 
49002405387SArmin Wolf 		if (copy_to_user(argp, &val, sizeof(val)))
49102405387SArmin Wolf 			return -EFAULT;
49202405387SArmin Wolf 
49302405387SArmin Wolf 		return 0;
494a5afba16SPali Rohár 	case I8K_MACHINE_ID:
4957613663cSPali Rohár 		if (restricted && !capable(CAP_SYS_ADMIN))
4967613663cSPali Rohár 			return -EPERM;
4977613663cSPali Rohár 
49802405387SArmin Wolf 		if (copy_to_user(argp, data->bios_machineid, sizeof(data->bios_machineid)))
49902405387SArmin Wolf 			return -EFAULT;
500a5afba16SPali Rohár 
50102405387SArmin Wolf 		return 0;
502a5afba16SPali Rohár 	case I8K_FN_STATUS:
503a5afba16SPali Rohár 		val = i8k_get_fn_status();
504a5afba16SPali Rohár 		break;
505a5afba16SPali Rohár 
506a5afba16SPali Rohár 	case I8K_POWER_STATUS:
507a5afba16SPali Rohár 		val = i8k_get_power_status();
508a5afba16SPali Rohár 		break;
509a5afba16SPali Rohár 
510a5afba16SPali Rohár 	case I8K_GET_TEMP:
511a5afba16SPali Rohár 		val = i8k_get_temp(0);
512a5afba16SPali Rohár 		break;
513a5afba16SPali Rohár 
514a5afba16SPali Rohár 	case I8K_GET_SPEED:
515a5afba16SPali Rohár 		if (copy_from_user(&val, argp, sizeof(int)))
516a5afba16SPali Rohár 			return -EFAULT;
517a5afba16SPali Rohár 
5184d9983deSArmin Wolf 		if (val > U8_MAX || val < 0)
5194d9983deSArmin Wolf 			return -EINVAL;
5204d9983deSArmin Wolf 
521ba04d73cSArmin Wolf 		val = i8k_get_fan_speed(data, val);
522a5afba16SPali Rohár 		break;
523a5afba16SPali Rohár 
524a5afba16SPali Rohár 	case I8K_GET_FAN:
525a5afba16SPali Rohár 		if (copy_from_user(&val, argp, sizeof(int)))
526a5afba16SPali Rohár 			return -EFAULT;
527a5afba16SPali Rohár 
5284d9983deSArmin Wolf 		if (val > U8_MAX || val < 0)
5294d9983deSArmin Wolf 			return -EINVAL;
5304d9983deSArmin Wolf 
531ba04d73cSArmin Wolf 		val = i8k_get_fan_status(data, val);
532a5afba16SPali Rohár 		break;
533a5afba16SPali Rohár 
534a5afba16SPali Rohár 	case I8K_SET_FAN:
535a5afba16SPali Rohár 		if (restricted && !capable(CAP_SYS_ADMIN))
536a5afba16SPali Rohár 			return -EPERM;
537a5afba16SPali Rohár 
538a5afba16SPali Rohár 		if (copy_from_user(&val, argp, sizeof(int)))
539a5afba16SPali Rohár 			return -EFAULT;
540a5afba16SPali Rohár 
5414d9983deSArmin Wolf 		if (val > U8_MAX || val < 0)
5424d9983deSArmin Wolf 			return -EINVAL;
5434d9983deSArmin Wolf 
544a5afba16SPali Rohár 		if (copy_from_user(&speed, argp + 1, sizeof(int)))
545a5afba16SPali Rohár 			return -EFAULT;
546a5afba16SPali Rohár 
54787b93329SArmin Wolf 		mutex_lock(&data->i8k_mutex);
548c0d79987SArmin Wolf 		err = i8k_set_fan(data, val, speed);
549c0d79987SArmin Wolf 		if (err < 0)
55087b93329SArmin Wolf 			val = err;
55187b93329SArmin Wolf 		else
552c0d79987SArmin Wolf 			val = i8k_get_fan_status(data, val);
55387b93329SArmin Wolf 		mutex_unlock(&data->i8k_mutex);
554a5afba16SPali Rohár 		break;
555a5afba16SPali Rohár 
556a5afba16SPali Rohár 	default:
557e64325e8SArmin Wolf 		return -ENOIOCTLCMD;
558a5afba16SPali Rohár 	}
559a5afba16SPali Rohár 
560a5afba16SPali Rohár 	if (val < 0)
561a5afba16SPali Rohár 		return val;
562a5afba16SPali Rohár 
563a5afba16SPali Rohár 	if (copy_to_user(argp, &val, sizeof(int)))
564a5afba16SPali Rohár 		return -EFAULT;
565a5afba16SPali Rohár 
566a5afba16SPali Rohár 	return 0;
567a5afba16SPali Rohár }
568a5afba16SPali Rohár 
569a5afba16SPali Rohár /*
570a5afba16SPali Rohár  * Print the information for /proc/i8k.
571a5afba16SPali Rohár  */
572a5afba16SPali Rohár static int i8k_proc_show(struct seq_file *seq, void *offset)
573a5afba16SPali Rohár {
574ba04d73cSArmin Wolf 	struct dell_smm_data *data = seq->private;
575a5afba16SPali Rohár 	int fn_key, cpu_temp, ac_power;
576a5afba16SPali Rohár 	int left_fan, right_fan, left_speed, right_speed;
577a5afba16SPali Rohár 
578a5afba16SPali Rohár 	cpu_temp	= i8k_get_temp(0);				/* 11100 µs */
579ba04d73cSArmin Wolf 	left_fan	= i8k_get_fan_status(data, I8K_FAN_LEFT);	/*   580 µs */
580ba04d73cSArmin Wolf 	right_fan	= i8k_get_fan_status(data, I8K_FAN_RIGHT);	/*   580 µs */
581ba04d73cSArmin Wolf 	left_speed	= i8k_get_fan_speed(data, I8K_FAN_LEFT);	/*   580 µs */
582ba04d73cSArmin Wolf 	right_speed	= i8k_get_fan_speed(data, I8K_FAN_RIGHT);	/*   580 µs */
583a5afba16SPali Rohár 	fn_key		= i8k_get_fn_status();				/*   750 µs */
584a5afba16SPali Rohár 	if (power_status)
585a5afba16SPali Rohár 		ac_power = i8k_get_power_status();			/* 14700 µs */
586a5afba16SPali Rohár 	else
587a5afba16SPali Rohár 		ac_power = -1;
588a5afba16SPali Rohár 
589a5afba16SPali Rohár 	/*
590a5afba16SPali Rohár 	 * Info:
591a5afba16SPali Rohár 	 *
592a5afba16SPali Rohár 	 * 1)  Format version (this will change if format changes)
593a5afba16SPali Rohár 	 * 2)  BIOS version
594a5afba16SPali Rohár 	 * 3)  BIOS machine ID
595a5afba16SPali Rohár 	 * 4)  Cpu temperature
596a5afba16SPali Rohár 	 * 5)  Left fan status
597a5afba16SPali Rohár 	 * 6)  Right fan status
598a5afba16SPali Rohár 	 * 7)  Left fan speed
599a5afba16SPali Rohár 	 * 8)  Right fan speed
600a5afba16SPali Rohár 	 * 9)  AC power
601a5afba16SPali Rohár 	 * 10) Fn Key status
602a5afba16SPali Rohár 	 */
603a5afba16SPali Rohár 	seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n",
604a5afba16SPali Rohár 		   I8K_PROC_FMT,
605ba04d73cSArmin Wolf 		   data->bios_version,
606ba04d73cSArmin Wolf 		   (restricted && !capable(CAP_SYS_ADMIN)) ? "-1" : data->bios_machineid,
607a5afba16SPali Rohár 		   cpu_temp,
608a5afba16SPali Rohár 		   left_fan, right_fan, left_speed, right_speed,
609a5afba16SPali Rohár 		   ac_power, fn_key);
610a5afba16SPali Rohár 
611a5afba16SPali Rohár 	return 0;
612a5afba16SPali Rohár }
613a5afba16SPali Rohár 
614a5afba16SPali Rohár static int i8k_open_fs(struct inode *inode, struct file *file)
615a5afba16SPali Rohár {
616359745d7SMuchun Song 	return single_open(file, i8k_proc_show, pde_data(inode));
617a5afba16SPali Rohár }
618a5afba16SPali Rohár 
61997a32539SAlexey Dobriyan static const struct proc_ops i8k_proc_ops = {
62097a32539SAlexey Dobriyan 	.proc_open	= i8k_open_fs,
62197a32539SAlexey Dobriyan 	.proc_read	= seq_read,
62297a32539SAlexey Dobriyan 	.proc_lseek	= seq_lseek,
62397a32539SAlexey Dobriyan 	.proc_release	= single_release,
62497a32539SAlexey Dobriyan 	.proc_ioctl	= i8k_ioctl,
625039ae585SPali Rohár };
626039ae585SPali Rohár 
627a2cb66b4SArmin Wolf static void i8k_exit_procfs(void *param)
628039ae585SPali Rohár {
629039ae585SPali Rohár 	remove_proc_entry("i8k", NULL);
630039ae585SPali Rohár }
631039ae585SPali Rohár 
632a2cb66b4SArmin Wolf static void __init i8k_init_procfs(struct device *dev)
633039ae585SPali Rohár {
634ba04d73cSArmin Wolf 	struct dell_smm_data *data = dev_get_drvdata(dev);
635ba04d73cSArmin Wolf 
636dbd3e6eaSArmin Wolf 	/* Only register exit function if creation was successful */
637dbd3e6eaSArmin Wolf 	if (proc_create_data("i8k", 0, NULL, &i8k_proc_ops, data))
638a2cb66b4SArmin Wolf 		devm_add_action_or_reset(dev, i8k_exit_procfs, NULL);
639039ae585SPali Rohár }
640039ae585SPali Rohár 
641a2cb66b4SArmin Wolf #else
642a2cb66b4SArmin Wolf 
643a2cb66b4SArmin Wolf static void __init i8k_init_procfs(struct device *dev)
644039ae585SPali Rohár {
645039ae585SPali Rohár }
646039ae585SPali Rohár 
647039ae585SPali Rohár #endif
648a5afba16SPali Rohár 
649e0d3f7cbSArmin Wolf static int dell_smm_get_max_state(struct thermal_cooling_device *dev, unsigned long *state)
650e0d3f7cbSArmin Wolf {
651e0d3f7cbSArmin Wolf 	struct dell_smm_cooling_data *cdata = dev->devdata;
652e0d3f7cbSArmin Wolf 
653e0d3f7cbSArmin Wolf 	*state = cdata->data->i8k_fan_max;
654e0d3f7cbSArmin Wolf 
655e0d3f7cbSArmin Wolf 	return 0;
656e0d3f7cbSArmin Wolf }
657e0d3f7cbSArmin Wolf 
658e0d3f7cbSArmin Wolf static int dell_smm_get_cur_state(struct thermal_cooling_device *dev, unsigned long *state)
659e0d3f7cbSArmin Wolf {
660e0d3f7cbSArmin Wolf 	struct dell_smm_cooling_data *cdata = dev->devdata;
661e0d3f7cbSArmin Wolf 	int ret;
662e0d3f7cbSArmin Wolf 
663e0d3f7cbSArmin Wolf 	ret = i8k_get_fan_status(cdata->data, cdata->fan_num);
664e0d3f7cbSArmin Wolf 	if (ret < 0)
665e0d3f7cbSArmin Wolf 		return ret;
666e0d3f7cbSArmin Wolf 
667e0d3f7cbSArmin Wolf 	*state = ret;
668e0d3f7cbSArmin Wolf 
669e0d3f7cbSArmin Wolf 	return 0;
670e0d3f7cbSArmin Wolf }
671e0d3f7cbSArmin Wolf 
672e0d3f7cbSArmin Wolf static int dell_smm_set_cur_state(struct thermal_cooling_device *dev, unsigned long state)
673e0d3f7cbSArmin Wolf {
674e0d3f7cbSArmin Wolf 	struct dell_smm_cooling_data *cdata = dev->devdata;
675e0d3f7cbSArmin Wolf 	struct dell_smm_data *data = cdata->data;
676e0d3f7cbSArmin Wolf 	int ret;
677e0d3f7cbSArmin Wolf 
678e0d3f7cbSArmin Wolf 	if (state > data->i8k_fan_max)
679e0d3f7cbSArmin Wolf 		return -EINVAL;
680e0d3f7cbSArmin Wolf 
681e0d3f7cbSArmin Wolf 	mutex_lock(&data->i8k_mutex);
682e0d3f7cbSArmin Wolf 	ret = i8k_set_fan(data, cdata->fan_num, (int)state);
683e0d3f7cbSArmin Wolf 	mutex_unlock(&data->i8k_mutex);
684e0d3f7cbSArmin Wolf 
685e0d3f7cbSArmin Wolf 	return ret;
686e0d3f7cbSArmin Wolf }
687e0d3f7cbSArmin Wolf 
688e0d3f7cbSArmin Wolf static const struct thermal_cooling_device_ops dell_smm_cooling_ops = {
689e0d3f7cbSArmin Wolf 	.get_max_state = dell_smm_get_max_state,
690e0d3f7cbSArmin Wolf 	.get_cur_state = dell_smm_get_cur_state,
691e0d3f7cbSArmin Wolf 	.set_cur_state = dell_smm_set_cur_state,
692e0d3f7cbSArmin Wolf };
693a5afba16SPali Rohár 
694deeba244SArmin Wolf static umode_t dell_smm_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr,
695deeba244SArmin Wolf 				   int channel)
696a5afba16SPali Rohár {
697deeba244SArmin Wolf 	const struct dell_smm_data *data = drvdata;
698a5afba16SPali Rohár 
699deeba244SArmin Wolf 	switch (type) {
700deeba244SArmin Wolf 	case hwmon_temp:
701deeba244SArmin Wolf 		switch (attr) {
702deeba244SArmin Wolf 		case hwmon_temp_input:
703c82fdd42SArmin Wolf 			/* _i8k_get_temp() is fine since we do not care about the actual value */
704c82fdd42SArmin Wolf 			if (data->temp_type[channel] >= 0 || _i8k_get_temp(channel) >= 0)
705c82fdd42SArmin Wolf 				return 0444;
706c82fdd42SArmin Wolf 
707c82fdd42SArmin Wolf 			break;
708deeba244SArmin Wolf 		case hwmon_temp_label:
709deeba244SArmin Wolf 			if (data->temp_type[channel] >= 0)
710deeba244SArmin Wolf 				return 0444;
711deeba244SArmin Wolf 
712deeba244SArmin Wolf 			break;
713deeba244SArmin Wolf 		default:
714deeba244SArmin Wolf 			break;
715deeba244SArmin Wolf 		}
716deeba244SArmin Wolf 		break;
717deeba244SArmin Wolf 	case hwmon_fan:
718deeba244SArmin Wolf 		if (data->disallow_fan_support)
719deeba244SArmin Wolf 			break;
720deeba244SArmin Wolf 
721deeba244SArmin Wolf 		switch (attr) {
722deeba244SArmin Wolf 		case hwmon_fan_input:
723deeba244SArmin Wolf 			if (data->fan[channel])
724deeba244SArmin Wolf 				return 0444;
725deeba244SArmin Wolf 
726deeba244SArmin Wolf 			break;
727deeba244SArmin Wolf 		case hwmon_fan_label:
728deeba244SArmin Wolf 			if (data->fan[channel] && !data->disallow_fan_type_call)
729deeba244SArmin Wolf 				return 0444;
730deeba244SArmin Wolf 
731deeba244SArmin Wolf 			break;
732b1986c8eSArmin Wolf 		case hwmon_fan_min:
733b1986c8eSArmin Wolf 		case hwmon_fan_max:
734b1986c8eSArmin Wolf 		case hwmon_fan_target:
735b1986c8eSArmin Wolf 			if (data->fan_nominal_speed[channel])
736b1986c8eSArmin Wolf 				return 0444;
737b1986c8eSArmin Wolf 
738b1986c8eSArmin Wolf 			break;
739deeba244SArmin Wolf 		default:
740deeba244SArmin Wolf 			break;
741deeba244SArmin Wolf 		}
742deeba244SArmin Wolf 		break;
743deeba244SArmin Wolf 	case hwmon_pwm:
744deeba244SArmin Wolf 		if (data->disallow_fan_support)
745deeba244SArmin Wolf 			break;
746deeba244SArmin Wolf 
747deeba244SArmin Wolf 		switch (attr) {
748deeba244SArmin Wolf 		case hwmon_pwm_input:
749deeba244SArmin Wolf 			if (data->fan[channel])
750deeba244SArmin Wolf 				return 0644;
751deeba244SArmin Wolf 
752deeba244SArmin Wolf 			break;
753deeba244SArmin Wolf 		case hwmon_pwm_enable:
754deeba244SArmin Wolf 			if (data->auto_fan)
755deeba244SArmin Wolf 				/*
756deeba244SArmin Wolf 				 * There is no command for retrieve the current status
757deeba244SArmin Wolf 				 * from BIOS, and userspace/firmware itself can change
758deeba244SArmin Wolf 				 * it.
759deeba244SArmin Wolf 				 * Thus we can only provide write-only access for now.
760deeba244SArmin Wolf 				 */
761deeba244SArmin Wolf 				return 0200;
762deeba244SArmin Wolf 
763deeba244SArmin Wolf 			break;
764deeba244SArmin Wolf 		default:
765deeba244SArmin Wolf 			break;
766deeba244SArmin Wolf 		}
767deeba244SArmin Wolf 		break;
768deeba244SArmin Wolf 	default:
769deeba244SArmin Wolf 		break;
770a5afba16SPali Rohár 	}
771a5afba16SPali Rohár 
772deeba244SArmin Wolf 	return 0;
773a5afba16SPali Rohár }
774a5afba16SPali Rohár 
775deeba244SArmin Wolf static int dell_smm_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
776deeba244SArmin Wolf 			 long *val)
777a5afba16SPali Rohár {
778ba04d73cSArmin Wolf 	struct dell_smm_data *data = dev_get_drvdata(dev);
779*4fc1a51cSArmin Wolf 	int mult = data->i8k_fan_mult;
780deeba244SArmin Wolf 	int ret;
781a5afba16SPali Rohár 
782deeba244SArmin Wolf 	switch (type) {
783deeba244SArmin Wolf 	case hwmon_temp:
784deeba244SArmin Wolf 		switch (attr) {
785deeba244SArmin Wolf 		case hwmon_temp_input:
786deeba244SArmin Wolf 			ret = i8k_get_temp(channel);
787deeba244SArmin Wolf 			if (ret < 0)
788deeba244SArmin Wolf 				return ret;
789deeba244SArmin Wolf 
790deeba244SArmin Wolf 			*val = ret * 1000;
791deeba244SArmin Wolf 
792deeba244SArmin Wolf 			return 0;
793deeba244SArmin Wolf 		default:
794deeba244SArmin Wolf 			break;
795deeba244SArmin Wolf 		}
796deeba244SArmin Wolf 		break;
797deeba244SArmin Wolf 	case hwmon_fan:
798deeba244SArmin Wolf 		switch (attr) {
799deeba244SArmin Wolf 		case hwmon_fan_input:
800deeba244SArmin Wolf 			ret = i8k_get_fan_speed(data, channel);
801deeba244SArmin Wolf 			if (ret < 0)
802deeba244SArmin Wolf 				return ret;
803deeba244SArmin Wolf 
804deeba244SArmin Wolf 			*val = ret;
805deeba244SArmin Wolf 
806deeba244SArmin Wolf 			return 0;
807b1986c8eSArmin Wolf 		case hwmon_fan_min:
808*4fc1a51cSArmin Wolf 			*val = data->fan_nominal_speed[channel][0] * mult;
809b1986c8eSArmin Wolf 
810b1986c8eSArmin Wolf 			return 0;
811b1986c8eSArmin Wolf 		case hwmon_fan_max:
812*4fc1a51cSArmin Wolf 			*val = data->fan_nominal_speed[channel][data->i8k_fan_max] * mult;
813b1986c8eSArmin Wolf 
814b1986c8eSArmin Wolf 			return 0;
815b1986c8eSArmin Wolf 		case hwmon_fan_target:
816b1986c8eSArmin Wolf 			ret = i8k_get_fan_status(data, channel);
817b1986c8eSArmin Wolf 			if (ret < 0)
818b1986c8eSArmin Wolf 				return ret;
819b1986c8eSArmin Wolf 
820b1986c8eSArmin Wolf 			if (ret > data->i8k_fan_max)
821b1986c8eSArmin Wolf 				ret = data->i8k_fan_max;
822b1986c8eSArmin Wolf 
823*4fc1a51cSArmin Wolf 			*val = data->fan_nominal_speed[channel][ret] * mult;
824b1986c8eSArmin Wolf 
825b1986c8eSArmin Wolf 			return 0;
826deeba244SArmin Wolf 		default:
827deeba244SArmin Wolf 			break;
828deeba244SArmin Wolf 		}
829deeba244SArmin Wolf 		break;
830deeba244SArmin Wolf 	case hwmon_pwm:
831deeba244SArmin Wolf 		switch (attr) {
832deeba244SArmin Wolf 		case hwmon_pwm_input:
833deeba244SArmin Wolf 			ret = i8k_get_fan_status(data, channel);
834deeba244SArmin Wolf 			if (ret < 0)
835deeba244SArmin Wolf 				return ret;
836deeba244SArmin Wolf 
837deeba244SArmin Wolf 			*val = clamp_val(ret * data->i8k_pwm_mult, 0, 255);
838deeba244SArmin Wolf 
839deeba244SArmin Wolf 			return 0;
840deeba244SArmin Wolf 		default:
841deeba244SArmin Wolf 			break;
842deeba244SArmin Wolf 		}
843deeba244SArmin Wolf 		break;
844deeba244SArmin Wolf 	default:
845deeba244SArmin Wolf 		break;
846deeba244SArmin Wolf 	}
847deeba244SArmin Wolf 
848deeba244SArmin Wolf 	return -EOPNOTSUPP;
849deeba244SArmin Wolf }
850deeba244SArmin Wolf 
851deeba244SArmin Wolf static const char *dell_smm_fan_label(struct dell_smm_data *data, int channel)
852deeba244SArmin Wolf {
853deeba244SArmin Wolf 	bool dock = false;
854deeba244SArmin Wolf 	int type = i8k_get_fan_type(data, channel);
855deeba244SArmin Wolf 
856a5afba16SPali Rohár 	if (type < 0)
857deeba244SArmin Wolf 		return ERR_PTR(type);
858a5afba16SPali Rohár 
859a5afba16SPali Rohár 	if (type & 0x10) {
860a5afba16SPali Rohár 		dock = true;
861a5afba16SPali Rohár 		type &= 0x0F;
862a5afba16SPali Rohár 	}
863a5afba16SPali Rohár 
864deeba244SArmin Wolf 	if (type >= ARRAY_SIZE(fan_labels))
865deeba244SArmin Wolf 		type = ARRAY_SIZE(fan_labels) - 1;
866a5afba16SPali Rohár 
867deeba244SArmin Wolf 	return dock ? docking_labels[type] : fan_labels[type];
868a5afba16SPali Rohár }
869a5afba16SPali Rohár 
870deeba244SArmin Wolf static int dell_smm_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
871deeba244SArmin Wolf 				int channel, const char **str)
872a5afba16SPali Rohár {
873ba04d73cSArmin Wolf 	struct dell_smm_data *data = dev_get_drvdata(dev);
874a5afba16SPali Rohár 
875deeba244SArmin Wolf 	switch (type) {
876deeba244SArmin Wolf 	case hwmon_temp:
877deeba244SArmin Wolf 		switch (attr) {
878deeba244SArmin Wolf 		case hwmon_temp_label:
879deeba244SArmin Wolf 			*str = temp_labels[data->temp_type[channel]];
880deeba244SArmin Wolf 			return 0;
881deeba244SArmin Wolf 		default:
882deeba244SArmin Wolf 			break;
883deeba244SArmin Wolf 		}
884deeba244SArmin Wolf 		break;
885deeba244SArmin Wolf 	case hwmon_fan:
886deeba244SArmin Wolf 		switch (attr) {
887deeba244SArmin Wolf 		case hwmon_fan_label:
888deeba244SArmin Wolf 			*str = dell_smm_fan_label(data, channel);
889deeba244SArmin Wolf 			return PTR_ERR_OR_ZERO(*str);
890deeba244SArmin Wolf 		default:
891deeba244SArmin Wolf 			break;
892deeba244SArmin Wolf 		}
893deeba244SArmin Wolf 		break;
894deeba244SArmin Wolf 	default:
895deeba244SArmin Wolf 		break;
896a5afba16SPali Rohár 	}
897a5afba16SPali Rohár 
898deeba244SArmin Wolf 	return -EOPNOTSUPP;
899a5afba16SPali Rohár }
900a5afba16SPali Rohár 
901deeba244SArmin Wolf static int dell_smm_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
902deeba244SArmin Wolf 			  long val)
903a5afba16SPali Rohár {
904ba04d73cSArmin Wolf 	struct dell_smm_data *data = dev_get_drvdata(dev);
905deeba244SArmin Wolf 	unsigned long pwm;
906deeba244SArmin Wolf 	bool enable;
907a5afba16SPali Rohár 	int err;
908a5afba16SPali Rohár 
909deeba244SArmin Wolf 	switch (type) {
910deeba244SArmin Wolf 	case hwmon_pwm:
911deeba244SArmin Wolf 		switch (attr) {
912deeba244SArmin Wolf 		case hwmon_pwm_input:
913deeba244SArmin Wolf 			pwm = clamp_val(DIV_ROUND_CLOSEST(val, data->i8k_pwm_mult), 0,
914deeba244SArmin Wolf 					data->i8k_fan_max);
915a5afba16SPali Rohár 
916ba04d73cSArmin Wolf 			mutex_lock(&data->i8k_mutex);
917deeba244SArmin Wolf 			err = i8k_set_fan(data, channel, pwm);
918ba04d73cSArmin Wolf 			mutex_unlock(&data->i8k_mutex);
919a5afba16SPali Rohár 
920deeba244SArmin Wolf 			if (err < 0)
921afe45277SGiovanni Mascellani 				return err;
922afe45277SGiovanni Mascellani 
923deeba244SArmin Wolf 			return 0;
924deeba244SArmin Wolf 		case hwmon_pwm_enable:
925deeba244SArmin Wolf 			if (!val)
926deeba244SArmin Wolf 				return -EINVAL;
927deeba244SArmin Wolf 
928afe45277SGiovanni Mascellani 			if (val == 1)
929afe45277SGiovanni Mascellani 				enable = false;
930afe45277SGiovanni Mascellani 			else
931deeba244SArmin Wolf 				enable = true;
932afe45277SGiovanni Mascellani 
933ba04d73cSArmin Wolf 			mutex_lock(&data->i8k_mutex);
934ba04d73cSArmin Wolf 			err = i8k_enable_fan_auto_mode(data, enable);
935ba04d73cSArmin Wolf 			mutex_unlock(&data->i8k_mutex);
936afe45277SGiovanni Mascellani 
937deeba244SArmin Wolf 			if (err < 0)
938deeba244SArmin Wolf 				return err;
939deeba244SArmin Wolf 
940deeba244SArmin Wolf 			return 0;
941deeba244SArmin Wolf 		default:
942deeba244SArmin Wolf 			break;
943deeba244SArmin Wolf 		}
944deeba244SArmin Wolf 		break;
945deeba244SArmin Wolf 	default:
946deeba244SArmin Wolf 		break;
947afe45277SGiovanni Mascellani 	}
948afe45277SGiovanni Mascellani 
949deeba244SArmin Wolf 	return -EOPNOTSUPP;
950deeba244SArmin Wolf }
951a5afba16SPali Rohár 
952deeba244SArmin Wolf static const struct hwmon_ops dell_smm_ops = {
953deeba244SArmin Wolf 	.is_visible = dell_smm_is_visible,
954deeba244SArmin Wolf 	.read = dell_smm_read,
955deeba244SArmin Wolf 	.read_string = dell_smm_read_string,
956deeba244SArmin Wolf 	.write = dell_smm_write,
957deeba244SArmin Wolf };
958deeba244SArmin Wolf 
959deeba244SArmin Wolf static const struct hwmon_channel_info *dell_smm_info[] = {
960deeba244SArmin Wolf 	HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
961deeba244SArmin Wolf 	HWMON_CHANNEL_INFO(temp,
962deeba244SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_LABEL,
963deeba244SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_LABEL,
964deeba244SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_LABEL,
965deeba244SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_LABEL,
966deeba244SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_LABEL,
967deeba244SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_LABEL,
968deeba244SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_LABEL,
969deeba244SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_LABEL,
970deeba244SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_LABEL,
971deeba244SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_LABEL
972deeba244SArmin Wolf 			   ),
973deeba244SArmin Wolf 	HWMON_CHANNEL_INFO(fan,
974b1986c8eSArmin Wolf 			   HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX |
975b1986c8eSArmin Wolf 			   HWMON_F_TARGET,
976b1986c8eSArmin Wolf 			   HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX |
977b1986c8eSArmin Wolf 			   HWMON_F_TARGET,
978b1986c8eSArmin Wolf 			   HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX |
979b1986c8eSArmin Wolf 			   HWMON_F_TARGET
980deeba244SArmin Wolf 			   ),
981deeba244SArmin Wolf 	HWMON_CHANNEL_INFO(pwm,
982deeba244SArmin Wolf 			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
983deeba244SArmin Wolf 			   HWMON_PWM_INPUT,
984deeba244SArmin Wolf 			   HWMON_PWM_INPUT
985deeba244SArmin Wolf 			   ),
986a5afba16SPali Rohár 	NULL
987a5afba16SPali Rohár };
988a5afba16SPali Rohár 
989deeba244SArmin Wolf static const struct hwmon_chip_info dell_smm_chip_info = {
990deeba244SArmin Wolf 	.ops = &dell_smm_ops,
991deeba244SArmin Wolf 	.info = dell_smm_info,
992a5afba16SPali Rohár };
993a5afba16SPali Rohár 
994e0d3f7cbSArmin Wolf static int __init dell_smm_init_cdev(struct device *dev, u8 fan_num)
995e0d3f7cbSArmin Wolf {
996e0d3f7cbSArmin Wolf 	struct dell_smm_data *data = dev_get_drvdata(dev);
997e0d3f7cbSArmin Wolf 	struct thermal_cooling_device *cdev;
998e0d3f7cbSArmin Wolf 	struct dell_smm_cooling_data *cdata;
999e0d3f7cbSArmin Wolf 	int ret = 0;
1000e0d3f7cbSArmin Wolf 	char *name;
1001e0d3f7cbSArmin Wolf 
1002e0d3f7cbSArmin Wolf 	name = kasprintf(GFP_KERNEL, "dell-smm-fan%u", fan_num + 1);
1003e0d3f7cbSArmin Wolf 	if (!name)
1004e0d3f7cbSArmin Wolf 		return -ENOMEM;
1005e0d3f7cbSArmin Wolf 
1006e0d3f7cbSArmin Wolf 	cdata = devm_kmalloc(dev, sizeof(*cdata), GFP_KERNEL);
1007e0d3f7cbSArmin Wolf 	if (cdata) {
1008e0d3f7cbSArmin Wolf 		cdata->fan_num = fan_num;
1009e0d3f7cbSArmin Wolf 		cdata->data = data;
1010e0d3f7cbSArmin Wolf 		cdev = devm_thermal_of_cooling_device_register(dev, NULL, name, cdata,
1011e0d3f7cbSArmin Wolf 							       &dell_smm_cooling_ops);
1012e0d3f7cbSArmin Wolf 		if (IS_ERR(cdev)) {
1013e0d3f7cbSArmin Wolf 			devm_kfree(dev, cdata);
1014e0d3f7cbSArmin Wolf 			ret = PTR_ERR(cdev);
1015e0d3f7cbSArmin Wolf 		}
1016e0d3f7cbSArmin Wolf 	} else {
1017e0d3f7cbSArmin Wolf 		ret = -ENOMEM;
1018e0d3f7cbSArmin Wolf 	}
1019e0d3f7cbSArmin Wolf 
1020e0d3f7cbSArmin Wolf 	kfree(name);
1021e0d3f7cbSArmin Wolf 
1022e0d3f7cbSArmin Wolf 	return ret;
1023e0d3f7cbSArmin Wolf }
1024e0d3f7cbSArmin Wolf 
10251492fa21SArmin Wolf static int __init dell_smm_init_hwmon(struct device *dev)
1026a5afba16SPali Rohár {
1027ba04d73cSArmin Wolf 	struct dell_smm_data *data = dev_get_drvdata(dev);
1028deeba244SArmin Wolf 	struct device *dell_smm_hwmon_dev;
10294d9983deSArmin Wolf 	int state, err;
10304d9983deSArmin Wolf 	u8 i;
1031a5afba16SPali Rohár 
1032deeba244SArmin Wolf 	for (i = 0; i < DELL_SMM_NO_TEMP; i++) {
1033deeba244SArmin Wolf 		data->temp_type[i] = i8k_get_temp_type(i);
1034deeba244SArmin Wolf 		if (data->temp_type[i] < 0)
1035deeba244SArmin Wolf 			continue;
1036a5afba16SPali Rohár 
1037deeba244SArmin Wolf 		if (data->temp_type[i] >= ARRAY_SIZE(temp_labels))
1038deeba244SArmin Wolf 			data->temp_type[i] = ARRAY_SIZE(temp_labels) - 1;
1039deeba244SArmin Wolf 	}
1040deeba244SArmin Wolf 
1041deeba244SArmin Wolf 	for (i = 0; i < DELL_SMM_NO_FANS; i++) {
1042deeba244SArmin Wolf 		data->fan_type[i] = INT_MIN;
1043deeba244SArmin Wolf 		err = i8k_get_fan_status(data, i);
10445ce91714SPali Rohár 		if (err < 0)
1045deeba244SArmin Wolf 			err = i8k_get_fan_type(data, i);
1046b1986c8eSArmin Wolf 
1047b1986c8eSArmin Wolf 		if (err < 0)
1048b1986c8eSArmin Wolf 			continue;
1049b1986c8eSArmin Wolf 
1050deeba244SArmin Wolf 		data->fan[i] = true;
1051e0d3f7cbSArmin Wolf 
1052e0d3f7cbSArmin Wolf 		/* the cooling device is not critical, ignore failures */
1053e0d3f7cbSArmin Wolf 		if (IS_REACHABLE(CONFIG_THERMAL)) {
1054e0d3f7cbSArmin Wolf 			err = dell_smm_init_cdev(dev, i);
1055e0d3f7cbSArmin Wolf 			if (err < 0)
1056e0d3f7cbSArmin Wolf 				dev_warn(dev, "Failed to register cooling device for fan %u\n",
1057e0d3f7cbSArmin Wolf 					 i + 1);
1058e0d3f7cbSArmin Wolf 		}
1059e0d3f7cbSArmin Wolf 
1060b1986c8eSArmin Wolf 		data->fan_nominal_speed[i] = devm_kmalloc_array(dev, data->i8k_fan_max + 1,
1061b1986c8eSArmin Wolf 								sizeof(*data->fan_nominal_speed[i]),
1062b1986c8eSArmin Wolf 								GFP_KERNEL);
1063b1986c8eSArmin Wolf 		if (!data->fan_nominal_speed[i])
1064b1986c8eSArmin Wolf 			continue;
1065b1986c8eSArmin Wolf 
1066b1986c8eSArmin Wolf 		for (state = 0; state <= data->i8k_fan_max; state++) {
1067b1986c8eSArmin Wolf 			err = i8k_get_fan_nominal_speed(data, i, state);
1068b1986c8eSArmin Wolf 			if (err < 0) {
1069b1986c8eSArmin Wolf 				/* Mark nominal speed table as invalid in case of error */
1070b1986c8eSArmin Wolf 				devm_kfree(dev, data->fan_nominal_speed[i]);
1071b1986c8eSArmin Wolf 				data->fan_nominal_speed[i] = NULL;
1072b1986c8eSArmin Wolf 				break;
1073b1986c8eSArmin Wolf 			}
1074b1986c8eSArmin Wolf 			data->fan_nominal_speed[i][state] = err;
1075*4fc1a51cSArmin Wolf 			/*
1076*4fc1a51cSArmin Wolf 			 * Autodetect fan multiplier based on nominal rpm if multiplier
1077*4fc1a51cSArmin Wolf 			 * was not specified as module param or in DMI. If fan reports
1078*4fc1a51cSArmin Wolf 			 * rpm value too high then set multiplier to 1.
1079*4fc1a51cSArmin Wolf 			 */
1080*4fc1a51cSArmin Wolf 			if (!fan_mult && err > I8K_FAN_RPM_THRESHOLD)
1081*4fc1a51cSArmin Wolf 				data->i8k_fan_mult = 1;
1082b1986c8eSArmin Wolf 		}
1083deeba244SArmin Wolf 	}
1084a5afba16SPali Rohár 
1085deeba244SArmin Wolf 	dell_smm_hwmon_dev = devm_hwmon_device_register_with_info(dev, "dell_smm", data,
1086deeba244SArmin Wolf 								  &dell_smm_chip_info, NULL);
1087a5afba16SPali Rohár 
1088deeba244SArmin Wolf 	return PTR_ERR_OR_ZERO(dell_smm_hwmon_dev);
1089a5afba16SPali Rohár }
1090a5afba16SPali Rohár 
1091a5afba16SPali Rohár struct i8k_config_data {
1092a5afba16SPali Rohár 	uint fan_mult;
1093a5afba16SPali Rohár 	uint fan_max;
1094a5afba16SPali Rohár };
1095a5afba16SPali Rohár 
1096a5afba16SPali Rohár enum i8k_configs {
1097a5afba16SPali Rohár 	DELL_LATITUDE_D520,
1098a5afba16SPali Rohár 	DELL_PRECISION_490,
1099a5afba16SPali Rohár 	DELL_STUDIO,
1100a5afba16SPali Rohár 	DELL_XPS,
1101a5afba16SPali Rohár };
1102a5afba16SPali Rohár 
1103927d89eeSArmin Wolf /*
1104927d89eeSArmin Wolf  * Only use for machines which need some special configuration
1105927d89eeSArmin Wolf  * in order to work correctly (e.g. if autoconfig fails on this machines).
1106927d89eeSArmin Wolf  */
1107927d89eeSArmin Wolf 
1108c510f6acSArmin Wolf static const struct i8k_config_data i8k_config_data[] __initconst = {
1109a5afba16SPali Rohár 	[DELL_LATITUDE_D520] = {
1110a5afba16SPali Rohár 		.fan_mult = 1,
1111a5afba16SPali Rohár 		.fan_max = I8K_FAN_TURBO,
1112a5afba16SPali Rohár 	},
1113a5afba16SPali Rohár 	[DELL_PRECISION_490] = {
1114a5afba16SPali Rohár 		.fan_mult = 1,
1115a5afba16SPali Rohár 		.fan_max = I8K_FAN_TURBO,
1116a5afba16SPali Rohár 	},
1117a5afba16SPali Rohár 	[DELL_STUDIO] = {
1118a5afba16SPali Rohár 		.fan_mult = 1,
1119a5afba16SPali Rohár 		.fan_max = I8K_FAN_HIGH,
1120a5afba16SPali Rohár 	},
1121a5afba16SPali Rohár 	[DELL_XPS] = {
1122a5afba16SPali Rohár 		.fan_mult = 1,
1123a5afba16SPali Rohár 		.fan_max = I8K_FAN_HIGH,
1124a5afba16SPali Rohár 	},
1125a5afba16SPali Rohár };
1126a5afba16SPali Rohár 
11276faadbbbSChristoph Hellwig static const struct dmi_system_id i8k_dmi_table[] __initconst = {
1128a5afba16SPali Rohár 	{
1129a5afba16SPali Rohár 		.ident = "Dell Inspiron",
1130a5afba16SPali Rohár 		.matches = {
1131a5afba16SPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"),
1132a5afba16SPali Rohár 			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"),
1133a5afba16SPali Rohár 		},
1134a5afba16SPali Rohár 	},
1135a5afba16SPali Rohár 	{
1136a5afba16SPali Rohár 		.ident = "Dell Latitude",
1137a5afba16SPali Rohár 		.matches = {
1138a5afba16SPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"),
1139a5afba16SPali Rohár 			DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"),
1140a5afba16SPali Rohár 		},
1141a5afba16SPali Rohár 	},
1142a5afba16SPali Rohár 	{
1143a5afba16SPali Rohár 		.ident = "Dell Inspiron 2",
1144a5afba16SPali Rohár 		.matches = {
1145a5afba16SPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1146a5afba16SPali Rohár 			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"),
1147a5afba16SPali Rohár 		},
1148a5afba16SPali Rohár 	},
1149a5afba16SPali Rohár 	{
1150a5afba16SPali Rohár 		.ident = "Dell Latitude D520",
1151a5afba16SPali Rohár 		.matches = {
1152a5afba16SPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1153a5afba16SPali Rohár 			DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D520"),
1154a5afba16SPali Rohár 		},
1155a5afba16SPali Rohár 		.driver_data = (void *)&i8k_config_data[DELL_LATITUDE_D520],
1156a5afba16SPali Rohár 	},
1157a5afba16SPali Rohár 	{
1158a5afba16SPali Rohár 		.ident = "Dell Latitude 2",
1159a5afba16SPali Rohár 		.matches = {
1160a5afba16SPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1161a5afba16SPali Rohár 			DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"),
1162a5afba16SPali Rohár 		},
1163a5afba16SPali Rohár 	},
1164a5afba16SPali Rohár 	{	/* UK Inspiron 6400  */
1165a5afba16SPali Rohár 		.ident = "Dell Inspiron 3",
1166a5afba16SPali Rohár 		.matches = {
1167a5afba16SPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1168a5afba16SPali Rohár 			DMI_MATCH(DMI_PRODUCT_NAME, "MM061"),
1169a5afba16SPali Rohár 		},
1170a5afba16SPali Rohár 	},
1171a5afba16SPali Rohár 	{
1172a5afba16SPali Rohár 		.ident = "Dell Inspiron 3",
1173a5afba16SPali Rohár 		.matches = {
1174a5afba16SPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1175a5afba16SPali Rohár 			DMI_MATCH(DMI_PRODUCT_NAME, "MP061"),
1176a5afba16SPali Rohár 		},
1177a5afba16SPali Rohár 	},
1178a5afba16SPali Rohár 	{
1179a5afba16SPali Rohár 		.ident = "Dell Precision 490",
1180a5afba16SPali Rohár 		.matches = {
1181a5afba16SPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1182a5afba16SPali Rohár 			DMI_MATCH(DMI_PRODUCT_NAME,
1183a5afba16SPali Rohár 				  "Precision WorkStation 490"),
1184a5afba16SPali Rohár 		},
1185a5afba16SPali Rohár 		.driver_data = (void *)&i8k_config_data[DELL_PRECISION_490],
1186a5afba16SPali Rohár 	},
1187a5afba16SPali Rohár 	{
1188a5afba16SPali Rohár 		.ident = "Dell Precision",
1189a5afba16SPali Rohár 		.matches = {
1190a5afba16SPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1191a5afba16SPali Rohár 			DMI_MATCH(DMI_PRODUCT_NAME, "Precision"),
1192a5afba16SPali Rohár 		},
1193a5afba16SPali Rohár 	},
1194a5afba16SPali Rohár 	{
1195a5afba16SPali Rohár 		.ident = "Dell Vostro",
1196a5afba16SPali Rohár 		.matches = {
1197a5afba16SPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1198a5afba16SPali Rohár 			DMI_MATCH(DMI_PRODUCT_NAME, "Vostro"),
1199a5afba16SPali Rohár 		},
1200a5afba16SPali Rohár 	},
1201a5afba16SPali Rohár 	{
1202a5afba16SPali Rohár 		.ident = "Dell Studio",
1203a5afba16SPali Rohár 		.matches = {
1204a5afba16SPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1205a5afba16SPali Rohár 			DMI_MATCH(DMI_PRODUCT_NAME, "Studio"),
1206a5afba16SPali Rohár 		},
1207a5afba16SPali Rohár 		.driver_data = (void *)&i8k_config_data[DELL_STUDIO],
1208a5afba16SPali Rohár 	},
1209a5afba16SPali Rohár 	{
1210a5afba16SPali Rohár 		.ident = "Dell XPS M140",
1211a5afba16SPali Rohár 		.matches = {
1212a5afba16SPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1213a5afba16SPali Rohár 			DMI_MATCH(DMI_PRODUCT_NAME, "MXC051"),
1214a5afba16SPali Rohár 		},
1215a5afba16SPali Rohár 		.driver_data = (void *)&i8k_config_data[DELL_XPS],
1216a5afba16SPali Rohár 	},
1217a4811b6cSPali Rohár 	{
1218b8a13e5eSThomas Hebb 		.ident = "Dell XPS",
1219a4811b6cSPali Rohár 		.matches = {
1220a4811b6cSPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1221b8a13e5eSThomas Hebb 			DMI_MATCH(DMI_PRODUCT_NAME, "XPS"),
1222162372b0SMichele Sorcinelli 		},
1223162372b0SMichele Sorcinelli 	},
1224a5afba16SPali Rohár 	{ }
1225a5afba16SPali Rohár };
1226a5afba16SPali Rohár 
1227a5afba16SPali Rohár MODULE_DEVICE_TABLE(dmi, i8k_dmi_table);
1228a5afba16SPali Rohár 
1229a4b45b25SPali Rohár /*
12302744d2fdSPali Rohár  * On some machines once I8K_SMM_GET_FAN_TYPE is issued then CPU fan speed
12312744d2fdSPali Rohár  * randomly going up and down due to bug in Dell SMM or BIOS. Here is blacklist
12322744d2fdSPali Rohár  * of affected Dell machines for which we disallow I8K_SMM_GET_FAN_TYPE call.
12332744d2fdSPali Rohár  * See bug: https://bugzilla.kernel.org/show_bug.cgi?id=100121
12346220f4ebSThorsten Leemhuis  */
12356faadbbbSChristoph Hellwig static const struct dmi_system_id i8k_blacklist_fan_type_dmi_table[] __initconst = {
12362744d2fdSPali Rohár 	{
12376220f4ebSThorsten Leemhuis 		.ident = "Dell Studio XPS 8000",
12386220f4ebSThorsten Leemhuis 		.matches = {
12396220f4ebSThorsten Leemhuis 			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
12406220f4ebSThorsten Leemhuis 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Studio XPS 8000"),
12416220f4ebSThorsten Leemhuis 		},
12426220f4ebSThorsten Leemhuis 	},
12436220f4ebSThorsten Leemhuis 	{
1244a4b45b25SPali Rohár 		.ident = "Dell Studio XPS 8100",
1245a4b45b25SPali Rohár 		.matches = {
1246a4b45b25SPali Rohár 			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1247a4b45b25SPali Rohár 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Studio XPS 8100"),
1248a4b45b25SPali Rohár 		},
1249a4b45b25SPali Rohár 	},
12502744d2fdSPali Rohár 	{
12512744d2fdSPali Rohár 		.ident = "Dell Inspiron 580",
12522744d2fdSPali Rohár 		.matches = {
12532744d2fdSPali Rohár 			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
12542744d2fdSPali Rohár 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Inspiron 580 "),
12552744d2fdSPali Rohár 		},
12562744d2fdSPali Rohár 	},
12576ba463edSArmin Wolf 	{
12586ba463edSArmin Wolf 		.ident = "Dell Inspiron 3505",
12596ba463edSArmin Wolf 		.matches = {
12606ba463edSArmin Wolf 			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
12616ba463edSArmin Wolf 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Inspiron 3505"),
12626ba463edSArmin Wolf 		},
12636ba463edSArmin Wolf 	},
1264a4b45b25SPali Rohár 	{ }
1265a4b45b25SPali Rohár };
1266a4b45b25SPali Rohár 
1267a5afba16SPali Rohár /*
1268f480ea90SPali Rohár  * On some machines all fan related SMM functions implemented by Dell BIOS
1269f480ea90SPali Rohár  * firmware freeze kernel for about 500ms. Until Dell fixes these problems fan
1270f480ea90SPali Rohár  * support for affected blacklisted Dell machines stay disabled.
1271f480ea90SPali Rohár  * See bug: https://bugzilla.kernel.org/show_bug.cgi?id=195751
1272f480ea90SPali Rohár  */
1273c510f6acSArmin Wolf static const struct dmi_system_id i8k_blacklist_fan_support_dmi_table[] __initconst = {
1274f480ea90SPali Rohár 	{
1275f480ea90SPali Rohár 		.ident = "Dell Inspiron 7720",
1276f480ea90SPali Rohár 		.matches = {
1277f480ea90SPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1278f480ea90SPali Rohár 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Inspiron 7720"),
1279f480ea90SPali Rohár 		},
1280f480ea90SPali Rohár 	},
12816fbc4232SOleksandr Natalenko 	{
12826fbc4232SOleksandr Natalenko 		.ident = "Dell Vostro 3360",
12836fbc4232SOleksandr Natalenko 		.matches = {
12846fbc4232SOleksandr Natalenko 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
12856fbc4232SOleksandr Natalenko 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Vostro 3360"),
12866fbc4232SOleksandr Natalenko 		},
12876fbc4232SOleksandr Natalenko 	},
1288536e0019SHelge Eichelberg 	{
1289536e0019SHelge Eichelberg 		.ident = "Dell XPS13 9333",
1290536e0019SHelge Eichelberg 		.matches = {
1291536e0019SHelge Eichelberg 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1292536e0019SHelge Eichelberg 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS13 9333"),
1293536e0019SHelge Eichelberg 		},
1294536e0019SHelge Eichelberg 	},
12954008bc7dSThomas Hebb 	{
12964008bc7dSThomas Hebb 		.ident = "Dell XPS 15 L502X",
12974008bc7dSThomas Hebb 		.matches = {
12984008bc7dSThomas Hebb 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
12994008bc7dSThomas Hebb 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Dell System XPS L502X"),
13004008bc7dSThomas Hebb 		},
13014008bc7dSThomas Hebb 	},
1302f480ea90SPali Rohár 	{ }
1303f480ea90SPali Rohár };
1304f480ea90SPali Rohár 
1305afe45277SGiovanni Mascellani struct i8k_fan_control_data {
1306afe45277SGiovanni Mascellani 	unsigned int manual_fan;
1307afe45277SGiovanni Mascellani 	unsigned int auto_fan;
1308afe45277SGiovanni Mascellani };
1309afe45277SGiovanni Mascellani 
1310afe45277SGiovanni Mascellani enum i8k_fan_controls {
1311afe45277SGiovanni Mascellani 	I8K_FAN_34A3_35A3,
1312afe45277SGiovanni Mascellani };
1313afe45277SGiovanni Mascellani 
1314c510f6acSArmin Wolf static const struct i8k_fan_control_data i8k_fan_control_data[] __initconst = {
1315afe45277SGiovanni Mascellani 	[I8K_FAN_34A3_35A3] = {
1316afe45277SGiovanni Mascellani 		.manual_fan = 0x34a3,
1317afe45277SGiovanni Mascellani 		.auto_fan = 0x35a3,
1318afe45277SGiovanni Mascellani 	},
1319afe45277SGiovanni Mascellani };
1320afe45277SGiovanni Mascellani 
1321c510f6acSArmin Wolf static const struct dmi_system_id i8k_whitelist_fan_control[] __initconst = {
1322afe45277SGiovanni Mascellani 	{
13230ca8bb2cSJeffrey Lin 		.ident = "Dell Latitude 5480",
13240ca8bb2cSJeffrey Lin 		.matches = {
13250ca8bb2cSJeffrey Lin 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
13260ca8bb2cSJeffrey Lin 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude 5480"),
13270ca8bb2cSJeffrey Lin 		},
13280ca8bb2cSJeffrey Lin 		.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
13290ca8bb2cSJeffrey Lin 	},
13300ca8bb2cSJeffrey Lin 	{
1331afe45277SGiovanni Mascellani 		.ident = "Dell Latitude E6440",
1332afe45277SGiovanni Mascellani 		.matches = {
1333afe45277SGiovanni Mascellani 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1334afe45277SGiovanni Mascellani 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude E6440"),
1335afe45277SGiovanni Mascellani 		},
1336afe45277SGiovanni Mascellani 		.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
1337afe45277SGiovanni Mascellani 	},
1338807b8c29SSebastian Oechsle 	{
1339807b8c29SSebastian Oechsle 		.ident = "Dell Latitude E7440",
1340807b8c29SSebastian Oechsle 		.matches = {
1341807b8c29SSebastian Oechsle 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1342807b8c29SSebastian Oechsle 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude E7440"),
1343807b8c29SSebastian Oechsle 		},
1344807b8c29SSebastian Oechsle 		.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
1345807b8c29SSebastian Oechsle 	},
134695d88d05SCarlos Alberto Lopez Perez 	{
134795d88d05SCarlos Alberto Lopez Perez 		.ident = "Dell Precision 5530",
134895d88d05SCarlos Alberto Lopez Perez 		.matches = {
134995d88d05SCarlos Alberto Lopez Perez 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
135095d88d05SCarlos Alberto Lopez Perez 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Precision 5530"),
135195d88d05SCarlos Alberto Lopez Perez 		},
135295d88d05SCarlos Alberto Lopez Perez 		.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
135395d88d05SCarlos Alberto Lopez Perez 	},
135495d88d05SCarlos Alberto Lopez Perez 	{
135595d88d05SCarlos Alberto Lopez Perez 		.ident = "Dell Precision 7510",
135695d88d05SCarlos Alberto Lopez Perez 		.matches = {
135795d88d05SCarlos Alberto Lopez Perez 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
135895d88d05SCarlos Alberto Lopez Perez 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Precision 7510"),
135995d88d05SCarlos Alberto Lopez Perez 		},
136095d88d05SCarlos Alberto Lopez Perez 		.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
136195d88d05SCarlos Alberto Lopez Perez 	},
1362afe45277SGiovanni Mascellani 	{ }
1363afe45277SGiovanni Mascellani };
1364afe45277SGiovanni Mascellani 
13651492fa21SArmin Wolf static int __init dell_smm_probe(struct platform_device *pdev)
1366a5afba16SPali Rohár {
1367ba04d73cSArmin Wolf 	struct dell_smm_data *data;
1368afe45277SGiovanni Mascellani 	const struct dmi_system_id *id, *fan_control;
13694d9983deSArmin Wolf 	int ret;
1370a5afba16SPali Rohár 
1371ba04d73cSArmin Wolf 	data = devm_kzalloc(&pdev->dev, sizeof(struct dell_smm_data), GFP_KERNEL);
1372ba04d73cSArmin Wolf 	if (!data)
1373ba04d73cSArmin Wolf 		return -ENOMEM;
1374ba04d73cSArmin Wolf 
1375ba04d73cSArmin Wolf 	mutex_init(&data->i8k_mutex);
1376ba04d73cSArmin Wolf 	data->i8k_fan_mult = I8K_FAN_MULT;
1377ba04d73cSArmin Wolf 	data->i8k_fan_max = I8K_FAN_HIGH;
1378ba04d73cSArmin Wolf 	platform_set_drvdata(pdev, data);
1379ba04d73cSArmin Wolf 
1380f480ea90SPali Rohár 	if (dmi_check_system(i8k_blacklist_fan_support_dmi_table)) {
1381ba04d73cSArmin Wolf 		dev_warn(&pdev->dev, "broken Dell BIOS detected, disallow fan support\n");
1382f480ea90SPali Rohár 		if (!force)
1383ba04d73cSArmin Wolf 			data->disallow_fan_support = true;
1384f480ea90SPali Rohár 	}
1385f480ea90SPali Rohár 
1386836ad112SPali Rohár 	if (dmi_check_system(i8k_blacklist_fan_type_dmi_table)) {
1387ba04d73cSArmin Wolf 		dev_warn(&pdev->dev, "broken Dell BIOS detected, disallow fan type call\n");
1388836ad112SPali Rohár 		if (!force)
1389ba04d73cSArmin Wolf 			data->disallow_fan_type_call = true;
1390836ad112SPali Rohár 	}
13912744d2fdSPali Rohár 
1392ba04d73cSArmin Wolf 	strscpy(data->bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION),
1393ba04d73cSArmin Wolf 		sizeof(data->bios_version));
1394ba04d73cSArmin Wolf 	strscpy(data->bios_machineid, i8k_get_dmi_data(DMI_PRODUCT_SERIAL),
1395ba04d73cSArmin Wolf 		sizeof(data->bios_machineid));
1396a5afba16SPali Rohár 
1397a5afba16SPali Rohár 	/*
1398a5afba16SPali Rohár 	 * Set fan multiplier and maximal fan speed from dmi config
1399a5afba16SPali Rohár 	 * Values specified in module parameters override values from dmi
1400a5afba16SPali Rohár 	 */
1401a5afba16SPali Rohár 	id = dmi_first_match(i8k_dmi_table);
1402a5afba16SPali Rohár 	if (id && id->driver_data) {
1403a5afba16SPali Rohár 		const struct i8k_config_data *conf = id->driver_data;
14041492fa21SArmin Wolf 
1405a5afba16SPali Rohár 		if (!fan_mult && conf->fan_mult)
1406a5afba16SPali Rohár 			fan_mult = conf->fan_mult;
1407ba04d73cSArmin Wolf 
1408a5afba16SPali Rohár 		if (!fan_max && conf->fan_max)
1409a5afba16SPali Rohár 			fan_max = conf->fan_max;
1410a5afba16SPali Rohár 	}
1411a5afba16SPali Rohár 
1412ba04d73cSArmin Wolf 	data->i8k_fan_max = fan_max ? : I8K_FAN_HIGH;	/* Must not be 0 */
1413ba04d73cSArmin Wolf 	data->i8k_pwm_mult = DIV_ROUND_UP(255, data->i8k_fan_max);
1414a5afba16SPali Rohár 
1415afe45277SGiovanni Mascellani 	fan_control = dmi_first_match(i8k_whitelist_fan_control);
1416afe45277SGiovanni Mascellani 	if (fan_control && fan_control->driver_data) {
1417ba04d73cSArmin Wolf 		const struct i8k_fan_control_data *control = fan_control->driver_data;
1418afe45277SGiovanni Mascellani 
1419ba04d73cSArmin Wolf 		data->manual_fan = control->manual_fan;
1420ba04d73cSArmin Wolf 		data->auto_fan = control->auto_fan;
1421ba04d73cSArmin Wolf 		dev_info(&pdev->dev, "enabling support for setting automatic/manual fan control\n");
1422afe45277SGiovanni Mascellani 	}
1423afe45277SGiovanni Mascellani 
1424*4fc1a51cSArmin Wolf 	if (fan_mult)
1425ba04d73cSArmin Wolf 		data->i8k_fan_mult = fan_mult;
1426a5afba16SPali Rohár 
14271492fa21SArmin Wolf 	ret = dell_smm_init_hwmon(&pdev->dev);
14281492fa21SArmin Wolf 	if (ret)
14291492fa21SArmin Wolf 		return ret;
14301492fa21SArmin Wolf 
1431a2cb66b4SArmin Wolf 	i8k_init_procfs(&pdev->dev);
14321492fa21SArmin Wolf 
14331492fa21SArmin Wolf 	return 0;
14341492fa21SArmin Wolf }
14351492fa21SArmin Wolf 
14361492fa21SArmin Wolf static struct platform_driver dell_smm_driver = {
14371492fa21SArmin Wolf 	.driver		= {
14381492fa21SArmin Wolf 		.name	= KBUILD_MODNAME,
14391492fa21SArmin Wolf 	},
14401492fa21SArmin Wolf };
14411492fa21SArmin Wolf 
14421492fa21SArmin Wolf static struct platform_device *dell_smm_device;
14431492fa21SArmin Wolf 
14441492fa21SArmin Wolf /*
14451492fa21SArmin Wolf  * Probe for the presence of a supported laptop.
14461492fa21SArmin Wolf  */
1447a5afba16SPali Rohár static int __init i8k_init(void)
1448a5afba16SPali Rohár {
14491492fa21SArmin Wolf 	/*
14501492fa21SArmin Wolf 	 * Get DMI information
14511492fa21SArmin Wolf 	 */
14521492fa21SArmin Wolf 	if (!dmi_check_system(i8k_dmi_table)) {
14531492fa21SArmin Wolf 		if (!ignore_dmi && !force)
1454a5afba16SPali Rohár 			return -ENODEV;
1455a5afba16SPali Rohár 
14561492fa21SArmin Wolf 		pr_info("not running on a supported Dell system.\n");
14571492fa21SArmin Wolf 		pr_info("vendor=%s, model=%s, version=%s\n",
14581492fa21SArmin Wolf 			i8k_get_dmi_data(DMI_SYS_VENDOR),
14591492fa21SArmin Wolf 			i8k_get_dmi_data(DMI_PRODUCT_NAME),
14601492fa21SArmin Wolf 			i8k_get_dmi_data(DMI_BIOS_VERSION));
14611492fa21SArmin Wolf 	}
1462039ae585SPali Rohár 
14631492fa21SArmin Wolf 	/*
14641492fa21SArmin Wolf 	 * Get SMM Dell signature
14651492fa21SArmin Wolf 	 */
14661492fa21SArmin Wolf 	if (i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG1) &&
14671492fa21SArmin Wolf 	    i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG2)) {
14681492fa21SArmin Wolf 		pr_err("unable to get SMM Dell signature\n");
14691492fa21SArmin Wolf 		if (!force)
14701492fa21SArmin Wolf 			return -ENODEV;
14711492fa21SArmin Wolf 	}
14721492fa21SArmin Wolf 
14731492fa21SArmin Wolf 	dell_smm_device = platform_create_bundle(&dell_smm_driver, dell_smm_probe, NULL, 0, NULL,
14741492fa21SArmin Wolf 						 0);
14751492fa21SArmin Wolf 
14761492fa21SArmin Wolf 	return PTR_ERR_OR_ZERO(dell_smm_device);
1477a5afba16SPali Rohár }
1478a5afba16SPali Rohár 
1479a5afba16SPali Rohár static void __exit i8k_exit(void)
1480a5afba16SPali Rohár {
14811492fa21SArmin Wolf 	platform_device_unregister(dell_smm_device);
14821492fa21SArmin Wolf 	platform_driver_unregister(&dell_smm_driver);
1483a5afba16SPali Rohár }
1484a5afba16SPali Rohár 
1485a5afba16SPali Rohár module_init(i8k_init);
1486a5afba16SPali Rohár module_exit(i8k_exit);
1487