0001
0002
0003
0004
0005 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0006
0007 #include <linux/acpi.h>
0008 #include <linux/dmi.h>
0009 #include <linux/hwmon.h>
0010 #include <linux/module.h>
0011 #include <linux/wmi.h>
0012
0013 #define GIGABYTE_WMI_GUID "DEADBEEF-2001-0000-00A0-C90629100000"
0014 #define NUM_TEMPERATURE_SENSORS 6
0015
0016 static bool force_load;
0017 module_param(force_load, bool, 0444);
0018 MODULE_PARM_DESC(force_load, "Force loading on unknown platform");
0019
0020 static u8 usable_sensors_mask;
0021
0022 enum gigabyte_wmi_commandtype {
0023 GIGABYTE_WMI_BUILD_DATE_QUERY = 0x1,
0024 GIGABYTE_WMI_MAINBOARD_TYPE_QUERY = 0x2,
0025 GIGABYTE_WMI_FIRMWARE_VERSION_QUERY = 0x4,
0026 GIGABYTE_WMI_MAINBOARD_NAME_QUERY = 0x5,
0027 GIGABYTE_WMI_TEMPERATURE_QUERY = 0x125,
0028 };
0029
0030 struct gigabyte_wmi_args {
0031 u32 arg1;
0032 };
0033
0034 static int gigabyte_wmi_perform_query(struct wmi_device *wdev,
0035 enum gigabyte_wmi_commandtype command,
0036 struct gigabyte_wmi_args *args, struct acpi_buffer *out)
0037 {
0038 const struct acpi_buffer in = {
0039 .length = sizeof(*args),
0040 .pointer = args,
0041 };
0042
0043 acpi_status ret = wmidev_evaluate_method(wdev, 0x0, command, &in, out);
0044
0045 if (ACPI_FAILURE(ret))
0046 return -EIO;
0047
0048 return 0;
0049 }
0050
0051 static int gigabyte_wmi_query_integer(struct wmi_device *wdev,
0052 enum gigabyte_wmi_commandtype command,
0053 struct gigabyte_wmi_args *args, u64 *res)
0054 {
0055 union acpi_object *obj;
0056 struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
0057 int ret;
0058
0059 ret = gigabyte_wmi_perform_query(wdev, command, args, &result);
0060 if (ret)
0061 return ret;
0062 obj = result.pointer;
0063 if (obj && obj->type == ACPI_TYPE_INTEGER)
0064 *res = obj->integer.value;
0065 else
0066 ret = -EIO;
0067 kfree(result.pointer);
0068 return ret;
0069 }
0070
0071 static int gigabyte_wmi_temperature(struct wmi_device *wdev, u8 sensor, long *res)
0072 {
0073 struct gigabyte_wmi_args args = {
0074 .arg1 = sensor,
0075 };
0076 u64 temp;
0077 acpi_status ret;
0078
0079 ret = gigabyte_wmi_query_integer(wdev, GIGABYTE_WMI_TEMPERATURE_QUERY, &args, &temp);
0080 if (ret == 0) {
0081 if (temp == 0)
0082 return -ENODEV;
0083 *res = (s8)temp * 1000;
0084 }
0085 return ret;
0086 }
0087
0088 static int gigabyte_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
0089 u32 attr, int channel, long *val)
0090 {
0091 struct wmi_device *wdev = dev_get_drvdata(dev);
0092
0093 return gigabyte_wmi_temperature(wdev, channel, val);
0094 }
0095
0096 static umode_t gigabyte_wmi_hwmon_is_visible(const void *data, enum hwmon_sensor_types type,
0097 u32 attr, int channel)
0098 {
0099 return usable_sensors_mask & BIT(channel) ? 0444 : 0;
0100 }
0101
0102 static const struct hwmon_channel_info *gigabyte_wmi_hwmon_info[] = {
0103 HWMON_CHANNEL_INFO(temp,
0104 HWMON_T_INPUT,
0105 HWMON_T_INPUT,
0106 HWMON_T_INPUT,
0107 HWMON_T_INPUT,
0108 HWMON_T_INPUT,
0109 HWMON_T_INPUT),
0110 NULL
0111 };
0112
0113 static const struct hwmon_ops gigabyte_wmi_hwmon_ops = {
0114 .read = gigabyte_wmi_hwmon_read,
0115 .is_visible = gigabyte_wmi_hwmon_is_visible,
0116 };
0117
0118 static const struct hwmon_chip_info gigabyte_wmi_hwmon_chip_info = {
0119 .ops = &gigabyte_wmi_hwmon_ops,
0120 .info = gigabyte_wmi_hwmon_info,
0121 };
0122
0123 static u8 gigabyte_wmi_detect_sensor_usability(struct wmi_device *wdev)
0124 {
0125 int i;
0126 long temp;
0127 u8 r = 0;
0128
0129 for (i = 0; i < NUM_TEMPERATURE_SENSORS; i++) {
0130 if (!gigabyte_wmi_temperature(wdev, i, &temp))
0131 r |= BIT(i);
0132 }
0133 return r;
0134 }
0135
0136 #define DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME(name) \
0137 { .matches = { \
0138 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."), \
0139 DMI_EXACT_MATCH(DMI_BOARD_NAME, name), \
0140 }}
0141
0142 static const struct dmi_system_id gigabyte_wmi_known_working_platforms[] = {
0143 DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B450M DS3H-CF"),
0144 DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B450M S2H V2"),
0145 DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550 AORUS ELITE AX V2"),
0146 DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550 AORUS ELITE"),
0147 DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550 AORUS ELITE V2"),
0148 DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550 GAMING X V2"),
0149 DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550I AORUS PRO AX"),
0150 DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550M AORUS PRO-P"),
0151 DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550M DS3H"),
0152 DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B660 GAMING X DDR4"),
0153 DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B660I AORUS PRO DDR4"),
0154 DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("Z390 I AORUS PRO WIFI-CF"),
0155 DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("Z490 AORUS ELITE AC"),
0156 DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("X570 AORUS ELITE"),
0157 DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("X570 AORUS ELITE WIFI"),
0158 DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("X570 GAMING X"),
0159 DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("X570 I AORUS PRO WIFI"),
0160 DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("X570 UD"),
0161 DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("Z690M AORUS ELITE AX DDR4"),
0162 { }
0163 };
0164
0165 static int gigabyte_wmi_probe(struct wmi_device *wdev, const void *context)
0166 {
0167 struct device *hwmon_dev;
0168
0169 if (!dmi_check_system(gigabyte_wmi_known_working_platforms)) {
0170 if (!force_load)
0171 return -ENODEV;
0172 dev_warn(&wdev->dev, "Forcing load on unknown platform");
0173 }
0174
0175 usable_sensors_mask = gigabyte_wmi_detect_sensor_usability(wdev);
0176 if (!usable_sensors_mask) {
0177 dev_info(&wdev->dev, "No temperature sensors usable");
0178 return -ENODEV;
0179 }
0180
0181 hwmon_dev = devm_hwmon_device_register_with_info(&wdev->dev, "gigabyte_wmi", wdev,
0182 &gigabyte_wmi_hwmon_chip_info, NULL);
0183
0184 return PTR_ERR_OR_ZERO(hwmon_dev);
0185 }
0186
0187 static const struct wmi_device_id gigabyte_wmi_id_table[] = {
0188 { GIGABYTE_WMI_GUID, NULL },
0189 { }
0190 };
0191
0192 static struct wmi_driver gigabyte_wmi_driver = {
0193 .driver = {
0194 .name = "gigabyte-wmi",
0195 },
0196 .id_table = gigabyte_wmi_id_table,
0197 .probe = gigabyte_wmi_probe,
0198 };
0199 module_wmi_driver(gigabyte_wmi_driver);
0200
0201 MODULE_DEVICE_TABLE(wmi, gigabyte_wmi_id_table);
0202 MODULE_AUTHOR("Thomas Weißschuh <thomas@weissschuh.net>");
0203 MODULE_DESCRIPTION("Gigabyte WMI temperature driver");
0204 MODULE_LICENSE("GPL");