1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2021 Thomas Weißschuh <thomas@weissschuh.net> 4 */ 5 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 6 7 #include <linux/acpi.h> 8 #include <linux/hwmon.h> 9 #include <linux/module.h> 10 #include <linux/wmi.h> 11 12 #define GIGABYTE_WMI_GUID "DEADBEEF-2001-0000-00A0-C90629100000" 13 #define NUM_TEMPERATURE_SENSORS 6 14 15 static u8 usable_sensors_mask; 16 17 enum gigabyte_wmi_commandtype { 18 GIGABYTE_WMI_BUILD_DATE_QUERY = 0x1, 19 GIGABYTE_WMI_MAINBOARD_TYPE_QUERY = 0x2, 20 GIGABYTE_WMI_FIRMWARE_VERSION_QUERY = 0x4, 21 GIGABYTE_WMI_MAINBOARD_NAME_QUERY = 0x5, 22 GIGABYTE_WMI_TEMPERATURE_QUERY = 0x125, 23 }; 24 25 struct gigabyte_wmi_args { 26 u32 arg1; 27 }; 28 29 static int gigabyte_wmi_perform_query(struct wmi_device *wdev, 30 enum gigabyte_wmi_commandtype command, 31 struct gigabyte_wmi_args *args, struct acpi_buffer *out) 32 { 33 const struct acpi_buffer in = { 34 .length = sizeof(*args), 35 .pointer = args, 36 }; 37 38 acpi_status ret = wmidev_evaluate_method(wdev, 0x0, command, &in, out); 39 40 if (ACPI_FAILURE(ret)) 41 return -EIO; 42 43 return 0; 44 } 45 46 static int gigabyte_wmi_query_integer(struct wmi_device *wdev, 47 enum gigabyte_wmi_commandtype command, 48 struct gigabyte_wmi_args *args, u64 *res) 49 { 50 union acpi_object *obj; 51 struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL }; 52 int ret; 53 54 ret = gigabyte_wmi_perform_query(wdev, command, args, &result); 55 if (ret) 56 return ret; 57 obj = result.pointer; 58 if (obj && obj->type == ACPI_TYPE_INTEGER) 59 *res = obj->integer.value; 60 else 61 ret = -EIO; 62 kfree(result.pointer); 63 return ret; 64 } 65 66 static int gigabyte_wmi_temperature(struct wmi_device *wdev, u8 sensor, long *res) 67 { 68 struct gigabyte_wmi_args args = { 69 .arg1 = sensor, 70 }; 71 u64 temp; 72 acpi_status ret; 73 74 ret = gigabyte_wmi_query_integer(wdev, GIGABYTE_WMI_TEMPERATURE_QUERY, &args, &temp); 75 if (ret == 0) { 76 if (temp == 0) 77 return -ENODEV; 78 *res = (s8)temp * 1000; // value is a signed 8-bit integer 79 } 80 return ret; 81 } 82 83 static int gigabyte_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, 84 u32 attr, int channel, long *val) 85 { 86 struct wmi_device *wdev = dev_get_drvdata(dev); 87 88 return gigabyte_wmi_temperature(wdev, channel, val); 89 } 90 91 static umode_t gigabyte_wmi_hwmon_is_visible(const void *data, enum hwmon_sensor_types type, 92 u32 attr, int channel) 93 { 94 return usable_sensors_mask & BIT(channel) ? 0444 : 0; 95 } 96 97 static const struct hwmon_channel_info * const gigabyte_wmi_hwmon_info[] = { 98 HWMON_CHANNEL_INFO(temp, 99 HWMON_T_INPUT, 100 HWMON_T_INPUT, 101 HWMON_T_INPUT, 102 HWMON_T_INPUT, 103 HWMON_T_INPUT, 104 HWMON_T_INPUT), 105 NULL 106 }; 107 108 static const struct hwmon_ops gigabyte_wmi_hwmon_ops = { 109 .read = gigabyte_wmi_hwmon_read, 110 .is_visible = gigabyte_wmi_hwmon_is_visible, 111 }; 112 113 static const struct hwmon_chip_info gigabyte_wmi_hwmon_chip_info = { 114 .ops = &gigabyte_wmi_hwmon_ops, 115 .info = gigabyte_wmi_hwmon_info, 116 }; 117 118 static u8 gigabyte_wmi_detect_sensor_usability(struct wmi_device *wdev) 119 { 120 int i; 121 long temp; 122 u8 r = 0; 123 124 for (i = 0; i < NUM_TEMPERATURE_SENSORS; i++) { 125 if (!gigabyte_wmi_temperature(wdev, i, &temp)) 126 r |= BIT(i); 127 } 128 return r; 129 } 130 131 static int gigabyte_wmi_probe(struct wmi_device *wdev, const void *context) 132 { 133 struct device *hwmon_dev; 134 135 usable_sensors_mask = gigabyte_wmi_detect_sensor_usability(wdev); 136 if (!usable_sensors_mask) { 137 dev_info(&wdev->dev, "No temperature sensors usable"); 138 return -ENODEV; 139 } 140 141 hwmon_dev = devm_hwmon_device_register_with_info(&wdev->dev, "gigabyte_wmi", wdev, 142 &gigabyte_wmi_hwmon_chip_info, NULL); 143 144 return PTR_ERR_OR_ZERO(hwmon_dev); 145 } 146 147 static const struct wmi_device_id gigabyte_wmi_id_table[] = { 148 { GIGABYTE_WMI_GUID, NULL }, 149 { } 150 }; 151 152 static struct wmi_driver gigabyte_wmi_driver = { 153 .driver = { 154 .name = "gigabyte-wmi", 155 }, 156 .id_table = gigabyte_wmi_id_table, 157 .probe = gigabyte_wmi_probe, 158 }; 159 module_wmi_driver(gigabyte_wmi_driver); 160 161 MODULE_DEVICE_TABLE(wmi, gigabyte_wmi_id_table); 162 MODULE_AUTHOR("Thomas Weißschuh <thomas@weissschuh.net>"); 163 MODULE_DESCRIPTION("Gigabyte WMI temperature driver"); 164 MODULE_LICENSE("GPL"); 165