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