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 
52981c5f3cSArmin Wolf /* in usecs */
53981c5f3cSArmin Wolf #define DELL_SMM_MAX_DURATION  250000
54981c5f3cSArmin Wolf 
55a5afba16SPali Rohár #define I8K_FAN_MULT		30
564fc1a51cSArmin Wolf #define I8K_FAN_RPM_THRESHOLD	1000
57a5afba16SPali Rohár #define I8K_MAX_TEMP		127
58a5afba16SPali Rohár 
59a5afba16SPali Rohár #define I8K_FN_NONE		0x00
60a5afba16SPali Rohár #define I8K_FN_UP		0x01
61a5afba16SPali Rohár #define I8K_FN_DOWN		0x02
62a5afba16SPali Rohár #define I8K_FN_MUTE		0x04
63a5afba16SPali Rohár #define I8K_FN_MASK		0x07
64a5afba16SPali Rohár #define I8K_FN_SHIFT		8
65a5afba16SPali Rohár 
66a5afba16SPali Rohár #define I8K_POWER_AC		0x05
67a5afba16SPali Rohár #define I8K_POWER_BATTERY	0x01
68a5afba16SPali Rohár 
69deeba244SArmin Wolf #define DELL_SMM_NO_TEMP	10
70deeba244SArmin Wolf #define DELL_SMM_NO_FANS	3
71deeba244SArmin Wolf 
72ba04d73cSArmin Wolf struct dell_smm_data {
73ba04d73cSArmin Wolf 	struct mutex i8k_mutex; /* lock for sensors writes */
74ba04d73cSArmin Wolf 	char bios_version[4];
75ba04d73cSArmin Wolf 	char bios_machineid[16];
76ba04d73cSArmin Wolf 	uint i8k_fan_mult;
77ba04d73cSArmin Wolf 	uint i8k_pwm_mult;
78ba04d73cSArmin Wolf 	uint i8k_fan_max;
79ba04d73cSArmin Wolf 	bool disallow_fan_type_call;
80ba04d73cSArmin Wolf 	bool disallow_fan_support;
81ba04d73cSArmin Wolf 	unsigned int manual_fan;
82ba04d73cSArmin Wolf 	unsigned int auto_fan;
83deeba244SArmin Wolf 	int temp_type[DELL_SMM_NO_TEMP];
84deeba244SArmin Wolf 	bool fan[DELL_SMM_NO_FANS];
85deeba244SArmin Wolf 	int fan_type[DELL_SMM_NO_FANS];
86b1986c8eSArmin Wolf 	int *fan_nominal_speed[DELL_SMM_NO_FANS];
87ba04d73cSArmin Wolf };
88a5afba16SPali Rohár 
89e0d3f7cbSArmin Wolf struct dell_smm_cooling_data {
90e0d3f7cbSArmin Wolf 	u8 fan_num;
91e0d3f7cbSArmin Wolf 	struct dell_smm_data *data;
92e0d3f7cbSArmin Wolf };
93e0d3f7cbSArmin Wolf 
94a5afba16SPali Rohár MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)");
95149ed3d4SPali Rohár MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
96039ae585SPali Rohár MODULE_DESCRIPTION("Dell laptop SMM BIOS hwmon driver");
97a5afba16SPali Rohár MODULE_LICENSE("GPL");
98a5afba16SPali Rohár MODULE_ALIAS("i8k");
99a5afba16SPali Rohár 
100a5afba16SPali Rohár static bool force;
1017cd682b0SArmin Wolf module_param_unsafe(force, bool, 0);
1027cd682b0SArmin Wolf MODULE_PARM_DESC(force, "Force loading without checking for supported models and features");
103a5afba16SPali Rohár 
104a5afba16SPali Rohár static bool ignore_dmi;
105a5afba16SPali Rohár module_param(ignore_dmi, bool, 0);
106a5afba16SPali Rohár MODULE_PARM_DESC(ignore_dmi, "Continue probing hardware even if DMI data does not match");
107a5afba16SPali Rohár 
108039ae585SPali Rohár #if IS_ENABLED(CONFIG_I8K)
1097613663cSPali Rohár static bool restricted = true;
110a5afba16SPali Rohár module_param(restricted, bool, 0);
1117613663cSPali Rohár MODULE_PARM_DESC(restricted, "Restrict fan control and serial number to CAP_SYS_ADMIN (default: 1)");
112a5afba16SPali Rohár 
113a5afba16SPali Rohár static bool power_status;
114a5afba16SPali Rohár module_param(power_status, bool, 0600);
1157613663cSPali Rohár MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k (default: 0)");
116039ae585SPali Rohár #endif
117a5afba16SPali Rohár 
118a5afba16SPali Rohár static uint fan_mult;
119a5afba16SPali Rohár module_param(fan_mult, uint, 0);
120a5afba16SPali Rohár MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with (default: autodetect)");
121a5afba16SPali Rohár 
122a5afba16SPali Rohár static uint fan_max;
123a5afba16SPali Rohár module_param(fan_max, uint, 0);
124a5afba16SPali Rohár MODULE_PARM_DESC(fan_max, "Maximum configurable fan speed (default: autodetect)");
125a5afba16SPali Rohár 
126a5afba16SPali Rohár struct smm_regs {
127a5afba16SPali Rohár 	unsigned int eax;
128565210c7SArmin Wolf 	unsigned int ebx;
129565210c7SArmin Wolf 	unsigned int ecx;
130565210c7SArmin Wolf 	unsigned int edx;
131565210c7SArmin Wolf 	unsigned int esi;
132565210c7SArmin Wolf 	unsigned int edi;
133c10d52d6SArmin Wolf };
134a5afba16SPali Rohár 
135deeba244SArmin Wolf static const char * const temp_labels[] = {
136deeba244SArmin Wolf 	"CPU",
137deeba244SArmin Wolf 	"GPU",
138deeba244SArmin Wolf 	"SODIMM",
139deeba244SArmin Wolf 	"Other",
140deeba244SArmin Wolf 	"Ambient",
141deeba244SArmin Wolf 	"Other",
142deeba244SArmin Wolf };
143deeba244SArmin Wolf 
144deeba244SArmin Wolf static const char * const fan_labels[] = {
145deeba244SArmin Wolf 	"Processor Fan",
146deeba244SArmin Wolf 	"Motherboard Fan",
147deeba244SArmin Wolf 	"Video Fan",
148deeba244SArmin Wolf 	"Power Supply Fan",
149deeba244SArmin Wolf 	"Chipset Fan",
150deeba244SArmin Wolf 	"Other Fan",
151deeba244SArmin Wolf };
152deeba244SArmin Wolf 
153deeba244SArmin Wolf static const char * const docking_labels[] = {
154deeba244SArmin Wolf 	"Docking Processor Fan",
155deeba244SArmin Wolf 	"Docking Motherboard Fan",
156deeba244SArmin Wolf 	"Docking Video Fan",
157deeba244SArmin Wolf 	"Docking Power Supply Fan",
158deeba244SArmin Wolf 	"Docking Chipset Fan",
159deeba244SArmin Wolf 	"Docking Other Fan",
160deeba244SArmin Wolf };
161deeba244SArmin Wolf 
i8k_get_dmi_data(int field)162c9363cdfSArmin Wolf static inline const char __init *i8k_get_dmi_data(int field)
163a5afba16SPali Rohár {
164a5afba16SPali Rohár 	const char *dmi_data = dmi_get_system_info(field);
165a5afba16SPali Rohár 
166a5afba16SPali Rohár 	return dmi_data && *dmi_data ? dmi_data : "?";
167a5afba16SPali Rohár }
168a5afba16SPali Rohár 
169a5afba16SPali Rohár /*
170a5afba16SPali Rohár  * Call the System Management Mode BIOS. Code provided by Jonathan Buzzard.
171a5afba16SPali Rohár  */
i8k_smm_func(void * par)17227046a3fSJuergen Gross static int i8k_smm_func(void *par)
173a5afba16SPali Rohár {
1748713b4a4SArmin Wolf 	ktime_t calltime = ktime_get();
17527046a3fSJuergen Gross 	struct smm_regs *regs = par;
176a5afba16SPali Rohár 	int eax = regs->eax;
1779d58bec0SPali Rohár 	int ebx = regs->ebx;
178c10d52d6SArmin Wolf 	unsigned char carry;
1798713b4a4SArmin Wolf 	long long duration;
1809d58bec0SPali Rohár 
181a5afba16SPali Rohár 	/* SMM requires CPU 0 */
18227046a3fSJuergen Gross 	if (smp_processor_id() != 0)
18327046a3fSJuergen Gross 		return -EBUSY;
184a5afba16SPali Rohár 
185c10d52d6SArmin Wolf 	asm volatile("out %%al,$0xb2\n\t"
186a5afba16SPali Rohár 		     "out %%al,$0x84\n\t"
187c10d52d6SArmin Wolf 		     "setc %0\n"
188c10d52d6SArmin Wolf 		     : "=mr" (carry),
189c10d52d6SArmin Wolf 		       "+a" (regs->eax),
190c10d52d6SArmin Wolf 		       "+b" (regs->ebx),
191c10d52d6SArmin Wolf 		       "+c" (regs->ecx),
192c10d52d6SArmin Wolf 		       "+d" (regs->edx),
193c10d52d6SArmin Wolf 		       "+S" (regs->esi),
194c10d52d6SArmin Wolf 		       "+D" (regs->edi));
195a5afba16SPali Rohár 
1968713b4a4SArmin Wolf 	duration = ktime_us_delta(ktime_get(), calltime);
197c10d52d6SArmin Wolf 	pr_debug("smm(0x%.4x 0x%.4x) = 0x%.4x carry: %d (took %7lld usecs)\n",
198c10d52d6SArmin Wolf 		 eax, ebx, regs->eax & 0xffff, carry, duration);
1999d58bec0SPali Rohár 
200981c5f3cSArmin Wolf 	if (duration > DELL_SMM_MAX_DURATION)
201981c5f3cSArmin Wolf 		pr_warn_once("SMM call took %lld usecs!\n", duration);
202981c5f3cSArmin Wolf 
203c10d52d6SArmin Wolf 	if (carry || (regs->eax & 0xffff) == 0xffff || regs->eax == eax)
204c10d52d6SArmin Wolf 		return -EINVAL;
205c10d52d6SArmin Wolf 
206c10d52d6SArmin Wolf 	return 0;
207a5afba16SPali Rohár }
208a5afba16SPali Rohár 
209a5afba16SPali Rohár /*
21027046a3fSJuergen Gross  * Call the System Management Mode BIOS.
21127046a3fSJuergen Gross  */
i8k_smm(struct smm_regs * regs)21227046a3fSJuergen Gross static int i8k_smm(struct smm_regs *regs)
21327046a3fSJuergen Gross {
21427046a3fSJuergen Gross 	int ret;
21527046a3fSJuergen Gross 
216e104d530SSebastian Andrzej Siewior 	cpus_read_lock();
21727046a3fSJuergen Gross 	ret = smp_call_on_cpu(0, i8k_smm_func, regs, true);
218e104d530SSebastian Andrzej Siewior 	cpus_read_unlock();
21927046a3fSJuergen Gross 
22027046a3fSJuergen Gross 	return ret;
22127046a3fSJuergen Gross }
22227046a3fSJuergen Gross 
22327046a3fSJuergen Gross /*
224a5afba16SPali Rohár  * Read the fan status.
225a5afba16SPali Rohár  */
i8k_get_fan_status(const struct dell_smm_data * data,u8 fan)2264d9983deSArmin Wolf static int i8k_get_fan_status(const struct dell_smm_data *data, u8 fan)
227a5afba16SPali Rohár {
2284d9983deSArmin Wolf 	struct smm_regs regs = {
2294d9983deSArmin Wolf 		.eax = I8K_SMM_GET_FAN,
2304d9983deSArmin Wolf 		.ebx = fan,
2314d9983deSArmin Wolf 	};
232a5afba16SPali Rohár 
233ba04d73cSArmin Wolf 	if (data->disallow_fan_support)
234f480ea90SPali Rohár 		return -EINVAL;
235f480ea90SPali Rohár 
236a5afba16SPali Rohár 	return i8k_smm(&regs) ? : regs.eax & 0xff;
237a5afba16SPali Rohár }
238a5afba16SPali Rohár 
239a5afba16SPali Rohár /*
240a5afba16SPali Rohár  * Read the fan speed in RPM.
241a5afba16SPali Rohár  */
i8k_get_fan_speed(const struct dell_smm_data * data,u8 fan)2424d9983deSArmin Wolf static int i8k_get_fan_speed(const struct dell_smm_data *data, u8 fan)
243a5afba16SPali Rohár {
2444d9983deSArmin Wolf 	struct smm_regs regs = {
2454d9983deSArmin Wolf 		.eax = I8K_SMM_GET_SPEED,
2464d9983deSArmin Wolf 		.ebx = fan,
2474d9983deSArmin Wolf 	};
248a5afba16SPali Rohár 
249ba04d73cSArmin Wolf 	if (data->disallow_fan_support)
250f480ea90SPali Rohár 		return -EINVAL;
251f480ea90SPali Rohár 
252ba04d73cSArmin Wolf 	return i8k_smm(&regs) ? : (regs.eax & 0xffff) * data->i8k_fan_mult;
253a5afba16SPali Rohár }
254a5afba16SPali Rohár 
255a5afba16SPali Rohár /*
256a5afba16SPali Rohár  * Read the fan type.
257a5afba16SPali Rohár  */
_i8k_get_fan_type(const struct dell_smm_data * data,u8 fan)2584d9983deSArmin Wolf static int _i8k_get_fan_type(const struct dell_smm_data *data, u8 fan)
259a5afba16SPali Rohár {
2604d9983deSArmin Wolf 	struct smm_regs regs = {
2614d9983deSArmin Wolf 		.eax = I8K_SMM_GET_FAN_TYPE,
2624d9983deSArmin Wolf 		.ebx = fan,
2634d9983deSArmin Wolf 	};
264a5afba16SPali Rohár 
265ba04d73cSArmin Wolf 	if (data->disallow_fan_support || data->disallow_fan_type_call)
2662744d2fdSPali Rohár 		return -EINVAL;
2672744d2fdSPali Rohár 
268a5afba16SPali Rohár 	return i8k_smm(&regs) ? : regs.eax & 0xff;
269a5afba16SPali Rohár }
270a5afba16SPali Rohár 
i8k_get_fan_type(struct dell_smm_data * data,u8 fan)2714d9983deSArmin Wolf static int i8k_get_fan_type(struct dell_smm_data *data, u8 fan)
2725ce91714SPali Rohár {
2735ce91714SPali Rohár 	/* I8K_SMM_GET_FAN_TYPE SMM call is expensive, so cache values */
274deeba244SArmin Wolf 	if (data->fan_type[fan] == INT_MIN)
275deeba244SArmin Wolf 		data->fan_type[fan] = _i8k_get_fan_type(data, fan);
2765ce91714SPali Rohár 
277deeba244SArmin Wolf 	return data->fan_type[fan];
2785ce91714SPali Rohár }
2795ce91714SPali Rohár 
280a5afba16SPali Rohár /*
281a5afba16SPali Rohár  * Read the fan nominal rpm for specific fan speed.
282a5afba16SPali Rohár  */
i8k_get_fan_nominal_speed(const struct dell_smm_data * data,u8 fan,int speed)2834d9983deSArmin Wolf static int __init i8k_get_fan_nominal_speed(const struct dell_smm_data *data, u8 fan, int speed)
284a5afba16SPali Rohár {
2854d9983deSArmin Wolf 	struct smm_regs regs = {
2864d9983deSArmin Wolf 		.eax = I8K_SMM_GET_NOM_SPEED,
2874d9983deSArmin Wolf 		.ebx = fan | (speed << 8),
2884d9983deSArmin Wolf 	};
289a5afba16SPali Rohár 
290ba04d73cSArmin Wolf 	if (data->disallow_fan_support)
291f480ea90SPali Rohár 		return -EINVAL;
292f480ea90SPali Rohár 
2934fc1a51cSArmin Wolf 	return i8k_smm(&regs) ? : (regs.eax & 0xffff);
294a5afba16SPali Rohár }
295a5afba16SPali Rohár 
296a5afba16SPali Rohár /*
297afe45277SGiovanni Mascellani  * Enable or disable automatic BIOS fan control support
298afe45277SGiovanni Mascellani  */
i8k_enable_fan_auto_mode(const struct dell_smm_data * data,bool enable)299ba04d73cSArmin Wolf static int i8k_enable_fan_auto_mode(const struct dell_smm_data *data, bool enable)
300afe45277SGiovanni Mascellani {
301afe45277SGiovanni Mascellani 	struct smm_regs regs = { };
302afe45277SGiovanni Mascellani 
303ba04d73cSArmin Wolf 	if (data->disallow_fan_support)
304afe45277SGiovanni Mascellani 		return -EINVAL;
305afe45277SGiovanni Mascellani 
306ba04d73cSArmin Wolf 	regs.eax = enable ? data->auto_fan : data->manual_fan;
307afe45277SGiovanni Mascellani 	return i8k_smm(&regs);
308afe45277SGiovanni Mascellani }
309afe45277SGiovanni Mascellani 
310afe45277SGiovanni Mascellani /*
311c0d79987SArmin Wolf  * Set the fan speed (off, low, high, ...).
312a5afba16SPali Rohár  */
i8k_set_fan(const struct dell_smm_data * data,u8 fan,int speed)3134d9983deSArmin Wolf static int i8k_set_fan(const struct dell_smm_data *data, u8 fan, int speed)
314a5afba16SPali Rohár {
315a5afba16SPali Rohár 	struct smm_regs regs = { .eax = I8K_SMM_SET_FAN, };
316a5afba16SPali Rohár 
317ba04d73cSArmin Wolf 	if (data->disallow_fan_support)
318f480ea90SPali Rohár 		return -EINVAL;
319f480ea90SPali Rohár 
320ba04d73cSArmin Wolf 	speed = (speed < 0) ? 0 : ((speed > data->i8k_fan_max) ? data->i8k_fan_max : speed);
3214d9983deSArmin Wolf 	regs.ebx = fan | (speed << 8);
322a5afba16SPali Rohár 
323c0d79987SArmin Wolf 	return i8k_smm(&regs);
324a5afba16SPali Rohár }
325a5afba16SPali Rohár 
i8k_get_temp_type(u8 sensor)3264d9983deSArmin Wolf static int __init i8k_get_temp_type(u8 sensor)
327a5afba16SPali Rohár {
3284d9983deSArmin Wolf 	struct smm_regs regs = {
3294d9983deSArmin Wolf 		.eax = I8K_SMM_GET_TEMP_TYPE,
3304d9983deSArmin Wolf 		.ebx = sensor,
3314d9983deSArmin Wolf 	};
332a5afba16SPali Rohár 
333a5afba16SPali Rohár 	return i8k_smm(&regs) ? : regs.eax & 0xff;
334a5afba16SPali Rohár }
335a5afba16SPali Rohár 
336a5afba16SPali Rohár /*
337a5afba16SPali Rohár  * Read the cpu temperature.
338a5afba16SPali Rohár  */
_i8k_get_temp(u8 sensor)3394d9983deSArmin Wolf static int _i8k_get_temp(u8 sensor)
340a5afba16SPali Rohár {
341a5afba16SPali Rohár 	struct smm_regs regs = {
342a5afba16SPali Rohár 		.eax = I8K_SMM_GET_TEMP,
3434d9983deSArmin Wolf 		.ebx = sensor,
344a5afba16SPali Rohár 	};
345a5afba16SPali Rohár 
346a5afba16SPali Rohár 	return i8k_smm(&regs) ? : regs.eax & 0xff;
347a5afba16SPali Rohár }
348a5afba16SPali Rohár 
i8k_get_temp(u8 sensor)3494d9983deSArmin Wolf static int i8k_get_temp(u8 sensor)
350a5afba16SPali Rohár {
351a5afba16SPali Rohár 	int temp = _i8k_get_temp(sensor);
352a5afba16SPali Rohár 
353a5afba16SPali Rohár 	/*
354a5afba16SPali Rohár 	 * Sometimes the temperature sensor returns 0x99, which is out of range.
355a5afba16SPali Rohár 	 * In this case we retry (once) before returning an error.
356a5afba16SPali Rohár 	 # 1003655137 00000058 00005a4b
357a5afba16SPali Rohár 	 # 1003655138 00000099 00003a80 <--- 0x99 = 153 degrees
358a5afba16SPali Rohár 	 # 1003655139 00000054 00005c52
359a5afba16SPali Rohár 	 */
360a5afba16SPali Rohár 	if (temp == 0x99) {
361a5afba16SPali Rohár 		msleep(100);
362a5afba16SPali Rohár 		temp = _i8k_get_temp(sensor);
363a5afba16SPali Rohár 	}
364a5afba16SPali Rohár 	/*
365a5afba16SPali Rohár 	 * Return -ENODATA for all invalid temperatures.
366a5afba16SPali Rohár 	 *
367a5afba16SPali Rohár 	 * Known instances are the 0x99 value as seen above as well as
368a5afba16SPali Rohár 	 * 0xc1 (193), which may be returned when trying to read the GPU
369a5afba16SPali Rohár 	 * temperature if the system supports a GPU and it is currently
370a5afba16SPali Rohár 	 * turned off.
371a5afba16SPali Rohár 	 */
372a5afba16SPali Rohár 	if (temp > I8K_MAX_TEMP)
373a5afba16SPali Rohár 		return -ENODATA;
374a5afba16SPali Rohár 
375a5afba16SPali Rohár 	return temp;
376a5afba16SPali Rohár }
377a5afba16SPali Rohár 
i8k_get_dell_signature(int req_fn)378c9363cdfSArmin Wolf static int __init i8k_get_dell_signature(int req_fn)
379a5afba16SPali Rohár {
380a5afba16SPali Rohár 	struct smm_regs regs = { .eax = req_fn, };
381a5afba16SPali Rohár 	int rc;
382a5afba16SPali Rohár 
383a5afba16SPali Rohár 	rc = i8k_smm(&regs);
384a5afba16SPali Rohár 	if (rc < 0)
385a5afba16SPali Rohár 		return rc;
386a5afba16SPali Rohár 
387a5afba16SPali Rohár 	return regs.eax == 1145651527 && regs.edx == 1145392204 ? 0 : -1;
388a5afba16SPali Rohár }
389a5afba16SPali Rohár 
390039ae585SPali Rohár #if IS_ENABLED(CONFIG_I8K)
391039ae585SPali Rohár 
392039ae585SPali Rohár /*
393039ae585SPali Rohár  * Read the Fn key status.
394039ae585SPali Rohár  */
i8k_get_fn_status(void)395039ae585SPali Rohár static int i8k_get_fn_status(void)
396039ae585SPali Rohár {
397039ae585SPali Rohár 	struct smm_regs regs = { .eax = I8K_SMM_FN_STATUS, };
398039ae585SPali Rohár 	int rc;
399039ae585SPali Rohár 
400039ae585SPali Rohár 	rc = i8k_smm(&regs);
401039ae585SPali Rohár 	if (rc < 0)
402039ae585SPali Rohár 		return rc;
403039ae585SPali Rohár 
404039ae585SPali Rohár 	switch ((regs.eax >> I8K_FN_SHIFT) & I8K_FN_MASK) {
405039ae585SPali Rohár 	case I8K_FN_UP:
406039ae585SPali Rohár 		return I8K_VOL_UP;
407039ae585SPali Rohár 	case I8K_FN_DOWN:
408039ae585SPali Rohár 		return I8K_VOL_DOWN;
409039ae585SPali Rohár 	case I8K_FN_MUTE:
410039ae585SPali Rohár 		return I8K_VOL_MUTE;
411039ae585SPali Rohár 	default:
412039ae585SPali Rohár 		return 0;
413039ae585SPali Rohár 	}
414039ae585SPali Rohár }
415039ae585SPali Rohár 
416039ae585SPali Rohár /*
417039ae585SPali Rohár  * Read the power status.
418039ae585SPali Rohár  */
i8k_get_power_status(void)419039ae585SPali Rohár static int i8k_get_power_status(void)
420039ae585SPali Rohár {
421039ae585SPali Rohár 	struct smm_regs regs = { .eax = I8K_SMM_POWER_STATUS, };
422039ae585SPali Rohár 	int rc;
423039ae585SPali Rohár 
424039ae585SPali Rohár 	rc = i8k_smm(&regs);
425039ae585SPali Rohár 	if (rc < 0)
426039ae585SPali Rohár 		return rc;
427039ae585SPali Rohár 
428039ae585SPali Rohár 	return (regs.eax & 0xff) == I8K_POWER_AC ? I8K_AC : I8K_BATTERY;
429039ae585SPali Rohár }
430039ae585SPali Rohár 
431039ae585SPali Rohár /*
432039ae585SPali Rohár  * Procfs interface
433039ae585SPali Rohár  */
434039ae585SPali Rohár 
i8k_ioctl(struct file * fp,unsigned int cmd,unsigned long arg)43587b93329SArmin Wolf static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
436a5afba16SPali Rohár {
437359745d7SMuchun Song 	struct dell_smm_data *data = pde_data(file_inode(fp));
438a5afba16SPali Rohár 	int __user *argp = (int __user *)arg;
43987b93329SArmin Wolf 	int speed, err;
44087b93329SArmin Wolf 	int val = 0;
441a5afba16SPali Rohár 
442a5afba16SPali Rohár 	if (!argp)
443a5afba16SPali Rohár 		return -EINVAL;
444a5afba16SPali Rohár 
445a5afba16SPali Rohár 	switch (cmd) {
446a5afba16SPali Rohár 	case I8K_BIOS_VERSION:
447ba04d73cSArmin Wolf 		if (!isdigit(data->bios_version[0]) || !isdigit(data->bios_version[1]) ||
448ba04d73cSArmin Wolf 		    !isdigit(data->bios_version[2]))
449053ea640SPali Rohár 			return -EINVAL;
450053ea640SPali Rohár 
451ba04d73cSArmin Wolf 		val = (data->bios_version[0] << 16) |
452ba04d73cSArmin Wolf 				(data->bios_version[1] << 8) | data->bios_version[2];
453a5afba16SPali Rohár 
45402405387SArmin Wolf 		if (copy_to_user(argp, &val, sizeof(val)))
45502405387SArmin Wolf 			return -EFAULT;
45602405387SArmin Wolf 
45702405387SArmin Wolf 		return 0;
458a5afba16SPali Rohár 	case I8K_MACHINE_ID:
4597613663cSPali Rohár 		if (restricted && !capable(CAP_SYS_ADMIN))
4607613663cSPali Rohár 			return -EPERM;
4617613663cSPali Rohár 
46202405387SArmin Wolf 		if (copy_to_user(argp, data->bios_machineid, sizeof(data->bios_machineid)))
46302405387SArmin Wolf 			return -EFAULT;
464a5afba16SPali Rohár 
46502405387SArmin Wolf 		return 0;
466a5afba16SPali Rohár 	case I8K_FN_STATUS:
467a5afba16SPali Rohár 		val = i8k_get_fn_status();
468a5afba16SPali Rohár 		break;
469a5afba16SPali Rohár 
470a5afba16SPali Rohár 	case I8K_POWER_STATUS:
471a5afba16SPali Rohár 		val = i8k_get_power_status();
472a5afba16SPali Rohár 		break;
473a5afba16SPali Rohár 
474a5afba16SPali Rohár 	case I8K_GET_TEMP:
475a5afba16SPali Rohár 		val = i8k_get_temp(0);
476a5afba16SPali Rohár 		break;
477a5afba16SPali Rohár 
478a5afba16SPali Rohár 	case I8K_GET_SPEED:
479a5afba16SPali Rohár 		if (copy_from_user(&val, argp, sizeof(int)))
480a5afba16SPali Rohár 			return -EFAULT;
481a5afba16SPali Rohár 
4824d9983deSArmin Wolf 		if (val > U8_MAX || val < 0)
4834d9983deSArmin Wolf 			return -EINVAL;
4844d9983deSArmin Wolf 
485ba04d73cSArmin Wolf 		val = i8k_get_fan_speed(data, val);
486a5afba16SPali Rohár 		break;
487a5afba16SPali Rohár 
488a5afba16SPali Rohár 	case I8K_GET_FAN:
489a5afba16SPali Rohár 		if (copy_from_user(&val, argp, sizeof(int)))
490a5afba16SPali Rohár 			return -EFAULT;
491a5afba16SPali Rohár 
4924d9983deSArmin Wolf 		if (val > U8_MAX || val < 0)
4934d9983deSArmin Wolf 			return -EINVAL;
4944d9983deSArmin Wolf 
495ba04d73cSArmin Wolf 		val = i8k_get_fan_status(data, val);
496a5afba16SPali Rohár 		break;
497a5afba16SPali Rohár 
498a5afba16SPali Rohár 	case I8K_SET_FAN:
499a5afba16SPali Rohár 		if (restricted && !capable(CAP_SYS_ADMIN))
500a5afba16SPali Rohár 			return -EPERM;
501a5afba16SPali Rohár 
502a5afba16SPali Rohár 		if (copy_from_user(&val, argp, sizeof(int)))
503a5afba16SPali Rohár 			return -EFAULT;
504a5afba16SPali Rohár 
5054d9983deSArmin Wolf 		if (val > U8_MAX || val < 0)
5064d9983deSArmin Wolf 			return -EINVAL;
5074d9983deSArmin Wolf 
508a5afba16SPali Rohár 		if (copy_from_user(&speed, argp + 1, sizeof(int)))
509a5afba16SPali Rohár 			return -EFAULT;
510a5afba16SPali Rohár 
51187b93329SArmin Wolf 		mutex_lock(&data->i8k_mutex);
512c0d79987SArmin Wolf 		err = i8k_set_fan(data, val, speed);
513c0d79987SArmin Wolf 		if (err < 0)
51487b93329SArmin Wolf 			val = err;
51587b93329SArmin Wolf 		else
516c0d79987SArmin Wolf 			val = i8k_get_fan_status(data, val);
51787b93329SArmin Wolf 		mutex_unlock(&data->i8k_mutex);
518a5afba16SPali Rohár 		break;
519a5afba16SPali Rohár 
520a5afba16SPali Rohár 	default:
521e64325e8SArmin Wolf 		return -ENOIOCTLCMD;
522a5afba16SPali Rohár 	}
523a5afba16SPali Rohár 
524a5afba16SPali Rohár 	if (val < 0)
525a5afba16SPali Rohár 		return val;
526a5afba16SPali Rohár 
527a5afba16SPali Rohár 	if (copy_to_user(argp, &val, sizeof(int)))
528a5afba16SPali Rohár 		return -EFAULT;
529a5afba16SPali Rohár 
530a5afba16SPali Rohár 	return 0;
531a5afba16SPali Rohár }
532a5afba16SPali Rohár 
533a5afba16SPali Rohár /*
534a5afba16SPali Rohár  * Print the information for /proc/i8k.
535a5afba16SPali Rohár  */
i8k_proc_show(struct seq_file * seq,void * offset)536a5afba16SPali Rohár static int i8k_proc_show(struct seq_file *seq, void *offset)
537a5afba16SPali Rohár {
538ba04d73cSArmin Wolf 	struct dell_smm_data *data = seq->private;
539a5afba16SPali Rohár 	int fn_key, cpu_temp, ac_power;
540a5afba16SPali Rohár 	int left_fan, right_fan, left_speed, right_speed;
541a5afba16SPali Rohár 
542a5afba16SPali Rohár 	cpu_temp	= i8k_get_temp(0);				/* 11100 µs */
543ba04d73cSArmin Wolf 	left_fan	= i8k_get_fan_status(data, I8K_FAN_LEFT);	/*   580 µs */
544ba04d73cSArmin Wolf 	right_fan	= i8k_get_fan_status(data, I8K_FAN_RIGHT);	/*   580 µs */
545ba04d73cSArmin Wolf 	left_speed	= i8k_get_fan_speed(data, I8K_FAN_LEFT);	/*   580 µs */
546ba04d73cSArmin Wolf 	right_speed	= i8k_get_fan_speed(data, I8K_FAN_RIGHT);	/*   580 µs */
547a5afba16SPali Rohár 	fn_key		= i8k_get_fn_status();				/*   750 µs */
548a5afba16SPali Rohár 	if (power_status)
549a5afba16SPali Rohár 		ac_power = i8k_get_power_status();			/* 14700 µs */
550a5afba16SPali Rohár 	else
551a5afba16SPali Rohár 		ac_power = -1;
552a5afba16SPali Rohár 
553a5afba16SPali Rohár 	/*
554a5afba16SPali Rohár 	 * Info:
555a5afba16SPali Rohár 	 *
556a5afba16SPali Rohár 	 * 1)  Format version (this will change if format changes)
557a5afba16SPali Rohár 	 * 2)  BIOS version
558a5afba16SPali Rohár 	 * 3)  BIOS machine ID
559a5afba16SPali Rohár 	 * 4)  Cpu temperature
560a5afba16SPali Rohár 	 * 5)  Left fan status
561a5afba16SPali Rohár 	 * 6)  Right fan status
562a5afba16SPali Rohár 	 * 7)  Left fan speed
563a5afba16SPali Rohár 	 * 8)  Right fan speed
564a5afba16SPali Rohár 	 * 9)  AC power
565a5afba16SPali Rohár 	 * 10) Fn Key status
566a5afba16SPali Rohár 	 */
567a5afba16SPali Rohár 	seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n",
568a5afba16SPali Rohár 		   I8K_PROC_FMT,
569ba04d73cSArmin Wolf 		   data->bios_version,
570ba04d73cSArmin Wolf 		   (restricted && !capable(CAP_SYS_ADMIN)) ? "-1" : data->bios_machineid,
571a5afba16SPali Rohár 		   cpu_temp,
572a5afba16SPali Rohár 		   left_fan, right_fan, left_speed, right_speed,
573a5afba16SPali Rohár 		   ac_power, fn_key);
574a5afba16SPali Rohár 
575a5afba16SPali Rohár 	return 0;
576a5afba16SPali Rohár }
577a5afba16SPali Rohár 
i8k_open_fs(struct inode * inode,struct file * file)578a5afba16SPali Rohár static int i8k_open_fs(struct inode *inode, struct file *file)
579a5afba16SPali Rohár {
580359745d7SMuchun Song 	return single_open(file, i8k_proc_show, pde_data(inode));
581a5afba16SPali Rohár }
582a5afba16SPali Rohár 
58397a32539SAlexey Dobriyan static const struct proc_ops i8k_proc_ops = {
58497a32539SAlexey Dobriyan 	.proc_open	= i8k_open_fs,
58597a32539SAlexey Dobriyan 	.proc_read	= seq_read,
58697a32539SAlexey Dobriyan 	.proc_lseek	= seq_lseek,
58797a32539SAlexey Dobriyan 	.proc_release	= single_release,
58897a32539SAlexey Dobriyan 	.proc_ioctl	= i8k_ioctl,
589039ae585SPali Rohár };
590039ae585SPali Rohár 
i8k_exit_procfs(void * param)591a2cb66b4SArmin Wolf static void i8k_exit_procfs(void *param)
592039ae585SPali Rohár {
593039ae585SPali Rohár 	remove_proc_entry("i8k", NULL);
594039ae585SPali Rohár }
595039ae585SPali Rohár 
i8k_init_procfs(struct device * dev)596a2cb66b4SArmin Wolf static void __init i8k_init_procfs(struct device *dev)
597039ae585SPali Rohár {
598ba04d73cSArmin Wolf 	struct dell_smm_data *data = dev_get_drvdata(dev);
599ba04d73cSArmin Wolf 
600dbd3e6eaSArmin Wolf 	/* Only register exit function if creation was successful */
601dbd3e6eaSArmin Wolf 	if (proc_create_data("i8k", 0, NULL, &i8k_proc_ops, data))
602a2cb66b4SArmin Wolf 		devm_add_action_or_reset(dev, i8k_exit_procfs, NULL);
603039ae585SPali Rohár }
604039ae585SPali Rohár 
605a2cb66b4SArmin Wolf #else
606a2cb66b4SArmin Wolf 
i8k_init_procfs(struct device * dev)607a2cb66b4SArmin Wolf static void __init i8k_init_procfs(struct device *dev)
608039ae585SPali Rohár {
609039ae585SPali Rohár }
610039ae585SPali Rohár 
611039ae585SPali Rohár #endif
612a5afba16SPali Rohár 
dell_smm_get_max_state(struct thermal_cooling_device * dev,unsigned long * state)613e0d3f7cbSArmin Wolf static int dell_smm_get_max_state(struct thermal_cooling_device *dev, unsigned long *state)
614e0d3f7cbSArmin Wolf {
615e0d3f7cbSArmin Wolf 	struct dell_smm_cooling_data *cdata = dev->devdata;
616e0d3f7cbSArmin Wolf 
617e0d3f7cbSArmin Wolf 	*state = cdata->data->i8k_fan_max;
618e0d3f7cbSArmin Wolf 
619e0d3f7cbSArmin Wolf 	return 0;
620e0d3f7cbSArmin Wolf }
621e0d3f7cbSArmin Wolf 
dell_smm_get_cur_state(struct thermal_cooling_device * dev,unsigned long * state)622e0d3f7cbSArmin Wolf static int dell_smm_get_cur_state(struct thermal_cooling_device *dev, unsigned long *state)
623e0d3f7cbSArmin Wolf {
624e0d3f7cbSArmin Wolf 	struct dell_smm_cooling_data *cdata = dev->devdata;
625e0d3f7cbSArmin Wolf 	int ret;
626e0d3f7cbSArmin Wolf 
627e0d3f7cbSArmin Wolf 	ret = i8k_get_fan_status(cdata->data, cdata->fan_num);
628e0d3f7cbSArmin Wolf 	if (ret < 0)
629e0d3f7cbSArmin Wolf 		return ret;
630e0d3f7cbSArmin Wolf 
631e0d3f7cbSArmin Wolf 	*state = ret;
632e0d3f7cbSArmin Wolf 
633e0d3f7cbSArmin Wolf 	return 0;
634e0d3f7cbSArmin Wolf }
635e0d3f7cbSArmin Wolf 
dell_smm_set_cur_state(struct thermal_cooling_device * dev,unsigned long state)636e0d3f7cbSArmin Wolf static int dell_smm_set_cur_state(struct thermal_cooling_device *dev, unsigned long state)
637e0d3f7cbSArmin Wolf {
638e0d3f7cbSArmin Wolf 	struct dell_smm_cooling_data *cdata = dev->devdata;
639e0d3f7cbSArmin Wolf 	struct dell_smm_data *data = cdata->data;
640e0d3f7cbSArmin Wolf 	int ret;
641e0d3f7cbSArmin Wolf 
642e0d3f7cbSArmin Wolf 	if (state > data->i8k_fan_max)
643e0d3f7cbSArmin Wolf 		return -EINVAL;
644e0d3f7cbSArmin Wolf 
645e0d3f7cbSArmin Wolf 	mutex_lock(&data->i8k_mutex);
646e0d3f7cbSArmin Wolf 	ret = i8k_set_fan(data, cdata->fan_num, (int)state);
647e0d3f7cbSArmin Wolf 	mutex_unlock(&data->i8k_mutex);
648e0d3f7cbSArmin Wolf 
649e0d3f7cbSArmin Wolf 	return ret;
650e0d3f7cbSArmin Wolf }
651e0d3f7cbSArmin Wolf 
652e0d3f7cbSArmin Wolf static const struct thermal_cooling_device_ops dell_smm_cooling_ops = {
653e0d3f7cbSArmin Wolf 	.get_max_state = dell_smm_get_max_state,
654e0d3f7cbSArmin Wolf 	.get_cur_state = dell_smm_get_cur_state,
655e0d3f7cbSArmin Wolf 	.set_cur_state = dell_smm_set_cur_state,
656e0d3f7cbSArmin Wolf };
657a5afba16SPali Rohár 
dell_smm_is_visible(const void * drvdata,enum hwmon_sensor_types type,u32 attr,int channel)658deeba244SArmin Wolf static umode_t dell_smm_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr,
659deeba244SArmin Wolf 				   int channel)
660a5afba16SPali Rohár {
661deeba244SArmin Wolf 	const struct dell_smm_data *data = drvdata;
662a5afba16SPali Rohár 
663deeba244SArmin Wolf 	switch (type) {
664deeba244SArmin Wolf 	case hwmon_temp:
665deeba244SArmin Wolf 		switch (attr) {
666deeba244SArmin Wolf 		case hwmon_temp_input:
667c82fdd42SArmin Wolf 			/* _i8k_get_temp() is fine since we do not care about the actual value */
668c82fdd42SArmin Wolf 			if (data->temp_type[channel] >= 0 || _i8k_get_temp(channel) >= 0)
669c82fdd42SArmin Wolf 				return 0444;
670c82fdd42SArmin Wolf 
671c82fdd42SArmin Wolf 			break;
672deeba244SArmin Wolf 		case hwmon_temp_label:
673deeba244SArmin Wolf 			if (data->temp_type[channel] >= 0)
674deeba244SArmin Wolf 				return 0444;
675deeba244SArmin Wolf 
676deeba244SArmin Wolf 			break;
677deeba244SArmin Wolf 		default:
678deeba244SArmin Wolf 			break;
679deeba244SArmin Wolf 		}
680deeba244SArmin Wolf 		break;
681deeba244SArmin Wolf 	case hwmon_fan:
682deeba244SArmin Wolf 		if (data->disallow_fan_support)
683deeba244SArmin Wolf 			break;
684deeba244SArmin Wolf 
685deeba244SArmin Wolf 		switch (attr) {
686deeba244SArmin Wolf 		case hwmon_fan_input:
687deeba244SArmin Wolf 			if (data->fan[channel])
688deeba244SArmin Wolf 				return 0444;
689deeba244SArmin Wolf 
690deeba244SArmin Wolf 			break;
691deeba244SArmin Wolf 		case hwmon_fan_label:
692deeba244SArmin Wolf 			if (data->fan[channel] && !data->disallow_fan_type_call)
693deeba244SArmin Wolf 				return 0444;
694deeba244SArmin Wolf 
695deeba244SArmin Wolf 			break;
696b1986c8eSArmin Wolf 		case hwmon_fan_min:
697b1986c8eSArmin Wolf 		case hwmon_fan_max:
698b1986c8eSArmin Wolf 		case hwmon_fan_target:
699b1986c8eSArmin Wolf 			if (data->fan_nominal_speed[channel])
700b1986c8eSArmin Wolf 				return 0444;
701b1986c8eSArmin Wolf 
702b1986c8eSArmin Wolf 			break;
703deeba244SArmin Wolf 		default:
704deeba244SArmin Wolf 			break;
705deeba244SArmin Wolf 		}
706deeba244SArmin Wolf 		break;
707deeba244SArmin Wolf 	case hwmon_pwm:
708deeba244SArmin Wolf 		if (data->disallow_fan_support)
709deeba244SArmin Wolf 			break;
710deeba244SArmin Wolf 
711deeba244SArmin Wolf 		switch (attr) {
712deeba244SArmin Wolf 		case hwmon_pwm_input:
713deeba244SArmin Wolf 			if (data->fan[channel])
714deeba244SArmin Wolf 				return 0644;
715deeba244SArmin Wolf 
716deeba244SArmin Wolf 			break;
717deeba244SArmin Wolf 		case hwmon_pwm_enable:
718deeba244SArmin Wolf 			if (data->auto_fan)
719deeba244SArmin Wolf 				/*
720deeba244SArmin Wolf 				 * There is no command for retrieve the current status
721deeba244SArmin Wolf 				 * from BIOS, and userspace/firmware itself can change
722deeba244SArmin Wolf 				 * it.
723deeba244SArmin Wolf 				 * Thus we can only provide write-only access for now.
724deeba244SArmin Wolf 				 */
725deeba244SArmin Wolf 				return 0200;
726deeba244SArmin Wolf 
727deeba244SArmin Wolf 			break;
728deeba244SArmin Wolf 		default:
729deeba244SArmin Wolf 			break;
730deeba244SArmin Wolf 		}
731deeba244SArmin Wolf 		break;
732deeba244SArmin Wolf 	default:
733deeba244SArmin Wolf 		break;
734a5afba16SPali Rohár 	}
735a5afba16SPali Rohár 
736deeba244SArmin Wolf 	return 0;
737a5afba16SPali Rohár }
738a5afba16SPali Rohár 
dell_smm_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)739deeba244SArmin Wolf static int dell_smm_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
740deeba244SArmin Wolf 			 long *val)
741a5afba16SPali Rohár {
742ba04d73cSArmin Wolf 	struct dell_smm_data *data = dev_get_drvdata(dev);
7434fc1a51cSArmin Wolf 	int mult = data->i8k_fan_mult;
744deeba244SArmin Wolf 	int ret;
745a5afba16SPali Rohár 
746deeba244SArmin Wolf 	switch (type) {
747deeba244SArmin Wolf 	case hwmon_temp:
748deeba244SArmin Wolf 		switch (attr) {
749deeba244SArmin Wolf 		case hwmon_temp_input:
750deeba244SArmin Wolf 			ret = i8k_get_temp(channel);
751deeba244SArmin Wolf 			if (ret < 0)
752deeba244SArmin Wolf 				return ret;
753deeba244SArmin Wolf 
754deeba244SArmin Wolf 			*val = ret * 1000;
755deeba244SArmin Wolf 
756deeba244SArmin Wolf 			return 0;
757deeba244SArmin Wolf 		default:
758deeba244SArmin Wolf 			break;
759deeba244SArmin Wolf 		}
760deeba244SArmin Wolf 		break;
761deeba244SArmin Wolf 	case hwmon_fan:
762deeba244SArmin Wolf 		switch (attr) {
763deeba244SArmin Wolf 		case hwmon_fan_input:
764deeba244SArmin Wolf 			ret = i8k_get_fan_speed(data, channel);
765deeba244SArmin Wolf 			if (ret < 0)
766deeba244SArmin Wolf 				return ret;
767deeba244SArmin Wolf 
768deeba244SArmin Wolf 			*val = ret;
769deeba244SArmin Wolf 
770deeba244SArmin Wolf 			return 0;
771b1986c8eSArmin Wolf 		case hwmon_fan_min:
7724fc1a51cSArmin Wolf 			*val = data->fan_nominal_speed[channel][0] * mult;
773b1986c8eSArmin Wolf 
774b1986c8eSArmin Wolf 			return 0;
775b1986c8eSArmin Wolf 		case hwmon_fan_max:
7764fc1a51cSArmin Wolf 			*val = data->fan_nominal_speed[channel][data->i8k_fan_max] * mult;
777b1986c8eSArmin Wolf 
778b1986c8eSArmin Wolf 			return 0;
779b1986c8eSArmin Wolf 		case hwmon_fan_target:
780b1986c8eSArmin Wolf 			ret = i8k_get_fan_status(data, channel);
781b1986c8eSArmin Wolf 			if (ret < 0)
782b1986c8eSArmin Wolf 				return ret;
783b1986c8eSArmin Wolf 
784b1986c8eSArmin Wolf 			if (ret > data->i8k_fan_max)
785b1986c8eSArmin Wolf 				ret = data->i8k_fan_max;
786b1986c8eSArmin Wolf 
7874fc1a51cSArmin Wolf 			*val = data->fan_nominal_speed[channel][ret] * mult;
788b1986c8eSArmin Wolf 
789b1986c8eSArmin Wolf 			return 0;
790deeba244SArmin Wolf 		default:
791deeba244SArmin Wolf 			break;
792deeba244SArmin Wolf 		}
793deeba244SArmin Wolf 		break;
794deeba244SArmin Wolf 	case hwmon_pwm:
795deeba244SArmin Wolf 		switch (attr) {
796deeba244SArmin Wolf 		case hwmon_pwm_input:
797deeba244SArmin Wolf 			ret = i8k_get_fan_status(data, channel);
798deeba244SArmin Wolf 			if (ret < 0)
799deeba244SArmin Wolf 				return ret;
800deeba244SArmin Wolf 
801deeba244SArmin Wolf 			*val = clamp_val(ret * data->i8k_pwm_mult, 0, 255);
802deeba244SArmin Wolf 
803deeba244SArmin Wolf 			return 0;
804deeba244SArmin Wolf 		default:
805deeba244SArmin Wolf 			break;
806deeba244SArmin Wolf 		}
807deeba244SArmin Wolf 		break;
808deeba244SArmin Wolf 	default:
809deeba244SArmin Wolf 		break;
810deeba244SArmin Wolf 	}
811deeba244SArmin Wolf 
812deeba244SArmin Wolf 	return -EOPNOTSUPP;
813deeba244SArmin Wolf }
814deeba244SArmin Wolf 
dell_smm_fan_label(struct dell_smm_data * data,int channel)815deeba244SArmin Wolf static const char *dell_smm_fan_label(struct dell_smm_data *data, int channel)
816deeba244SArmin Wolf {
817deeba244SArmin Wolf 	bool dock = false;
818deeba244SArmin Wolf 	int type = i8k_get_fan_type(data, channel);
819deeba244SArmin Wolf 
820a5afba16SPali Rohár 	if (type < 0)
821deeba244SArmin Wolf 		return ERR_PTR(type);
822a5afba16SPali Rohár 
823a5afba16SPali Rohár 	if (type & 0x10) {
824a5afba16SPali Rohár 		dock = true;
825a5afba16SPali Rohár 		type &= 0x0F;
826a5afba16SPali Rohár 	}
827a5afba16SPali Rohár 
828deeba244SArmin Wolf 	if (type >= ARRAY_SIZE(fan_labels))
829deeba244SArmin Wolf 		type = ARRAY_SIZE(fan_labels) - 1;
830a5afba16SPali Rohár 
831deeba244SArmin Wolf 	return dock ? docking_labels[type] : fan_labels[type];
832a5afba16SPali Rohár }
833a5afba16SPali Rohár 
dell_smm_read_string(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,const char ** str)834deeba244SArmin Wolf static int dell_smm_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
835deeba244SArmin Wolf 				int channel, const char **str)
836a5afba16SPali Rohár {
837ba04d73cSArmin Wolf 	struct dell_smm_data *data = dev_get_drvdata(dev);
838a5afba16SPali Rohár 
839deeba244SArmin Wolf 	switch (type) {
840deeba244SArmin Wolf 	case hwmon_temp:
841deeba244SArmin Wolf 		switch (attr) {
842deeba244SArmin Wolf 		case hwmon_temp_label:
843deeba244SArmin Wolf 			*str = temp_labels[data->temp_type[channel]];
844deeba244SArmin Wolf 			return 0;
845deeba244SArmin Wolf 		default:
846deeba244SArmin Wolf 			break;
847deeba244SArmin Wolf 		}
848deeba244SArmin Wolf 		break;
849deeba244SArmin Wolf 	case hwmon_fan:
850deeba244SArmin Wolf 		switch (attr) {
851deeba244SArmin Wolf 		case hwmon_fan_label:
852deeba244SArmin Wolf 			*str = dell_smm_fan_label(data, channel);
853deeba244SArmin Wolf 			return PTR_ERR_OR_ZERO(*str);
854deeba244SArmin Wolf 		default:
855deeba244SArmin Wolf 			break;
856deeba244SArmin Wolf 		}
857deeba244SArmin Wolf 		break;
858deeba244SArmin Wolf 	default:
859deeba244SArmin Wolf 		break;
860a5afba16SPali Rohár 	}
861a5afba16SPali Rohár 
862deeba244SArmin Wolf 	return -EOPNOTSUPP;
863a5afba16SPali Rohár }
864a5afba16SPali Rohár 
dell_smm_write(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long val)865deeba244SArmin Wolf static int dell_smm_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
866deeba244SArmin Wolf 			  long val)
867a5afba16SPali Rohár {
868ba04d73cSArmin Wolf 	struct dell_smm_data *data = dev_get_drvdata(dev);
869deeba244SArmin Wolf 	unsigned long pwm;
870deeba244SArmin Wolf 	bool enable;
871a5afba16SPali Rohár 	int err;
872a5afba16SPali Rohár 
873deeba244SArmin Wolf 	switch (type) {
874deeba244SArmin Wolf 	case hwmon_pwm:
875deeba244SArmin Wolf 		switch (attr) {
876deeba244SArmin Wolf 		case hwmon_pwm_input:
877deeba244SArmin Wolf 			pwm = clamp_val(DIV_ROUND_CLOSEST(val, data->i8k_pwm_mult), 0,
878deeba244SArmin Wolf 					data->i8k_fan_max);
879a5afba16SPali Rohár 
880ba04d73cSArmin Wolf 			mutex_lock(&data->i8k_mutex);
881deeba244SArmin Wolf 			err = i8k_set_fan(data, channel, pwm);
882ba04d73cSArmin Wolf 			mutex_unlock(&data->i8k_mutex);
883a5afba16SPali Rohár 
884deeba244SArmin Wolf 			if (err < 0)
885afe45277SGiovanni Mascellani 				return err;
886afe45277SGiovanni Mascellani 
887deeba244SArmin Wolf 			return 0;
888deeba244SArmin Wolf 		case hwmon_pwm_enable:
889deeba244SArmin Wolf 			if (!val)
890deeba244SArmin Wolf 				return -EINVAL;
891deeba244SArmin Wolf 
892afe45277SGiovanni Mascellani 			if (val == 1)
893afe45277SGiovanni Mascellani 				enable = false;
894afe45277SGiovanni Mascellani 			else
895deeba244SArmin Wolf 				enable = true;
896afe45277SGiovanni Mascellani 
897ba04d73cSArmin Wolf 			mutex_lock(&data->i8k_mutex);
898ba04d73cSArmin Wolf 			err = i8k_enable_fan_auto_mode(data, enable);
899ba04d73cSArmin Wolf 			mutex_unlock(&data->i8k_mutex);
900afe45277SGiovanni Mascellani 
901deeba244SArmin Wolf 			if (err < 0)
902deeba244SArmin Wolf 				return err;
903deeba244SArmin Wolf 
904deeba244SArmin Wolf 			return 0;
905deeba244SArmin Wolf 		default:
906deeba244SArmin Wolf 			break;
907deeba244SArmin Wolf 		}
908deeba244SArmin Wolf 		break;
909deeba244SArmin Wolf 	default:
910deeba244SArmin Wolf 		break;
911afe45277SGiovanni Mascellani 	}
912afe45277SGiovanni Mascellani 
913deeba244SArmin Wolf 	return -EOPNOTSUPP;
914deeba244SArmin Wolf }
915a5afba16SPali Rohár 
916deeba244SArmin Wolf static const struct hwmon_ops dell_smm_ops = {
917deeba244SArmin Wolf 	.is_visible = dell_smm_is_visible,
918deeba244SArmin Wolf 	.read = dell_smm_read,
919deeba244SArmin Wolf 	.read_string = dell_smm_read_string,
920deeba244SArmin Wolf 	.write = dell_smm_write,
921deeba244SArmin Wolf };
922deeba244SArmin Wolf 
923*f4ddd8f2SKrzysztof Kozlowski static const struct hwmon_channel_info * const dell_smm_info[] = {
924deeba244SArmin Wolf 	HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
925deeba244SArmin Wolf 	HWMON_CHANNEL_INFO(temp,
926deeba244SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_LABEL,
927deeba244SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_LABEL,
928deeba244SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_LABEL,
929deeba244SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_LABEL,
930deeba244SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_LABEL,
931deeba244SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_LABEL,
932deeba244SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_LABEL,
933deeba244SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_LABEL,
934deeba244SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_LABEL,
935deeba244SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_LABEL
936deeba244SArmin Wolf 			   ),
937deeba244SArmin Wolf 	HWMON_CHANNEL_INFO(fan,
938b1986c8eSArmin Wolf 			   HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX |
939b1986c8eSArmin Wolf 			   HWMON_F_TARGET,
940b1986c8eSArmin Wolf 			   HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX |
941b1986c8eSArmin Wolf 			   HWMON_F_TARGET,
942b1986c8eSArmin Wolf 			   HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX |
943b1986c8eSArmin Wolf 			   HWMON_F_TARGET
944deeba244SArmin Wolf 			   ),
945deeba244SArmin Wolf 	HWMON_CHANNEL_INFO(pwm,
946deeba244SArmin Wolf 			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
947deeba244SArmin Wolf 			   HWMON_PWM_INPUT,
948deeba244SArmin Wolf 			   HWMON_PWM_INPUT
949deeba244SArmin Wolf 			   ),
950a5afba16SPali Rohár 	NULL
951a5afba16SPali Rohár };
952a5afba16SPali Rohár 
953deeba244SArmin Wolf static const struct hwmon_chip_info dell_smm_chip_info = {
954deeba244SArmin Wolf 	.ops = &dell_smm_ops,
955deeba244SArmin Wolf 	.info = dell_smm_info,
956a5afba16SPali Rohár };
957a5afba16SPali Rohár 
dell_smm_init_cdev(struct device * dev,u8 fan_num)958e0d3f7cbSArmin Wolf static int __init dell_smm_init_cdev(struct device *dev, u8 fan_num)
959e0d3f7cbSArmin Wolf {
960e0d3f7cbSArmin Wolf 	struct dell_smm_data *data = dev_get_drvdata(dev);
961e0d3f7cbSArmin Wolf 	struct thermal_cooling_device *cdev;
962e0d3f7cbSArmin Wolf 	struct dell_smm_cooling_data *cdata;
963e0d3f7cbSArmin Wolf 	int ret = 0;
964e0d3f7cbSArmin Wolf 	char *name;
965e0d3f7cbSArmin Wolf 
966e0d3f7cbSArmin Wolf 	name = kasprintf(GFP_KERNEL, "dell-smm-fan%u", fan_num + 1);
967e0d3f7cbSArmin Wolf 	if (!name)
968e0d3f7cbSArmin Wolf 		return -ENOMEM;
969e0d3f7cbSArmin Wolf 
970e0d3f7cbSArmin Wolf 	cdata = devm_kmalloc(dev, sizeof(*cdata), GFP_KERNEL);
971e0d3f7cbSArmin Wolf 	if (cdata) {
972e0d3f7cbSArmin Wolf 		cdata->fan_num = fan_num;
973e0d3f7cbSArmin Wolf 		cdata->data = data;
974e0d3f7cbSArmin Wolf 		cdev = devm_thermal_of_cooling_device_register(dev, NULL, name, cdata,
975e0d3f7cbSArmin Wolf 							       &dell_smm_cooling_ops);
976e0d3f7cbSArmin Wolf 		if (IS_ERR(cdev)) {
977e0d3f7cbSArmin Wolf 			devm_kfree(dev, cdata);
978e0d3f7cbSArmin Wolf 			ret = PTR_ERR(cdev);
979e0d3f7cbSArmin Wolf 		}
980e0d3f7cbSArmin Wolf 	} else {
981e0d3f7cbSArmin Wolf 		ret = -ENOMEM;
982e0d3f7cbSArmin Wolf 	}
983e0d3f7cbSArmin Wolf 
984e0d3f7cbSArmin Wolf 	kfree(name);
985e0d3f7cbSArmin Wolf 
986e0d3f7cbSArmin Wolf 	return ret;
987e0d3f7cbSArmin Wolf }
988e0d3f7cbSArmin Wolf 
dell_smm_init_hwmon(struct device * dev)9891492fa21SArmin Wolf static int __init dell_smm_init_hwmon(struct device *dev)
990a5afba16SPali Rohár {
991ba04d73cSArmin Wolf 	struct dell_smm_data *data = dev_get_drvdata(dev);
992deeba244SArmin Wolf 	struct device *dell_smm_hwmon_dev;
9934d9983deSArmin Wolf 	int state, err;
9944d9983deSArmin Wolf 	u8 i;
995a5afba16SPali Rohár 
996deeba244SArmin Wolf 	for (i = 0; i < DELL_SMM_NO_TEMP; i++) {
997deeba244SArmin Wolf 		data->temp_type[i] = i8k_get_temp_type(i);
998deeba244SArmin Wolf 		if (data->temp_type[i] < 0)
999deeba244SArmin Wolf 			continue;
1000a5afba16SPali Rohár 
1001deeba244SArmin Wolf 		if (data->temp_type[i] >= ARRAY_SIZE(temp_labels))
1002deeba244SArmin Wolf 			data->temp_type[i] = ARRAY_SIZE(temp_labels) - 1;
1003deeba244SArmin Wolf 	}
1004deeba244SArmin Wolf 
1005deeba244SArmin Wolf 	for (i = 0; i < DELL_SMM_NO_FANS; i++) {
1006deeba244SArmin Wolf 		data->fan_type[i] = INT_MIN;
1007deeba244SArmin Wolf 		err = i8k_get_fan_status(data, i);
10085ce91714SPali Rohár 		if (err < 0)
1009deeba244SArmin Wolf 			err = i8k_get_fan_type(data, i);
1010b1986c8eSArmin Wolf 
1011b1986c8eSArmin Wolf 		if (err < 0)
1012b1986c8eSArmin Wolf 			continue;
1013b1986c8eSArmin Wolf 
1014deeba244SArmin Wolf 		data->fan[i] = true;
1015e0d3f7cbSArmin Wolf 
1016e0d3f7cbSArmin Wolf 		/* the cooling device is not critical, ignore failures */
1017e0d3f7cbSArmin Wolf 		if (IS_REACHABLE(CONFIG_THERMAL)) {
1018e0d3f7cbSArmin Wolf 			err = dell_smm_init_cdev(dev, i);
1019e0d3f7cbSArmin Wolf 			if (err < 0)
1020e0d3f7cbSArmin Wolf 				dev_warn(dev, "Failed to register cooling device for fan %u\n",
1021e0d3f7cbSArmin Wolf 					 i + 1);
1022e0d3f7cbSArmin Wolf 		}
1023e0d3f7cbSArmin Wolf 
1024b1986c8eSArmin Wolf 		data->fan_nominal_speed[i] = devm_kmalloc_array(dev, data->i8k_fan_max + 1,
1025b1986c8eSArmin Wolf 								sizeof(*data->fan_nominal_speed[i]),
1026b1986c8eSArmin Wolf 								GFP_KERNEL);
1027b1986c8eSArmin Wolf 		if (!data->fan_nominal_speed[i])
1028b1986c8eSArmin Wolf 			continue;
1029b1986c8eSArmin Wolf 
1030b1986c8eSArmin Wolf 		for (state = 0; state <= data->i8k_fan_max; state++) {
1031b1986c8eSArmin Wolf 			err = i8k_get_fan_nominal_speed(data, i, state);
1032b1986c8eSArmin Wolf 			if (err < 0) {
1033b1986c8eSArmin Wolf 				/* Mark nominal speed table as invalid in case of error */
1034b1986c8eSArmin Wolf 				devm_kfree(dev, data->fan_nominal_speed[i]);
1035b1986c8eSArmin Wolf 				data->fan_nominal_speed[i] = NULL;
1036b1986c8eSArmin Wolf 				break;
1037b1986c8eSArmin Wolf 			}
1038b1986c8eSArmin Wolf 			data->fan_nominal_speed[i][state] = err;
10394fc1a51cSArmin Wolf 			/*
10404fc1a51cSArmin Wolf 			 * Autodetect fan multiplier based on nominal rpm if multiplier
10414fc1a51cSArmin Wolf 			 * was not specified as module param or in DMI. If fan reports
10424fc1a51cSArmin Wolf 			 * rpm value too high then set multiplier to 1.
10434fc1a51cSArmin Wolf 			 */
10444fc1a51cSArmin Wolf 			if (!fan_mult && err > I8K_FAN_RPM_THRESHOLD)
10454fc1a51cSArmin Wolf 				data->i8k_fan_mult = 1;
1046b1986c8eSArmin Wolf 		}
1047deeba244SArmin Wolf 	}
1048a5afba16SPali Rohár 
1049deeba244SArmin Wolf 	dell_smm_hwmon_dev = devm_hwmon_device_register_with_info(dev, "dell_smm", data,
1050deeba244SArmin Wolf 								  &dell_smm_chip_info, NULL);
1051a5afba16SPali Rohár 
1052deeba244SArmin Wolf 	return PTR_ERR_OR_ZERO(dell_smm_hwmon_dev);
1053a5afba16SPali Rohár }
1054a5afba16SPali Rohár 
1055a5afba16SPali Rohár struct i8k_config_data {
1056a5afba16SPali Rohár 	uint fan_mult;
1057a5afba16SPali Rohár 	uint fan_max;
1058a5afba16SPali Rohár };
1059a5afba16SPali Rohár 
1060a5afba16SPali Rohár enum i8k_configs {
1061a5afba16SPali Rohár 	DELL_LATITUDE_D520,
1062a5afba16SPali Rohár 	DELL_PRECISION_490,
1063a5afba16SPali Rohár 	DELL_STUDIO,
1064a5afba16SPali Rohár 	DELL_XPS,
1065a5afba16SPali Rohár };
1066a5afba16SPali Rohár 
1067927d89eeSArmin Wolf /*
1068927d89eeSArmin Wolf  * Only use for machines which need some special configuration
1069927d89eeSArmin Wolf  * in order to work correctly (e.g. if autoconfig fails on this machines).
1070927d89eeSArmin Wolf  */
1071927d89eeSArmin Wolf 
1072c510f6acSArmin Wolf static const struct i8k_config_data i8k_config_data[] __initconst = {
1073a5afba16SPali Rohár 	[DELL_LATITUDE_D520] = {
1074a5afba16SPali Rohár 		.fan_mult = 1,
1075a5afba16SPali Rohár 		.fan_max = I8K_FAN_TURBO,
1076a5afba16SPali Rohár 	},
1077a5afba16SPali Rohár 	[DELL_PRECISION_490] = {
1078a5afba16SPali Rohár 		.fan_mult = 1,
1079a5afba16SPali Rohár 		.fan_max = I8K_FAN_TURBO,
1080a5afba16SPali Rohár 	},
1081a5afba16SPali Rohár 	[DELL_STUDIO] = {
1082a5afba16SPali Rohár 		.fan_mult = 1,
1083a5afba16SPali Rohár 		.fan_max = I8K_FAN_HIGH,
1084a5afba16SPali Rohár 	},
1085a5afba16SPali Rohár 	[DELL_XPS] = {
1086a5afba16SPali Rohár 		.fan_mult = 1,
1087a5afba16SPali Rohár 		.fan_max = I8K_FAN_HIGH,
1088a5afba16SPali Rohár 	},
1089a5afba16SPali Rohár };
1090a5afba16SPali Rohár 
10916faadbbbSChristoph Hellwig static const struct dmi_system_id i8k_dmi_table[] __initconst = {
1092a5afba16SPali Rohár 	{
1093489dd8f0SArmin Wolf 		.ident = "Dell G5 5590",
1094489dd8f0SArmin Wolf 		.matches = {
1095489dd8f0SArmin Wolf 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1096489dd8f0SArmin Wolf 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "G5 5590"),
1097489dd8f0SArmin Wolf 		},
1098489dd8f0SArmin Wolf 	},
1099489dd8f0SArmin Wolf 	{
1100a5afba16SPali Rohár 		.ident = "Dell Inspiron",
1101a5afba16SPali Rohár 		.matches = {
1102a5afba16SPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"),
1103a5afba16SPali Rohár 			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"),
1104a5afba16SPali Rohár 		},
1105a5afba16SPali Rohár 	},
1106a5afba16SPali Rohár 	{
1107a5afba16SPali Rohár 		.ident = "Dell Latitude",
1108a5afba16SPali Rohár 		.matches = {
1109a5afba16SPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"),
1110a5afba16SPali Rohár 			DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"),
1111a5afba16SPali Rohár 		},
1112a5afba16SPali Rohár 	},
1113a5afba16SPali Rohár 	{
1114a5afba16SPali Rohár 		.ident = "Dell Inspiron 2",
1115a5afba16SPali Rohár 		.matches = {
1116a5afba16SPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1117a5afba16SPali Rohár 			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"),
1118a5afba16SPali Rohár 		},
1119a5afba16SPali Rohár 	},
1120a5afba16SPali Rohár 	{
1121a5afba16SPali Rohár 		.ident = "Dell Latitude D520",
1122a5afba16SPali Rohár 		.matches = {
1123a5afba16SPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1124a5afba16SPali Rohár 			DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D520"),
1125a5afba16SPali Rohár 		},
1126a5afba16SPali Rohár 		.driver_data = (void *)&i8k_config_data[DELL_LATITUDE_D520],
1127a5afba16SPali Rohár 	},
1128a5afba16SPali Rohár 	{
1129a5afba16SPali Rohár 		.ident = "Dell Latitude 2",
1130a5afba16SPali Rohár 		.matches = {
1131a5afba16SPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1132a5afba16SPali Rohár 			DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"),
1133a5afba16SPali Rohár 		},
1134a5afba16SPali Rohár 	},
1135a5afba16SPali Rohár 	{	/* UK Inspiron 6400  */
1136a5afba16SPali Rohár 		.ident = "Dell Inspiron 3",
1137a5afba16SPali Rohár 		.matches = {
1138a5afba16SPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1139a5afba16SPali Rohár 			DMI_MATCH(DMI_PRODUCT_NAME, "MM061"),
1140a5afba16SPali Rohár 		},
1141a5afba16SPali Rohár 	},
1142a5afba16SPali Rohár 	{
1143a5afba16SPali Rohár 		.ident = "Dell Inspiron 3",
1144a5afba16SPali Rohár 		.matches = {
1145a5afba16SPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1146a5afba16SPali Rohár 			DMI_MATCH(DMI_PRODUCT_NAME, "MP061"),
1147a5afba16SPali Rohár 		},
1148a5afba16SPali Rohár 	},
1149a5afba16SPali Rohár 	{
1150a5afba16SPali Rohár 		.ident = "Dell Precision 490",
1151a5afba16SPali Rohár 		.matches = {
1152a5afba16SPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1153a5afba16SPali Rohár 			DMI_MATCH(DMI_PRODUCT_NAME,
1154a5afba16SPali Rohár 				  "Precision WorkStation 490"),
1155a5afba16SPali Rohár 		},
1156a5afba16SPali Rohár 		.driver_data = (void *)&i8k_config_data[DELL_PRECISION_490],
1157a5afba16SPali Rohár 	},
1158a5afba16SPali Rohár 	{
1159a5afba16SPali Rohár 		.ident = "Dell Precision",
1160a5afba16SPali Rohár 		.matches = {
1161a5afba16SPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1162a5afba16SPali Rohár 			DMI_MATCH(DMI_PRODUCT_NAME, "Precision"),
1163a5afba16SPali Rohár 		},
1164a5afba16SPali Rohár 	},
1165a5afba16SPali Rohár 	{
1166a5afba16SPali Rohár 		.ident = "Dell Vostro",
1167a5afba16SPali Rohár 		.matches = {
1168a5afba16SPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1169a5afba16SPali Rohár 			DMI_MATCH(DMI_PRODUCT_NAME, "Vostro"),
1170a5afba16SPali Rohár 		},
1171a5afba16SPali Rohár 	},
1172a5afba16SPali Rohár 	{
1173a5afba16SPali Rohár 		.ident = "Dell Studio",
1174a5afba16SPali Rohár 		.matches = {
1175a5afba16SPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1176a5afba16SPali Rohár 			DMI_MATCH(DMI_PRODUCT_NAME, "Studio"),
1177a5afba16SPali Rohár 		},
1178a5afba16SPali Rohár 		.driver_data = (void *)&i8k_config_data[DELL_STUDIO],
1179a5afba16SPali Rohár 	},
1180a5afba16SPali Rohár 	{
1181a5afba16SPali Rohár 		.ident = "Dell XPS M140",
1182a5afba16SPali Rohár 		.matches = {
1183a5afba16SPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1184a5afba16SPali Rohár 			DMI_MATCH(DMI_PRODUCT_NAME, "MXC051"),
1185a5afba16SPali Rohár 		},
1186a5afba16SPali Rohár 		.driver_data = (void *)&i8k_config_data[DELL_XPS],
1187a5afba16SPali Rohár 	},
1188a4811b6cSPali Rohár 	{
1189b8a13e5eSThomas Hebb 		.ident = "Dell XPS",
1190a4811b6cSPali Rohár 		.matches = {
1191a4811b6cSPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1192b8a13e5eSThomas Hebb 			DMI_MATCH(DMI_PRODUCT_NAME, "XPS"),
1193162372b0SMichele Sorcinelli 		},
1194162372b0SMichele Sorcinelli 	},
1195a5afba16SPali Rohár 	{ }
1196a5afba16SPali Rohár };
1197a5afba16SPali Rohár 
1198a5afba16SPali Rohár MODULE_DEVICE_TABLE(dmi, i8k_dmi_table);
1199a5afba16SPali Rohár 
1200a4b45b25SPali Rohár /*
12012744d2fdSPali Rohár  * On some machines once I8K_SMM_GET_FAN_TYPE is issued then CPU fan speed
12022744d2fdSPali Rohár  * randomly going up and down due to bug in Dell SMM or BIOS. Here is blacklist
12032744d2fdSPali Rohár  * of affected Dell machines for which we disallow I8K_SMM_GET_FAN_TYPE call.
12042744d2fdSPali Rohár  * See bug: https://bugzilla.kernel.org/show_bug.cgi?id=100121
12056220f4ebSThorsten Leemhuis  */
12066faadbbbSChristoph Hellwig static const struct dmi_system_id i8k_blacklist_fan_type_dmi_table[] __initconst = {
12072744d2fdSPali Rohár 	{
12086220f4ebSThorsten Leemhuis 		.ident = "Dell Studio XPS 8000",
12096220f4ebSThorsten Leemhuis 		.matches = {
12106220f4ebSThorsten Leemhuis 			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
12116220f4ebSThorsten Leemhuis 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Studio XPS 8000"),
12126220f4ebSThorsten Leemhuis 		},
12136220f4ebSThorsten Leemhuis 	},
12146220f4ebSThorsten Leemhuis 	{
1215a4b45b25SPali Rohár 		.ident = "Dell Studio XPS 8100",
1216a4b45b25SPali Rohár 		.matches = {
1217a4b45b25SPali Rohár 			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1218a4b45b25SPali Rohár 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Studio XPS 8100"),
1219a4b45b25SPali Rohár 		},
1220a4b45b25SPali Rohár 	},
12212744d2fdSPali Rohár 	{
12222744d2fdSPali Rohár 		.ident = "Dell Inspiron 580",
12232744d2fdSPali Rohár 		.matches = {
12242744d2fdSPali Rohár 			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
12252744d2fdSPali Rohár 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Inspiron 580 "),
12262744d2fdSPali Rohár 		},
12272744d2fdSPali Rohár 	},
12286ba463edSArmin Wolf 	{
12296ba463edSArmin Wolf 		.ident = "Dell Inspiron 3505",
12306ba463edSArmin Wolf 		.matches = {
12316ba463edSArmin Wolf 			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
12326ba463edSArmin Wolf 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Inspiron 3505"),
12336ba463edSArmin Wolf 		},
12346ba463edSArmin Wolf 	},
1235a4b45b25SPali Rohár 	{ }
1236a4b45b25SPali Rohár };
1237a4b45b25SPali Rohár 
1238a5afba16SPali Rohár /*
1239f480ea90SPali Rohár  * On some machines all fan related SMM functions implemented by Dell BIOS
1240f480ea90SPali Rohár  * firmware freeze kernel for about 500ms. Until Dell fixes these problems fan
1241f480ea90SPali Rohár  * support for affected blacklisted Dell machines stay disabled.
1242f480ea90SPali Rohár  * See bug: https://bugzilla.kernel.org/show_bug.cgi?id=195751
1243f480ea90SPali Rohár  */
1244c510f6acSArmin Wolf static const struct dmi_system_id i8k_blacklist_fan_support_dmi_table[] __initconst = {
1245f480ea90SPali Rohár 	{
1246f480ea90SPali Rohár 		.ident = "Dell Inspiron 7720",
1247f480ea90SPali Rohár 		.matches = {
1248f480ea90SPali Rohár 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1249f480ea90SPali Rohár 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Inspiron 7720"),
1250f480ea90SPali Rohár 		},
1251f480ea90SPali Rohár 	},
12526fbc4232SOleksandr Natalenko 	{
12536fbc4232SOleksandr Natalenko 		.ident = "Dell Vostro 3360",
12546fbc4232SOleksandr Natalenko 		.matches = {
12556fbc4232SOleksandr Natalenko 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
12566fbc4232SOleksandr Natalenko 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Vostro 3360"),
12576fbc4232SOleksandr Natalenko 		},
12586fbc4232SOleksandr Natalenko 	},
1259536e0019SHelge Eichelberg 	{
1260536e0019SHelge Eichelberg 		.ident = "Dell XPS13 9333",
1261536e0019SHelge Eichelberg 		.matches = {
1262536e0019SHelge Eichelberg 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1263536e0019SHelge Eichelberg 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS13 9333"),
1264536e0019SHelge Eichelberg 		},
1265536e0019SHelge Eichelberg 	},
12664008bc7dSThomas Hebb 	{
12674008bc7dSThomas Hebb 		.ident = "Dell XPS 15 L502X",
12684008bc7dSThomas Hebb 		.matches = {
12694008bc7dSThomas Hebb 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
12704008bc7dSThomas Hebb 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Dell System XPS L502X"),
12714008bc7dSThomas Hebb 		},
12724008bc7dSThomas Hebb 	},
1273f480ea90SPali Rohár 	{ }
1274f480ea90SPali Rohár };
1275f480ea90SPali Rohár 
1276afe45277SGiovanni Mascellani struct i8k_fan_control_data {
1277afe45277SGiovanni Mascellani 	unsigned int manual_fan;
1278afe45277SGiovanni Mascellani 	unsigned int auto_fan;
1279afe45277SGiovanni Mascellani };
1280afe45277SGiovanni Mascellani 
1281afe45277SGiovanni Mascellani enum i8k_fan_controls {
1282afe45277SGiovanni Mascellani 	I8K_FAN_34A3_35A3,
1283afe45277SGiovanni Mascellani };
1284afe45277SGiovanni Mascellani 
1285c510f6acSArmin Wolf static const struct i8k_fan_control_data i8k_fan_control_data[] __initconst = {
1286afe45277SGiovanni Mascellani 	[I8K_FAN_34A3_35A3] = {
1287afe45277SGiovanni Mascellani 		.manual_fan = 0x34a3,
1288afe45277SGiovanni Mascellani 		.auto_fan = 0x35a3,
1289afe45277SGiovanni Mascellani 	},
1290afe45277SGiovanni Mascellani };
1291afe45277SGiovanni Mascellani 
1292c510f6acSArmin Wolf static const struct dmi_system_id i8k_whitelist_fan_control[] __initconst = {
1293afe45277SGiovanni Mascellani 	{
12940ca8bb2cSJeffrey Lin 		.ident = "Dell Latitude 5480",
12950ca8bb2cSJeffrey Lin 		.matches = {
12960ca8bb2cSJeffrey Lin 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
12970ca8bb2cSJeffrey Lin 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude 5480"),
12980ca8bb2cSJeffrey Lin 		},
12990ca8bb2cSJeffrey Lin 		.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
13000ca8bb2cSJeffrey Lin 	},
13010ca8bb2cSJeffrey Lin 	{
1302afe45277SGiovanni Mascellani 		.ident = "Dell Latitude E6440",
1303afe45277SGiovanni Mascellani 		.matches = {
1304afe45277SGiovanni Mascellani 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1305afe45277SGiovanni Mascellani 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude E6440"),
1306afe45277SGiovanni Mascellani 		},
1307afe45277SGiovanni Mascellani 		.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
1308afe45277SGiovanni Mascellani 	},
1309807b8c29SSebastian Oechsle 	{
1310807b8c29SSebastian Oechsle 		.ident = "Dell Latitude E7440",
1311807b8c29SSebastian Oechsle 		.matches = {
1312807b8c29SSebastian Oechsle 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1313807b8c29SSebastian Oechsle 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude E7440"),
1314807b8c29SSebastian Oechsle 		},
1315807b8c29SSebastian Oechsle 		.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
1316807b8c29SSebastian Oechsle 	},
131795d88d05SCarlos Alberto Lopez Perez 	{
131895d88d05SCarlos Alberto Lopez Perez 		.ident = "Dell Precision 5530",
131995d88d05SCarlos Alberto Lopez Perez 		.matches = {
132095d88d05SCarlos Alberto Lopez Perez 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
132195d88d05SCarlos Alberto Lopez Perez 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Precision 5530"),
132295d88d05SCarlos Alberto Lopez Perez 		},
132395d88d05SCarlos Alberto Lopez Perez 		.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
132495d88d05SCarlos Alberto Lopez Perez 	},
132595d88d05SCarlos Alberto Lopez Perez 	{
132695d88d05SCarlos Alberto Lopez Perez 		.ident = "Dell Precision 7510",
132795d88d05SCarlos Alberto Lopez Perez 		.matches = {
132895d88d05SCarlos Alberto Lopez Perez 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
132995d88d05SCarlos Alberto Lopez Perez 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Precision 7510"),
133095d88d05SCarlos Alberto Lopez Perez 		},
133195d88d05SCarlos Alberto Lopez Perez 		.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
133295d88d05SCarlos Alberto Lopez Perez 	},
1333385e5f57SArmin Wolf 	{
1334385e5f57SArmin Wolf 		.ident = "Dell XPS 13 7390",
1335385e5f57SArmin Wolf 		.matches = {
1336385e5f57SArmin Wolf 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1337385e5f57SArmin Wolf 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS 13 7390"),
1338385e5f57SArmin Wolf 		},
1339385e5f57SArmin Wolf 		.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
1340385e5f57SArmin Wolf 	},
1341afe45277SGiovanni Mascellani 	{ }
1342afe45277SGiovanni Mascellani };
1343afe45277SGiovanni Mascellani 
dell_smm_probe(struct platform_device * pdev)13441492fa21SArmin Wolf static int __init dell_smm_probe(struct platform_device *pdev)
1345a5afba16SPali Rohár {
1346ba04d73cSArmin Wolf 	struct dell_smm_data *data;
1347afe45277SGiovanni Mascellani 	const struct dmi_system_id *id, *fan_control;
13484d9983deSArmin Wolf 	int ret;
1349a5afba16SPali Rohár 
1350ba04d73cSArmin Wolf 	data = devm_kzalloc(&pdev->dev, sizeof(struct dell_smm_data), GFP_KERNEL);
1351ba04d73cSArmin Wolf 	if (!data)
1352ba04d73cSArmin Wolf 		return -ENOMEM;
1353ba04d73cSArmin Wolf 
1354ba04d73cSArmin Wolf 	mutex_init(&data->i8k_mutex);
1355ba04d73cSArmin Wolf 	platform_set_drvdata(pdev, data);
1356ba04d73cSArmin Wolf 
1357f480ea90SPali Rohár 	if (dmi_check_system(i8k_blacklist_fan_support_dmi_table)) {
13586ebab74eSArmin Wolf 		if (!force) {
13596ebab74eSArmin Wolf 			dev_notice(&pdev->dev, "Disabling fan support due to BIOS bugs\n");
1360ba04d73cSArmin Wolf 			data->disallow_fan_support = true;
13616ebab74eSArmin Wolf 		} else {
13626ebab74eSArmin Wolf 			dev_warn(&pdev->dev, "Enabling fan support despite BIOS bugs\n");
13636ebab74eSArmin Wolf 		}
1364f480ea90SPali Rohár 	}
1365f480ea90SPali Rohár 
1366836ad112SPali Rohár 	if (dmi_check_system(i8k_blacklist_fan_type_dmi_table)) {
13676ebab74eSArmin Wolf 		if (!force) {
13686ebab74eSArmin Wolf 			dev_notice(&pdev->dev, "Disabling fan type call due to BIOS bugs\n");
1369ba04d73cSArmin Wolf 			data->disallow_fan_type_call = true;
13706ebab74eSArmin Wolf 		} else {
13716ebab74eSArmin Wolf 			dev_warn(&pdev->dev, "Enabling fan type call despite BIOS bugs\n");
13726ebab74eSArmin Wolf 		}
1373836ad112SPali Rohár 	}
13742744d2fdSPali Rohár 
1375ba04d73cSArmin Wolf 	strscpy(data->bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION),
1376ba04d73cSArmin Wolf 		sizeof(data->bios_version));
1377ba04d73cSArmin Wolf 	strscpy(data->bios_machineid, i8k_get_dmi_data(DMI_PRODUCT_SERIAL),
1378ba04d73cSArmin Wolf 		sizeof(data->bios_machineid));
1379a5afba16SPali Rohár 
1380a5afba16SPali Rohár 	/*
1381a5afba16SPali Rohár 	 * Set fan multiplier and maximal fan speed from dmi config
1382a5afba16SPali Rohár 	 * Values specified in module parameters override values from dmi
1383a5afba16SPali Rohár 	 */
1384a5afba16SPali Rohár 	id = dmi_first_match(i8k_dmi_table);
1385a5afba16SPali Rohár 	if (id && id->driver_data) {
1386a5afba16SPali Rohár 		const struct i8k_config_data *conf = id->driver_data;
13871492fa21SArmin Wolf 
1388a5afba16SPali Rohár 		if (!fan_mult && conf->fan_mult)
1389a5afba16SPali Rohár 			fan_mult = conf->fan_mult;
1390ba04d73cSArmin Wolf 
1391a5afba16SPali Rohár 		if (!fan_max && conf->fan_max)
1392a5afba16SPali Rohár 			fan_max = conf->fan_max;
1393a5afba16SPali Rohár 	}
1394a5afba16SPali Rohár 
1395f44aa665SArmin Wolf 	/* All options must not be 0 */
1396f44aa665SArmin Wolf 	data->i8k_fan_mult = fan_mult ? : I8K_FAN_MULT;
1397f44aa665SArmin Wolf 	data->i8k_fan_max = fan_max ? : I8K_FAN_HIGH;
1398ba04d73cSArmin Wolf 	data->i8k_pwm_mult = DIV_ROUND_UP(255, data->i8k_fan_max);
1399a5afba16SPali Rohár 
1400afe45277SGiovanni Mascellani 	fan_control = dmi_first_match(i8k_whitelist_fan_control);
1401afe45277SGiovanni Mascellani 	if (fan_control && fan_control->driver_data) {
1402ba04d73cSArmin Wolf 		const struct i8k_fan_control_data *control = fan_control->driver_data;
1403afe45277SGiovanni Mascellani 
1404ba04d73cSArmin Wolf 		data->manual_fan = control->manual_fan;
1405ba04d73cSArmin Wolf 		data->auto_fan = control->auto_fan;
1406ba04d73cSArmin Wolf 		dev_info(&pdev->dev, "enabling support for setting automatic/manual fan control\n");
1407afe45277SGiovanni Mascellani 	}
1408afe45277SGiovanni Mascellani 
14091492fa21SArmin Wolf 	ret = dell_smm_init_hwmon(&pdev->dev);
14101492fa21SArmin Wolf 	if (ret)
14111492fa21SArmin Wolf 		return ret;
14121492fa21SArmin Wolf 
1413a2cb66b4SArmin Wolf 	i8k_init_procfs(&pdev->dev);
14141492fa21SArmin Wolf 
14151492fa21SArmin Wolf 	return 0;
14161492fa21SArmin Wolf }
14171492fa21SArmin Wolf 
14181492fa21SArmin Wolf static struct platform_driver dell_smm_driver = {
14191492fa21SArmin Wolf 	.driver		= {
14201492fa21SArmin Wolf 		.name	= KBUILD_MODNAME,
14211492fa21SArmin Wolf 	},
14221492fa21SArmin Wolf };
14231492fa21SArmin Wolf 
14241492fa21SArmin Wolf static struct platform_device *dell_smm_device;
14251492fa21SArmin Wolf 
14261492fa21SArmin Wolf /*
14271492fa21SArmin Wolf  * Probe for the presence of a supported laptop.
14281492fa21SArmin Wolf  */
i8k_init(void)1429a5afba16SPali Rohár static int __init i8k_init(void)
1430a5afba16SPali Rohár {
14311492fa21SArmin Wolf 	/*
14321492fa21SArmin Wolf 	 * Get DMI information
14331492fa21SArmin Wolf 	 */
14341492fa21SArmin Wolf 	if (!dmi_check_system(i8k_dmi_table)) {
14351492fa21SArmin Wolf 		if (!ignore_dmi && !force)
1436a5afba16SPali Rohár 			return -ENODEV;
1437a5afba16SPali Rohár 
14381492fa21SArmin Wolf 		pr_info("not running on a supported Dell system.\n");
14391492fa21SArmin Wolf 		pr_info("vendor=%s, model=%s, version=%s\n",
14401492fa21SArmin Wolf 			i8k_get_dmi_data(DMI_SYS_VENDOR),
14411492fa21SArmin Wolf 			i8k_get_dmi_data(DMI_PRODUCT_NAME),
14421492fa21SArmin Wolf 			i8k_get_dmi_data(DMI_BIOS_VERSION));
14431492fa21SArmin Wolf 	}
1444039ae585SPali Rohár 
14451492fa21SArmin Wolf 	/*
14461492fa21SArmin Wolf 	 * Get SMM Dell signature
14471492fa21SArmin Wolf 	 */
14481492fa21SArmin Wolf 	if (i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG1) &&
14491492fa21SArmin Wolf 	    i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG2)) {
14501492fa21SArmin Wolf 		if (!force)
14511492fa21SArmin Wolf 			return -ENODEV;
1452688fcd04SArmin Wolf 
1453688fcd04SArmin Wolf 		pr_err("Unable to get Dell SMM signature\n");
14541492fa21SArmin Wolf 	}
14551492fa21SArmin Wolf 
14561492fa21SArmin Wolf 	dell_smm_device = platform_create_bundle(&dell_smm_driver, dell_smm_probe, NULL, 0, NULL,
14571492fa21SArmin Wolf 						 0);
14581492fa21SArmin Wolf 
14591492fa21SArmin Wolf 	return PTR_ERR_OR_ZERO(dell_smm_device);
1460a5afba16SPali Rohár }
1461a5afba16SPali Rohár 
i8k_exit(void)1462a5afba16SPali Rohár static void __exit i8k_exit(void)
1463a5afba16SPali Rohár {
14641492fa21SArmin Wolf 	platform_device_unregister(dell_smm_device);
14651492fa21SArmin Wolf 	platform_driver_unregister(&dell_smm_driver);
1466a5afba16SPali Rohár }
1467a5afba16SPali Rohár 
1468a5afba16SPali Rohár module_init(i8k_init);
1469a5afba16SPali Rohár module_exit(i8k_exit);
1470