asus_wmi_sensors.c (15779B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 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. 5 * 6 * Copyright (C) 2018-2019 Ed Brindley <kernel@maidavale.org> 7 * 8 * WMI interface provides: 9 * - CPU Core Voltage, 10 * - CPU SOC Voltage, 11 * - DRAM Voltage, 12 * - VDDP Voltage, 13 * - 1.8V PLL Voltage, 14 * - +12V Voltage, 15 * - +5V Voltage, 16 * - 3VSB Voltage, 17 * - VBAT Voltage, 18 * - AVCC3 Voltage, 19 * - SB 1.05V Voltage, 20 * - CPU Core Voltage, 21 * - CPU SOC Voltage, 22 * - DRAM Voltage, 23 * - CPU Fan RPM, 24 * - Chassis Fan 1 RPM, 25 * - Chassis Fan 2 RPM, 26 * - Chassis Fan 3 RPM, 27 * - HAMP Fan RPM, 28 * - Water Pump RPM, 29 * - CPU OPT RPM, 30 * - Water Flow RPM, 31 * - AIO Pump RPM, 32 * - CPU Temperature, 33 * - CPU Socket Temperature, 34 * - Motherboard Temperature, 35 * - Chipset Temperature, 36 * - Tsensor 1 Temperature, 37 * - CPU VRM Temperature, 38 * - Water In, 39 * - Water Out, 40 * - CPU VRM Output Current. 41 */ 42 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> 53 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 */ 60 61#define ASUS_WMI_MAX_STR_SIZE 32 62 63#define DMI_EXACT_MATCH_ASUS_BOARD_NAME(name) { \ 64 .matches = { \ 65 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), \ 66 DMI_EXACT_MATCH(DMI_BOARD_NAME, name), \ 67 }, \ 68} 69 70static 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"), 87 {} 88}; 89MODULE_DEVICE_TABLE(dmi, asus_wmi_dmi_table); 90 91enum asus_wmi_sensor_class { 92 VOLTAGE = 0x0, 93 TEMPERATURE_C = 0x1, 94 FAN_RPM = 0x2, 95 CURRENT = 0x3, 96 WATER_FLOW = 0x4, 97}; 98 99enum asus_wmi_location { 100 CPU = 0x0, 101 CPU_SOC = 0x1, 102 DRAM = 0x2, 103 MOTHERBOARD = 0x3, 104 CHIPSET = 0x4, 105 AUX = 0x5, 106 VRM = 0x6, 107 COOLER = 0x7 108}; 109 110enum asus_wmi_type { 111 SIGNED_INT = 0x0, 112 UNSIGNED_INT = 0x1, 113 SCALED = 0x3, 114}; 115 116enum asus_wmi_source { 117 SIO = 0x1, 118 EC = 0x2 119}; 120 121static 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, 127}; 128 129static 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, 135}; 136 137/** 138 * struct asus_wmi_sensor_info - sensor info. 139 * @id: sensor id. 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. 146 */ 147struct asus_wmi_sensor_info { 148 u32 id; 149 int data_type; 150 int location; 151 char name[ASUS_WMI_MAX_STR_SIZE]; 152 int source; 153 int type; 154 long cached_value; 155}; 156 157struct asus_wmi_wmi_info { 158 unsigned long source_last_updated[3]; /* in jiffies */ 159 int sensor_count; 160 161 const struct asus_wmi_sensor_info **info[hwmon_max]; 162 struct asus_wmi_sensor_info **info_by_id; 163}; 164 165struct asus_wmi_sensors { 166 struct asus_wmi_wmi_info wmi; 167 /* lock access to internal cache */ 168 struct mutex lock; 169}; 170 171/* 172 * Universal method for calling WMI method 173 */ 174static int asus_wmi_call_method(u32 method_id, u32 *args, struct acpi_buffer *output) 175{ 176 struct acpi_buffer input = {(acpi_size) sizeof(*args), args }; 177 acpi_status status; 178 179 status = wmi_evaluate_method(ASUSWMI_MONITORING_GUID, 0, 180 method_id, &input, output); 181 if (ACPI_FAILURE(status)) 182 return -EIO; 183 184 return 0; 185} 186 187/* 188 * Gets the version of the ASUS sensors interface implemented 189 */ 190static int asus_wmi_get_version(u32 *version) 191{ 192 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 193 u32 args[] = {0, 0, 0}; 194 union acpi_object *obj; 195 int err; 196 197 err = asus_wmi_call_method(ASUSWMI_METHODID_GET_VERSION, args, &output); 198 if (err) 199 return err; 200 201 obj = output.pointer; 202 if (!obj) 203 return -EIO; 204 205 if (obj->type != ACPI_TYPE_INTEGER) { 206 err = -EIO; 207 goto out_free_obj; 208 } 209 210 err = 0; 211 *version = obj->integer.value; 212 213out_free_obj: 214 ACPI_FREE(obj); 215 return err; 216} 217 218/* 219 * Gets the number of sensor items 220 */ 221static int asus_wmi_get_item_count(u32 *count) 222{ 223 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 224 u32 args[] = {0, 0, 0}; 225 union acpi_object *obj; 226 int err; 227 228 err = asus_wmi_call_method(ASUSWMI_METHODID_GET_NUMBER, args, &output); 229 if (err) 230 return err; 231 232 obj = output.pointer; 233 if (!obj) 234 return -EIO; 235 236 if (obj->type != ACPI_TYPE_INTEGER) { 237 err = -EIO; 238 goto out_free_obj; 239 } 240 241 err = 0; 242 *count = obj->integer.value; 243 244out_free_obj: 245 ACPI_FREE(obj); 246 return err; 247} 248 249static 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) 252{ 253 u32 *cfg; 254 255 cfg = devm_kcalloc(dev, num + 1, sizeof(*cfg), GFP_KERNEL); 256 if (!cfg) 257 return -ENOMEM; 258 259 asus_wmi_hwmon_chan->type = type; 260 asus_wmi_hwmon_chan->config = cfg; 261 memset32(cfg, config, num); 262 263 return 0; 264} 265 266/* 267 * For a given sensor item returns details e.g. type (voltage/temperature/fan speed etc), bank etc 268 */ 269static int asus_wmi_sensor_info(int index, struct asus_wmi_sensor_info *s) 270{ 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; 275 int err; 276 277 err = asus_wmi_call_method(ASUSWMI_METHODID_GET_INFO, args, &output); 278 if (err) 279 return err; 280 281 s->id = index; 282 283 obj = output.pointer; 284 if (!obj) 285 return -EIO; 286 287 if (obj->type != ACPI_TYPE_PACKAGE) { 288 err = -EIO; 289 goto out_free_obj; 290 } 291 292 if (obj->package.count != 5) { 293 err = -EIO; 294 goto out_free_obj; 295 } 296 297 name_obj = obj->package.elements[0]; 298 if (name_obj.type != ACPI_TYPE_STRING) { 299 err = -EIO; 300 goto out_free_obj; 301 } 302 303 strncpy(s->name, name_obj.string.pointer, sizeof(s->name) - 1); 304 305 data_type_obj = obj->package.elements[1]; 306 if (data_type_obj.type != ACPI_TYPE_INTEGER) { 307 err = -EIO; 308 goto out_free_obj; 309 } 310 311 s->data_type = data_type_obj.integer.value; 312 313 location_obj = obj->package.elements[2]; 314 if (location_obj.type != ACPI_TYPE_INTEGER) { 315 err = -EIO; 316 goto out_free_obj; 317 } 318 319 s->location = location_obj.integer.value; 320 321 source_obj = obj->package.elements[3]; 322 if (source_obj.type != ACPI_TYPE_INTEGER) { 323 err = -EIO; 324 goto out_free_obj; 325 } 326 327 s->source = source_obj.integer.value; 328 329 type_obj = obj->package.elements[4]; 330 if (type_obj.type != ACPI_TYPE_INTEGER) { 331 err = -EIO; 332 goto out_free_obj; 333 } 334 335 err = 0; 336 s->type = type_obj.integer.value; 337 338out_free_obj: 339 ACPI_FREE(obj); 340 return err; 341} 342 343static int asus_wmi_update_buffer(int source) 344{ 345 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 346 u32 args[] = {source, 0}; 347 348 return asus_wmi_call_method(ASUSWMI_METHODID_UPDATE_BUFFER, args, &output); 349} 350 351static int asus_wmi_get_sensor_value(u8 index, long *value) 352{ 353 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 354 u32 args[] = {index, 0}; 355 union acpi_object *obj; 356 int err; 357 358 err = asus_wmi_call_method(ASUSWMI_METHODID_GET_VALUE, args, &output); 359 if (err) 360 return err; 361 362 obj = output.pointer; 363 if (!obj) 364 return -EIO; 365 366 if (obj->type != ACPI_TYPE_INTEGER) { 367 err = -EIO; 368 goto out_free_obj; 369 } 370 371 err = 0; 372 *value = obj->integer.value; 373 374out_free_obj: 375 ACPI_FREE(obj); 376 return err; 377} 378 379static int asus_wmi_update_values_for_source(u8 source, struct asus_wmi_sensors *sensor_data) 380{ 381 struct asus_wmi_sensor_info *sensor; 382 long value = 0; 383 int ret; 384 int i; 385 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); 390 if (ret) 391 return ret; 392 393 sensor->cached_value = value; 394 } 395 } 396 397 return 0; 398} 399 400static int asus_wmi_scale_sensor_value(u32 value, int data_type) 401{ 402 /* FAN_RPM and WATER_FLOW don't need scaling */ 403 switch (data_type) { 404 case VOLTAGE: 405 /* value in microVolts */ 406 return DIV_ROUND_CLOSEST(value, KILO); 407 case TEMPERATURE_C: 408 /* value in Celsius */ 409 return value * MILLIDEGREE_PER_DEGREE; 410 case CURRENT: 411 /* value in Amperes */ 412 return value * MILLI; 413 } 414 return value; 415} 416 417static int asus_wmi_get_cached_value_or_update(const struct asus_wmi_sensor_info *sensor, 418 struct asus_wmi_sensors *sensor_data, 419 u32 *value) 420{ 421 int ret = 0; 422 423 mutex_lock(&sensor_data->lock); 424 425 if (time_after(jiffies, sensor_data->wmi.source_last_updated[sensor->source] + HZ)) { 426 ret = asus_wmi_update_buffer(sensor->source); 427 if (ret) 428 goto unlock; 429 430 ret = asus_wmi_update_values_for_source(sensor->source, sensor_data); 431 if (ret) 432 goto unlock; 433 434 sensor_data->wmi.source_last_updated[sensor->source] = jiffies; 435 } 436 437 *value = sensor->cached_value; 438 439unlock: 440 mutex_unlock(&sensor_data->lock); 441 442 return ret; 443} 444 445/* Now follow the functions that implement the hwmon interface */ 446static int asus_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, 447 u32 attr, int channel, long *val) 448{ 449 const struct asus_wmi_sensor_info *sensor; 450 u32 value = 0; 451 int ret; 452 453 struct asus_wmi_sensors *sensor_data = dev_get_drvdata(dev); 454 455 sensor = *(sensor_data->wmi.info[type] + channel); 456 457 ret = asus_wmi_get_cached_value_or_update(sensor, sensor_data, &value); 458 if (ret) 459 return ret; 460 461 *val = asus_wmi_scale_sensor_value(value, sensor->data_type); 462 463 return ret; 464} 465 466static int asus_wmi_hwmon_read_string(struct device *dev, 467 enum hwmon_sensor_types type, u32 attr, 468 int channel, const char **str) 469{ 470 struct asus_wmi_sensors *sensor_data = dev_get_drvdata(dev); 471 const struct asus_wmi_sensor_info *sensor; 472 473 sensor = *(sensor_data->wmi.info[type] + channel); 474 *str = sensor->name; 475 476 return 0; 477} 478 479static umode_t asus_wmi_hwmon_is_visible(const void *drvdata, 480 enum hwmon_sensor_types type, u32 attr, 481 int channel) 482{ 483 const struct asus_wmi_sensors *sensor_data = drvdata; 484 const struct asus_wmi_sensor_info *sensor; 485 486 sensor = *(sensor_data->wmi.info[type] + channel); 487 if (sensor) 488 return 0444; 489 490 return 0; 491} 492 493static 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, 497}; 498 499static struct hwmon_chip_info asus_wmi_chip_info = { 500 .ops = &asus_wmi_hwmon_ops, 501 .info = NULL, 502}; 503 504static int asus_wmi_configure_sensor_setup(struct device *dev, 505 struct asus_wmi_sensors *sensor_data) 506{ 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; 514 int i, idx; 515 int err; 516 517 temp_sensor = devm_kcalloc(dev, 1, sizeof(*temp_sensor), GFP_KERNEL); 518 if (!temp_sensor) 519 return -ENOMEM; 520 521 for (i = 0; i < sensor_data->wmi.sensor_count; i++) { 522 err = asus_wmi_sensor_info(i, temp_sensor); 523 if (err) 524 return err; 525 526 switch (temp_sensor->data_type) { 527 case TEMPERATURE_C: 528 case VOLTAGE: 529 case CURRENT: 530 case FAN_RPM: 531 case WATER_FLOW: 532 type = asus_data_types[temp_sensor->data_type]; 533 if (!nr_count[type]) 534 nr_types++; 535 nr_count[type]++; 536 break; 537 } 538 } 539 540 if (nr_count[hwmon_temp]) 541 nr_count[hwmon_chip]++, nr_types++; 542 543 asus_wmi_hwmon_chan = devm_kcalloc(dev, nr_types, 544 sizeof(*asus_wmi_hwmon_chan), 545 GFP_KERNEL); 546 if (!asus_wmi_hwmon_chan) 547 return -ENOMEM; 548 549 ptr_asus_wmi_ci = devm_kcalloc(dev, nr_types + 1, 550 sizeof(*ptr_asus_wmi_ci), GFP_KERNEL); 551 if (!ptr_asus_wmi_ci) 552 return -ENOMEM; 553 554 asus_wmi_chip_info.info = ptr_asus_wmi_ci; 555 chip_info = &asus_wmi_chip_info; 556 557 sensor_data->wmi.info_by_id = devm_kcalloc(dev, sensor_data->wmi.sensor_count, 558 sizeof(*sensor_data->wmi.info_by_id), 559 GFP_KERNEL); 560 561 if (!sensor_data->wmi.info_by_id) 562 return -ENOMEM; 563 564 for (type = 0; type < hwmon_max; type++) { 565 if (!nr_count[type]) 566 continue; 567 568 err = asus_wmi_hwmon_add_chan_info(asus_wmi_hwmon_chan, dev, 569 nr_count[type], type, 570 hwmon_attributes[type]); 571 if (err) 572 return err; 573 574 *ptr_asus_wmi_ci++ = asus_wmi_hwmon_chan++; 575 576 sensor_data->wmi.info[type] = devm_kcalloc(dev, 577 nr_count[type], 578 sizeof(*sensor_data->wmi.info), 579 GFP_KERNEL); 580 if (!sensor_data->wmi.info[type]) 581 return -ENOMEM; 582 } 583 584 for (i = sensor_data->wmi.sensor_count - 1; i >= 0; i--) { 585 temp_sensor = devm_kzalloc(dev, sizeof(*temp_sensor), GFP_KERNEL); 586 if (!temp_sensor) 587 return -ENOMEM; 588 589 err = asus_wmi_sensor_info(i, temp_sensor); 590 if (err) 591 continue; 592 593 switch (temp_sensor->data_type) { 594 case TEMPERATURE_C: 595 case VOLTAGE: 596 case CURRENT: 597 case FAN_RPM: 598 case WATER_FLOW: 599 type = asus_data_types[temp_sensor->data_type]; 600 idx = --nr_count[type]; 601 *(sensor_data->wmi.info[type] + idx) = temp_sensor; 602 sensor_data->wmi.info_by_id[i] = temp_sensor; 603 break; 604 } 605 } 606 607 dev_dbg(dev, "board has %d sensors", 608 sensor_data->wmi.sensor_count); 609 610 hwdev = devm_hwmon_device_register_with_info(dev, "asus_wmi_sensors", 611 sensor_data, chip_info, NULL); 612 613 return PTR_ERR_OR_ZERO(hwdev); 614} 615 616static int asus_wmi_probe(struct wmi_device *wdev, const void *context) 617{ 618 struct asus_wmi_sensors *sensor_data; 619 struct device *dev = &wdev->dev; 620 u32 version = 0; 621 622 if (!dmi_check_system(asus_wmi_dmi_table)) 623 return -ENODEV; 624 625 sensor_data = devm_kzalloc(dev, sizeof(*sensor_data), GFP_KERNEL); 626 if (!sensor_data) 627 return -ENOMEM; 628 629 if (asus_wmi_get_version(&version)) 630 return -ENODEV; 631 632 if (asus_wmi_get_item_count(&sensor_data->wmi.sensor_count)) 633 return -ENODEV; 634 635 if (sensor_data->wmi.sensor_count <= 0 || version < 2) { 636 dev_info(dev, "version: %u with %d sensors is unsupported\n", 637 version, sensor_data->wmi.sensor_count); 638 639 return -ENODEV; 640 } 641 642 mutex_init(&sensor_data->lock); 643 644 dev_set_drvdata(dev, sensor_data); 645 646 return asus_wmi_configure_sensor_setup(dev, sensor_data); 647} 648 649static const struct wmi_device_id asus_wmi_id_table[] = { 650 { ASUSWMI_MONITORING_GUID, NULL }, 651 { } 652}; 653 654static struct wmi_driver asus_sensors_wmi_driver = { 655 .driver = { 656 .name = "asus_wmi_sensors", 657 }, 658 .id_table = asus_wmi_id_table, 659 .probe = asus_wmi_probe, 660}; 661module_wmi_driver(asus_sensors_wmi_driver); 662 663MODULE_AUTHOR("Ed Brindley <kernel@maidavale.org>"); 664MODULE_DESCRIPTION("Asus WMI Sensors Driver"); 665MODULE_LICENSE("GPL");