1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Linux driver for WMI sensor information on Dell notebooks.
4 *
5 * Copyright (C) 2022 Armin Wolf <W_Armin@gmx.de>
6 */
7
8#define pr_format(fmt) KBUILD_MODNAME ": " fmt
9
10#include <linux/acpi.h>
11#include <linux/debugfs.h>
12#include <linux/device.h>
13#include <linux/device/driver.h>
14#include <linux/dev_printk.h>
15#include <linux/errno.h>
16#include <linux/kconfig.h>
17#include <linux/kernel.h>
18#include <linux/hwmon.h>
19#include <linux/kstrtox.h>
20#include <linux/math64.h>
21#include <linux/module.h>
22#include <linux/mutex.h>
23#include <linux/limits.h>
24#include <linux/pm.h>
25#include <linux/power_supply.h>
26#include <linux/printk.h>
27#include <linux/seq_file.h>
28#include <linux/sysfs.h>
29#include <linux/types.h>
30#include <linux/wmi.h>
31
32#include <acpi/battery.h>
33
34#include <asm/unaligned.h>
35
36#define DRIVER_NAME "dell-wmi-ddv"
37
38#define DELL_DDV_SUPPORTED_VERSION_MIN 2
39#define DELL_DDV_SUPPORTED_VERSION_MAX 3
40#define DELL_DDV_GUID "8A42EA14-4F2A-FD45-6422-0087F7A7E608"
41
42#define DELL_EPPID_LENGTH 20
43#define DELL_EPPID_EXT_LENGTH 23
44
45static bool force;
46module_param_unsafe(force, bool, 0);
47MODULE_PARM_DESC(force, "Force loading without checking for supported WMI interface versions");
48
49enum dell_ddv_method {
50 DELL_DDV_BATTERY_DESIGN_CAPACITY = 0x01,
51 DELL_DDV_BATTERY_FULL_CHARGE_CAPACITY = 0x02,
52 DELL_DDV_BATTERY_MANUFACTURE_NAME = 0x03,
53 DELL_DDV_BATTERY_MANUFACTURE_DATE = 0x04,
54 DELL_DDV_BATTERY_SERIAL_NUMBER = 0x05,
55 DELL_DDV_BATTERY_CHEMISTRY_VALUE = 0x06,
56 DELL_DDV_BATTERY_TEMPERATURE = 0x07,
57 DELL_DDV_BATTERY_CURRENT = 0x08,
58 DELL_DDV_BATTERY_VOLTAGE = 0x09,
59 DELL_DDV_BATTERY_MANUFACTURER_ACCESS = 0x0A,
60 DELL_DDV_BATTERY_RELATIVE_CHARGE_STATE = 0x0B,
61 DELL_DDV_BATTERY_CYCLE_COUNT = 0x0C,
62 DELL_DDV_BATTERY_EPPID = 0x0D,
63 DELL_DDV_BATTERY_RAW_ANALYTICS_START = 0x0E,
64 DELL_DDV_BATTERY_RAW_ANALYTICS = 0x0F,
65 DELL_DDV_BATTERY_DESIGN_VOLTAGE = 0x10,
66 DELL_DDV_BATTERY_RAW_ANALYTICS_A_BLOCK = 0x11, /* version 3 */
67
68 DELL_DDV_INTERFACE_VERSION = 0x12,
69
70 DELL_DDV_FAN_SENSOR_INFORMATION = 0x20,
71 DELL_DDV_THERMAL_SENSOR_INFORMATION = 0x22,
72};
73
74struct fan_sensor_entry {
75 u8 type;
76 __le16 rpm;
77} __packed;
78
79struct thermal_sensor_entry {
80 u8 type;
81 s8 now;
82 s8 min;
83 s8 max;
84 u8 unknown;
85} __packed;
86
87struct combined_channel_info {
88 struct hwmon_channel_info info;
89 u32 config[];
90};
91
92struct combined_chip_info {
93 struct hwmon_chip_info chip;
94 const struct hwmon_channel_info *info[];
95};
96
97struct dell_wmi_ddv_sensors {
98 bool active;
99 struct mutex lock; /* protect caching */
100 unsigned long timestamp;
101 union acpi_object *obj;
102 u64 entries;
103};
104
105struct dell_wmi_ddv_data {
106 struct acpi_battery_hook hook;
107 struct device_attribute temp_attr;
108 struct device_attribute eppid_attr;
109 struct dell_wmi_ddv_sensors fans;
110 struct dell_wmi_ddv_sensors temps;
111 struct wmi_device *wdev;
112};
113
114static const char * const fan_labels[] = {
115 "CPU Fan",
116 "Chassis Motherboard Fan",
117 "Video Fan",
118 "Power Supply Fan",
119 "Chipset Fan",
120 "Memory Fan",
121 "PCI Fan",
122 "HDD Fan",
123};
124
125static const char * const fan_dock_labels[] = {
126 "Docking Chassis/Motherboard Fan",
127 "Docking Video Fan",
128 "Docking Power Supply Fan",
129 "Docking Chipset Fan",
130};
131
132static int dell_wmi_ddv_query_type(struct wmi_device *wdev, enum dell_ddv_method method, u32 arg,
133 union acpi_object **result, acpi_object_type type)
134{
135 struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
136 const struct acpi_buffer in = {
137 .length = sizeof(arg),
138 .pointer = &arg,
139 };
140 union acpi_object *obj;
141 acpi_status ret;
142
143 ret = wmidev_evaluate_method(wdev, instance: 0x0, method_id: method, in: &in, out: &out);
144 if (ACPI_FAILURE(ret))
145 return -EIO;
146
147 obj = out.pointer;
148 if (!obj)
149 return -ENODATA;
150
151 if (obj->type != type) {
152 kfree(objp: obj);
153 return -ENOMSG;
154 }
155
156 *result = obj;
157
158 return 0;
159}
160
161static int dell_wmi_ddv_query_integer(struct wmi_device *wdev, enum dell_ddv_method method,
162 u32 arg, u32 *res)
163{
164 union acpi_object *obj;
165 int ret;
166
167 ret = dell_wmi_ddv_query_type(wdev, method, arg, result: &obj, ACPI_TYPE_INTEGER);
168 if (ret < 0)
169 return ret;
170
171 if (obj->integer.value <= U32_MAX)
172 *res = (u32)obj->integer.value;
173 else
174 ret = -ERANGE;
175
176 kfree(objp: obj);
177
178 return ret;
179}
180
181static int dell_wmi_ddv_query_buffer(struct wmi_device *wdev, enum dell_ddv_method method,
182 u32 arg, union acpi_object **result)
183{
184 union acpi_object *obj;
185 u64 buffer_size;
186 int ret;
187
188 ret = dell_wmi_ddv_query_type(wdev, method, arg, result: &obj, ACPI_TYPE_PACKAGE);
189 if (ret < 0)
190 return ret;
191
192 if (obj->package.count != 2 ||
193 obj->package.elements[0].type != ACPI_TYPE_INTEGER ||
194 obj->package.elements[1].type != ACPI_TYPE_BUFFER) {
195 ret = -ENOMSG;
196
197 goto err_free;
198 }
199
200 buffer_size = obj->package.elements[0].integer.value;
201
202 if (!buffer_size) {
203 ret = -ENODATA;
204
205 goto err_free;
206 }
207
208 if (buffer_size > obj->package.elements[1].buffer.length) {
209 dev_warn(&wdev->dev,
210 FW_WARN "WMI buffer size (%llu) exceeds ACPI buffer size (%d)\n",
211 buffer_size, obj->package.elements[1].buffer.length);
212 ret = -EMSGSIZE;
213
214 goto err_free;
215 }
216
217 *result = obj;
218
219 return 0;
220
221err_free:
222 kfree(objp: obj);
223
224 return ret;
225}
226
227static int dell_wmi_ddv_query_string(struct wmi_device *wdev, enum dell_ddv_method method,
228 u32 arg, union acpi_object **result)
229{
230 return dell_wmi_ddv_query_type(wdev, method, arg, result, ACPI_TYPE_STRING);
231}
232
233/*
234 * Needs to be called with lock held, except during initialization.
235 */
236static int dell_wmi_ddv_update_sensors(struct wmi_device *wdev, enum dell_ddv_method method,
237 struct dell_wmi_ddv_sensors *sensors, size_t entry_size)
238{
239 u64 buffer_size, rem, entries;
240 union acpi_object *obj;
241 u8 *buffer;
242 int ret;
243
244 if (sensors->obj) {
245 if (time_before(jiffies, sensors->timestamp + HZ))
246 return 0;
247
248 kfree(objp: sensors->obj);
249 sensors->obj = NULL;
250 }
251
252 ret = dell_wmi_ddv_query_buffer(wdev, method, arg: 0, result: &obj);
253 if (ret < 0)
254 return ret;
255
256 /* buffer format sanity check */
257 buffer_size = obj->package.elements[0].integer.value;
258 buffer = obj->package.elements[1].buffer.pointer;
259 entries = div64_u64_rem(dividend: buffer_size, divisor: entry_size, remainder: &rem);
260 if (rem != 1 || buffer[buffer_size - 1] != 0xff) {
261 ret = -ENOMSG;
262 goto err_free;
263 }
264
265 if (!entries) {
266 ret = -ENODATA;
267 goto err_free;
268 }
269
270 sensors->obj = obj;
271 sensors->entries = entries;
272 sensors->timestamp = jiffies;
273
274 return 0;
275
276err_free:
277 kfree(objp: obj);
278
279 return ret;
280}
281
282static umode_t dell_wmi_ddv_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr,
283 int channel)
284{
285 return 0444;
286}
287
288static int dell_wmi_ddv_fan_read_channel(struct dell_wmi_ddv_data *data, u32 attr, int channel,
289 long *val)
290{
291 struct fan_sensor_entry *entry;
292 int ret;
293
294 ret = dell_wmi_ddv_update_sensors(wdev: data->wdev, method: DELL_DDV_FAN_SENSOR_INFORMATION,
295 sensors: &data->fans, entry_size: sizeof(*entry));
296 if (ret < 0)
297 return ret;
298
299 if (channel >= data->fans.entries)
300 return -ENXIO;
301
302 entry = (struct fan_sensor_entry *)data->fans.obj->package.elements[1].buffer.pointer;
303 switch (attr) {
304 case hwmon_fan_input:
305 *val = get_unaligned_le16(p: &entry[channel].rpm);
306 return 0;
307 default:
308 break;
309 }
310
311 return -EOPNOTSUPP;
312}
313
314static int dell_wmi_ddv_temp_read_channel(struct dell_wmi_ddv_data *data, u32 attr, int channel,
315 long *val)
316{
317 struct thermal_sensor_entry *entry;
318 int ret;
319
320 ret = dell_wmi_ddv_update_sensors(wdev: data->wdev, method: DELL_DDV_THERMAL_SENSOR_INFORMATION,
321 sensors: &data->temps, entry_size: sizeof(*entry));
322 if (ret < 0)
323 return ret;
324
325 if (channel >= data->temps.entries)
326 return -ENXIO;
327
328 entry = (struct thermal_sensor_entry *)data->temps.obj->package.elements[1].buffer.pointer;
329 switch (attr) {
330 case hwmon_temp_input:
331 *val = entry[channel].now * 1000;
332 return 0;
333 case hwmon_temp_min:
334 *val = entry[channel].min * 1000;
335 return 0;
336 case hwmon_temp_max:
337 *val = entry[channel].max * 1000;
338 return 0;
339 default:
340 break;
341 }
342
343 return -EOPNOTSUPP;
344}
345
346static int dell_wmi_ddv_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
347 int channel, long *val)
348{
349 struct dell_wmi_ddv_data *data = dev_get_drvdata(dev);
350 int ret;
351
352 switch (type) {
353 case hwmon_fan:
354 mutex_lock(&data->fans.lock);
355 ret = dell_wmi_ddv_fan_read_channel(data, attr, channel, val);
356 mutex_unlock(lock: &data->fans.lock);
357 return ret;
358 case hwmon_temp:
359 mutex_lock(&data->temps.lock);
360 ret = dell_wmi_ddv_temp_read_channel(data, attr, channel, val);
361 mutex_unlock(lock: &data->temps.lock);
362 return ret;
363 default:
364 break;
365 }
366
367 return -EOPNOTSUPP;
368}
369
370static int dell_wmi_ddv_fan_read_string(struct dell_wmi_ddv_data *data, int channel,
371 const char **str)
372{
373 struct fan_sensor_entry *entry;
374 int ret;
375 u8 type;
376
377 ret = dell_wmi_ddv_update_sensors(wdev: data->wdev, method: DELL_DDV_FAN_SENSOR_INFORMATION,
378 sensors: &data->fans, entry_size: sizeof(*entry));
379 if (ret < 0)
380 return ret;
381
382 if (channel >= data->fans.entries)
383 return -ENXIO;
384
385 entry = (struct fan_sensor_entry *)data->fans.obj->package.elements[1].buffer.pointer;
386 type = entry[channel].type;
387 switch (type) {
388 case 0x00 ... 0x07:
389 *str = fan_labels[type];
390 break;
391 case 0x11 ... 0x14:
392 *str = fan_dock_labels[type - 0x11];
393 break;
394 default:
395 *str = "Unknown Fan";
396 break;
397 }
398
399 return 0;
400}
401
402static int dell_wmi_ddv_temp_read_string(struct dell_wmi_ddv_data *data, int channel,
403 const char **str)
404{
405 struct thermal_sensor_entry *entry;
406 int ret;
407
408 ret = dell_wmi_ddv_update_sensors(wdev: data->wdev, method: DELL_DDV_THERMAL_SENSOR_INFORMATION,
409 sensors: &data->temps, entry_size: sizeof(*entry));
410 if (ret < 0)
411 return ret;
412
413 if (channel >= data->temps.entries)
414 return -ENXIO;
415
416 entry = (struct thermal_sensor_entry *)data->temps.obj->package.elements[1].buffer.pointer;
417 switch (entry[channel].type) {
418 case 0x00:
419 *str = "CPU";
420 break;
421 case 0x11:
422 *str = "Video";
423 break;
424 case 0x22:
425 *str = "Memory"; /* sometimes called DIMM */
426 break;
427 case 0x33:
428 *str = "Other";
429 break;
430 case 0x44:
431 *str = "Ambient"; /* sometimes called SKIN */
432 break;
433 case 0x52:
434 *str = "SODIMM";
435 break;
436 case 0x55:
437 *str = "HDD";
438 break;
439 case 0x62:
440 *str = "SODIMM 2";
441 break;
442 case 0x73:
443 *str = "NB";
444 break;
445 case 0x83:
446 *str = "Charger";
447 break;
448 case 0xbb:
449 *str = "Memory 3";
450 break;
451 default:
452 *str = "Unknown";
453 break;
454 }
455
456 return 0;
457}
458
459static int dell_wmi_ddv_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
460 int channel, const char **str)
461{
462 struct dell_wmi_ddv_data *data = dev_get_drvdata(dev);
463 int ret;
464
465 switch (type) {
466 case hwmon_fan:
467 switch (attr) {
468 case hwmon_fan_label:
469 mutex_lock(&data->fans.lock);
470 ret = dell_wmi_ddv_fan_read_string(data, channel, str);
471 mutex_unlock(lock: &data->fans.lock);
472 return ret;
473 default:
474 break;
475 }
476 break;
477 case hwmon_temp:
478 switch (attr) {
479 case hwmon_temp_label:
480 mutex_lock(&data->temps.lock);
481 ret = dell_wmi_ddv_temp_read_string(data, channel, str);
482 mutex_unlock(lock: &data->temps.lock);
483 return ret;
484 default:
485 break;
486 }
487 break;
488 default:
489 break;
490 }
491
492 return -EOPNOTSUPP;
493}
494
495static const struct hwmon_ops dell_wmi_ddv_ops = {
496 .is_visible = dell_wmi_ddv_is_visible,
497 .read = dell_wmi_ddv_read,
498 .read_string = dell_wmi_ddv_read_string,
499};
500
501static struct hwmon_channel_info *dell_wmi_ddv_channel_create(struct device *dev, u64 count,
502 enum hwmon_sensor_types type,
503 u32 config)
504{
505 struct combined_channel_info *cinfo;
506 int i;
507
508 cinfo = devm_kzalloc(dev, struct_size(cinfo, config, count + 1), GFP_KERNEL);
509 if (!cinfo)
510 return ERR_PTR(error: -ENOMEM);
511
512 cinfo->info.type = type;
513 cinfo->info.config = cinfo->config;
514
515 for (i = 0; i < count; i++)
516 cinfo->config[i] = config;
517
518 return &cinfo->info;
519}
520
521static void dell_wmi_ddv_hwmon_cache_invalidate(struct dell_wmi_ddv_sensors *sensors)
522{
523 if (!sensors->active)
524 return;
525
526 mutex_lock(&sensors->lock);
527 kfree(objp: sensors->obj);
528 sensors->obj = NULL;
529 mutex_unlock(lock: &sensors->lock);
530}
531
532static void dell_wmi_ddv_hwmon_cache_destroy(void *data)
533{
534 struct dell_wmi_ddv_sensors *sensors = data;
535
536 sensors->active = false;
537 mutex_destroy(lock: &sensors->lock);
538 kfree(objp: sensors->obj);
539}
540
541static struct hwmon_channel_info *dell_wmi_ddv_channel_init(struct wmi_device *wdev,
542 enum dell_ddv_method method,
543 struct dell_wmi_ddv_sensors *sensors,
544 size_t entry_size,
545 enum hwmon_sensor_types type,
546 u32 config)
547{
548 struct hwmon_channel_info *info;
549 int ret;
550
551 ret = dell_wmi_ddv_update_sensors(wdev, method, sensors, entry_size);
552 if (ret < 0)
553 return ERR_PTR(error: ret);
554
555 mutex_init(&sensors->lock);
556 sensors->active = true;
557
558 ret = devm_add_action_or_reset(&wdev->dev, dell_wmi_ddv_hwmon_cache_destroy, sensors);
559 if (ret < 0)
560 return ERR_PTR(error: ret);
561
562 info = dell_wmi_ddv_channel_create(dev: &wdev->dev, count: sensors->entries, type, config);
563 if (IS_ERR(ptr: info))
564 devm_release_action(dev: &wdev->dev, action: dell_wmi_ddv_hwmon_cache_destroy, data: sensors);
565
566 return info;
567}
568
569static int dell_wmi_ddv_hwmon_add(struct dell_wmi_ddv_data *data)
570{
571 struct wmi_device *wdev = data->wdev;
572 struct combined_chip_info *cinfo;
573 struct hwmon_channel_info *info;
574 struct device *hdev;
575 int index = 0;
576 int ret;
577
578 if (!devres_open_group(dev: &wdev->dev, id: dell_wmi_ddv_hwmon_add, GFP_KERNEL))
579 return -ENOMEM;
580
581 cinfo = devm_kzalloc(dev: &wdev->dev, struct_size(cinfo, info, 4), GFP_KERNEL);
582 if (!cinfo) {
583 ret = -ENOMEM;
584
585 goto err_release;
586 }
587
588 cinfo->chip.ops = &dell_wmi_ddv_ops;
589 cinfo->chip.info = cinfo->info;
590
591 info = dell_wmi_ddv_channel_create(dev: &wdev->dev, count: 1, type: hwmon_chip, HWMON_C_REGISTER_TZ);
592 if (IS_ERR(ptr: info)) {
593 ret = PTR_ERR(ptr: info);
594
595 goto err_release;
596 }
597
598 cinfo->info[index] = info;
599 index++;
600
601 info = dell_wmi_ddv_channel_init(wdev, method: DELL_DDV_FAN_SENSOR_INFORMATION, sensors: &data->fans,
602 entry_size: sizeof(struct fan_sensor_entry), type: hwmon_fan,
603 config: (HWMON_F_INPUT | HWMON_F_LABEL));
604 if (!IS_ERR(ptr: info)) {
605 cinfo->info[index] = info;
606 index++;
607 }
608
609 info = dell_wmi_ddv_channel_init(wdev, method: DELL_DDV_THERMAL_SENSOR_INFORMATION, sensors: &data->temps,
610 entry_size: sizeof(struct thermal_sensor_entry), type: hwmon_temp,
611 config: (HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
612 HWMON_T_LABEL));
613 if (!IS_ERR(ptr: info)) {
614 cinfo->info[index] = info;
615 index++;
616 }
617
618 if (index < 2) {
619 /* Finding no available sensors is not an error */
620 ret = 0;
621
622 goto err_release;
623 }
624
625 hdev = devm_hwmon_device_register_with_info(dev: &wdev->dev, name: "dell_ddv", drvdata: data, info: &cinfo->chip,
626 NULL);
627 if (IS_ERR(ptr: hdev)) {
628 ret = PTR_ERR(ptr: hdev);
629
630 goto err_release;
631 }
632
633 devres_close_group(dev: &wdev->dev, id: dell_wmi_ddv_hwmon_add);
634
635 return 0;
636
637err_release:
638 devres_release_group(dev: &wdev->dev, id: dell_wmi_ddv_hwmon_add);
639
640 return ret;
641}
642
643static int dell_wmi_ddv_battery_index(struct acpi_device *acpi_dev, u32 *index)
644{
645 const char *uid_str;
646
647 uid_str = acpi_device_uid(acpi_dev);
648 if (!uid_str)
649 return -ENODEV;
650
651 return kstrtou32(s: uid_str, base: 10, res: index);
652}
653
654static ssize_t temp_show(struct device *dev, struct device_attribute *attr, char *buf)
655{
656 struct dell_wmi_ddv_data *data = container_of(attr, struct dell_wmi_ddv_data, temp_attr);
657 u32 index, value;
658 int ret;
659
660 ret = dell_wmi_ddv_battery_index(to_acpi_device(dev->parent), index: &index);
661 if (ret < 0)
662 return ret;
663
664 ret = dell_wmi_ddv_query_integer(wdev: data->wdev, method: DELL_DDV_BATTERY_TEMPERATURE, arg: index, res: &value);
665 if (ret < 0)
666 return ret;
667
668 /* Use 2731 instead of 2731.5 to avoid unnecessary rounding */
669 return sysfs_emit(buf, fmt: "%d\n", value - 2731);
670}
671
672static ssize_t eppid_show(struct device *dev, struct device_attribute *attr, char *buf)
673{
674 struct dell_wmi_ddv_data *data = container_of(attr, struct dell_wmi_ddv_data, eppid_attr);
675 union acpi_object *obj;
676 u32 index;
677 int ret;
678
679 ret = dell_wmi_ddv_battery_index(to_acpi_device(dev->parent), index: &index);
680 if (ret < 0)
681 return ret;
682
683 ret = dell_wmi_ddv_query_string(wdev: data->wdev, method: DELL_DDV_BATTERY_EPPID, arg: index, result: &obj);
684 if (ret < 0)
685 return ret;
686
687 if (obj->string.length != DELL_EPPID_LENGTH && obj->string.length != DELL_EPPID_EXT_LENGTH)
688 dev_info_once(&data->wdev->dev, FW_INFO "Suspicious ePPID length (%d)\n",
689 obj->string.length);
690
691 ret = sysfs_emit(buf, fmt: "%s\n", obj->string.pointer);
692
693 kfree(objp: obj);
694
695 return ret;
696}
697
698static int dell_wmi_ddv_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
699{
700 struct dell_wmi_ddv_data *data = container_of(hook, struct dell_wmi_ddv_data, hook);
701 u32 index;
702 int ret;
703
704 /* Return 0 instead of error to avoid being unloaded */
705 ret = dell_wmi_ddv_battery_index(to_acpi_device(battery->dev.parent), index: &index);
706 if (ret < 0)
707 return 0;
708
709 ret = device_create_file(device: &battery->dev, entry: &data->temp_attr);
710 if (ret < 0)
711 return ret;
712
713 ret = device_create_file(device: &battery->dev, entry: &data->eppid_attr);
714 if (ret < 0) {
715 device_remove_file(dev: &battery->dev, attr: &data->temp_attr);
716
717 return ret;
718 }
719
720 return 0;
721}
722
723static int dell_wmi_ddv_remove_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
724{
725 struct dell_wmi_ddv_data *data = container_of(hook, struct dell_wmi_ddv_data, hook);
726
727 device_remove_file(dev: &battery->dev, attr: &data->temp_attr);
728 device_remove_file(dev: &battery->dev, attr: &data->eppid_attr);
729
730 return 0;
731}
732
733static void dell_wmi_ddv_battery_remove(void *data)
734{
735 struct acpi_battery_hook *hook = data;
736
737 battery_hook_unregister(hook);
738}
739
740static int dell_wmi_ddv_battery_add(struct dell_wmi_ddv_data *data)
741{
742 data->hook.name = "Dell DDV Battery Extension";
743 data->hook.add_battery = dell_wmi_ddv_add_battery;
744 data->hook.remove_battery = dell_wmi_ddv_remove_battery;
745
746 sysfs_attr_init(&data->temp_attr.attr);
747 data->temp_attr.attr.name = "temp";
748 data->temp_attr.attr.mode = 0444;
749 data->temp_attr.show = temp_show;
750
751 sysfs_attr_init(&data->eppid_attr.attr);
752 data->eppid_attr.attr.name = "eppid";
753 data->eppid_attr.attr.mode = 0444;
754 data->eppid_attr.show = eppid_show;
755
756 battery_hook_register(hook: &data->hook);
757
758 return devm_add_action_or_reset(&data->wdev->dev, dell_wmi_ddv_battery_remove, &data->hook);
759}
760
761static int dell_wmi_ddv_buffer_read(struct seq_file *seq, enum dell_ddv_method method)
762{
763 struct device *dev = seq->private;
764 struct dell_wmi_ddv_data *data = dev_get_drvdata(dev);
765 union acpi_object *obj;
766 u64 size;
767 u8 *buf;
768 int ret;
769
770 ret = dell_wmi_ddv_query_buffer(wdev: data->wdev, method, arg: 0, result: &obj);
771 if (ret < 0)
772 return ret;
773
774 size = obj->package.elements[0].integer.value;
775 buf = obj->package.elements[1].buffer.pointer;
776 ret = seq_write(seq, data: buf, len: size);
777 kfree(objp: obj);
778
779 return ret;
780}
781
782static int dell_wmi_ddv_fan_read(struct seq_file *seq, void *offset)
783{
784 return dell_wmi_ddv_buffer_read(seq, method: DELL_DDV_FAN_SENSOR_INFORMATION);
785}
786
787static int dell_wmi_ddv_temp_read(struct seq_file *seq, void *offset)
788{
789 return dell_wmi_ddv_buffer_read(seq, method: DELL_DDV_THERMAL_SENSOR_INFORMATION);
790}
791
792static void dell_wmi_ddv_debugfs_remove(void *data)
793{
794 struct dentry *entry = data;
795
796 debugfs_remove(dentry: entry);
797}
798
799static void dell_wmi_ddv_debugfs_init(struct wmi_device *wdev)
800{
801 struct dentry *entry;
802 char name[64];
803
804 scnprintf(buf: name, ARRAY_SIZE(name), fmt: "%s-%s", DRIVER_NAME, dev_name(dev: &wdev->dev));
805 entry = debugfs_create_dir(name, NULL);
806
807 debugfs_create_devm_seqfile(dev: &wdev->dev, name: "fan_sensor_information", parent: entry,
808 read_fn: dell_wmi_ddv_fan_read);
809 debugfs_create_devm_seqfile(dev: &wdev->dev, name: "thermal_sensor_information", parent: entry,
810 read_fn: dell_wmi_ddv_temp_read);
811
812 devm_add_action_or_reset(&wdev->dev, dell_wmi_ddv_debugfs_remove, entry);
813}
814
815static int dell_wmi_ddv_probe(struct wmi_device *wdev, const void *context)
816{
817 struct dell_wmi_ddv_data *data;
818 u32 version;
819 int ret;
820
821 ret = dell_wmi_ddv_query_integer(wdev, method: DELL_DDV_INTERFACE_VERSION, arg: 0, res: &version);
822 if (ret < 0)
823 return ret;
824
825 dev_dbg(&wdev->dev, "WMI interface version: %d\n", version);
826 if (version < DELL_DDV_SUPPORTED_VERSION_MIN || version > DELL_DDV_SUPPORTED_VERSION_MAX) {
827 if (!force)
828 return -ENODEV;
829
830 dev_warn(&wdev->dev, "Loading despite unsupported WMI interface version (%u)\n",
831 version);
832 }
833
834 data = devm_kzalloc(dev: &wdev->dev, size: sizeof(*data), GFP_KERNEL);
835 if (!data)
836 return -ENOMEM;
837
838 dev_set_drvdata(dev: &wdev->dev, data);
839 data->wdev = wdev;
840
841 dell_wmi_ddv_debugfs_init(wdev);
842
843 if (IS_REACHABLE(CONFIG_ACPI_BATTERY)) {
844 ret = dell_wmi_ddv_battery_add(data);
845 if (ret < 0)
846 dev_warn(&wdev->dev, "Unable to register ACPI battery hook: %d\n", ret);
847 }
848
849 if (IS_REACHABLE(CONFIG_HWMON)) {
850 ret = dell_wmi_ddv_hwmon_add(data);
851 if (ret < 0)
852 dev_warn(&wdev->dev, "Unable to register hwmon interface: %d\n", ret);
853 }
854
855 return 0;
856}
857
858static int dell_wmi_ddv_resume(struct device *dev)
859{
860 struct dell_wmi_ddv_data *data = dev_get_drvdata(dev);
861
862 /* Force re-reading of all active sensors */
863 dell_wmi_ddv_hwmon_cache_invalidate(sensors: &data->fans);
864 dell_wmi_ddv_hwmon_cache_invalidate(sensors: &data->temps);
865
866 return 0;
867}
868
869static DEFINE_SIMPLE_DEV_PM_OPS(dell_wmi_ddv_dev_pm_ops, NULL, dell_wmi_ddv_resume);
870
871static const struct wmi_device_id dell_wmi_ddv_id_table[] = {
872 { DELL_DDV_GUID, NULL },
873 { }
874};
875MODULE_DEVICE_TABLE(wmi, dell_wmi_ddv_id_table);
876
877static struct wmi_driver dell_wmi_ddv_driver = {
878 .driver = {
879 .name = DRIVER_NAME,
880 .probe_type = PROBE_PREFER_ASYNCHRONOUS,
881 .pm = pm_sleep_ptr(&dell_wmi_ddv_dev_pm_ops),
882 },
883 .id_table = dell_wmi_ddv_id_table,
884 .probe = dell_wmi_ddv_probe,
885 .no_singleton = true,
886};
887module_wmi_driver(dell_wmi_ddv_driver);
888
889MODULE_AUTHOR("Armin Wolf <W_Armin@gmx.de>");
890MODULE_DESCRIPTION("Dell WMI sensor driver");
891MODULE_LICENSE("GPL");
892

source code of linux/drivers/platform/x86/dell/dell-wmi-ddv.c