1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * HWMON driver for ASUS motherboards that provides sensor readouts via WMI
4 * interface present in the UEFI of the X370/X470/B450/X399 Ryzen motherboards.
6 * Copyright (C) 2018-2019 Ed Brindley <kernel@maidavale.org>
8 * WMI interface provides:
24 * - Chassis Fan 1 RPM,
25 * - Chassis Fan 2 RPM,
26 * - Chassis Fan 3 RPM,
33 * - CPU Socket Temperature,
34 * - Motherboard Temperature,
35 * - Chipset Temperature,
36 * - Tsensor 1 Temperature,
37 * - CPU VRM Temperature,
40 * - CPU VRM Output Current.
43 #include <linux/acpi.h>
44 #include <linux/dmi.h>
45 #include <linux/hwmon.h>
46 #include <linux/init.h>
47 #include <linux/jiffies.h>
48 #include <linux/kernel.h>
49 #include <linux/module.h>
50 #include <linux/mutex.h>
51 #include <linux/units.h>
52 #include <linux/wmi.h>
54 #define ASUSWMI_MONITORING_GUID "466747A0-70EC-11DE-8A39-0800200C9A66"
55 #define ASUSWMI_METHODID_GET_VALUE 0x52574543 /* RWEC */
56 #define ASUSWMI_METHODID_UPDATE_BUFFER 0x51574543 /* QWEC */
57 #define ASUSWMI_METHODID_GET_INFO 0x50574543 /* PWEC */
58 #define ASUSWMI_METHODID_GET_NUMBER 0x50574572 /* PWEr */
59 #define ASUSWMI_METHODID_GET_VERSION 0x50574574 /* PWEt */
61 #define ASUS_WMI_MAX_STR_SIZE 32
63 #define DMI_EXACT_MATCH_ASUS_BOARD_NAME(name) { \
65 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), \
66 DMI_EXACT_MATCH(DMI_BOARD_NAME, name), \
70 static const struct dmi_system_id asus_wmi_dmi_table[] = {
71 DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X399-A"),
72 DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X470-PRO"),
73 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VI EXTREME"),
74 DMI_EXACT_MATCH_ASUS_BOARD_NAME("CROSSHAIR VI HERO"),
75 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VI HERO (WI-FI AC)"),
76 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VII HERO"),
77 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VII HERO (WI-FI)"),
78 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B450-E GAMING"),
79 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B450-F GAMING"),
80 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B450-F GAMING II"),
81 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B450-I GAMING"),
82 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X399-E GAMING"),
83 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X470-F GAMING"),
84 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X470-I GAMING"),
85 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG ZENITH EXTREME"),
86 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG ZENITH EXTREME ALPHA"),
89 MODULE_DEVICE_TABLE(dmi, asus_wmi_dmi_table);
91 enum asus_wmi_sensor_class {
99 enum asus_wmi_location {
116 enum asus_wmi_source {
121 static enum hwmon_sensor_types asus_data_types[] = {
122 [VOLTAGE] = hwmon_in,
123 [TEMPERATURE_C] = hwmon_temp,
124 [FAN_RPM] = hwmon_fan,
125 [CURRENT] = hwmon_curr,
126 [WATER_FLOW] = hwmon_fan,
129 static u32 hwmon_attributes[hwmon_max] = {
130 [hwmon_chip] = HWMON_C_REGISTER_TZ,
131 [hwmon_temp] = HWMON_T_INPUT | HWMON_T_LABEL,
132 [hwmon_in] = HWMON_I_INPUT | HWMON_I_LABEL,
133 [hwmon_curr] = HWMON_C_INPUT | HWMON_C_LABEL,
134 [hwmon_fan] = HWMON_F_INPUT | HWMON_F_LABEL,
138 * struct asus_wmi_sensor_info - sensor info.
140 * @data_type: sensor class e.g. voltage, temp etc.
141 * @location: sensor location.
142 * @name: sensor name.
143 * @source: sensor source.
144 * @type: sensor type signed, unsigned etc.
145 * @cached_value: cached sensor value.
147 struct asus_wmi_sensor_info {
151 char name[ASUS_WMI_MAX_STR_SIZE];
157 struct asus_wmi_wmi_info {
158 unsigned long source_last_updated[3]; /* in jiffies */
161 const struct asus_wmi_sensor_info **info[hwmon_max];
162 struct asus_wmi_sensor_info **info_by_id;
165 struct asus_wmi_sensors {
166 struct asus_wmi_wmi_info wmi;
167 /* lock access to internal cache */
172 * Universal method for calling WMI method
174 static int asus_wmi_call_method(u32 method_id, u32 *args, struct acpi_buffer *output)
176 struct acpi_buffer input = {(acpi_size) sizeof(*args), args };
179 status = wmi_evaluate_method(ASUSWMI_MONITORING_GUID, 0,
180 method_id, &input, output);
181 if (ACPI_FAILURE(status))
188 * Gets the version of the ASUS sensors interface implemented
190 static int asus_wmi_get_version(u32 *version)
192 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
193 u32 args[] = {0, 0, 0};
194 union acpi_object *obj;
197 err = asus_wmi_call_method(ASUSWMI_METHODID_GET_VERSION, args, &output);
201 obj = output.pointer;
205 if (obj->type != ACPI_TYPE_INTEGER) {
211 *version = obj->integer.value;
219 * Gets the number of sensor items
221 static int asus_wmi_get_item_count(u32 *count)
223 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
224 u32 args[] = {0, 0, 0};
225 union acpi_object *obj;
228 err = asus_wmi_call_method(ASUSWMI_METHODID_GET_NUMBER, args, &output);
232 obj = output.pointer;
236 if (obj->type != ACPI_TYPE_INTEGER) {
242 *count = obj->integer.value;
249 static int asus_wmi_hwmon_add_chan_info(struct hwmon_channel_info *asus_wmi_hwmon_chan,
250 struct device *dev, int num,
251 enum hwmon_sensor_types type, u32 config)
255 cfg = devm_kcalloc(dev, num + 1, sizeof(*cfg), GFP_KERNEL);
259 asus_wmi_hwmon_chan->type = type;
260 asus_wmi_hwmon_chan->config = cfg;
261 memset32(cfg, config, num);
267 * For a given sensor item returns details e.g. type (voltage/temperature/fan speed etc), bank etc
269 static int asus_wmi_sensor_info(int index, struct asus_wmi_sensor_info *s)
271 union acpi_object name_obj, data_type_obj, location_obj, source_obj, type_obj;
272 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
273 u32 args[] = {index, 0};
274 union acpi_object *obj;
277 err = asus_wmi_call_method(ASUSWMI_METHODID_GET_INFO, args, &output);
283 obj = output.pointer;
287 if (obj->type != ACPI_TYPE_PACKAGE) {
292 if (obj->package.count != 5) {
297 name_obj = obj->package.elements[0];
298 if (name_obj.type != ACPI_TYPE_STRING) {
303 strncpy(s->name, name_obj.string.pointer, sizeof(s->name) - 1);
305 data_type_obj = obj->package.elements[1];
306 if (data_type_obj.type != ACPI_TYPE_INTEGER) {
311 s->data_type = data_type_obj.integer.value;
313 location_obj = obj->package.elements[2];
314 if (location_obj.type != ACPI_TYPE_INTEGER) {
319 s->location = location_obj.integer.value;
321 source_obj = obj->package.elements[3];
322 if (source_obj.type != ACPI_TYPE_INTEGER) {
327 s->source = source_obj.integer.value;
329 type_obj = obj->package.elements[4];
330 if (type_obj.type != ACPI_TYPE_INTEGER) {
336 s->type = type_obj.integer.value;
343 static int asus_wmi_update_buffer(int source)
345 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
346 u32 args[] = {source, 0};
348 return asus_wmi_call_method(ASUSWMI_METHODID_UPDATE_BUFFER, args, &output);
351 static int asus_wmi_get_sensor_value(u8 index, long *value)
353 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
354 u32 args[] = {index, 0};
355 union acpi_object *obj;
358 err = asus_wmi_call_method(ASUSWMI_METHODID_GET_VALUE, args, &output);
362 obj = output.pointer;
366 if (obj->type != ACPI_TYPE_INTEGER) {
372 *value = obj->integer.value;
379 static int asus_wmi_update_values_for_source(u8 source, struct asus_wmi_sensors *sensor_data)
381 struct asus_wmi_sensor_info *sensor;
386 for (i = 0; i < sensor_data->wmi.sensor_count; i++) {
387 sensor = sensor_data->wmi.info_by_id[i];
388 if (sensor && sensor->source == source) {
389 ret = asus_wmi_get_sensor_value(sensor->id, &value);
393 sensor->cached_value = value;
400 static int asus_wmi_scale_sensor_value(u32 value, int data_type)
402 /* FAN_RPM and WATER_FLOW don't need scaling */
405 /* value in microVolts */
406 return DIV_ROUND_CLOSEST(value, KILO);
408 /* value in Celsius */
409 return value * MILLIDEGREE_PER_DEGREE;
411 /* value in Amperes */
412 return value * MILLI;
417 static int asus_wmi_get_cached_value_or_update(const struct asus_wmi_sensor_info *sensor,
418 struct asus_wmi_sensors *sensor_data,
423 mutex_lock(&sensor_data->lock);
425 if (time_after(jiffies, sensor_data->wmi.source_last_updated[sensor->source] + HZ)) {
426 ret = asus_wmi_update_buffer(sensor->source);
430 ret = asus_wmi_update_values_for_source(sensor->source, sensor_data);
434 sensor_data->wmi.source_last_updated[sensor->source] = jiffies;
437 *value = sensor->cached_value;
440 mutex_unlock(&sensor_data->lock);
445 /* Now follow the functions that implement the hwmon interface */
446 static int asus_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
447 u32 attr, int channel, long *val)
449 const struct asus_wmi_sensor_info *sensor;
453 struct asus_wmi_sensors *sensor_data = dev_get_drvdata(dev);
455 sensor = *(sensor_data->wmi.info[type] + channel);
457 ret = asus_wmi_get_cached_value_or_update(sensor, sensor_data, &value);
461 *val = asus_wmi_scale_sensor_value(value, sensor->data_type);
466 static int asus_wmi_hwmon_read_string(struct device *dev,
467 enum hwmon_sensor_types type, u32 attr,
468 int channel, const char **str)
470 struct asus_wmi_sensors *sensor_data = dev_get_drvdata(dev);
471 const struct asus_wmi_sensor_info *sensor;
473 sensor = *(sensor_data->wmi.info[type] + channel);
479 static umode_t asus_wmi_hwmon_is_visible(const void *drvdata,
480 enum hwmon_sensor_types type, u32 attr,
483 const struct asus_wmi_sensors *sensor_data = drvdata;
484 const struct asus_wmi_sensor_info *sensor;
486 sensor = *(sensor_data->wmi.info[type] + channel);
493 static const struct hwmon_ops asus_wmi_hwmon_ops = {
494 .is_visible = asus_wmi_hwmon_is_visible,
495 .read = asus_wmi_hwmon_read,
496 .read_string = asus_wmi_hwmon_read_string,
499 static struct hwmon_chip_info asus_wmi_chip_info = {
500 .ops = &asus_wmi_hwmon_ops,
504 static int asus_wmi_configure_sensor_setup(struct device *dev,
505 struct asus_wmi_sensors *sensor_data)
507 const struct hwmon_channel_info **ptr_asus_wmi_ci;
508 struct hwmon_channel_info *asus_wmi_hwmon_chan;
509 int nr_count[hwmon_max] = {}, nr_types = 0;
510 struct asus_wmi_sensor_info *temp_sensor;
511 const struct hwmon_chip_info *chip_info;
512 enum hwmon_sensor_types type;
513 struct device *hwdev;
517 for (i = 0; i < sensor_data->wmi.sensor_count; i++) {
518 struct asus_wmi_sensor_info sensor;
520 err = asus_wmi_sensor_info(i, &sensor);
524 switch (sensor.data_type) {
530 type = asus_data_types[sensor.data_type];
538 if (nr_count[hwmon_temp])
539 nr_count[hwmon_chip]++, nr_types++;
541 asus_wmi_hwmon_chan = devm_kcalloc(dev, nr_types,
542 sizeof(*asus_wmi_hwmon_chan),
544 if (!asus_wmi_hwmon_chan)
547 ptr_asus_wmi_ci = devm_kcalloc(dev, nr_types + 1,
548 sizeof(*ptr_asus_wmi_ci), GFP_KERNEL);
549 if (!ptr_asus_wmi_ci)
552 asus_wmi_chip_info.info = ptr_asus_wmi_ci;
553 chip_info = &asus_wmi_chip_info;
555 sensor_data->wmi.info_by_id = devm_kcalloc(dev, sensor_data->wmi.sensor_count,
556 sizeof(*sensor_data->wmi.info_by_id),
559 if (!sensor_data->wmi.info_by_id)
562 for (type = 0; type < hwmon_max; type++) {
566 err = asus_wmi_hwmon_add_chan_info(asus_wmi_hwmon_chan, dev,
567 nr_count[type], type,
568 hwmon_attributes[type]);
572 *ptr_asus_wmi_ci++ = asus_wmi_hwmon_chan++;
574 sensor_data->wmi.info[type] = devm_kcalloc(dev,
576 sizeof(*sensor_data->wmi.info),
578 if (!sensor_data->wmi.info[type])
582 for (i = sensor_data->wmi.sensor_count - 1; i >= 0; i--) {
583 temp_sensor = devm_kzalloc(dev, sizeof(*temp_sensor), GFP_KERNEL);
587 err = asus_wmi_sensor_info(i, temp_sensor);
591 switch (temp_sensor->data_type) {
597 type = asus_data_types[temp_sensor->data_type];
598 idx = --nr_count[type];
599 *(sensor_data->wmi.info[type] + idx) = temp_sensor;
600 sensor_data->wmi.info_by_id[i] = temp_sensor;
605 dev_dbg(dev, "board has %d sensors",
606 sensor_data->wmi.sensor_count);
608 hwdev = devm_hwmon_device_register_with_info(dev, "asus_wmi_sensors",
609 sensor_data, chip_info, NULL);
611 return PTR_ERR_OR_ZERO(hwdev);
614 static int asus_wmi_probe(struct wmi_device *wdev, const void *context)
616 struct asus_wmi_sensors *sensor_data;
617 struct device *dev = &wdev->dev;
620 if (!dmi_check_system(asus_wmi_dmi_table))
623 sensor_data = devm_kzalloc(dev, sizeof(*sensor_data), GFP_KERNEL);
627 if (asus_wmi_get_version(&version))
630 if (asus_wmi_get_item_count(&sensor_data->wmi.sensor_count))
633 if (sensor_data->wmi.sensor_count <= 0 || version < 2) {
634 dev_info(dev, "version: %u with %d sensors is unsupported\n",
635 version, sensor_data->wmi.sensor_count);
640 mutex_init(&sensor_data->lock);
642 dev_set_drvdata(dev, sensor_data);
644 return asus_wmi_configure_sensor_setup(dev, sensor_data);
647 static const struct wmi_device_id asus_wmi_id_table[] = {
648 { ASUSWMI_MONITORING_GUID, NULL },
652 static struct wmi_driver asus_sensors_wmi_driver = {
654 .name = "asus_wmi_sensors",
656 .id_table = asus_wmi_id_table,
657 .probe = asus_wmi_probe,
659 module_wmi_driver(asus_sensors_wmi_driver);
661 MODULE_AUTHOR("Ed Brindley <kernel@maidavale.org>");
662 MODULE_DESCRIPTION("Asus WMI Sensors Driver");
663 MODULE_LICENSE("GPL");