0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043 #include <linux/acpi.h>
0044 #include <linux/dmi.h>
0045 #include <linux/hwmon.h>
0046 #include <linux/init.h>
0047 #include <linux/jiffies.h>
0048 #include <linux/kernel.h>
0049 #include <linux/module.h>
0050 #include <linux/mutex.h>
0051 #include <linux/units.h>
0052 #include <linux/wmi.h>
0053
0054 #define ASUSWMI_MONITORING_GUID "466747A0-70EC-11DE-8A39-0800200C9A66"
0055 #define ASUSWMI_METHODID_GET_VALUE 0x52574543
0056 #define ASUSWMI_METHODID_UPDATE_BUFFER 0x51574543
0057 #define ASUSWMI_METHODID_GET_INFO 0x50574543
0058 #define ASUSWMI_METHODID_GET_NUMBER 0x50574572
0059 #define ASUSWMI_METHODID_GET_VERSION 0x50574574
0060
0061 #define ASUS_WMI_MAX_STR_SIZE 32
0062
0063 #define DMI_EXACT_MATCH_ASUS_BOARD_NAME(name) { \
0064 .matches = { \
0065 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), \
0066 DMI_EXACT_MATCH(DMI_BOARD_NAME, name), \
0067 }, \
0068 }
0069
0070 static const struct dmi_system_id asus_wmi_dmi_table[] = {
0071 DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X399-A"),
0072 DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X470-PRO"),
0073 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VI EXTREME"),
0074 DMI_EXACT_MATCH_ASUS_BOARD_NAME("CROSSHAIR VI HERO"),
0075 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VI HERO (WI-FI AC)"),
0076 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VII HERO"),
0077 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VII HERO (WI-FI)"),
0078 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B450-E GAMING"),
0079 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B450-F GAMING"),
0080 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B450-F GAMING II"),
0081 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B450-I GAMING"),
0082 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X399-E GAMING"),
0083 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X470-F GAMING"),
0084 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X470-I GAMING"),
0085 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG ZENITH EXTREME"),
0086 DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG ZENITH EXTREME ALPHA"),
0087 {}
0088 };
0089 MODULE_DEVICE_TABLE(dmi, asus_wmi_dmi_table);
0090
0091 enum asus_wmi_sensor_class {
0092 VOLTAGE = 0x0,
0093 TEMPERATURE_C = 0x1,
0094 FAN_RPM = 0x2,
0095 CURRENT = 0x3,
0096 WATER_FLOW = 0x4,
0097 };
0098
0099 enum asus_wmi_location {
0100 CPU = 0x0,
0101 CPU_SOC = 0x1,
0102 DRAM = 0x2,
0103 MOTHERBOARD = 0x3,
0104 CHIPSET = 0x4,
0105 AUX = 0x5,
0106 VRM = 0x6,
0107 COOLER = 0x7
0108 };
0109
0110 enum asus_wmi_type {
0111 SIGNED_INT = 0x0,
0112 UNSIGNED_INT = 0x1,
0113 SCALED = 0x3,
0114 };
0115
0116 enum asus_wmi_source {
0117 SIO = 0x1,
0118 EC = 0x2
0119 };
0120
0121 static enum hwmon_sensor_types asus_data_types[] = {
0122 [VOLTAGE] = hwmon_in,
0123 [TEMPERATURE_C] = hwmon_temp,
0124 [FAN_RPM] = hwmon_fan,
0125 [CURRENT] = hwmon_curr,
0126 [WATER_FLOW] = hwmon_fan,
0127 };
0128
0129 static u32 hwmon_attributes[hwmon_max] = {
0130 [hwmon_chip] = HWMON_C_REGISTER_TZ,
0131 [hwmon_temp] = HWMON_T_INPUT | HWMON_T_LABEL,
0132 [hwmon_in] = HWMON_I_INPUT | HWMON_I_LABEL,
0133 [hwmon_curr] = HWMON_C_INPUT | HWMON_C_LABEL,
0134 [hwmon_fan] = HWMON_F_INPUT | HWMON_F_LABEL,
0135 };
0136
0137
0138
0139
0140
0141
0142
0143
0144
0145
0146
0147 struct asus_wmi_sensor_info {
0148 u32 id;
0149 int data_type;
0150 int location;
0151 char name[ASUS_WMI_MAX_STR_SIZE];
0152 int source;
0153 int type;
0154 long cached_value;
0155 };
0156
0157 struct asus_wmi_wmi_info {
0158 unsigned long source_last_updated[3];
0159 int sensor_count;
0160
0161 const struct asus_wmi_sensor_info **info[hwmon_max];
0162 struct asus_wmi_sensor_info **info_by_id;
0163 };
0164
0165 struct asus_wmi_sensors {
0166 struct asus_wmi_wmi_info wmi;
0167
0168 struct mutex lock;
0169 };
0170
0171
0172
0173
0174 static int asus_wmi_call_method(u32 method_id, u32 *args, struct acpi_buffer *output)
0175 {
0176 struct acpi_buffer input = {(acpi_size) sizeof(*args), args };
0177 acpi_status status;
0178
0179 status = wmi_evaluate_method(ASUSWMI_MONITORING_GUID, 0,
0180 method_id, &input, output);
0181 if (ACPI_FAILURE(status))
0182 return -EIO;
0183
0184 return 0;
0185 }
0186
0187
0188
0189
0190 static int asus_wmi_get_version(u32 *version)
0191 {
0192 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
0193 u32 args[] = {0, 0, 0};
0194 union acpi_object *obj;
0195 int err;
0196
0197 err = asus_wmi_call_method(ASUSWMI_METHODID_GET_VERSION, args, &output);
0198 if (err)
0199 return err;
0200
0201 obj = output.pointer;
0202 if (!obj)
0203 return -EIO;
0204
0205 if (obj->type != ACPI_TYPE_INTEGER) {
0206 err = -EIO;
0207 goto out_free_obj;
0208 }
0209
0210 err = 0;
0211 *version = obj->integer.value;
0212
0213 out_free_obj:
0214 ACPI_FREE(obj);
0215 return err;
0216 }
0217
0218
0219
0220
0221 static int asus_wmi_get_item_count(u32 *count)
0222 {
0223 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
0224 u32 args[] = {0, 0, 0};
0225 union acpi_object *obj;
0226 int err;
0227
0228 err = asus_wmi_call_method(ASUSWMI_METHODID_GET_NUMBER, args, &output);
0229 if (err)
0230 return err;
0231
0232 obj = output.pointer;
0233 if (!obj)
0234 return -EIO;
0235
0236 if (obj->type != ACPI_TYPE_INTEGER) {
0237 err = -EIO;
0238 goto out_free_obj;
0239 }
0240
0241 err = 0;
0242 *count = obj->integer.value;
0243
0244 out_free_obj:
0245 ACPI_FREE(obj);
0246 return err;
0247 }
0248
0249 static int asus_wmi_hwmon_add_chan_info(struct hwmon_channel_info *asus_wmi_hwmon_chan,
0250 struct device *dev, int num,
0251 enum hwmon_sensor_types type, u32 config)
0252 {
0253 u32 *cfg;
0254
0255 cfg = devm_kcalloc(dev, num + 1, sizeof(*cfg), GFP_KERNEL);
0256 if (!cfg)
0257 return -ENOMEM;
0258
0259 asus_wmi_hwmon_chan->type = type;
0260 asus_wmi_hwmon_chan->config = cfg;
0261 memset32(cfg, config, num);
0262
0263 return 0;
0264 }
0265
0266
0267
0268
0269 static int asus_wmi_sensor_info(int index, struct asus_wmi_sensor_info *s)
0270 {
0271 union acpi_object name_obj, data_type_obj, location_obj, source_obj, type_obj;
0272 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
0273 u32 args[] = {index, 0};
0274 union acpi_object *obj;
0275 int err;
0276
0277 err = asus_wmi_call_method(ASUSWMI_METHODID_GET_INFO, args, &output);
0278 if (err)
0279 return err;
0280
0281 s->id = index;
0282
0283 obj = output.pointer;
0284 if (!obj)
0285 return -EIO;
0286
0287 if (obj->type != ACPI_TYPE_PACKAGE) {
0288 err = -EIO;
0289 goto out_free_obj;
0290 }
0291
0292 if (obj->package.count != 5) {
0293 err = -EIO;
0294 goto out_free_obj;
0295 }
0296
0297 name_obj = obj->package.elements[0];
0298 if (name_obj.type != ACPI_TYPE_STRING) {
0299 err = -EIO;
0300 goto out_free_obj;
0301 }
0302
0303 strncpy(s->name, name_obj.string.pointer, sizeof(s->name) - 1);
0304
0305 data_type_obj = obj->package.elements[1];
0306 if (data_type_obj.type != ACPI_TYPE_INTEGER) {
0307 err = -EIO;
0308 goto out_free_obj;
0309 }
0310
0311 s->data_type = data_type_obj.integer.value;
0312
0313 location_obj = obj->package.elements[2];
0314 if (location_obj.type != ACPI_TYPE_INTEGER) {
0315 err = -EIO;
0316 goto out_free_obj;
0317 }
0318
0319 s->location = location_obj.integer.value;
0320
0321 source_obj = obj->package.elements[3];
0322 if (source_obj.type != ACPI_TYPE_INTEGER) {
0323 err = -EIO;
0324 goto out_free_obj;
0325 }
0326
0327 s->source = source_obj.integer.value;
0328
0329 type_obj = obj->package.elements[4];
0330 if (type_obj.type != ACPI_TYPE_INTEGER) {
0331 err = -EIO;
0332 goto out_free_obj;
0333 }
0334
0335 err = 0;
0336 s->type = type_obj.integer.value;
0337
0338 out_free_obj:
0339 ACPI_FREE(obj);
0340 return err;
0341 }
0342
0343 static int asus_wmi_update_buffer(int source)
0344 {
0345 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
0346 u32 args[] = {source, 0};
0347
0348 return asus_wmi_call_method(ASUSWMI_METHODID_UPDATE_BUFFER, args, &output);
0349 }
0350
0351 static int asus_wmi_get_sensor_value(u8 index, long *value)
0352 {
0353 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
0354 u32 args[] = {index, 0};
0355 union acpi_object *obj;
0356 int err;
0357
0358 err = asus_wmi_call_method(ASUSWMI_METHODID_GET_VALUE, args, &output);
0359 if (err)
0360 return err;
0361
0362 obj = output.pointer;
0363 if (!obj)
0364 return -EIO;
0365
0366 if (obj->type != ACPI_TYPE_INTEGER) {
0367 err = -EIO;
0368 goto out_free_obj;
0369 }
0370
0371 err = 0;
0372 *value = obj->integer.value;
0373
0374 out_free_obj:
0375 ACPI_FREE(obj);
0376 return err;
0377 }
0378
0379 static int asus_wmi_update_values_for_source(u8 source, struct asus_wmi_sensors *sensor_data)
0380 {
0381 struct asus_wmi_sensor_info *sensor;
0382 long value = 0;
0383 int ret;
0384 int i;
0385
0386 for (i = 0; i < sensor_data->wmi.sensor_count; i++) {
0387 sensor = sensor_data->wmi.info_by_id[i];
0388 if (sensor && sensor->source == source) {
0389 ret = asus_wmi_get_sensor_value(sensor->id, &value);
0390 if (ret)
0391 return ret;
0392
0393 sensor->cached_value = value;
0394 }
0395 }
0396
0397 return 0;
0398 }
0399
0400 static int asus_wmi_scale_sensor_value(u32 value, int data_type)
0401 {
0402
0403 switch (data_type) {
0404 case VOLTAGE:
0405
0406 return DIV_ROUND_CLOSEST(value, KILO);
0407 case TEMPERATURE_C:
0408
0409 return value * MILLIDEGREE_PER_DEGREE;
0410 case CURRENT:
0411
0412 return value * MILLI;
0413 }
0414 return value;
0415 }
0416
0417 static int asus_wmi_get_cached_value_or_update(const struct asus_wmi_sensor_info *sensor,
0418 struct asus_wmi_sensors *sensor_data,
0419 u32 *value)
0420 {
0421 int ret = 0;
0422
0423 mutex_lock(&sensor_data->lock);
0424
0425 if (time_after(jiffies, sensor_data->wmi.source_last_updated[sensor->source] + HZ)) {
0426 ret = asus_wmi_update_buffer(sensor->source);
0427 if (ret)
0428 goto unlock;
0429
0430 ret = asus_wmi_update_values_for_source(sensor->source, sensor_data);
0431 if (ret)
0432 goto unlock;
0433
0434 sensor_data->wmi.source_last_updated[sensor->source] = jiffies;
0435 }
0436
0437 *value = sensor->cached_value;
0438
0439 unlock:
0440 mutex_unlock(&sensor_data->lock);
0441
0442 return ret;
0443 }
0444
0445
0446 static int asus_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
0447 u32 attr, int channel, long *val)
0448 {
0449 const struct asus_wmi_sensor_info *sensor;
0450 u32 value = 0;
0451 int ret;
0452
0453 struct asus_wmi_sensors *sensor_data = dev_get_drvdata(dev);
0454
0455 sensor = *(sensor_data->wmi.info[type] + channel);
0456
0457 ret = asus_wmi_get_cached_value_or_update(sensor, sensor_data, &value);
0458 if (ret)
0459 return ret;
0460
0461 *val = asus_wmi_scale_sensor_value(value, sensor->data_type);
0462
0463 return ret;
0464 }
0465
0466 static int asus_wmi_hwmon_read_string(struct device *dev,
0467 enum hwmon_sensor_types type, u32 attr,
0468 int channel, const char **str)
0469 {
0470 struct asus_wmi_sensors *sensor_data = dev_get_drvdata(dev);
0471 const struct asus_wmi_sensor_info *sensor;
0472
0473 sensor = *(sensor_data->wmi.info[type] + channel);
0474 *str = sensor->name;
0475
0476 return 0;
0477 }
0478
0479 static umode_t asus_wmi_hwmon_is_visible(const void *drvdata,
0480 enum hwmon_sensor_types type, u32 attr,
0481 int channel)
0482 {
0483 const struct asus_wmi_sensors *sensor_data = drvdata;
0484 const struct asus_wmi_sensor_info *sensor;
0485
0486 sensor = *(sensor_data->wmi.info[type] + channel);
0487 if (sensor)
0488 return 0444;
0489
0490 return 0;
0491 }
0492
0493 static const struct hwmon_ops asus_wmi_hwmon_ops = {
0494 .is_visible = asus_wmi_hwmon_is_visible,
0495 .read = asus_wmi_hwmon_read,
0496 .read_string = asus_wmi_hwmon_read_string,
0497 };
0498
0499 static struct hwmon_chip_info asus_wmi_chip_info = {
0500 .ops = &asus_wmi_hwmon_ops,
0501 .info = NULL,
0502 };
0503
0504 static int asus_wmi_configure_sensor_setup(struct device *dev,
0505 struct asus_wmi_sensors *sensor_data)
0506 {
0507 const struct hwmon_channel_info **ptr_asus_wmi_ci;
0508 struct hwmon_channel_info *asus_wmi_hwmon_chan;
0509 int nr_count[hwmon_max] = {}, nr_types = 0;
0510 struct asus_wmi_sensor_info *temp_sensor;
0511 const struct hwmon_chip_info *chip_info;
0512 enum hwmon_sensor_types type;
0513 struct device *hwdev;
0514 int i, idx;
0515 int err;
0516
0517 for (i = 0; i < sensor_data->wmi.sensor_count; i++) {
0518 struct asus_wmi_sensor_info sensor;
0519
0520 err = asus_wmi_sensor_info(i, &sensor);
0521 if (err)
0522 return err;
0523
0524 switch (sensor.data_type) {
0525 case TEMPERATURE_C:
0526 case VOLTAGE:
0527 case CURRENT:
0528 case FAN_RPM:
0529 case WATER_FLOW:
0530 type = asus_data_types[sensor.data_type];
0531 if (!nr_count[type])
0532 nr_types++;
0533 nr_count[type]++;
0534 break;
0535 }
0536 }
0537
0538 if (nr_count[hwmon_temp])
0539 nr_count[hwmon_chip]++, nr_types++;
0540
0541 asus_wmi_hwmon_chan = devm_kcalloc(dev, nr_types,
0542 sizeof(*asus_wmi_hwmon_chan),
0543 GFP_KERNEL);
0544 if (!asus_wmi_hwmon_chan)
0545 return -ENOMEM;
0546
0547 ptr_asus_wmi_ci = devm_kcalloc(dev, nr_types + 1,
0548 sizeof(*ptr_asus_wmi_ci), GFP_KERNEL);
0549 if (!ptr_asus_wmi_ci)
0550 return -ENOMEM;
0551
0552 asus_wmi_chip_info.info = ptr_asus_wmi_ci;
0553 chip_info = &asus_wmi_chip_info;
0554
0555 sensor_data->wmi.info_by_id = devm_kcalloc(dev, sensor_data->wmi.sensor_count,
0556 sizeof(*sensor_data->wmi.info_by_id),
0557 GFP_KERNEL);
0558
0559 if (!sensor_data->wmi.info_by_id)
0560 return -ENOMEM;
0561
0562 for (type = 0; type < hwmon_max; type++) {
0563 if (!nr_count[type])
0564 continue;
0565
0566 err = asus_wmi_hwmon_add_chan_info(asus_wmi_hwmon_chan, dev,
0567 nr_count[type], type,
0568 hwmon_attributes[type]);
0569 if (err)
0570 return err;
0571
0572 *ptr_asus_wmi_ci++ = asus_wmi_hwmon_chan++;
0573
0574 sensor_data->wmi.info[type] = devm_kcalloc(dev,
0575 nr_count[type],
0576 sizeof(*sensor_data->wmi.info),
0577 GFP_KERNEL);
0578 if (!sensor_data->wmi.info[type])
0579 return -ENOMEM;
0580 }
0581
0582 for (i = sensor_data->wmi.sensor_count - 1; i >= 0; i--) {
0583 temp_sensor = devm_kzalloc(dev, sizeof(*temp_sensor), GFP_KERNEL);
0584 if (!temp_sensor)
0585 return -ENOMEM;
0586
0587 err = asus_wmi_sensor_info(i, temp_sensor);
0588 if (err)
0589 continue;
0590
0591 switch (temp_sensor->data_type) {
0592 case TEMPERATURE_C:
0593 case VOLTAGE:
0594 case CURRENT:
0595 case FAN_RPM:
0596 case WATER_FLOW:
0597 type = asus_data_types[temp_sensor->data_type];
0598 idx = --nr_count[type];
0599 *(sensor_data->wmi.info[type] + idx) = temp_sensor;
0600 sensor_data->wmi.info_by_id[i] = temp_sensor;
0601 break;
0602 }
0603 }
0604
0605 dev_dbg(dev, "board has %d sensors",
0606 sensor_data->wmi.sensor_count);
0607
0608 hwdev = devm_hwmon_device_register_with_info(dev, "asus_wmi_sensors",
0609 sensor_data, chip_info, NULL);
0610
0611 return PTR_ERR_OR_ZERO(hwdev);
0612 }
0613
0614 static int asus_wmi_probe(struct wmi_device *wdev, const void *context)
0615 {
0616 struct asus_wmi_sensors *sensor_data;
0617 struct device *dev = &wdev->dev;
0618 u32 version = 0;
0619
0620 if (!dmi_check_system(asus_wmi_dmi_table))
0621 return -ENODEV;
0622
0623 sensor_data = devm_kzalloc(dev, sizeof(*sensor_data), GFP_KERNEL);
0624 if (!sensor_data)
0625 return -ENOMEM;
0626
0627 if (asus_wmi_get_version(&version))
0628 return -ENODEV;
0629
0630 if (asus_wmi_get_item_count(&sensor_data->wmi.sensor_count))
0631 return -ENODEV;
0632
0633 if (sensor_data->wmi.sensor_count <= 0 || version < 2) {
0634 dev_info(dev, "version: %u with %d sensors is unsupported\n",
0635 version, sensor_data->wmi.sensor_count);
0636
0637 return -ENODEV;
0638 }
0639
0640 mutex_init(&sensor_data->lock);
0641
0642 dev_set_drvdata(dev, sensor_data);
0643
0644 return asus_wmi_configure_sensor_setup(dev, sensor_data);
0645 }
0646
0647 static const struct wmi_device_id asus_wmi_id_table[] = {
0648 { ASUSWMI_MONITORING_GUID, NULL },
0649 { }
0650 };
0651
0652 static struct wmi_driver asus_sensors_wmi_driver = {
0653 .driver = {
0654 .name = "asus_wmi_sensors",
0655 },
0656 .id_table = asus_wmi_id_table,
0657 .probe = asus_wmi_probe,
0658 };
0659 module_wmi_driver(asus_sensors_wmi_driver);
0660
0661 MODULE_AUTHOR("Ed Brindley <kernel@maidavale.org>");
0662 MODULE_DESCRIPTION("Asus WMI Sensors Driver");
0663 MODULE_LICENSE("GPL");