Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  Copyright (C) 2021 Thomas Weißschuh <thomas@weissschuh.net>
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; // value is a signed 8-bit integer
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");