431 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * hwmon driver for Gigabyte AORUS Waterforce AIO CPU coolers: X240, X280 and X360.4 *5 * Copyright 2023 Aleksa Savic <savicaleksa83@gmail.com>6 */7 8#include <linux/debugfs.h>9#include <linux/hid.h>10#include <linux/hwmon.h>11#include <linux/jiffies.h>12#include <linux/module.h>13#include <linux/spinlock.h>14#include <linux/unaligned.h>15 16#define DRIVER_NAME "gigabyte_waterforce"17 18#define USB_VENDOR_ID_GIGABYTE 0x104419#define USB_PRODUCT_ID_WATERFORCE 0x7a4d /* Gigabyte AORUS WATERFORCE X240, X280 and X360 */20 21#define STATUS_VALIDITY (2 * 1000) /* ms */22#define MAX_REPORT_LENGTH 614423 24#define WATERFORCE_TEMP_SENSOR 0xD25#define WATERFORCE_FAN_SPEED 0x0226#define WATERFORCE_PUMP_SPEED 0x0527#define WATERFORCE_FAN_DUTY 0x0828#define WATERFORCE_PUMP_DUTY 0x0929 30/* Control commands, inner offsets and lengths */31static const u8 get_status_cmd[] = { 0x99, 0xDA };32 33#define FIRMWARE_VER_START_OFFSET_1 234#define FIRMWARE_VER_START_OFFSET_2 335static const u8 get_firmware_ver_cmd[] = { 0x99, 0xD6 };36 37/* Command lengths */38#define GET_STATUS_CMD_LENGTH 239#define GET_FIRMWARE_VER_CMD_LENGTH 240 41static const char *const waterforce_temp_label[] = {42 "Coolant temp"43};44 45static const char *const waterforce_speed_label[] = {46 "Fan speed",47 "Pump speed"48};49 50struct waterforce_data {51 struct hid_device *hdev;52 struct device *hwmon_dev;53 struct dentry *debugfs;54 /* For locking access to buffer */55 struct mutex buffer_lock;56 /* For queueing multiple readers */57 struct mutex status_report_request_mutex;58 /* For reinitializing the completion below */59 spinlock_t status_report_request_lock;60 struct completion status_report_received;61 struct completion fw_version_processed;62 63 /* Sensor data */64 s32 temp_input[1];65 u16 speed_input[2]; /* Fan and pump speed in RPM */66 u8 duty_input[2]; /* Fan and pump duty in 0-100% */67 68 u8 *buffer;69 int firmware_version;70 unsigned long updated; /* jiffies */71};72 73static umode_t waterforce_is_visible(const void *data,74 enum hwmon_sensor_types type, u32 attr, int channel)75{76 switch (type) {77 case hwmon_temp:78 switch (attr) {79 case hwmon_temp_label:80 case hwmon_temp_input:81 return 0444;82 default:83 break;84 }85 break;86 case hwmon_fan:87 switch (attr) {88 case hwmon_fan_label:89 case hwmon_fan_input:90 return 0444;91 default:92 break;93 }94 break;95 case hwmon_pwm:96 switch (attr) {97 case hwmon_pwm_input:98 return 0444;99 default:100 break;101 }102 break;103 default:104 break;105 }106 107 return 0;108}109 110/* Writes the command to the device with the rest of the report filled with zeroes */111static int waterforce_write_expanded(struct waterforce_data *priv, const u8 *cmd, int cmd_length)112{113 int ret;114 115 mutex_lock(&priv->buffer_lock);116 117 memcpy_and_pad(priv->buffer, MAX_REPORT_LENGTH, cmd, cmd_length, 0x00);118 ret = hid_hw_output_report(priv->hdev, priv->buffer, MAX_REPORT_LENGTH);119 120 mutex_unlock(&priv->buffer_lock);121 return ret;122}123 124static int waterforce_get_status(struct waterforce_data *priv)125{126 int ret = mutex_lock_interruptible(&priv->status_report_request_mutex);127 128 if (ret < 0)129 return ret;130 131 if (!time_after(jiffies, priv->updated + msecs_to_jiffies(STATUS_VALIDITY))) {132 /* Data is up to date */133 goto unlock_and_return;134 }135 136 /*137 * Disable raw event parsing for a moment to safely reinitialize the138 * completion. Reinit is done because hidraw could have triggered139 * the raw event parsing and marked the priv->status_report_received140 * completion as done.141 */142 spin_lock_bh(&priv->status_report_request_lock);143 reinit_completion(&priv->status_report_received);144 spin_unlock_bh(&priv->status_report_request_lock);145 146 /* Send command for getting status */147 ret = waterforce_write_expanded(priv, get_status_cmd, GET_STATUS_CMD_LENGTH);148 if (ret < 0)149 goto unlock_and_return;150 151 ret = wait_for_completion_interruptible_timeout(&priv->status_report_received,152 msecs_to_jiffies(STATUS_VALIDITY));153 if (ret == 0)154 ret = -ETIMEDOUT;155 156unlock_and_return:157 mutex_unlock(&priv->status_report_request_mutex);158 if (ret < 0)159 return ret;160 161 return 0;162}163 164static int waterforce_read(struct device *dev, enum hwmon_sensor_types type,165 u32 attr, int channel, long *val)166{167 struct waterforce_data *priv = dev_get_drvdata(dev);168 int ret = waterforce_get_status(priv);169 170 if (ret < 0)171 return ret;172 173 switch (type) {174 case hwmon_temp:175 *val = priv->temp_input[channel];176 break;177 case hwmon_fan:178 *val = priv->speed_input[channel];179 break;180 case hwmon_pwm:181 switch (attr) {182 case hwmon_pwm_input:183 *val = DIV_ROUND_CLOSEST(priv->duty_input[channel] * 255, 100);184 break;185 default:186 return -EOPNOTSUPP;187 }188 break;189 default:190 return -EOPNOTSUPP; /* unreachable */191 }192 193 return 0;194}195 196static int waterforce_read_string(struct device *dev, enum hwmon_sensor_types type,197 u32 attr, int channel, const char **str)198{199 switch (type) {200 case hwmon_temp:201 *str = waterforce_temp_label[channel];202 break;203 case hwmon_fan:204 *str = waterforce_speed_label[channel];205 break;206 default:207 return -EOPNOTSUPP; /* unreachable */208 }209 210 return 0;211}212 213static int waterforce_get_fw_ver(struct hid_device *hdev)214{215 struct waterforce_data *priv = hid_get_drvdata(hdev);216 int ret;217 218 ret = waterforce_write_expanded(priv, get_firmware_ver_cmd, GET_FIRMWARE_VER_CMD_LENGTH);219 if (ret < 0)220 return ret;221 222 ret = wait_for_completion_interruptible_timeout(&priv->fw_version_processed,223 msecs_to_jiffies(STATUS_VALIDITY));224 if (ret == 0)225 return -ETIMEDOUT;226 else if (ret < 0)227 return ret;228 229 return 0;230}231 232static const struct hwmon_ops waterforce_hwmon_ops = {233 .is_visible = waterforce_is_visible,234 .read = waterforce_read,235 .read_string = waterforce_read_string236};237 238static const struct hwmon_channel_info *waterforce_info[] = {239 HWMON_CHANNEL_INFO(temp,240 HWMON_T_INPUT | HWMON_T_LABEL),241 HWMON_CHANNEL_INFO(fan,242 HWMON_F_INPUT | HWMON_F_LABEL,243 HWMON_F_INPUT | HWMON_F_LABEL),244 HWMON_CHANNEL_INFO(pwm,245 HWMON_PWM_INPUT,246 HWMON_PWM_INPUT),247 NULL248};249 250static const struct hwmon_chip_info waterforce_chip_info = {251 .ops = &waterforce_hwmon_ops,252 .info = waterforce_info,253};254 255static int waterforce_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data,256 int size)257{258 struct waterforce_data *priv = hid_get_drvdata(hdev);259 260 if (data[0] == get_firmware_ver_cmd[0] && data[1] == get_firmware_ver_cmd[1]) {261 /* Received a firmware version report */262 priv->firmware_version =263 data[FIRMWARE_VER_START_OFFSET_1] * 10 + data[FIRMWARE_VER_START_OFFSET_2];264 265 if (!completion_done(&priv->fw_version_processed))266 complete_all(&priv->fw_version_processed);267 return 0;268 }269 270 if (data[0] != get_status_cmd[0] || data[1] != get_status_cmd[1])271 return 0;272 273 priv->temp_input[0] = data[WATERFORCE_TEMP_SENSOR] * 1000;274 priv->speed_input[0] = get_unaligned_le16(data + WATERFORCE_FAN_SPEED);275 priv->speed_input[1] = get_unaligned_le16(data + WATERFORCE_PUMP_SPEED);276 priv->duty_input[0] = data[WATERFORCE_FAN_DUTY];277 priv->duty_input[1] = data[WATERFORCE_PUMP_DUTY];278 279 spin_lock(&priv->status_report_request_lock);280 if (!completion_done(&priv->status_report_received))281 complete_all(&priv->status_report_received);282 spin_unlock(&priv->status_report_request_lock);283 284 priv->updated = jiffies;285 286 return 0;287}288 289static int firmware_version_show(struct seq_file *seqf, void *unused)290{291 struct waterforce_data *priv = seqf->private;292 293 seq_printf(seqf, "%u\n", priv->firmware_version);294 295 return 0;296}297DEFINE_SHOW_ATTRIBUTE(firmware_version);298 299static void waterforce_debugfs_init(struct waterforce_data *priv)300{301 char name[64];302 303 if (!priv->firmware_version)304 return; /* There's nothing to show in debugfs */305 306 scnprintf(name, sizeof(name), "%s-%s", DRIVER_NAME, dev_name(&priv->hdev->dev));307 308 priv->debugfs = debugfs_create_dir(name, NULL);309 debugfs_create_file("firmware_version", 0444, priv->debugfs, priv, &firmware_version_fops);310}311 312static int waterforce_probe(struct hid_device *hdev, const struct hid_device_id *id)313{314 struct waterforce_data *priv;315 int ret;316 317 priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL);318 if (!priv)319 return -ENOMEM;320 321 priv->hdev = hdev;322 hid_set_drvdata(hdev, priv);323 324 /*325 * Initialize priv->updated to STATUS_VALIDITY seconds in the past, making326 * the initial empty data invalid for waterforce_read() without the need for327 * a special case there.328 */329 priv->updated = jiffies - msecs_to_jiffies(STATUS_VALIDITY);330 331 ret = hid_parse(hdev);332 if (ret) {333 hid_err(hdev, "hid parse failed with %d\n", ret);334 return ret;335 }336 337 /*338 * Enable hidraw so existing user-space tools can continue to work.339 */340 ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);341 if (ret) {342 hid_err(hdev, "hid hw start failed with %d\n", ret);343 return ret;344 }345 346 ret = hid_hw_open(hdev);347 if (ret) {348 hid_err(hdev, "hid hw open failed with %d\n", ret);349 goto fail_and_stop;350 }351 352 priv->buffer = devm_kzalloc(&hdev->dev, MAX_REPORT_LENGTH, GFP_KERNEL);353 if (!priv->buffer) {354 ret = -ENOMEM;355 goto fail_and_close;356 }357 358 mutex_init(&priv->status_report_request_mutex);359 mutex_init(&priv->buffer_lock);360 spin_lock_init(&priv->status_report_request_lock);361 init_completion(&priv->status_report_received);362 init_completion(&priv->fw_version_processed);363 364 hid_device_io_start(hdev);365 ret = waterforce_get_fw_ver(hdev);366 if (ret < 0)367 hid_warn(hdev, "fw version request failed with %d\n", ret);368 369 priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "waterforce",370 priv, &waterforce_chip_info, NULL);371 if (IS_ERR(priv->hwmon_dev)) {372 ret = PTR_ERR(priv->hwmon_dev);373 hid_err(hdev, "hwmon registration failed with %d\n", ret);374 goto fail_and_close;375 }376 377 waterforce_debugfs_init(priv);378 379 return 0;380 381fail_and_close:382 hid_hw_close(hdev);383fail_and_stop:384 hid_hw_stop(hdev);385 return ret;386}387 388static void waterforce_remove(struct hid_device *hdev)389{390 struct waterforce_data *priv = hid_get_drvdata(hdev);391 392 debugfs_remove_recursive(priv->debugfs);393 hwmon_device_unregister(priv->hwmon_dev);394 395 hid_hw_close(hdev);396 hid_hw_stop(hdev);397}398 399static const struct hid_device_id waterforce_table[] = {400 { HID_USB_DEVICE(USB_VENDOR_ID_GIGABYTE, USB_PRODUCT_ID_WATERFORCE) },401 { }402};403 404MODULE_DEVICE_TABLE(hid, waterforce_table);405 406static struct hid_driver waterforce_driver = {407 .name = "waterforce",408 .id_table = waterforce_table,409 .probe = waterforce_probe,410 .remove = waterforce_remove,411 .raw_event = waterforce_raw_event,412};413 414static int __init waterforce_init(void)415{416 return hid_register_driver(&waterforce_driver);417}418 419static void __exit waterforce_exit(void)420{421 hid_unregister_driver(&waterforce_driver);422}423 424/* When compiled into the kernel, initialize after the HID bus */425late_initcall(waterforce_init);426module_exit(waterforce_exit);427 428MODULE_LICENSE("GPL");429MODULE_AUTHOR("Aleksa Savic <savicaleksa83@gmail.com>");430MODULE_DESCRIPTION("Hwmon driver for Gigabyte AORUS Waterforce AIO coolers");431