1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * dell-smm-hwmon.c -- Linux driver for accessing the SMM BIOS on Dell laptops. |
4 | * |
5 | * Copyright (C) 2001 Massimo Dal Zotto <dz@debian.org> |
6 | * |
7 | * Hwmon integration: |
8 | * Copyright (C) 2011 Jean Delvare <jdelvare@suse.de> |
9 | * Copyright (C) 2013, 2014 Guenter Roeck <linux@roeck-us.net> |
10 | * Copyright (C) 2014, 2015 Pali Rohár <pali@kernel.org> |
11 | */ |
12 | |
13 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
14 | |
15 | #include <linux/acpi.h> |
16 | #include <linux/capability.h> |
17 | #include <linux/cpu.h> |
18 | #include <linux/ctype.h> |
19 | #include <linux/delay.h> |
20 | #include <linux/dmi.h> |
21 | #include <linux/err.h> |
22 | #include <linux/errno.h> |
23 | #include <linux/hwmon.h> |
24 | #include <linux/init.h> |
25 | #include <linux/kconfig.h> |
26 | #include <linux/kernel.h> |
27 | #include <linux/module.h> |
28 | #include <linux/mutex.h> |
29 | #include <linux/platform_device.h> |
30 | #include <linux/proc_fs.h> |
31 | #include <linux/seq_file.h> |
32 | #include <linux/slab.h> |
33 | #include <linux/smp.h> |
34 | #include <linux/string.h> |
35 | #include <linux/thermal.h> |
36 | #include <linux/types.h> |
37 | #include <linux/uaccess.h> |
38 | #include <linux/wmi.h> |
39 | |
40 | #include <linux/i8k.h> |
41 | #include <asm/unaligned.h> |
42 | |
43 | #define I8K_SMM_FN_STATUS 0x0025 |
44 | #define I8K_SMM_POWER_STATUS 0x0069 |
45 | #define I8K_SMM_SET_FAN 0x01a3 |
46 | #define I8K_SMM_GET_FAN 0x00a3 |
47 | #define I8K_SMM_GET_SPEED 0x02a3 |
48 | #define I8K_SMM_GET_FAN_TYPE 0x03a3 |
49 | #define I8K_SMM_GET_NOM_SPEED 0x04a3 |
50 | #define I8K_SMM_GET_TEMP 0x10a3 |
51 | #define I8K_SMM_GET_TEMP_TYPE 0x11a3 |
52 | #define I8K_SMM_GET_DELL_SIG1 0xfea3 |
53 | #define I8K_SMM_GET_DELL_SIG2 0xffa3 |
54 | |
55 | /* in usecs */ |
56 | #define DELL_SMM_MAX_DURATION 250000 |
57 | |
58 | #define I8K_FAN_MULT 30 |
59 | #define I8K_FAN_RPM_THRESHOLD 1000 |
60 | #define I8K_MAX_TEMP 127 |
61 | |
62 | #define I8K_FN_NONE 0x00 |
63 | #define I8K_FN_UP 0x01 |
64 | #define I8K_FN_DOWN 0x02 |
65 | #define I8K_FN_MUTE 0x04 |
66 | #define I8K_FN_MASK 0x07 |
67 | #define I8K_FN_SHIFT 8 |
68 | |
69 | #define I8K_POWER_AC 0x05 |
70 | #define I8K_POWER_BATTERY 0x01 |
71 | |
72 | #define DELL_SMM_WMI_GUID "F1DDEE52-063C-4784-A11E-8A06684B9B01" |
73 | #define DELL_SMM_LEGACY_EXECUTE 0x1 |
74 | |
75 | #define DELL_SMM_NO_TEMP 10 |
76 | #define DELL_SMM_NO_FANS 3 |
77 | |
78 | struct smm_regs { |
79 | unsigned int eax; |
80 | unsigned int ebx; |
81 | unsigned int ecx; |
82 | unsigned int edx; |
83 | unsigned int esi; |
84 | unsigned int edi; |
85 | }; |
86 | |
87 | struct dell_smm_ops { |
88 | struct device *smm_dev; |
89 | int (*smm_call)(struct device *smm_dev, struct smm_regs *regs); |
90 | }; |
91 | |
92 | struct dell_smm_data { |
93 | struct mutex i8k_mutex; /* lock for sensors writes */ |
94 | char bios_version[4]; |
95 | char bios_machineid[16]; |
96 | uint i8k_fan_mult; |
97 | uint i8k_pwm_mult; |
98 | uint i8k_fan_max; |
99 | int temp_type[DELL_SMM_NO_TEMP]; |
100 | bool fan[DELL_SMM_NO_FANS]; |
101 | int fan_type[DELL_SMM_NO_FANS]; |
102 | int *fan_nominal_speed[DELL_SMM_NO_FANS]; |
103 | const struct dell_smm_ops *ops; |
104 | }; |
105 | |
106 | struct dell_smm_cooling_data { |
107 | u8 fan_num; |
108 | struct dell_smm_data *data; |
109 | }; |
110 | |
111 | MODULE_AUTHOR("Massimo Dal Zotto <dz@debian.org>" ); |
112 | MODULE_AUTHOR("Pali Rohár <pali@kernel.org>" ); |
113 | MODULE_DESCRIPTION("Dell laptop SMM BIOS hwmon driver" ); |
114 | MODULE_LICENSE("GPL" ); |
115 | MODULE_ALIAS("i8k" ); |
116 | |
117 | static bool force; |
118 | module_param_unsafe(force, bool, 0); |
119 | MODULE_PARM_DESC(force, "Force loading without checking for supported models and features" ); |
120 | |
121 | static bool ignore_dmi; |
122 | module_param(ignore_dmi, bool, 0); |
123 | MODULE_PARM_DESC(ignore_dmi, "Continue probing hardware even if DMI data does not match" ); |
124 | |
125 | #if IS_ENABLED(CONFIG_I8K) |
126 | static bool restricted = true; |
127 | module_param(restricted, bool, 0); |
128 | MODULE_PARM_DESC(restricted, "Restrict fan control and serial number to CAP_SYS_ADMIN (default: 1)" ); |
129 | |
130 | static bool power_status; |
131 | module_param(power_status, bool, 0600); |
132 | MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k (default: 0)" ); |
133 | #endif |
134 | |
135 | static uint fan_mult; |
136 | module_param(fan_mult, uint, 0); |
137 | MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with (default: autodetect)" ); |
138 | |
139 | static uint fan_max; |
140 | module_param(fan_max, uint, 0); |
141 | MODULE_PARM_DESC(fan_max, "Maximum configurable fan speed (default: autodetect)" ); |
142 | |
143 | static bool disallow_fan_type_call, disallow_fan_support; |
144 | |
145 | static unsigned int manual_fan, auto_fan; |
146 | |
147 | static const char * const temp_labels[] = { |
148 | "CPU" , |
149 | "GPU" , |
150 | "SODIMM" , |
151 | "Other" , |
152 | "Ambient" , |
153 | "Other" , |
154 | }; |
155 | |
156 | static const char * const fan_labels[] = { |
157 | "Processor Fan" , |
158 | "Motherboard Fan" , |
159 | "Video Fan" , |
160 | "Power Supply Fan" , |
161 | "Chipset Fan" , |
162 | "Other Fan" , |
163 | }; |
164 | |
165 | static const char * const docking_labels[] = { |
166 | "Docking Processor Fan" , |
167 | "Docking Motherboard Fan" , |
168 | "Docking Video Fan" , |
169 | "Docking Power Supply Fan" , |
170 | "Docking Chipset Fan" , |
171 | "Docking Other Fan" , |
172 | }; |
173 | |
174 | static inline const char __init *i8k_get_dmi_data(int field) |
175 | { |
176 | const char *dmi_data = dmi_get_system_info(field); |
177 | |
178 | return dmi_data && *dmi_data ? dmi_data : "?" ; |
179 | } |
180 | |
181 | /* |
182 | * Call the System Management Mode BIOS. Code provided by Jonathan Buzzard. |
183 | */ |
184 | static int i8k_smm_func(void *par) |
185 | { |
186 | struct smm_regs *regs = par; |
187 | unsigned char carry; |
188 | |
189 | /* SMM requires CPU 0 */ |
190 | if (smp_processor_id() != 0) |
191 | return -EBUSY; |
192 | |
193 | asm volatile("out %%al,$0xb2\n\t" |
194 | "out %%al,$0x84\n\t" |
195 | "setc %0\n" |
196 | : "=mr" (carry), |
197 | "+a" (regs->eax), |
198 | "+b" (regs->ebx), |
199 | "+c" (regs->ecx), |
200 | "+d" (regs->edx), |
201 | "+S" (regs->esi), |
202 | "+D" (regs->edi)); |
203 | |
204 | if (carry) |
205 | return -EINVAL; |
206 | |
207 | return 0; |
208 | } |
209 | |
210 | /* |
211 | * Call the System Management Mode BIOS. |
212 | */ |
213 | static int i8k_smm_call(struct device *dummy, struct smm_regs *regs) |
214 | { |
215 | int ret; |
216 | |
217 | cpus_read_lock(); |
218 | ret = smp_call_on_cpu(cpu: 0, func: i8k_smm_func, par: regs, phys: true); |
219 | cpus_read_unlock(); |
220 | |
221 | return ret; |
222 | } |
223 | |
224 | static const struct dell_smm_ops i8k_smm_ops = { |
225 | .smm_call = i8k_smm_call, |
226 | }; |
227 | |
228 | /* |
229 | * Call the System Management Mode BIOS over WMI. |
230 | */ |
231 | static ssize_t wmi_parse_register(u8 *buffer, u32 length, unsigned int *reg) |
232 | { |
233 | __le32 value; |
234 | u32 reg_size; |
235 | |
236 | if (length <= sizeof(reg_size)) |
237 | return -ENODATA; |
238 | |
239 | reg_size = get_unaligned_le32(p: buffer); |
240 | if (!reg_size || reg_size > sizeof(value)) |
241 | return -ENOMSG; |
242 | |
243 | if (length < sizeof(reg_size) + reg_size) |
244 | return -ENODATA; |
245 | |
246 | memcpy_and_pad(dest: &value, dest_len: sizeof(value), src: buffer + sizeof(reg_size), count: reg_size, pad: 0); |
247 | *reg = le32_to_cpu(value); |
248 | |
249 | return reg_size + sizeof(reg_size); |
250 | } |
251 | |
252 | static int wmi_parse_response(u8 *buffer, u32 length, struct smm_regs *regs) |
253 | { |
254 | unsigned int *registers[] = { |
255 | ®s->eax, |
256 | ®s->ebx, |
257 | ®s->ecx, |
258 | ®s->edx |
259 | }; |
260 | u32 offset = 0; |
261 | ssize_t ret; |
262 | int i; |
263 | |
264 | for (i = 0; i < ARRAY_SIZE(registers); i++) { |
265 | if (offset >= length) |
266 | return -ENODATA; |
267 | |
268 | ret = wmi_parse_register(buffer: buffer + offset, length: length - offset, reg: registers[i]); |
269 | if (ret < 0) |
270 | return ret; |
271 | |
272 | offset += ret; |
273 | } |
274 | |
275 | if (offset != length) |
276 | return -ENOMSG; |
277 | |
278 | return 0; |
279 | } |
280 | |
281 | static int wmi_smm_call(struct device *dev, struct smm_regs *regs) |
282 | { |
283 | struct wmi_device *wdev = container_of(dev, struct wmi_device, dev); |
284 | struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; |
285 | u32 wmi_payload[] = { |
286 | sizeof(regs->eax), |
287 | regs->eax, |
288 | sizeof(regs->ebx), |
289 | regs->ebx, |
290 | sizeof(regs->ecx), |
291 | regs->ecx, |
292 | sizeof(regs->edx), |
293 | regs->edx |
294 | }; |
295 | const struct acpi_buffer in = { |
296 | .length = sizeof(wmi_payload), |
297 | .pointer = &wmi_payload, |
298 | }; |
299 | union acpi_object *obj; |
300 | acpi_status status; |
301 | int ret; |
302 | |
303 | status = wmidev_evaluate_method(wdev, instance: 0x0, DELL_SMM_LEGACY_EXECUTE, in: &in, out: &out); |
304 | if (ACPI_FAILURE(status)) |
305 | return -EIO; |
306 | |
307 | obj = out.pointer; |
308 | if (!obj) |
309 | return -ENODATA; |
310 | |
311 | if (obj->type != ACPI_TYPE_BUFFER) { |
312 | ret = -ENOMSG; |
313 | |
314 | goto err_free; |
315 | } |
316 | |
317 | ret = wmi_parse_response(buffer: obj->buffer.pointer, length: obj->buffer.length, regs); |
318 | |
319 | err_free: |
320 | kfree(objp: obj); |
321 | |
322 | return ret; |
323 | } |
324 | |
325 | static int dell_smm_call(const struct dell_smm_ops *ops, struct smm_regs *regs) |
326 | { |
327 | unsigned int eax = regs->eax; |
328 | unsigned int ebx = regs->ebx; |
329 | long long duration; |
330 | ktime_t calltime; |
331 | int ret; |
332 | |
333 | calltime = ktime_get(); |
334 | ret = ops->smm_call(ops->smm_dev, regs); |
335 | duration = ktime_us_delta(later: ktime_get(), earlier: calltime); |
336 | |
337 | pr_debug("SMM(0x%.4x 0x%.4x) = 0x%.4x status: %d (took %7lld usecs)\n" , |
338 | eax, ebx, regs->eax & 0xffff, ret, duration); |
339 | |
340 | if (duration > DELL_SMM_MAX_DURATION) |
341 | pr_warn_once("SMM call took %lld usecs!\n" , duration); |
342 | |
343 | if (ret < 0) |
344 | return ret; |
345 | |
346 | if ((regs->eax & 0xffff) == 0xffff || regs->eax == eax) |
347 | return -EINVAL; |
348 | |
349 | return 0; |
350 | } |
351 | |
352 | /* |
353 | * Read the fan status. |
354 | */ |
355 | static int i8k_get_fan_status(const struct dell_smm_data *data, u8 fan) |
356 | { |
357 | struct smm_regs regs = { |
358 | .eax = I8K_SMM_GET_FAN, |
359 | .ebx = fan, |
360 | }; |
361 | |
362 | if (disallow_fan_support) |
363 | return -EINVAL; |
364 | |
365 | return dell_smm_call(ops: data->ops, regs: ®s) ? : regs.eax & 0xff; |
366 | } |
367 | |
368 | /* |
369 | * Read the fan speed in RPM. |
370 | */ |
371 | static int i8k_get_fan_speed(const struct dell_smm_data *data, u8 fan) |
372 | { |
373 | struct smm_regs regs = { |
374 | .eax = I8K_SMM_GET_SPEED, |
375 | .ebx = fan, |
376 | }; |
377 | |
378 | if (disallow_fan_support) |
379 | return -EINVAL; |
380 | |
381 | return dell_smm_call(ops: data->ops, regs: ®s) ? : (regs.eax & 0xffff) * data->i8k_fan_mult; |
382 | } |
383 | |
384 | /* |
385 | * Read the fan type. |
386 | */ |
387 | static int _i8k_get_fan_type(const struct dell_smm_data *data, u8 fan) |
388 | { |
389 | struct smm_regs regs = { |
390 | .eax = I8K_SMM_GET_FAN_TYPE, |
391 | .ebx = fan, |
392 | }; |
393 | |
394 | if (disallow_fan_support || disallow_fan_type_call) |
395 | return -EINVAL; |
396 | |
397 | return dell_smm_call(ops: data->ops, regs: ®s) ? : regs.eax & 0xff; |
398 | } |
399 | |
400 | static int i8k_get_fan_type(struct dell_smm_data *data, u8 fan) |
401 | { |
402 | /* I8K_SMM_GET_FAN_TYPE SMM call is expensive, so cache values */ |
403 | if (data->fan_type[fan] == INT_MIN) |
404 | data->fan_type[fan] = _i8k_get_fan_type(data, fan); |
405 | |
406 | return data->fan_type[fan]; |
407 | } |
408 | |
409 | /* |
410 | * Read the fan nominal rpm for specific fan speed. |
411 | */ |
412 | static int i8k_get_fan_nominal_speed(const struct dell_smm_data *data, u8 fan, int speed) |
413 | { |
414 | struct smm_regs regs = { |
415 | .eax = I8K_SMM_GET_NOM_SPEED, |
416 | .ebx = fan | (speed << 8), |
417 | }; |
418 | |
419 | if (disallow_fan_support) |
420 | return -EINVAL; |
421 | |
422 | return dell_smm_call(ops: data->ops, regs: ®s) ? : (regs.eax & 0xffff); |
423 | } |
424 | |
425 | /* |
426 | * Enable or disable automatic BIOS fan control support |
427 | */ |
428 | static int i8k_enable_fan_auto_mode(const struct dell_smm_data *data, bool enable) |
429 | { |
430 | struct smm_regs regs = { }; |
431 | |
432 | if (disallow_fan_support) |
433 | return -EINVAL; |
434 | |
435 | regs.eax = enable ? auto_fan : manual_fan; |
436 | return dell_smm_call(ops: data->ops, regs: ®s); |
437 | } |
438 | |
439 | /* |
440 | * Set the fan speed (off, low, high, ...). |
441 | */ |
442 | static int i8k_set_fan(const struct dell_smm_data *data, u8 fan, int speed) |
443 | { |
444 | struct smm_regs regs = { .eax = I8K_SMM_SET_FAN, }; |
445 | |
446 | if (disallow_fan_support) |
447 | return -EINVAL; |
448 | |
449 | speed = (speed < 0) ? 0 : ((speed > data->i8k_fan_max) ? data->i8k_fan_max : speed); |
450 | regs.ebx = fan | (speed << 8); |
451 | |
452 | return dell_smm_call(ops: data->ops, regs: ®s); |
453 | } |
454 | |
455 | static int i8k_get_temp_type(const struct dell_smm_data *data, u8 sensor) |
456 | { |
457 | struct smm_regs regs = { |
458 | .eax = I8K_SMM_GET_TEMP_TYPE, |
459 | .ebx = sensor, |
460 | }; |
461 | |
462 | return dell_smm_call(ops: data->ops, regs: ®s) ? : regs.eax & 0xff; |
463 | } |
464 | |
465 | /* |
466 | * Read the cpu temperature. |
467 | */ |
468 | static int _i8k_get_temp(const struct dell_smm_data *data, u8 sensor) |
469 | { |
470 | struct smm_regs regs = { |
471 | .eax = I8K_SMM_GET_TEMP, |
472 | .ebx = sensor, |
473 | }; |
474 | |
475 | return dell_smm_call(ops: data->ops, regs: ®s) ? : regs.eax & 0xff; |
476 | } |
477 | |
478 | static int i8k_get_temp(const struct dell_smm_data *data, u8 sensor) |
479 | { |
480 | int temp = _i8k_get_temp(data, sensor); |
481 | |
482 | /* |
483 | * Sometimes the temperature sensor returns 0x99, which is out of range. |
484 | * In this case we retry (once) before returning an error. |
485 | # 1003655137 00000058 00005a4b |
486 | # 1003655138 00000099 00003a80 <--- 0x99 = 153 degrees |
487 | # 1003655139 00000054 00005c52 |
488 | */ |
489 | if (temp == 0x99) { |
490 | msleep(msecs: 100); |
491 | temp = _i8k_get_temp(data, sensor); |
492 | } |
493 | /* |
494 | * Return -ENODATA for all invalid temperatures. |
495 | * |
496 | * Known instances are the 0x99 value as seen above as well as |
497 | * 0xc1 (193), which may be returned when trying to read the GPU |
498 | * temperature if the system supports a GPU and it is currently |
499 | * turned off. |
500 | */ |
501 | if (temp > I8K_MAX_TEMP) |
502 | return -ENODATA; |
503 | |
504 | return temp; |
505 | } |
506 | |
507 | static int dell_smm_get_signature(const struct dell_smm_ops *ops, int req_fn) |
508 | { |
509 | struct smm_regs regs = { .eax = req_fn, }; |
510 | int rc; |
511 | |
512 | rc = dell_smm_call(ops, regs: ®s); |
513 | if (rc < 0) |
514 | return rc; |
515 | |
516 | return regs.eax == 1145651527 && regs.edx == 1145392204 ? 0 : -1; |
517 | } |
518 | |
519 | #if IS_ENABLED(CONFIG_I8K) |
520 | |
521 | /* |
522 | * Read the Fn key status. |
523 | */ |
524 | static int i8k_get_fn_status(const struct dell_smm_data *data) |
525 | { |
526 | struct smm_regs regs = { .eax = I8K_SMM_FN_STATUS, }; |
527 | int rc; |
528 | |
529 | rc = dell_smm_call(ops: data->ops, regs: ®s); |
530 | if (rc < 0) |
531 | return rc; |
532 | |
533 | switch ((regs.eax >> I8K_FN_SHIFT) & I8K_FN_MASK) { |
534 | case I8K_FN_UP: |
535 | return I8K_VOL_UP; |
536 | case I8K_FN_DOWN: |
537 | return I8K_VOL_DOWN; |
538 | case I8K_FN_MUTE: |
539 | return I8K_VOL_MUTE; |
540 | default: |
541 | return 0; |
542 | } |
543 | } |
544 | |
545 | /* |
546 | * Read the power status. |
547 | */ |
548 | static int i8k_get_power_status(const struct dell_smm_data *data) |
549 | { |
550 | struct smm_regs regs = { .eax = I8K_SMM_POWER_STATUS, }; |
551 | int rc; |
552 | |
553 | rc = dell_smm_call(ops: data->ops, regs: ®s); |
554 | if (rc < 0) |
555 | return rc; |
556 | |
557 | return (regs.eax & 0xff) == I8K_POWER_AC ? I8K_AC : I8K_BATTERY; |
558 | } |
559 | |
560 | /* |
561 | * Procfs interface |
562 | */ |
563 | |
564 | static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) |
565 | { |
566 | struct dell_smm_data *data = pde_data(inode: file_inode(f: fp)); |
567 | int __user *argp = (int __user *)arg; |
568 | int speed, err; |
569 | int val = 0; |
570 | |
571 | if (!argp) |
572 | return -EINVAL; |
573 | |
574 | switch (cmd) { |
575 | case I8K_BIOS_VERSION: |
576 | if (!isdigit(c: data->bios_version[0]) || !isdigit(c: data->bios_version[1]) || |
577 | !isdigit(c: data->bios_version[2])) |
578 | return -EINVAL; |
579 | |
580 | val = (data->bios_version[0] << 16) | |
581 | (data->bios_version[1] << 8) | data->bios_version[2]; |
582 | |
583 | if (copy_to_user(to: argp, from: &val, n: sizeof(val))) |
584 | return -EFAULT; |
585 | |
586 | return 0; |
587 | case I8K_MACHINE_ID: |
588 | if (restricted && !capable(CAP_SYS_ADMIN)) |
589 | return -EPERM; |
590 | |
591 | if (copy_to_user(to: argp, from: data->bios_machineid, n: sizeof(data->bios_machineid))) |
592 | return -EFAULT; |
593 | |
594 | return 0; |
595 | case I8K_FN_STATUS: |
596 | val = i8k_get_fn_status(data); |
597 | break; |
598 | |
599 | case I8K_POWER_STATUS: |
600 | val = i8k_get_power_status(data); |
601 | break; |
602 | |
603 | case I8K_GET_TEMP: |
604 | val = i8k_get_temp(data, sensor: 0); |
605 | break; |
606 | |
607 | case I8K_GET_SPEED: |
608 | if (copy_from_user(to: &val, from: argp, n: sizeof(int))) |
609 | return -EFAULT; |
610 | |
611 | if (val > U8_MAX || val < 0) |
612 | return -EINVAL; |
613 | |
614 | val = i8k_get_fan_speed(data, fan: val); |
615 | break; |
616 | |
617 | case I8K_GET_FAN: |
618 | if (copy_from_user(to: &val, from: argp, n: sizeof(int))) |
619 | return -EFAULT; |
620 | |
621 | if (val > U8_MAX || val < 0) |
622 | return -EINVAL; |
623 | |
624 | val = i8k_get_fan_status(data, fan: val); |
625 | break; |
626 | |
627 | case I8K_SET_FAN: |
628 | if (restricted && !capable(CAP_SYS_ADMIN)) |
629 | return -EPERM; |
630 | |
631 | if (copy_from_user(to: &val, from: argp, n: sizeof(int))) |
632 | return -EFAULT; |
633 | |
634 | if (val > U8_MAX || val < 0) |
635 | return -EINVAL; |
636 | |
637 | if (copy_from_user(to: &speed, from: argp + 1, n: sizeof(int))) |
638 | return -EFAULT; |
639 | |
640 | mutex_lock(&data->i8k_mutex); |
641 | err = i8k_set_fan(data, fan: val, speed); |
642 | if (err < 0) |
643 | val = err; |
644 | else |
645 | val = i8k_get_fan_status(data, fan: val); |
646 | mutex_unlock(lock: &data->i8k_mutex); |
647 | break; |
648 | |
649 | default: |
650 | return -ENOIOCTLCMD; |
651 | } |
652 | |
653 | if (val < 0) |
654 | return val; |
655 | |
656 | if (copy_to_user(to: argp, from: &val, n: sizeof(int))) |
657 | return -EFAULT; |
658 | |
659 | return 0; |
660 | } |
661 | |
662 | /* |
663 | * Print the information for /proc/i8k. |
664 | */ |
665 | static int i8k_proc_show(struct seq_file *seq, void *offset) |
666 | { |
667 | struct dell_smm_data *data = seq->private; |
668 | int fn_key, cpu_temp, ac_power; |
669 | int left_fan, right_fan, left_speed, right_speed; |
670 | |
671 | cpu_temp = i8k_get_temp(data, sensor: 0); /* 11100 µs */ |
672 | left_fan = i8k_get_fan_status(data, I8K_FAN_LEFT); /* 580 µs */ |
673 | right_fan = i8k_get_fan_status(data, I8K_FAN_RIGHT); /* 580 µs */ |
674 | left_speed = i8k_get_fan_speed(data, I8K_FAN_LEFT); /* 580 µs */ |
675 | right_speed = i8k_get_fan_speed(data, I8K_FAN_RIGHT); /* 580 µs */ |
676 | fn_key = i8k_get_fn_status(data); /* 750 µs */ |
677 | if (power_status) |
678 | ac_power = i8k_get_power_status(data); /* 14700 µs */ |
679 | else |
680 | ac_power = -1; |
681 | |
682 | /* |
683 | * Info: |
684 | * |
685 | * 1) Format version (this will change if format changes) |
686 | * 2) BIOS version |
687 | * 3) BIOS machine ID |
688 | * 4) Cpu temperature |
689 | * 5) Left fan status |
690 | * 6) Right fan status |
691 | * 7) Left fan speed |
692 | * 8) Right fan speed |
693 | * 9) AC power |
694 | * 10) Fn Key status |
695 | */ |
696 | seq_printf(m: seq, fmt: "%s %s %s %d %d %d %d %d %d %d\n" , |
697 | I8K_PROC_FMT, |
698 | data->bios_version, |
699 | (restricted && !capable(CAP_SYS_ADMIN)) ? "-1" : data->bios_machineid, |
700 | cpu_temp, |
701 | left_fan, right_fan, left_speed, right_speed, |
702 | ac_power, fn_key); |
703 | |
704 | return 0; |
705 | } |
706 | |
707 | static int i8k_open_fs(struct inode *inode, struct file *file) |
708 | { |
709 | return single_open(file, i8k_proc_show, pde_data(inode)); |
710 | } |
711 | |
712 | static const struct proc_ops i8k_proc_ops = { |
713 | .proc_open = i8k_open_fs, |
714 | .proc_read = seq_read, |
715 | .proc_lseek = seq_lseek, |
716 | .proc_release = single_release, |
717 | .proc_ioctl = i8k_ioctl, |
718 | }; |
719 | |
720 | static void i8k_exit_procfs(void *param) |
721 | { |
722 | remove_proc_entry("i8k" , NULL); |
723 | } |
724 | |
725 | static void __init i8k_init_procfs(struct device *dev) |
726 | { |
727 | struct dell_smm_data *data = dev_get_drvdata(dev); |
728 | |
729 | strscpy(data->bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION), |
730 | sizeof(data->bios_version)); |
731 | strscpy(data->bios_machineid, i8k_get_dmi_data(DMI_PRODUCT_SERIAL), |
732 | sizeof(data->bios_machineid)); |
733 | |
734 | /* Only register exit function if creation was successful */ |
735 | if (proc_create_data("i8k" , 0, NULL, &i8k_proc_ops, data)) |
736 | devm_add_action_or_reset(dev, i8k_exit_procfs, NULL); |
737 | } |
738 | |
739 | #else |
740 | |
741 | static void __init i8k_init_procfs(struct device *dev) |
742 | { |
743 | } |
744 | |
745 | #endif |
746 | |
747 | static int dell_smm_get_max_state(struct thermal_cooling_device *dev, unsigned long *state) |
748 | { |
749 | struct dell_smm_cooling_data *cdata = dev->devdata; |
750 | |
751 | *state = cdata->data->i8k_fan_max; |
752 | |
753 | return 0; |
754 | } |
755 | |
756 | static int dell_smm_get_cur_state(struct thermal_cooling_device *dev, unsigned long *state) |
757 | { |
758 | struct dell_smm_cooling_data *cdata = dev->devdata; |
759 | int ret; |
760 | |
761 | ret = i8k_get_fan_status(data: cdata->data, fan: cdata->fan_num); |
762 | if (ret < 0) |
763 | return ret; |
764 | |
765 | *state = ret; |
766 | |
767 | return 0; |
768 | } |
769 | |
770 | static int dell_smm_set_cur_state(struct thermal_cooling_device *dev, unsigned long state) |
771 | { |
772 | struct dell_smm_cooling_data *cdata = dev->devdata; |
773 | struct dell_smm_data *data = cdata->data; |
774 | int ret; |
775 | |
776 | if (state > data->i8k_fan_max) |
777 | return -EINVAL; |
778 | |
779 | mutex_lock(&data->i8k_mutex); |
780 | ret = i8k_set_fan(data, fan: cdata->fan_num, speed: (int)state); |
781 | mutex_unlock(lock: &data->i8k_mutex); |
782 | |
783 | return ret; |
784 | } |
785 | |
786 | static const struct thermal_cooling_device_ops dell_smm_cooling_ops = { |
787 | .get_max_state = dell_smm_get_max_state, |
788 | .get_cur_state = dell_smm_get_cur_state, |
789 | .set_cur_state = dell_smm_set_cur_state, |
790 | }; |
791 | |
792 | static umode_t dell_smm_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr, |
793 | int channel) |
794 | { |
795 | const struct dell_smm_data *data = drvdata; |
796 | |
797 | switch (type) { |
798 | case hwmon_temp: |
799 | switch (attr) { |
800 | case hwmon_temp_input: |
801 | /* _i8k_get_temp() is fine since we do not care about the actual value */ |
802 | if (data->temp_type[channel] >= 0 || _i8k_get_temp(data, sensor: channel) >= 0) |
803 | return 0444; |
804 | |
805 | break; |
806 | case hwmon_temp_label: |
807 | if (data->temp_type[channel] >= 0) |
808 | return 0444; |
809 | |
810 | break; |
811 | default: |
812 | break; |
813 | } |
814 | break; |
815 | case hwmon_fan: |
816 | if (disallow_fan_support) |
817 | break; |
818 | |
819 | switch (attr) { |
820 | case hwmon_fan_input: |
821 | if (data->fan[channel]) |
822 | return 0444; |
823 | |
824 | break; |
825 | case hwmon_fan_label: |
826 | if (data->fan[channel] && !disallow_fan_type_call) |
827 | return 0444; |
828 | |
829 | break; |
830 | case hwmon_fan_min: |
831 | case hwmon_fan_max: |
832 | case hwmon_fan_target: |
833 | if (data->fan_nominal_speed[channel]) |
834 | return 0444; |
835 | |
836 | break; |
837 | default: |
838 | break; |
839 | } |
840 | break; |
841 | case hwmon_pwm: |
842 | if (disallow_fan_support) |
843 | break; |
844 | |
845 | switch (attr) { |
846 | case hwmon_pwm_input: |
847 | if (data->fan[channel]) |
848 | return 0644; |
849 | |
850 | break; |
851 | case hwmon_pwm_enable: |
852 | if (auto_fan) |
853 | /* |
854 | * There is no command for retrieve the current status |
855 | * from BIOS, and userspace/firmware itself can change |
856 | * it. |
857 | * Thus we can only provide write-only access for now. |
858 | */ |
859 | return 0200; |
860 | |
861 | break; |
862 | default: |
863 | break; |
864 | } |
865 | break; |
866 | default: |
867 | break; |
868 | } |
869 | |
870 | return 0; |
871 | } |
872 | |
873 | static int dell_smm_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, |
874 | long *val) |
875 | { |
876 | struct dell_smm_data *data = dev_get_drvdata(dev); |
877 | int mult = data->i8k_fan_mult; |
878 | int ret; |
879 | |
880 | switch (type) { |
881 | case hwmon_temp: |
882 | switch (attr) { |
883 | case hwmon_temp_input: |
884 | ret = i8k_get_temp(data, sensor: channel); |
885 | if (ret < 0) |
886 | return ret; |
887 | |
888 | *val = ret * 1000; |
889 | |
890 | return 0; |
891 | default: |
892 | break; |
893 | } |
894 | break; |
895 | case hwmon_fan: |
896 | switch (attr) { |
897 | case hwmon_fan_input: |
898 | ret = i8k_get_fan_speed(data, fan: channel); |
899 | if (ret < 0) |
900 | return ret; |
901 | |
902 | *val = ret; |
903 | |
904 | return 0; |
905 | case hwmon_fan_min: |
906 | *val = data->fan_nominal_speed[channel][0] * mult; |
907 | |
908 | return 0; |
909 | case hwmon_fan_max: |
910 | *val = data->fan_nominal_speed[channel][data->i8k_fan_max] * mult; |
911 | |
912 | return 0; |
913 | case hwmon_fan_target: |
914 | ret = i8k_get_fan_status(data, fan: channel); |
915 | if (ret < 0) |
916 | return ret; |
917 | |
918 | if (ret > data->i8k_fan_max) |
919 | ret = data->i8k_fan_max; |
920 | |
921 | *val = data->fan_nominal_speed[channel][ret] * mult; |
922 | |
923 | return 0; |
924 | default: |
925 | break; |
926 | } |
927 | break; |
928 | case hwmon_pwm: |
929 | switch (attr) { |
930 | case hwmon_pwm_input: |
931 | ret = i8k_get_fan_status(data, fan: channel); |
932 | if (ret < 0) |
933 | return ret; |
934 | |
935 | *val = clamp_val(ret * data->i8k_pwm_mult, 0, 255); |
936 | |
937 | return 0; |
938 | default: |
939 | break; |
940 | } |
941 | break; |
942 | default: |
943 | break; |
944 | } |
945 | |
946 | return -EOPNOTSUPP; |
947 | } |
948 | |
949 | static const char *dell_smm_fan_label(struct dell_smm_data *data, int channel) |
950 | { |
951 | bool dock = false; |
952 | int type = i8k_get_fan_type(data, fan: channel); |
953 | |
954 | if (type < 0) |
955 | return ERR_PTR(error: type); |
956 | |
957 | if (type & 0x10) { |
958 | dock = true; |
959 | type &= 0x0F; |
960 | } |
961 | |
962 | if (type >= ARRAY_SIZE(fan_labels)) |
963 | type = ARRAY_SIZE(fan_labels) - 1; |
964 | |
965 | return dock ? docking_labels[type] : fan_labels[type]; |
966 | } |
967 | |
968 | static int dell_smm_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, |
969 | int channel, const char **str) |
970 | { |
971 | struct dell_smm_data *data = dev_get_drvdata(dev); |
972 | |
973 | switch (type) { |
974 | case hwmon_temp: |
975 | switch (attr) { |
976 | case hwmon_temp_label: |
977 | *str = temp_labels[data->temp_type[channel]]; |
978 | return 0; |
979 | default: |
980 | break; |
981 | } |
982 | break; |
983 | case hwmon_fan: |
984 | switch (attr) { |
985 | case hwmon_fan_label: |
986 | *str = dell_smm_fan_label(data, channel); |
987 | return PTR_ERR_OR_ZERO(ptr: *str); |
988 | default: |
989 | break; |
990 | } |
991 | break; |
992 | default: |
993 | break; |
994 | } |
995 | |
996 | return -EOPNOTSUPP; |
997 | } |
998 | |
999 | static int dell_smm_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, |
1000 | long val) |
1001 | { |
1002 | struct dell_smm_data *data = dev_get_drvdata(dev); |
1003 | unsigned long pwm; |
1004 | bool enable; |
1005 | int err; |
1006 | |
1007 | switch (type) { |
1008 | case hwmon_pwm: |
1009 | switch (attr) { |
1010 | case hwmon_pwm_input: |
1011 | pwm = clamp_val(DIV_ROUND_CLOSEST(val, data->i8k_pwm_mult), 0, |
1012 | data->i8k_fan_max); |
1013 | |
1014 | mutex_lock(&data->i8k_mutex); |
1015 | err = i8k_set_fan(data, fan: channel, speed: pwm); |
1016 | mutex_unlock(lock: &data->i8k_mutex); |
1017 | |
1018 | if (err < 0) |
1019 | return err; |
1020 | |
1021 | return 0; |
1022 | case hwmon_pwm_enable: |
1023 | if (!val) |
1024 | return -EINVAL; |
1025 | |
1026 | if (val == 1) |
1027 | enable = false; |
1028 | else |
1029 | enable = true; |
1030 | |
1031 | mutex_lock(&data->i8k_mutex); |
1032 | err = i8k_enable_fan_auto_mode(data, enable); |
1033 | mutex_unlock(lock: &data->i8k_mutex); |
1034 | |
1035 | if (err < 0) |
1036 | return err; |
1037 | |
1038 | return 0; |
1039 | default: |
1040 | break; |
1041 | } |
1042 | break; |
1043 | default: |
1044 | break; |
1045 | } |
1046 | |
1047 | return -EOPNOTSUPP; |
1048 | } |
1049 | |
1050 | static const struct hwmon_ops dell_smm_ops = { |
1051 | .is_visible = dell_smm_is_visible, |
1052 | .read = dell_smm_read, |
1053 | .read_string = dell_smm_read_string, |
1054 | .write = dell_smm_write, |
1055 | }; |
1056 | |
1057 | static const struct hwmon_channel_info * const dell_smm_info[] = { |
1058 | HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), |
1059 | HWMON_CHANNEL_INFO(temp, |
1060 | HWMON_T_INPUT | HWMON_T_LABEL, |
1061 | HWMON_T_INPUT | HWMON_T_LABEL, |
1062 | HWMON_T_INPUT | HWMON_T_LABEL, |
1063 | HWMON_T_INPUT | HWMON_T_LABEL, |
1064 | HWMON_T_INPUT | HWMON_T_LABEL, |
1065 | HWMON_T_INPUT | HWMON_T_LABEL, |
1066 | HWMON_T_INPUT | HWMON_T_LABEL, |
1067 | HWMON_T_INPUT | HWMON_T_LABEL, |
1068 | HWMON_T_INPUT | HWMON_T_LABEL, |
1069 | HWMON_T_INPUT | HWMON_T_LABEL |
1070 | ), |
1071 | HWMON_CHANNEL_INFO(fan, |
1072 | HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX | |
1073 | HWMON_F_TARGET, |
1074 | HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX | |
1075 | HWMON_F_TARGET, |
1076 | HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX | |
1077 | HWMON_F_TARGET |
1078 | ), |
1079 | HWMON_CHANNEL_INFO(pwm, |
1080 | HWMON_PWM_INPUT | HWMON_PWM_ENABLE, |
1081 | HWMON_PWM_INPUT, |
1082 | HWMON_PWM_INPUT |
1083 | ), |
1084 | NULL |
1085 | }; |
1086 | |
1087 | static const struct hwmon_chip_info dell_smm_chip_info = { |
1088 | .ops = &dell_smm_ops, |
1089 | .info = dell_smm_info, |
1090 | }; |
1091 | |
1092 | static int dell_smm_init_cdev(struct device *dev, u8 fan_num) |
1093 | { |
1094 | struct dell_smm_data *data = dev_get_drvdata(dev); |
1095 | struct thermal_cooling_device *cdev; |
1096 | struct dell_smm_cooling_data *cdata; |
1097 | int ret = 0; |
1098 | char *name; |
1099 | |
1100 | name = kasprintf(GFP_KERNEL, fmt: "dell-smm-fan%u" , fan_num + 1); |
1101 | if (!name) |
1102 | return -ENOMEM; |
1103 | |
1104 | cdata = devm_kmalloc(dev, size: sizeof(*cdata), GFP_KERNEL); |
1105 | if (cdata) { |
1106 | cdata->fan_num = fan_num; |
1107 | cdata->data = data; |
1108 | cdev = devm_thermal_of_cooling_device_register(dev, NULL, type: name, devdata: cdata, |
1109 | ops: &dell_smm_cooling_ops); |
1110 | if (IS_ERR(ptr: cdev)) { |
1111 | devm_kfree(dev, p: cdata); |
1112 | ret = PTR_ERR(ptr: cdev); |
1113 | } |
1114 | } else { |
1115 | ret = -ENOMEM; |
1116 | } |
1117 | |
1118 | kfree(objp: name); |
1119 | |
1120 | return ret; |
1121 | } |
1122 | |
1123 | static int dell_smm_init_hwmon(struct device *dev) |
1124 | { |
1125 | struct dell_smm_data *data = dev_get_drvdata(dev); |
1126 | struct device *dell_smm_hwmon_dev; |
1127 | int state, err; |
1128 | u8 i; |
1129 | |
1130 | for (i = 0; i < DELL_SMM_NO_TEMP; i++) { |
1131 | data->temp_type[i] = i8k_get_temp_type(data, sensor: i); |
1132 | if (data->temp_type[i] < 0) |
1133 | continue; |
1134 | |
1135 | if (data->temp_type[i] >= ARRAY_SIZE(temp_labels)) |
1136 | data->temp_type[i] = ARRAY_SIZE(temp_labels) - 1; |
1137 | } |
1138 | |
1139 | for (i = 0; i < DELL_SMM_NO_FANS; i++) { |
1140 | data->fan_type[i] = INT_MIN; |
1141 | err = i8k_get_fan_status(data, fan: i); |
1142 | if (err < 0) |
1143 | err = i8k_get_fan_type(data, fan: i); |
1144 | |
1145 | if (err < 0) |
1146 | continue; |
1147 | |
1148 | data->fan[i] = true; |
1149 | |
1150 | /* the cooling device is not critical, ignore failures */ |
1151 | if (IS_REACHABLE(CONFIG_THERMAL)) { |
1152 | err = dell_smm_init_cdev(dev, fan_num: i); |
1153 | if (err < 0) |
1154 | dev_warn(dev, "Failed to register cooling device for fan %u\n" , |
1155 | i + 1); |
1156 | } |
1157 | |
1158 | data->fan_nominal_speed[i] = devm_kmalloc_array(dev, n: data->i8k_fan_max + 1, |
1159 | size: sizeof(*data->fan_nominal_speed[i]), |
1160 | GFP_KERNEL); |
1161 | if (!data->fan_nominal_speed[i]) |
1162 | continue; |
1163 | |
1164 | for (state = 0; state <= data->i8k_fan_max; state++) { |
1165 | err = i8k_get_fan_nominal_speed(data, fan: i, speed: state); |
1166 | if (err < 0) { |
1167 | /* Mark nominal speed table as invalid in case of error */ |
1168 | devm_kfree(dev, p: data->fan_nominal_speed[i]); |
1169 | data->fan_nominal_speed[i] = NULL; |
1170 | break; |
1171 | } |
1172 | data->fan_nominal_speed[i][state] = err; |
1173 | /* |
1174 | * Autodetect fan multiplier based on nominal rpm if multiplier |
1175 | * was not specified as module param or in DMI. If fan reports |
1176 | * rpm value too high then set multiplier to 1. |
1177 | */ |
1178 | if (!fan_mult && err > I8K_FAN_RPM_THRESHOLD) |
1179 | data->i8k_fan_mult = 1; |
1180 | } |
1181 | } |
1182 | |
1183 | dell_smm_hwmon_dev = devm_hwmon_device_register_with_info(dev, name: "dell_smm" , drvdata: data, |
1184 | info: &dell_smm_chip_info, NULL); |
1185 | |
1186 | return PTR_ERR_OR_ZERO(ptr: dell_smm_hwmon_dev); |
1187 | } |
1188 | |
1189 | static int dell_smm_init_data(struct device *dev, const struct dell_smm_ops *ops) |
1190 | { |
1191 | struct dell_smm_data *data; |
1192 | |
1193 | data = devm_kzalloc(dev, size: sizeof(*data), GFP_KERNEL); |
1194 | if (!data) |
1195 | return -ENOMEM; |
1196 | |
1197 | mutex_init(&data->i8k_mutex); |
1198 | dev_set_drvdata(dev, data); |
1199 | |
1200 | data->ops = ops; |
1201 | /* All options must not be 0 */ |
1202 | data->i8k_fan_mult = fan_mult ? : I8K_FAN_MULT; |
1203 | data->i8k_fan_max = fan_max ? : I8K_FAN_HIGH; |
1204 | data->i8k_pwm_mult = DIV_ROUND_UP(255, data->i8k_fan_max); |
1205 | |
1206 | return 0; |
1207 | } |
1208 | |
1209 | static const struct dmi_system_id i8k_dmi_table[] __initconst = { |
1210 | { |
1211 | .ident = "Dell G5 5590" , |
1212 | .matches = { |
1213 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1214 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "G5 5590" ), |
1215 | }, |
1216 | }, |
1217 | { |
1218 | .ident = "Dell Inspiron" , |
1219 | .matches = { |
1220 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer" ), |
1221 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron" ), |
1222 | }, |
1223 | }, |
1224 | { |
1225 | .ident = "Dell Latitude" , |
1226 | .matches = { |
1227 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer" ), |
1228 | DMI_MATCH(DMI_PRODUCT_NAME, "Latitude" ), |
1229 | }, |
1230 | }, |
1231 | { |
1232 | .ident = "Dell Inspiron 2" , |
1233 | .matches = { |
1234 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1235 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron" ), |
1236 | }, |
1237 | }, |
1238 | { |
1239 | .ident = "Dell Latitude 2" , |
1240 | .matches = { |
1241 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1242 | DMI_MATCH(DMI_PRODUCT_NAME, "Latitude" ), |
1243 | }, |
1244 | }, |
1245 | { /* UK Inspiron 6400 */ |
1246 | .ident = "Dell Inspiron 3" , |
1247 | .matches = { |
1248 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1249 | DMI_MATCH(DMI_PRODUCT_NAME, "MM061" ), |
1250 | }, |
1251 | }, |
1252 | { |
1253 | .ident = "Dell Inspiron 3" , |
1254 | .matches = { |
1255 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1256 | DMI_MATCH(DMI_PRODUCT_NAME, "MP061" ), |
1257 | }, |
1258 | }, |
1259 | { |
1260 | .ident = "Dell Precision" , |
1261 | .matches = { |
1262 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1263 | DMI_MATCH(DMI_PRODUCT_NAME, "Precision" ), |
1264 | }, |
1265 | }, |
1266 | { |
1267 | .ident = "Dell Vostro" , |
1268 | .matches = { |
1269 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1270 | DMI_MATCH(DMI_PRODUCT_NAME, "Vostro" ), |
1271 | }, |
1272 | }, |
1273 | { |
1274 | .ident = "Dell Studio" , |
1275 | .matches = { |
1276 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1277 | DMI_MATCH(DMI_PRODUCT_NAME, "Studio" ), |
1278 | }, |
1279 | }, |
1280 | { |
1281 | .ident = "Dell XPS M140" , |
1282 | .matches = { |
1283 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1284 | DMI_MATCH(DMI_PRODUCT_NAME, "MXC051" ), |
1285 | }, |
1286 | }, |
1287 | { |
1288 | .ident = "Dell XPS" , |
1289 | .matches = { |
1290 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1291 | DMI_MATCH(DMI_PRODUCT_NAME, "XPS" ), |
1292 | }, |
1293 | }, |
1294 | { } |
1295 | }; |
1296 | |
1297 | MODULE_DEVICE_TABLE(dmi, i8k_dmi_table); |
1298 | |
1299 | /* |
1300 | * Only use for machines which need some special configuration |
1301 | * in order to work correctly (e.g. if autoconfig fails on this machines). |
1302 | */ |
1303 | struct i8k_config_data { |
1304 | uint fan_mult; |
1305 | uint fan_max; |
1306 | }; |
1307 | |
1308 | enum i8k_configs { |
1309 | DELL_LATITUDE_D520, |
1310 | DELL_PRECISION_490, |
1311 | DELL_STUDIO, |
1312 | DELL_XPS, |
1313 | }; |
1314 | |
1315 | static const struct i8k_config_data i8k_config_data[] __initconst = { |
1316 | [DELL_LATITUDE_D520] = { |
1317 | .fan_mult = 1, |
1318 | .fan_max = I8K_FAN_TURBO, |
1319 | }, |
1320 | [DELL_PRECISION_490] = { |
1321 | .fan_mult = 1, |
1322 | .fan_max = I8K_FAN_TURBO, |
1323 | }, |
1324 | [DELL_STUDIO] = { |
1325 | .fan_mult = 1, |
1326 | .fan_max = I8K_FAN_HIGH, |
1327 | }, |
1328 | [DELL_XPS] = { |
1329 | .fan_mult = 1, |
1330 | .fan_max = I8K_FAN_HIGH, |
1331 | }, |
1332 | }; |
1333 | |
1334 | static const struct dmi_system_id i8k_config_dmi_table[] __initconst = { |
1335 | { |
1336 | .ident = "Dell Latitude D520" , |
1337 | .matches = { |
1338 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1339 | DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D520" ), |
1340 | }, |
1341 | .driver_data = (void *)&i8k_config_data[DELL_LATITUDE_D520], |
1342 | }, |
1343 | { |
1344 | .ident = "Dell Precision 490" , |
1345 | .matches = { |
1346 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1347 | DMI_MATCH(DMI_PRODUCT_NAME, |
1348 | "Precision WorkStation 490" ), |
1349 | }, |
1350 | .driver_data = (void *)&i8k_config_data[DELL_PRECISION_490], |
1351 | }, |
1352 | { |
1353 | .ident = "Dell Studio" , |
1354 | .matches = { |
1355 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1356 | DMI_MATCH(DMI_PRODUCT_NAME, "Studio" ), |
1357 | }, |
1358 | .driver_data = (void *)&i8k_config_data[DELL_STUDIO], |
1359 | }, |
1360 | { |
1361 | .ident = "Dell XPS M140" , |
1362 | .matches = { |
1363 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1364 | DMI_MATCH(DMI_PRODUCT_NAME, "MXC051" ), |
1365 | }, |
1366 | .driver_data = (void *)&i8k_config_data[DELL_XPS], |
1367 | }, |
1368 | { } |
1369 | }; |
1370 | |
1371 | /* |
1372 | * On some machines once I8K_SMM_GET_FAN_TYPE is issued then CPU fan speed |
1373 | * randomly going up and down due to bug in Dell SMM or BIOS. Here is blacklist |
1374 | * of affected Dell machines for which we disallow I8K_SMM_GET_FAN_TYPE call. |
1375 | * See bug: https://bugzilla.kernel.org/show_bug.cgi?id=100121 |
1376 | */ |
1377 | static const struct dmi_system_id i8k_blacklist_fan_type_dmi_table[] __initconst = { |
1378 | { |
1379 | .ident = "Dell Studio XPS 8000" , |
1380 | .matches = { |
1381 | DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1382 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Studio XPS 8000" ), |
1383 | }, |
1384 | }, |
1385 | { |
1386 | .ident = "Dell Studio XPS 8100" , |
1387 | .matches = { |
1388 | DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1389 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Studio XPS 8100" ), |
1390 | }, |
1391 | }, |
1392 | { |
1393 | .ident = "Dell Inspiron 580" , |
1394 | .matches = { |
1395 | DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1396 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Inspiron 580 " ), |
1397 | }, |
1398 | }, |
1399 | { |
1400 | .ident = "Dell Inspiron 3505" , |
1401 | .matches = { |
1402 | DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1403 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Inspiron 3505" ), |
1404 | }, |
1405 | }, |
1406 | { } |
1407 | }; |
1408 | |
1409 | /* |
1410 | * On some machines all fan related SMM functions implemented by Dell BIOS |
1411 | * firmware freeze kernel for about 500ms. Until Dell fixes these problems fan |
1412 | * support for affected blacklisted Dell machines stay disabled. |
1413 | * See bug: https://bugzilla.kernel.org/show_bug.cgi?id=195751 |
1414 | */ |
1415 | static const struct dmi_system_id i8k_blacklist_fan_support_dmi_table[] __initconst = { |
1416 | { |
1417 | .ident = "Dell Inspiron 7720" , |
1418 | .matches = { |
1419 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1420 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Inspiron 7720" ), |
1421 | }, |
1422 | }, |
1423 | { |
1424 | .ident = "Dell Vostro 3360" , |
1425 | .matches = { |
1426 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1427 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Vostro 3360" ), |
1428 | }, |
1429 | }, |
1430 | { |
1431 | .ident = "Dell XPS13 9333" , |
1432 | .matches = { |
1433 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1434 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS13 9333" ), |
1435 | }, |
1436 | }, |
1437 | { |
1438 | .ident = "Dell XPS 15 L502X" , |
1439 | .matches = { |
1440 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1441 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Dell System XPS L502X" ), |
1442 | }, |
1443 | }, |
1444 | { } |
1445 | }; |
1446 | |
1447 | struct i8k_fan_control_data { |
1448 | unsigned int manual_fan; |
1449 | unsigned int auto_fan; |
1450 | }; |
1451 | |
1452 | enum i8k_fan_controls { |
1453 | I8K_FAN_30A3_31A3, |
1454 | I8K_FAN_34A3_35A3, |
1455 | }; |
1456 | |
1457 | static const struct i8k_fan_control_data i8k_fan_control_data[] __initconst = { |
1458 | [I8K_FAN_30A3_31A3] = { |
1459 | .manual_fan = 0x30a3, |
1460 | .auto_fan = 0x31a3, |
1461 | }, |
1462 | [I8K_FAN_34A3_35A3] = { |
1463 | .manual_fan = 0x34a3, |
1464 | .auto_fan = 0x35a3, |
1465 | }, |
1466 | }; |
1467 | |
1468 | static const struct dmi_system_id i8k_whitelist_fan_control[] __initconst = { |
1469 | { |
1470 | .ident = "Dell Latitude 5480" , |
1471 | .matches = { |
1472 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1473 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude 5480" ), |
1474 | }, |
1475 | .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], |
1476 | }, |
1477 | { |
1478 | .ident = "Dell Latitude E6440" , |
1479 | .matches = { |
1480 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1481 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude E6440" ), |
1482 | }, |
1483 | .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], |
1484 | }, |
1485 | { |
1486 | .ident = "Dell Latitude E7440" , |
1487 | .matches = { |
1488 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1489 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude E7440" ), |
1490 | }, |
1491 | .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], |
1492 | }, |
1493 | { |
1494 | .ident = "Dell Precision 5530" , |
1495 | .matches = { |
1496 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1497 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Precision 5530" ), |
1498 | }, |
1499 | .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], |
1500 | }, |
1501 | { |
1502 | .ident = "Dell Precision 7510" , |
1503 | .matches = { |
1504 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1505 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Precision 7510" ), |
1506 | }, |
1507 | .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], |
1508 | }, |
1509 | { |
1510 | .ident = "Dell XPS 13 7390" , |
1511 | .matches = { |
1512 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1513 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS 13 7390" ), |
1514 | }, |
1515 | .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], |
1516 | }, |
1517 | { |
1518 | .ident = "Dell Optiplex 7000" , |
1519 | .matches = { |
1520 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1521 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "OptiPlex 7000" ), |
1522 | }, |
1523 | .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], |
1524 | }, |
1525 | { |
1526 | .ident = "Dell XPS 9315" , |
1527 | .matches = { |
1528 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc." ), |
1529 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS 9315" ), |
1530 | }, |
1531 | .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_30A3_31A3], |
1532 | }, |
1533 | { } |
1534 | }; |
1535 | |
1536 | /* |
1537 | * Legacy SMM backend driver. |
1538 | */ |
1539 | static int __init dell_smm_probe(struct platform_device *pdev) |
1540 | { |
1541 | int ret; |
1542 | |
1543 | ret = dell_smm_init_data(dev: &pdev->dev, ops: &i8k_smm_ops); |
1544 | if (ret < 0) |
1545 | return ret; |
1546 | |
1547 | ret = dell_smm_init_hwmon(dev: &pdev->dev); |
1548 | if (ret) |
1549 | return ret; |
1550 | |
1551 | i8k_init_procfs(dev: &pdev->dev); |
1552 | |
1553 | return 0; |
1554 | } |
1555 | |
1556 | static struct platform_driver dell_smm_driver = { |
1557 | .driver = { |
1558 | .name = KBUILD_MODNAME, |
1559 | }, |
1560 | }; |
1561 | |
1562 | static struct platform_device *dell_smm_device; |
1563 | |
1564 | /* |
1565 | * WMI SMM backend driver. |
1566 | */ |
1567 | static int dell_smm_wmi_probe(struct wmi_device *wdev, const void *context) |
1568 | { |
1569 | struct dell_smm_ops *ops; |
1570 | int ret; |
1571 | |
1572 | ops = devm_kzalloc(dev: &wdev->dev, size: sizeof(*ops), GFP_KERNEL); |
1573 | if (!ops) |
1574 | return -ENOMEM; |
1575 | |
1576 | ops->smm_call = wmi_smm_call; |
1577 | ops->smm_dev = &wdev->dev; |
1578 | |
1579 | if (dell_smm_get_signature(ops, I8K_SMM_GET_DELL_SIG1) && |
1580 | dell_smm_get_signature(ops, I8K_SMM_GET_DELL_SIG2)) |
1581 | return -ENODEV; |
1582 | |
1583 | ret = dell_smm_init_data(dev: &wdev->dev, ops); |
1584 | if (ret < 0) |
1585 | return ret; |
1586 | |
1587 | return dell_smm_init_hwmon(dev: &wdev->dev); |
1588 | } |
1589 | |
1590 | static const struct wmi_device_id dell_smm_wmi_id_table[] = { |
1591 | { DELL_SMM_WMI_GUID, NULL }, |
1592 | { } |
1593 | }; |
1594 | MODULE_DEVICE_TABLE(wmi, dell_smm_wmi_id_table); |
1595 | |
1596 | static struct wmi_driver dell_smm_wmi_driver = { |
1597 | .driver = { |
1598 | .name = KBUILD_MODNAME, |
1599 | .probe_type = PROBE_PREFER_ASYNCHRONOUS, |
1600 | }, |
1601 | .id_table = dell_smm_wmi_id_table, |
1602 | .probe = dell_smm_wmi_probe, |
1603 | .no_singleton = true, |
1604 | }; |
1605 | |
1606 | /* |
1607 | * Probe for the presence of a supported laptop. |
1608 | */ |
1609 | static void __init dell_smm_init_dmi(void) |
1610 | { |
1611 | struct i8k_fan_control_data *control; |
1612 | struct i8k_config_data *config; |
1613 | const struct dmi_system_id *id; |
1614 | |
1615 | if (dmi_check_system(list: i8k_blacklist_fan_support_dmi_table)) { |
1616 | if (!force) { |
1617 | pr_notice("Disabling fan support due to BIOS bugs\n" ); |
1618 | disallow_fan_support = true; |
1619 | } else { |
1620 | pr_warn("Enabling fan support despite BIOS bugs\n" ); |
1621 | } |
1622 | } |
1623 | |
1624 | if (dmi_check_system(list: i8k_blacklist_fan_type_dmi_table)) { |
1625 | if (!force) { |
1626 | pr_notice("Disabling fan type call due to BIOS bugs\n" ); |
1627 | disallow_fan_type_call = true; |
1628 | } else { |
1629 | pr_warn("Enabling fan type call despite BIOS bugs\n" ); |
1630 | } |
1631 | } |
1632 | |
1633 | /* |
1634 | * Set fan multiplier and maximal fan speed from DMI config. |
1635 | * Values specified in module parameters override values from DMI. |
1636 | */ |
1637 | id = dmi_first_match(list: i8k_config_dmi_table); |
1638 | if (id && id->driver_data) { |
1639 | config = id->driver_data; |
1640 | if (!fan_mult && config->fan_mult) |
1641 | fan_mult = config->fan_mult; |
1642 | |
1643 | if (!fan_max && config->fan_max) |
1644 | fan_max = config->fan_max; |
1645 | } |
1646 | |
1647 | id = dmi_first_match(list: i8k_whitelist_fan_control); |
1648 | if (id && id->driver_data) { |
1649 | control = id->driver_data; |
1650 | manual_fan = control->manual_fan; |
1651 | auto_fan = control->auto_fan; |
1652 | |
1653 | pr_info("Enabling support for setting automatic/manual fan control\n" ); |
1654 | } |
1655 | } |
1656 | |
1657 | static int __init dell_smm_legacy_check(void) |
1658 | { |
1659 | if (!dmi_check_system(list: i8k_dmi_table)) { |
1660 | if (!ignore_dmi && !force) |
1661 | return -ENODEV; |
1662 | |
1663 | pr_info("Probing for legacy SMM handler on unsupported machine\n" ); |
1664 | pr_info("vendor=%s, model=%s, version=%s\n" , |
1665 | i8k_get_dmi_data(DMI_SYS_VENDOR), |
1666 | i8k_get_dmi_data(DMI_PRODUCT_NAME), |
1667 | i8k_get_dmi_data(DMI_BIOS_VERSION)); |
1668 | } |
1669 | |
1670 | if (dell_smm_get_signature(ops: &i8k_smm_ops, I8K_SMM_GET_DELL_SIG1) && |
1671 | dell_smm_get_signature(ops: &i8k_smm_ops, I8K_SMM_GET_DELL_SIG2)) { |
1672 | if (!force) |
1673 | return -ENODEV; |
1674 | |
1675 | pr_warn("Forcing legacy SMM calls on a possibly incompatible machine\n" ); |
1676 | } |
1677 | |
1678 | return 0; |
1679 | } |
1680 | |
1681 | static int __init i8k_init(void) |
1682 | { |
1683 | int ret; |
1684 | |
1685 | dell_smm_init_dmi(); |
1686 | |
1687 | ret = dell_smm_legacy_check(); |
1688 | if (ret < 0) { |
1689 | /* |
1690 | * On modern machines, SMM communication happens over WMI, meaning |
1691 | * the SMM handler might not react to legacy SMM calls. |
1692 | */ |
1693 | return wmi_driver_register(&dell_smm_wmi_driver); |
1694 | } |
1695 | |
1696 | dell_smm_device = platform_create_bundle(&dell_smm_driver, dell_smm_probe, NULL, 0, NULL, |
1697 | 0); |
1698 | |
1699 | return PTR_ERR_OR_ZERO(ptr: dell_smm_device); |
1700 | } |
1701 | |
1702 | static void __exit i8k_exit(void) |
1703 | { |
1704 | if (dell_smm_device) { |
1705 | platform_device_unregister(dell_smm_device); |
1706 | platform_driver_unregister(&dell_smm_driver); |
1707 | } else { |
1708 | wmi_driver_unregister(driver: &dell_smm_wmi_driver); |
1709 | } |
1710 | } |
1711 | |
1712 | module_init(i8k_init); |
1713 | module_exit(i8k_exit); |
1714 | |