asus_wmi_ec_sensors.c (16986B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * HWMON driver for ASUS B550/X570 motherboards that publish sensor 4 * values via the embedded controller registers. 5 * 6 * Copyright (C) 2021 Eugene Shalygin <eugene.shalygin@gmail.com> 7 * Copyright (C) 2018-2019 Ed Brindley <kernel@maidavale.org> 8 * 9 * EC provides: 10 * - Chipset temperature 11 * - CPU temperature 12 * - Motherboard temperature 13 * - T_Sensor temperature 14 * - VRM temperature 15 * - Water In temperature 16 * - Water Out temperature 17 * - CPU Optional Fan RPM 18 * - Chipset Fan RPM 19 * - Water Flow Fan RPM 20 * - CPU current 21 */ 22 23#include <linux/acpi.h> 24#include <linux/dmi.h> 25#include <linux/hwmon.h> 26#include <linux/init.h> 27#include <linux/jiffies.h> 28#include <linux/kernel.h> 29#include <linux/module.h> 30#include <linux/mutex.h> 31#include <linux/nls.h> 32#include <linux/units.h> 33#include <linux/wmi.h> 34 35#include <asm/unaligned.h> 36 37#define ASUSWMI_MONITORING_GUID "466747A0-70EC-11DE-8A39-0800200C9A66" 38#define ASUSWMI_METHODID_BLOCK_READ_EC 0x42524543 /* BREC */ 39/* From the ASUS DSDT source */ 40#define ASUSWMI_BREC_REGISTERS_MAX 16 41#define ASUSWMI_MAX_BUF_LEN 128 42#define SENSOR_LABEL_LEN 16 43 44static u32 hwmon_attributes[hwmon_max] = { 45 [hwmon_chip] = HWMON_C_REGISTER_TZ, 46 [hwmon_temp] = HWMON_T_INPUT | HWMON_T_LABEL, 47 [hwmon_in] = HWMON_I_INPUT | HWMON_I_LABEL, 48 [hwmon_curr] = HWMON_C_INPUT | HWMON_C_LABEL, 49 [hwmon_fan] = HWMON_F_INPUT | HWMON_F_LABEL, 50}; 51 52struct asus_wmi_ec_sensor_address { 53 u8 index; 54 u8 bank; 55 u8 size; 56}; 57 58#define MAKE_SENSOR_ADDRESS(size_i, bank_i, index_i) { \ 59 .size = size_i, \ 60 .bank = bank_i, \ 61 .index = index_i, \ 62} 63 64struct ec_sensor_info { 65 struct asus_wmi_ec_sensor_address addr; 66 char label[SENSOR_LABEL_LEN]; 67 enum hwmon_sensor_types type; 68}; 69 70#define EC_SENSOR(sensor_label, sensor_type, size, bank, index) { \ 71 .addr = MAKE_SENSOR_ADDRESS(size, bank, index), \ 72 .label = sensor_label, \ 73 .type = sensor_type, \ 74} 75 76enum known_ec_sensor { 77 SENSOR_TEMP_CHIPSET, 78 SENSOR_TEMP_CPU, 79 SENSOR_TEMP_MB, 80 SENSOR_TEMP_T_SENSOR, 81 SENSOR_TEMP_VRM, 82 SENSOR_FAN_CPU_OPT, 83 SENSOR_FAN_CHIPSET, 84 SENSOR_FAN_VRM_HS, 85 SENSOR_FAN_WATER_FLOW, 86 SENSOR_CURR_CPU, 87 SENSOR_TEMP_WATER_IN, 88 SENSOR_TEMP_WATER_OUT, 89 SENSOR_MAX 90}; 91 92/* All known sensors for ASUS EC controllers */ 93static const struct ec_sensor_info known_ec_sensors[] = { 94 [SENSOR_TEMP_CHIPSET] = EC_SENSOR("Chipset", hwmon_temp, 1, 0x00, 0x3a), 95 [SENSOR_TEMP_CPU] = EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x3b), 96 [SENSOR_TEMP_MB] = EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x3c), 97 [SENSOR_TEMP_T_SENSOR] = EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d), 98 [SENSOR_TEMP_VRM] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e), 99 [SENSOR_FAN_CPU_OPT] = EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0), 100 [SENSOR_FAN_VRM_HS] = EC_SENSOR("VRM HS", hwmon_fan, 2, 0x00, 0xb2), 101 [SENSOR_FAN_CHIPSET] = EC_SENSOR("Chipset", hwmon_fan, 2, 0x00, 0xb4), 102 [SENSOR_FAN_WATER_FLOW] = EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xbc), 103 [SENSOR_CURR_CPU] = EC_SENSOR("CPU", hwmon_curr, 1, 0x00, 0xf4), 104 [SENSOR_TEMP_WATER_IN] = EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00), 105 [SENSOR_TEMP_WATER_OUT] = EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01), 106}; 107 108struct asus_wmi_data { 109 const enum known_ec_sensor known_board_sensors[SENSOR_MAX + 1]; 110}; 111 112/* boards with EC support */ 113static struct asus_wmi_data sensors_board_PW_X570_P = { 114 .known_board_sensors = { 115 SENSOR_TEMP_CHIPSET, SENSOR_TEMP_CPU, SENSOR_TEMP_MB, 116 SENSOR_TEMP_T_SENSOR, SENSOR_TEMP_VRM, 117 SENSOR_FAN_CHIPSET, 118 SENSOR_MAX 119 }, 120}; 121 122static struct asus_wmi_data sensors_board_PW_X570_A = { 123 .known_board_sensors = { 124 SENSOR_TEMP_CHIPSET, SENSOR_TEMP_CPU, SENSOR_TEMP_MB, SENSOR_TEMP_VRM, 125 SENSOR_FAN_CHIPSET, 126 SENSOR_CURR_CPU, 127 SENSOR_MAX 128 }, 129}; 130 131static struct asus_wmi_data sensors_board_R_C8H = { 132 .known_board_sensors = { 133 SENSOR_TEMP_CHIPSET, SENSOR_TEMP_CPU, SENSOR_TEMP_MB, 134 SENSOR_TEMP_T_SENSOR, SENSOR_TEMP_VRM, 135 SENSOR_TEMP_WATER_IN, SENSOR_TEMP_WATER_OUT, 136 SENSOR_FAN_CPU_OPT, SENSOR_FAN_CHIPSET, SENSOR_FAN_WATER_FLOW, 137 SENSOR_CURR_CPU, 138 SENSOR_MAX 139 }, 140}; 141 142/* Same as Hero but without chipset fan */ 143static struct asus_wmi_data sensors_board_R_C8DH = { 144 .known_board_sensors = { 145 SENSOR_TEMP_CHIPSET, SENSOR_TEMP_CPU, SENSOR_TEMP_MB, 146 SENSOR_TEMP_T_SENSOR, SENSOR_TEMP_VRM, 147 SENSOR_TEMP_WATER_IN, SENSOR_TEMP_WATER_OUT, 148 SENSOR_FAN_CPU_OPT, SENSOR_FAN_WATER_FLOW, 149 SENSOR_CURR_CPU, 150 SENSOR_MAX 151 }, 152}; 153 154/* Same as Hero but without water */ 155static struct asus_wmi_data sensors_board_R_C8F = { 156 .known_board_sensors = { 157 SENSOR_TEMP_CHIPSET, SENSOR_TEMP_CPU, SENSOR_TEMP_MB, 158 SENSOR_TEMP_T_SENSOR, SENSOR_TEMP_VRM, 159 SENSOR_FAN_CPU_OPT, SENSOR_FAN_CHIPSET, 160 SENSOR_CURR_CPU, 161 SENSOR_MAX 162 }, 163}; 164 165static struct asus_wmi_data sensors_board_RS_B550_E_G = { 166 .known_board_sensors = { 167 SENSOR_TEMP_CHIPSET, SENSOR_TEMP_CPU, SENSOR_TEMP_MB, 168 SENSOR_TEMP_T_SENSOR, SENSOR_TEMP_VRM, 169 SENSOR_FAN_CPU_OPT, 170 SENSOR_MAX 171 }, 172}; 173 174static struct asus_wmi_data sensors_board_RS_B550_I_G = { 175 .known_board_sensors = { 176 SENSOR_TEMP_CHIPSET, SENSOR_TEMP_CPU, SENSOR_TEMP_MB, 177 SENSOR_TEMP_T_SENSOR, SENSOR_TEMP_VRM, 178 SENSOR_FAN_VRM_HS, 179 SENSOR_CURR_CPU, 180 SENSOR_MAX 181 }, 182}; 183 184static struct asus_wmi_data sensors_board_RS_X570_E_G = { 185 .known_board_sensors = { 186 SENSOR_TEMP_CHIPSET, SENSOR_TEMP_CPU, SENSOR_TEMP_MB, 187 SENSOR_TEMP_T_SENSOR, SENSOR_TEMP_VRM, 188 SENSOR_FAN_CHIPSET, 189 SENSOR_CURR_CPU, 190 SENSOR_MAX 191 }, 192}; 193 194#define DMI_EXACT_MATCH_ASUS_BOARD_NAME(name, sensors) { \ 195 .matches = { \ 196 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), \ 197 DMI_EXACT_MATCH(DMI_BOARD_NAME, name), \ 198 }, \ 199 .driver_data = sensors, \ 200} 201 202static const struct dmi_system_id asus_wmi_ec_dmi_table[] = { 203 DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X570-PRO", &sensors_board_PW_X570_P), 204 DMI_EXACT_MATCH_ASUS_BOARD_NAME("Pro WS X570-ACE", &sensors_board_PW_X570_A), 205 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII DARK HERO", &sensors_board_R_C8DH), 206 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII FORMULA", &sensors_board_R_C8F), 207 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII HERO", &sensors_board_R_C8H), 208 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-E GAMING", &sensors_board_RS_B550_E_G), 209 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-I GAMING", &sensors_board_RS_B550_I_G), 210 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X570-E GAMING", &sensors_board_RS_X570_E_G), 211 {} 212}; 213MODULE_DEVICE_TABLE(dmi, asus_wmi_ec_dmi_table); 214 215struct ec_sensor { 216 enum known_ec_sensor info_index; 217 long cached_value; 218}; 219 220/** 221 * struct asus_wmi_ec_info - sensor info. 222 * @sensors: list of sensors. 223 * @read_arg: UTF-16LE string to pass to BRxx() WMI function. 224 * @read_buffer: decoded output from WMI result. 225 * @nr_sensors: number of board EC sensors. 226 * @nr_registers: number of EC registers to read (sensor might span more than 1 register). 227 * @last_updated: in jiffies. 228 */ 229struct asus_wmi_ec_info { 230 struct ec_sensor sensors[SENSOR_MAX]; 231 char read_arg[(ASUSWMI_BREC_REGISTERS_MAX * 4 + 1) * 2]; 232 u8 read_buffer[ASUSWMI_BREC_REGISTERS_MAX]; 233 unsigned int nr_sensors; 234 unsigned int nr_registers; 235 unsigned long last_updated; 236}; 237 238struct asus_wmi_sensors { 239 struct asus_wmi_ec_info ec; 240 /* lock access to internal cache */ 241 struct mutex lock; 242}; 243 244static int asus_wmi_ec_fill_board_sensors(struct asus_wmi_ec_info *ec, 245 const enum known_ec_sensor *bsi) 246{ 247 struct ec_sensor *s = ec->sensors; 248 int i; 249 250 ec->nr_sensors = 0; 251 ec->nr_registers = 0; 252 253 for (i = 0; bsi[i] != SENSOR_MAX; i++) { 254 s[i].info_index = bsi[i]; 255 ec->nr_sensors++; 256 ec->nr_registers += known_ec_sensors[bsi[i]].addr.size; 257 } 258 259 return 0; 260} 261 262/* 263 * The next four functions convert to or from BRxx string argument format. 264 * The format of the string is as follows: 265 * - The string consists of two-byte UTF-16LE characters. 266 * - The value of the very first byte in the string is equal to the total 267 * length of the next string in bytes, thus excluding the first two-byte 268 * character. 269 * - The rest of the string encodes the pairs of (bank, index) pairs, where 270 * both values are byte-long (0x00 to 0xFF). 271 * - Numbers are encoded as UTF-16LE hex values. 272 */ 273static int asus_wmi_ec_decode_reply_buffer(const u8 *in, u32 length, u8 *out) 274{ 275 char buffer[ASUSWMI_MAX_BUF_LEN * 2]; 276 u32 len = min_t(u32, get_unaligned_le16(in), length - 2); 277 278 utf16s_to_utf8s((wchar_t *)(in + 2), len / 2, UTF16_LITTLE_ENDIAN, buffer, sizeof(buffer)); 279 280 return hex2bin(out, buffer, len / 4); 281} 282 283static void asus_wmi_ec_encode_registers(const u8 *in, u32 len, char *out) 284{ 285 char buffer[ASUSWMI_MAX_BUF_LEN * 2]; 286 287 bin2hex(buffer, in, len); 288 289 utf8s_to_utf16s(buffer, len * 2, UTF16_LITTLE_ENDIAN, (wchar_t *)(out + 2), len * 2); 290 291 put_unaligned_le16(len * 4, out); 292} 293 294static void asus_wmi_ec_make_block_read_query(struct asus_wmi_ec_info *ec) 295{ 296 u8 registers[ASUSWMI_BREC_REGISTERS_MAX * 2]; 297 const struct ec_sensor_info *si; 298 int i, j, offset; 299 300 offset = 0; 301 for (i = 0; i < ec->nr_sensors; i++) { 302 si = &known_ec_sensors[ec->sensors[i].info_index]; 303 for (j = 0; j < si->addr.size; j++) { 304 registers[offset++] = si->addr.bank; 305 registers[offset++] = si->addr.index + j; 306 } 307 } 308 309 asus_wmi_ec_encode_registers(registers, offset, ec->read_arg); 310} 311 312static int asus_wmi_ec_block_read(u32 method_id, char *query, u8 *out) 313{ 314 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 315 struct acpi_buffer input; 316 union acpi_object *obj; 317 acpi_status status; 318 int ret; 319 320 /* The first byte of the BRxx() argument string has to be the string size. */ 321 input.length = query[0] + 2; 322 input.pointer = query; 323 status = wmi_evaluate_method(ASUSWMI_MONITORING_GUID, 0, method_id, &input, &output); 324 if (ACPI_FAILURE(status)) 325 return -EIO; 326 327 obj = output.pointer; 328 if (!obj) 329 return -EIO; 330 331 if (obj->type != ACPI_TYPE_BUFFER || obj->buffer.length < 2) { 332 ret = -EIO; 333 goto out_free_obj; 334 } 335 336 ret = asus_wmi_ec_decode_reply_buffer(obj->buffer.pointer, obj->buffer.length, out); 337 338out_free_obj: 339 ACPI_FREE(obj); 340 return ret; 341} 342 343static inline long get_sensor_value(const struct ec_sensor_info *si, u8 *data) 344{ 345 switch (si->addr.size) { 346 case 1: 347 return *data; 348 case 2: 349 return get_unaligned_be16(data); 350 case 4: 351 return get_unaligned_be32(data); 352 default: 353 return 0; 354 } 355} 356 357static void asus_wmi_ec_update_ec_sensors(struct asus_wmi_ec_info *ec) 358{ 359 const struct ec_sensor_info *si; 360 struct ec_sensor *s; 361 u8 i_sensor; 362 u8 *data; 363 364 data = ec->read_buffer; 365 for (i_sensor = 0; i_sensor < ec->nr_sensors; i_sensor++) { 366 s = &ec->sensors[i_sensor]; 367 si = &known_ec_sensors[s->info_index]; 368 s->cached_value = get_sensor_value(si, data); 369 data += si->addr.size; 370 } 371} 372 373static long asus_wmi_ec_scale_sensor_value(long value, int data_type) 374{ 375 switch (data_type) { 376 case hwmon_curr: 377 case hwmon_temp: 378 case hwmon_in: 379 return value * MILLI; 380 default: 381 return value; 382 } 383} 384 385static int asus_wmi_ec_find_sensor_index(const struct asus_wmi_ec_info *ec, 386 enum hwmon_sensor_types type, int channel) 387{ 388 int i; 389 390 for (i = 0; i < ec->nr_sensors; i++) { 391 if (known_ec_sensors[ec->sensors[i].info_index].type == type) { 392 if (channel == 0) 393 return i; 394 395 channel--; 396 } 397 } 398 return -EINVAL; 399} 400 401static int asus_wmi_ec_get_cached_value_or_update(struct asus_wmi_sensors *sensor_data, 402 int sensor_index, 403 long *value) 404{ 405 struct asus_wmi_ec_info *ec = &sensor_data->ec; 406 int ret = 0; 407 408 mutex_lock(&sensor_data->lock); 409 410 if (time_after(jiffies, ec->last_updated + HZ)) { 411 ret = asus_wmi_ec_block_read(ASUSWMI_METHODID_BLOCK_READ_EC, 412 ec->read_arg, ec->read_buffer); 413 if (ret) 414 goto unlock; 415 416 asus_wmi_ec_update_ec_sensors(ec); 417 ec->last_updated = jiffies; 418 } 419 420 *value = ec->sensors[sensor_index].cached_value; 421 422unlock: 423 mutex_unlock(&sensor_data->lock); 424 425 return ret; 426} 427 428/* Now follow the functions that implement the hwmon interface */ 429 430static int asus_wmi_ec_hwmon_read(struct device *dev, enum hwmon_sensor_types type, 431 u32 attr, int channel, long *val) 432{ 433 struct asus_wmi_sensors *sensor_data = dev_get_drvdata(dev); 434 struct asus_wmi_ec_info *ec = &sensor_data->ec; 435 int ret, sidx, info_index; 436 long value = 0; 437 438 sidx = asus_wmi_ec_find_sensor_index(ec, type, channel); 439 if (sidx < 0) 440 return sidx; 441 442 ret = asus_wmi_ec_get_cached_value_or_update(sensor_data, sidx, &value); 443 if (ret) 444 return ret; 445 446 info_index = ec->sensors[sidx].info_index; 447 *val = asus_wmi_ec_scale_sensor_value(value, known_ec_sensors[info_index].type); 448 449 return ret; 450} 451 452static int asus_wmi_ec_hwmon_read_string(struct device *dev, 453 enum hwmon_sensor_types type, u32 attr, 454 int channel, const char **str) 455{ 456 struct asus_wmi_sensors *sensor_data = dev_get_drvdata(dev); 457 struct asus_wmi_ec_info *ec = &sensor_data->ec; 458 int sensor_index; 459 460 sensor_index = asus_wmi_ec_find_sensor_index(ec, type, channel); 461 *str = known_ec_sensors[ec->sensors[sensor_index].info_index].label; 462 463 return 0; 464} 465 466static umode_t asus_wmi_ec_hwmon_is_visible(const void *drvdata, 467 enum hwmon_sensor_types type, u32 attr, 468 int channel) 469{ 470 const struct asus_wmi_sensors *sensor_data = drvdata; 471 const struct asus_wmi_ec_info *ec = &sensor_data->ec; 472 int index; 473 474 index = asus_wmi_ec_find_sensor_index(ec, type, channel); 475 476 return index < 0 ? 0 : 0444; 477} 478 479static int asus_wmi_hwmon_add_chan_info(struct hwmon_channel_info *asus_wmi_hwmon_chan, 480 struct device *dev, int num, 481 enum hwmon_sensor_types type, u32 config) 482{ 483 u32 *cfg; 484 485 cfg = devm_kcalloc(dev, num + 1, sizeof(*cfg), GFP_KERNEL); 486 if (!cfg) 487 return -ENOMEM; 488 489 asus_wmi_hwmon_chan->type = type; 490 asus_wmi_hwmon_chan->config = cfg; 491 memset32(cfg, config, num); 492 493 return 0; 494} 495 496static const struct hwmon_ops asus_wmi_ec_hwmon_ops = { 497 .is_visible = asus_wmi_ec_hwmon_is_visible, 498 .read = asus_wmi_ec_hwmon_read, 499 .read_string = asus_wmi_ec_hwmon_read_string, 500}; 501 502static struct hwmon_chip_info asus_wmi_ec_chip_info = { 503 .ops = &asus_wmi_ec_hwmon_ops, 504}; 505 506static int asus_wmi_ec_configure_sensor_setup(struct device *dev, 507 const enum known_ec_sensor *bsi) 508{ 509 struct asus_wmi_sensors *sensor_data = dev_get_drvdata(dev); 510 struct asus_wmi_ec_info *ec = &sensor_data->ec; 511 struct hwmon_channel_info *asus_wmi_hwmon_chan; 512 const struct hwmon_channel_info **asus_wmi_ci; 513 int nr_count[hwmon_max] = {}, nr_types = 0; 514 const struct hwmon_chip_info *chip_info; 515 const struct ec_sensor_info *si; 516 enum hwmon_sensor_types type; 517 struct device *hwdev; 518 int i, ret; 519 520 ret = asus_wmi_ec_fill_board_sensors(ec, bsi); 521 if (ret) 522 return ret; 523 524 if (!sensor_data->ec.nr_sensors) 525 return -ENODEV; 526 527 for (i = 0; i < ec->nr_sensors; i++) { 528 si = &known_ec_sensors[ec->sensors[i].info_index]; 529 if (!nr_count[si->type]) 530 nr_types++; 531 nr_count[si->type]++; 532 } 533 534 if (nr_count[hwmon_temp]) { 535 nr_count[hwmon_chip]++; 536 nr_types++; 537 } 538 539 /* 540 * If we can get values for all the registers in a single query, 541 * the query will not change from call to call. 542 */ 543 asus_wmi_ec_make_block_read_query(ec); 544 545 asus_wmi_hwmon_chan = devm_kcalloc(dev, nr_types, sizeof(*asus_wmi_hwmon_chan), 546 GFP_KERNEL); 547 if (!asus_wmi_hwmon_chan) 548 return -ENOMEM; 549 550 asus_wmi_ci = devm_kcalloc(dev, nr_types + 1, sizeof(*asus_wmi_ci), GFP_KERNEL); 551 if (!asus_wmi_ci) 552 return -ENOMEM; 553 554 asus_wmi_ec_chip_info.info = asus_wmi_ci; 555 chip_info = &asus_wmi_ec_chip_info; 556 557 for (type = 0; type < hwmon_max; type++) { 558 if (!nr_count[type]) 559 continue; 560 561 ret = asus_wmi_hwmon_add_chan_info(asus_wmi_hwmon_chan, dev, 562 nr_count[type], type, 563 hwmon_attributes[type]); 564 if (ret) 565 return ret; 566 567 *asus_wmi_ci++ = asus_wmi_hwmon_chan++; 568 } 569 570 dev_dbg(dev, "board has %d EC sensors that span %d registers", 571 ec->nr_sensors, ec->nr_registers); 572 573 hwdev = devm_hwmon_device_register_with_info(dev, "asus_wmi_ec_sensors", 574 sensor_data, chip_info, NULL); 575 576 return PTR_ERR_OR_ZERO(hwdev); 577} 578 579static int asus_wmi_probe(struct wmi_device *wdev, const void *context) 580{ 581 struct asus_wmi_sensors *sensor_data; 582 struct asus_wmi_data *board_sensors; 583 const struct dmi_system_id *dmi_id; 584 const enum known_ec_sensor *bsi; 585 struct device *dev = &wdev->dev; 586 587 dmi_id = dmi_first_match(asus_wmi_ec_dmi_table); 588 if (!dmi_id) 589 return -ENODEV; 590 591 board_sensors = dmi_id->driver_data; 592 bsi = board_sensors->known_board_sensors; 593 594 sensor_data = devm_kzalloc(dev, sizeof(*sensor_data), GFP_KERNEL); 595 if (!sensor_data) 596 return -ENOMEM; 597 598 mutex_init(&sensor_data->lock); 599 600 dev_set_drvdata(dev, sensor_data); 601 602 return asus_wmi_ec_configure_sensor_setup(dev, bsi); 603} 604 605static const struct wmi_device_id asus_ec_wmi_id_table[] = { 606 { ASUSWMI_MONITORING_GUID, NULL }, 607 { } 608}; 609 610static struct wmi_driver asus_sensors_wmi_driver = { 611 .driver = { 612 .name = "asus_wmi_ec_sensors", 613 }, 614 .id_table = asus_ec_wmi_id_table, 615 .probe = asus_wmi_probe, 616}; 617module_wmi_driver(asus_sensors_wmi_driver); 618 619MODULE_AUTHOR("Ed Brindley <kernel@maidavale.org>"); 620MODULE_AUTHOR("Eugene Shalygin <eugene.shalygin@gmail.com>"); 621MODULE_DESCRIPTION("Asus WMI Sensors Driver"); 622MODULE_LICENSE("GPL");