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 15static u8 usable_sensors_mask; 16 17enum 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 25struct gigabyte_wmi_args { 26 u32 arg1; 27}; 28 29static 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 46static 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 66static 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 83static 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 91static 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 97static 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 108static const struct hwmon_ops gigabyte_wmi_hwmon_ops = { 109 .read = gigabyte_wmi_hwmon_read, 110 .is_visible = gigabyte_wmi_hwmon_is_visible, 111}; 112 113static const struct hwmon_chip_info gigabyte_wmi_hwmon_chip_info = { 114 .ops = &gigabyte_wmi_hwmon_ops, 115 .info = gigabyte_wmi_hwmon_info, 116}; 117 118static 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 131static 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 147static const struct wmi_device_id gigabyte_wmi_id_table[] = { 148 { GIGABYTE_WMI_GUID, NULL }, 149 { } 150}; 151 152static 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}; 159module_wmi_driver(gigabyte_wmi_driver); 160 161MODULE_DEVICE_TABLE(wmi, gigabyte_wmi_id_table); 162MODULE_AUTHOR("Thomas Weißschuh <thomas@weissschuh.net>"); 163MODULE_DESCRIPTION("Gigabyte WMI temperature driver"); 164MODULE_LICENSE("GPL"); 165