924 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * corsair-psu.c - Linux driver for Corsair power supplies with HID sensors interface4 * Copyright (C) 2020 Wilken Gottwalt <wilken.gottwalt@posteo.net>5 */6 7#include <linux/completion.h>8#include <linux/debugfs.h>9#include <linux/errno.h>10#include <linux/hid.h>11#include <linux/hwmon.h>12#include <linux/hwmon-sysfs.h>13#include <linux/jiffies.h>14#include <linux/kernel.h>15#include <linux/module.h>16#include <linux/mutex.h>17#include <linux/slab.h>18#include <linux/types.h>19 20/*21 * Corsair protocol for PSUs22 *23 * message size = 64 bytes (request and response, little endian)24 * request:25 * [length][command][param0][param1][paramX]...26 * reply:27 * [echo of length][echo of command][data0][data1][dataX]...28 *29 * - commands are byte sized opcodes30 * - length is the sum of all bytes of the commands/params31 * - the micro-controller of most of these PSUs support concatenation in the request and reply,32 * but it is better to not rely on this (it is also hard to parse)33 * - the driver uses raw events to be accessible from userspace (though this is not really34 * supported, it is just there for convenience, may be removed in the future)35 * - a reply always starts with the length and command in the same order the request used it36 * - length of the reply data is specific to the command used37 * - some of the commands work on a rail and can be switched to a specific rail (0 = 12v,38 * 1 = 5v, 2 = 3.3v)39 * - the format of the init command 0xFE is swapped length/command bytes40 * - parameter bytes amount and values are specific to the command (rail setting is the only41 * one for now that uses non-zero values)42 * - the driver supports debugfs for values not fitting into the hwmon class43 * - not every device class (HXi or RMi) supports all commands44 * - if configured wrong the PSU resets or shuts down, often before actually hitting the45 * reported critical temperature46 * - new models like HX1500i Series 2023 have changes in the reported vendor and product47 * strings, both are slightly longer now, report vendor and product in one string and are48 * the same now49 */50 51#define DRIVER_NAME "corsair-psu"52 53#define REPLY_SIZE 24 /* max length of a reply to a single command */54#define CMD_BUFFER_SIZE 6455#define CMD_TIMEOUT_MS 25056#define SECONDS_PER_HOUR (60 * 60)57#define SECONDS_PER_DAY (SECONDS_PER_HOUR * 24)58#define RAIL_COUNT 3 /* 3v3 + 5v + 12v */59#define TEMP_COUNT 260#define OCP_MULTI_RAIL 0x0261 62#define PSU_CMD_SELECT_RAIL 0x00 /* expects length 2 */63#define PSU_CMD_FAN_PWM 0x3B /* the rest of the commands expect length 3 */64#define PSU_CMD_RAIL_VOLTS_HCRIT 0x4065#define PSU_CMD_RAIL_VOLTS_LCRIT 0x4466#define PSU_CMD_RAIL_AMPS_HCRIT 0x4667#define PSU_CMD_TEMP_HCRIT 0x4F68#define PSU_CMD_IN_VOLTS 0x8869#define PSU_CMD_IN_AMPS 0x8970#define PSU_CMD_RAIL_VOLTS 0x8B71#define PSU_CMD_RAIL_AMPS 0x8C72#define PSU_CMD_TEMP0 0x8D73#define PSU_CMD_TEMP1 0x8E74#define PSU_CMD_FAN 0x9075#define PSU_CMD_RAIL_WATTS 0x9676#define PSU_CMD_VEND_STR 0x9977#define PSU_CMD_PROD_STR 0x9A78#define PSU_CMD_TOTAL_UPTIME 0xD179#define PSU_CMD_UPTIME 0xD280#define PSU_CMD_OCPMODE 0xD881#define PSU_CMD_TOTAL_WATTS 0xEE82#define PSU_CMD_FAN_PWM_ENABLE 0xF083#define PSU_CMD_INIT 0xFE84 85#define L_IN_VOLTS "v_in"86#define L_OUT_VOLTS_12V "v_out +12v"87#define L_OUT_VOLTS_5V "v_out +5v"88#define L_OUT_VOLTS_3_3V "v_out +3.3v"89#define L_IN_AMPS "curr in"90#define L_AMPS_12V "curr +12v"91#define L_AMPS_5V "curr +5v"92#define L_AMPS_3_3V "curr +3.3v"93#define L_FAN "psu fan"94#define L_TEMP0 "vrm temp"95#define L_TEMP1 "case temp"96#define L_WATTS "power total"97#define L_WATTS_12V "power +12v"98#define L_WATTS_5V "power +5v"99#define L_WATTS_3_3V "power +3.3v"100 101static const char *const label_watts[] = {102 L_WATTS,103 L_WATTS_12V,104 L_WATTS_5V,105 L_WATTS_3_3V106};107 108static const char *const label_volts[] = {109 L_IN_VOLTS,110 L_OUT_VOLTS_12V,111 L_OUT_VOLTS_5V,112 L_OUT_VOLTS_3_3V113};114 115static const char *const label_amps[] = {116 L_IN_AMPS,117 L_AMPS_12V,118 L_AMPS_5V,119 L_AMPS_3_3V120};121 122struct corsairpsu_data {123 struct hid_device *hdev;124 struct device *hwmon_dev;125 struct dentry *debugfs;126 struct completion wait_completion;127 struct mutex lock; /* for locking access to cmd_buffer */128 u8 *cmd_buffer;129 char vendor[REPLY_SIZE];130 char product[REPLY_SIZE];131 long temp_crit[TEMP_COUNT];132 long in_crit[RAIL_COUNT];133 long in_lcrit[RAIL_COUNT];134 long curr_crit[RAIL_COUNT];135 u8 temp_crit_support;136 u8 in_crit_support;137 u8 in_lcrit_support;138 u8 curr_crit_support;139 bool in_curr_cmd_support; /* not all commands are supported on every PSU */140};141 142/* some values are SMBus LINEAR11 data which need a conversion */143static int corsairpsu_linear11_to_int(const u16 val, const int scale)144{145 const int exp = ((s16)val) >> 11;146 const int mant = (((s16)(val & 0x7ff)) << 5) >> 5;147 const int result = mant * scale;148 149 return (exp >= 0) ? (result << exp) : (result >> -exp);150}151 152/* the micro-controller uses percentage values to control pwm */153static int corsairpsu_dutycycle_to_pwm(const long dutycycle)154{155 const int result = (256 << 16) / 100;156 157 return (result * dutycycle) >> 16;158}159 160static int corsairpsu_usb_cmd(struct corsairpsu_data *priv, u8 p0, u8 p1, u8 p2, void *data)161{162 unsigned long time;163 int ret;164 165 memset(priv->cmd_buffer, 0, CMD_BUFFER_SIZE);166 priv->cmd_buffer[0] = p0;167 priv->cmd_buffer[1] = p1;168 priv->cmd_buffer[2] = p2;169 170 reinit_completion(&priv->wait_completion);171 172 ret = hid_hw_output_report(priv->hdev, priv->cmd_buffer, CMD_BUFFER_SIZE);173 if (ret < 0)174 return ret;175 176 time = wait_for_completion_timeout(&priv->wait_completion,177 msecs_to_jiffies(CMD_TIMEOUT_MS));178 if (!time)179 return -ETIMEDOUT;180 181 /*182 * at the start of the reply is an echo of the send command/length in the same order it183 * was send, not every command is supported on every device class, if a command is not184 * supported, the length value in the reply is okay, but the command value is set to 0185 */186 if (p0 != priv->cmd_buffer[0] || p1 != priv->cmd_buffer[1])187 return -EOPNOTSUPP;188 189 if (data)190 memcpy(data, priv->cmd_buffer + 2, REPLY_SIZE);191 192 return 0;193}194 195static int corsairpsu_init(struct corsairpsu_data *priv)196{197 /*198 * PSU_CMD_INIT uses swapped length/command and expects 2 parameter bytes, this command199 * actually generates a reply, but we don't need it200 */201 return corsairpsu_usb_cmd(priv, PSU_CMD_INIT, 3, 0, NULL);202}203 204static int corsairpsu_fwinfo(struct corsairpsu_data *priv)205{206 int ret;207 208 ret = corsairpsu_usb_cmd(priv, 3, PSU_CMD_VEND_STR, 0, priv->vendor);209 if (ret < 0)210 return ret;211 212 ret = corsairpsu_usb_cmd(priv, 3, PSU_CMD_PROD_STR, 0, priv->product);213 if (ret < 0)214 return ret;215 216 return 0;217}218 219static int corsairpsu_request(struct corsairpsu_data *priv, u8 cmd, u8 rail, void *data)220{221 int ret;222 223 mutex_lock(&priv->lock);224 switch (cmd) {225 case PSU_CMD_RAIL_VOLTS_HCRIT:226 case PSU_CMD_RAIL_VOLTS_LCRIT:227 case PSU_CMD_RAIL_AMPS_HCRIT:228 case PSU_CMD_RAIL_VOLTS:229 case PSU_CMD_RAIL_AMPS:230 case PSU_CMD_RAIL_WATTS:231 ret = corsairpsu_usb_cmd(priv, 2, PSU_CMD_SELECT_RAIL, rail, NULL);232 if (ret < 0)233 goto cmd_fail;234 break;235 default:236 break;237 }238 239 ret = corsairpsu_usb_cmd(priv, 3, cmd, 0, data);240 241cmd_fail:242 mutex_unlock(&priv->lock);243 return ret;244}245 246static int corsairpsu_get_value(struct corsairpsu_data *priv, u8 cmd, u8 rail, long *val)247{248 u8 data[REPLY_SIZE];249 long tmp;250 int ret;251 252 ret = corsairpsu_request(priv, cmd, rail, data);253 if (ret < 0)254 return ret;255 256 /*257 * the biggest value here comes from the uptime command and to exceed MAXINT total uptime258 * needs to be about 68 years, the rest are u16 values and the biggest value coming out of259 * the LINEAR11 conversion are the watts values which are about 1500 for the strongest psu260 * supported (HX1500i)261 */262 tmp = ((long)data[3] << 24) + (data[2] << 16) + (data[1] << 8) + data[0];263 switch (cmd) {264 case PSU_CMD_RAIL_VOLTS_HCRIT:265 case PSU_CMD_RAIL_VOLTS_LCRIT:266 case PSU_CMD_RAIL_AMPS_HCRIT:267 case PSU_CMD_TEMP_HCRIT:268 case PSU_CMD_IN_VOLTS:269 case PSU_CMD_IN_AMPS:270 case PSU_CMD_RAIL_VOLTS:271 case PSU_CMD_RAIL_AMPS:272 case PSU_CMD_TEMP0:273 case PSU_CMD_TEMP1:274 *val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1000);275 break;276 case PSU_CMD_FAN:277 *val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1);278 break;279 case PSU_CMD_FAN_PWM_ENABLE:280 *val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1);281 /*282 * 0 = automatic mode, means the micro-controller controls the fan using a plan283 * which can be modified, but changing this plan is not supported by this284 * driver, the matching PWM mode is automatic fan speed control = PWM 2285 * 1 = fixed mode, fan runs at a fixed speed represented by a percentage286 * value 0-100, this matches the PWM manual fan speed control = PWM 1287 * technically there is no PWM no fan speed control mode, it would be a combination288 * of 1 at 100%289 */290 if (*val == 0)291 *val = 2;292 break;293 case PSU_CMD_FAN_PWM:294 *val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1);295 *val = corsairpsu_dutycycle_to_pwm(*val);296 break;297 case PSU_CMD_RAIL_WATTS:298 case PSU_CMD_TOTAL_WATTS:299 *val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1000000);300 break;301 case PSU_CMD_TOTAL_UPTIME:302 case PSU_CMD_UPTIME:303 case PSU_CMD_OCPMODE:304 *val = tmp;305 break;306 default:307 ret = -EOPNOTSUPP;308 break;309 }310 311 return ret;312}313 314static void corsairpsu_get_criticals(struct corsairpsu_data *priv)315{316 long tmp;317 int rail;318 319 for (rail = 0; rail < TEMP_COUNT; ++rail) {320 if (!corsairpsu_get_value(priv, PSU_CMD_TEMP_HCRIT, rail, &tmp)) {321 priv->temp_crit_support |= BIT(rail);322 priv->temp_crit[rail] = tmp;323 }324 }325 326 for (rail = 0; rail < RAIL_COUNT; ++rail) {327 if (!corsairpsu_get_value(priv, PSU_CMD_RAIL_VOLTS_HCRIT, rail, &tmp)) {328 priv->in_crit_support |= BIT(rail);329 priv->in_crit[rail] = tmp;330 }331 332 if (!corsairpsu_get_value(priv, PSU_CMD_RAIL_VOLTS_LCRIT, rail, &tmp)) {333 priv->in_lcrit_support |= BIT(rail);334 priv->in_lcrit[rail] = tmp;335 }336 337 if (!corsairpsu_get_value(priv, PSU_CMD_RAIL_AMPS_HCRIT, rail, &tmp)) {338 priv->curr_crit_support |= BIT(rail);339 priv->curr_crit[rail] = tmp;340 }341 }342}343 344static void corsairpsu_check_cmd_support(struct corsairpsu_data *priv)345{346 long tmp;347 348 priv->in_curr_cmd_support = !corsairpsu_get_value(priv, PSU_CMD_IN_AMPS, 0, &tmp);349}350 351static umode_t corsairpsu_hwmon_temp_is_visible(const struct corsairpsu_data *priv, u32 attr,352 int channel)353{354 umode_t res = 0444;355 356 switch (attr) {357 case hwmon_temp_input:358 case hwmon_temp_label:359 case hwmon_temp_crit:360 if (channel > 0 && !(priv->temp_crit_support & BIT(channel - 1)))361 res = 0;362 break;363 default:364 break;365 }366 367 return res;368}369 370static umode_t corsairpsu_hwmon_fan_is_visible(const struct corsairpsu_data *priv, u32 attr,371 int channel)372{373 switch (attr) {374 case hwmon_fan_input:375 case hwmon_fan_label:376 return 0444;377 default:378 return 0;379 }380}381 382static umode_t corsairpsu_hwmon_pwm_is_visible(const struct corsairpsu_data *priv, u32 attr,383 int channel)384{385 switch (attr) {386 case hwmon_pwm_input:387 case hwmon_pwm_enable:388 return 0444;389 default:390 return 0;391 }392}393 394static umode_t corsairpsu_hwmon_power_is_visible(const struct corsairpsu_data *priv, u32 attr,395 int channel)396{397 switch (attr) {398 case hwmon_power_input:399 case hwmon_power_label:400 return 0444;401 default:402 return 0;403 }404}405 406static umode_t corsairpsu_hwmon_in_is_visible(const struct corsairpsu_data *priv, u32 attr,407 int channel)408{409 umode_t res = 0444;410 411 switch (attr) {412 case hwmon_in_input:413 case hwmon_in_label:414 case hwmon_in_crit:415 if (channel > 0 && !(priv->in_crit_support & BIT(channel - 1)))416 res = 0;417 break;418 case hwmon_in_lcrit:419 if (channel > 0 && !(priv->in_lcrit_support & BIT(channel - 1)))420 res = 0;421 break;422 default:423 break;424 }425 426 return res;427}428 429static umode_t corsairpsu_hwmon_curr_is_visible(const struct corsairpsu_data *priv, u32 attr,430 int channel)431{432 umode_t res = 0444;433 434 switch (attr) {435 case hwmon_curr_input:436 if (channel == 0 && !priv->in_curr_cmd_support)437 res = 0;438 break;439 case hwmon_curr_label:440 case hwmon_curr_crit:441 if (channel > 0 && !(priv->curr_crit_support & BIT(channel - 1)))442 res = 0;443 break;444 default:445 break;446 }447 448 return res;449}450 451static umode_t corsairpsu_hwmon_ops_is_visible(const void *data, enum hwmon_sensor_types type,452 u32 attr, int channel)453{454 const struct corsairpsu_data *priv = data;455 456 switch (type) {457 case hwmon_temp:458 return corsairpsu_hwmon_temp_is_visible(priv, attr, channel);459 case hwmon_fan:460 return corsairpsu_hwmon_fan_is_visible(priv, attr, channel);461 case hwmon_pwm:462 return corsairpsu_hwmon_pwm_is_visible(priv, attr, channel);463 case hwmon_power:464 return corsairpsu_hwmon_power_is_visible(priv, attr, channel);465 case hwmon_in:466 return corsairpsu_hwmon_in_is_visible(priv, attr, channel);467 case hwmon_curr:468 return corsairpsu_hwmon_curr_is_visible(priv, attr, channel);469 default:470 return 0;471 }472}473 474static int corsairpsu_hwmon_temp_read(struct corsairpsu_data *priv, u32 attr, int channel,475 long *val)476{477 int err = -EOPNOTSUPP;478 479 switch (attr) {480 case hwmon_temp_input:481 return corsairpsu_get_value(priv, channel ? PSU_CMD_TEMP1 : PSU_CMD_TEMP0,482 channel, val);483 case hwmon_temp_crit:484 *val = priv->temp_crit[channel];485 err = 0;486 break;487 default:488 break;489 }490 491 return err;492}493 494static int corsairpsu_hwmon_pwm_read(struct corsairpsu_data *priv, u32 attr, int channel, long *val)495{496 switch (attr) {497 case hwmon_pwm_input:498 return corsairpsu_get_value(priv, PSU_CMD_FAN_PWM, 0, val);499 case hwmon_pwm_enable:500 return corsairpsu_get_value(priv, PSU_CMD_FAN_PWM_ENABLE, 0, val);501 default:502 break;503 }504 505 return -EOPNOTSUPP;506}507 508static int corsairpsu_hwmon_power_read(struct corsairpsu_data *priv, u32 attr, int channel,509 long *val)510{511 if (attr == hwmon_power_input) {512 switch (channel) {513 case 0:514 return corsairpsu_get_value(priv, PSU_CMD_TOTAL_WATTS, 0, val);515 case 1 ... 3:516 return corsairpsu_get_value(priv, PSU_CMD_RAIL_WATTS, channel - 1, val);517 default:518 break;519 }520 }521 522 return -EOPNOTSUPP;523}524 525static int corsairpsu_hwmon_in_read(struct corsairpsu_data *priv, u32 attr, int channel, long *val)526{527 int err = -EOPNOTSUPP;528 529 switch (attr) {530 case hwmon_in_input:531 switch (channel) {532 case 0:533 return corsairpsu_get_value(priv, PSU_CMD_IN_VOLTS, 0, val);534 case 1 ... 3:535 return corsairpsu_get_value(priv, PSU_CMD_RAIL_VOLTS, channel - 1, val);536 default:537 break;538 }539 break;540 case hwmon_in_crit:541 *val = priv->in_crit[channel - 1];542 err = 0;543 break;544 case hwmon_in_lcrit:545 *val = priv->in_lcrit[channel - 1];546 err = 0;547 break;548 }549 550 return err;551}552 553static int corsairpsu_hwmon_curr_read(struct corsairpsu_data *priv, u32 attr, int channel,554 long *val)555{556 int err = -EOPNOTSUPP;557 558 switch (attr) {559 case hwmon_curr_input:560 switch (channel) {561 case 0:562 return corsairpsu_get_value(priv, PSU_CMD_IN_AMPS, 0, val);563 case 1 ... 3:564 return corsairpsu_get_value(priv, PSU_CMD_RAIL_AMPS, channel - 1, val);565 default:566 break;567 }568 break;569 case hwmon_curr_crit:570 *val = priv->curr_crit[channel - 1];571 err = 0;572 break;573 default:574 break;575 }576 577 return err;578}579 580static int corsairpsu_hwmon_ops_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,581 int channel, long *val)582{583 struct corsairpsu_data *priv = dev_get_drvdata(dev);584 585 switch (type) {586 case hwmon_temp:587 return corsairpsu_hwmon_temp_read(priv, attr, channel, val);588 case hwmon_fan:589 if (attr == hwmon_fan_input)590 return corsairpsu_get_value(priv, PSU_CMD_FAN, 0, val);591 return -EOPNOTSUPP;592 case hwmon_pwm:593 return corsairpsu_hwmon_pwm_read(priv, attr, channel, val);594 case hwmon_power:595 return corsairpsu_hwmon_power_read(priv, attr, channel, val);596 case hwmon_in:597 return corsairpsu_hwmon_in_read(priv, attr, channel, val);598 case hwmon_curr:599 return corsairpsu_hwmon_curr_read(priv, attr, channel, val);600 default:601 return -EOPNOTSUPP;602 }603}604 605static int corsairpsu_hwmon_ops_read_string(struct device *dev, enum hwmon_sensor_types type,606 u32 attr, int channel, const char **str)607{608 if (type == hwmon_temp && attr == hwmon_temp_label) {609 *str = channel ? L_TEMP1 : L_TEMP0;610 return 0;611 } else if (type == hwmon_fan && attr == hwmon_fan_label) {612 *str = L_FAN;613 return 0;614 } else if (type == hwmon_power && attr == hwmon_power_label && channel < 4) {615 *str = label_watts[channel];616 return 0;617 } else if (type == hwmon_in && attr == hwmon_in_label && channel < 4) {618 *str = label_volts[channel];619 return 0;620 } else if (type == hwmon_curr && attr == hwmon_curr_label && channel < 4) {621 *str = label_amps[channel];622 return 0;623 }624 625 return -EOPNOTSUPP;626}627 628static const struct hwmon_ops corsairpsu_hwmon_ops = {629 .is_visible = corsairpsu_hwmon_ops_is_visible,630 .read = corsairpsu_hwmon_ops_read,631 .read_string = corsairpsu_hwmon_ops_read_string,632};633 634static const struct hwmon_channel_info *const corsairpsu_info[] = {635 HWMON_CHANNEL_INFO(chip,636 HWMON_C_REGISTER_TZ),637 HWMON_CHANNEL_INFO(temp,638 HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT,639 HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT),640 HWMON_CHANNEL_INFO(fan,641 HWMON_F_INPUT | HWMON_F_LABEL),642 HWMON_CHANNEL_INFO(pwm,643 HWMON_PWM_INPUT | HWMON_PWM_ENABLE),644 HWMON_CHANNEL_INFO(power,645 HWMON_P_INPUT | HWMON_P_LABEL,646 HWMON_P_INPUT | HWMON_P_LABEL,647 HWMON_P_INPUT | HWMON_P_LABEL,648 HWMON_P_INPUT | HWMON_P_LABEL),649 HWMON_CHANNEL_INFO(in,650 HWMON_I_INPUT | HWMON_I_LABEL,651 HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_LCRIT | HWMON_I_CRIT,652 HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_LCRIT | HWMON_I_CRIT,653 HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_LCRIT | HWMON_I_CRIT),654 HWMON_CHANNEL_INFO(curr,655 HWMON_C_INPUT | HWMON_C_LABEL,656 HWMON_C_INPUT | HWMON_C_LABEL | HWMON_C_CRIT,657 HWMON_C_INPUT | HWMON_C_LABEL | HWMON_C_CRIT,658 HWMON_C_INPUT | HWMON_C_LABEL | HWMON_C_CRIT),659 NULL660};661 662static const struct hwmon_chip_info corsairpsu_chip_info = {663 .ops = &corsairpsu_hwmon_ops,664 .info = corsairpsu_info,665};666 667#ifdef CONFIG_DEBUG_FS668 669static void print_uptime(struct seq_file *seqf, u8 cmd)670{671 struct corsairpsu_data *priv = seqf->private;672 long val;673 int ret;674 675 ret = corsairpsu_get_value(priv, cmd, 0, &val);676 if (ret < 0) {677 seq_puts(seqf, "N/A\n");678 return;679 }680 681 if (val > SECONDS_PER_DAY) {682 seq_printf(seqf, "%ld day(s), %02ld:%02ld:%02ld\n", val / SECONDS_PER_DAY,683 val % SECONDS_PER_DAY / SECONDS_PER_HOUR, val % SECONDS_PER_HOUR / 60,684 val % 60);685 return;686 }687 688 seq_printf(seqf, "%02ld:%02ld:%02ld\n", val % SECONDS_PER_DAY / SECONDS_PER_HOUR,689 val % SECONDS_PER_HOUR / 60, val % 60);690}691 692static int uptime_show(struct seq_file *seqf, void *unused)693{694 print_uptime(seqf, PSU_CMD_UPTIME);695 696 return 0;697}698DEFINE_SHOW_ATTRIBUTE(uptime);699 700static int uptime_total_show(struct seq_file *seqf, void *unused)701{702 print_uptime(seqf, PSU_CMD_TOTAL_UPTIME);703 704 return 0;705}706DEFINE_SHOW_ATTRIBUTE(uptime_total);707 708static int vendor_show(struct seq_file *seqf, void *unused)709{710 struct corsairpsu_data *priv = seqf->private;711 712 seq_printf(seqf, "%s\n", priv->vendor);713 714 return 0;715}716DEFINE_SHOW_ATTRIBUTE(vendor);717 718static int product_show(struct seq_file *seqf, void *unused)719{720 struct corsairpsu_data *priv = seqf->private;721 722 seq_printf(seqf, "%s\n", priv->product);723 724 return 0;725}726DEFINE_SHOW_ATTRIBUTE(product);727 728static int ocpmode_show(struct seq_file *seqf, void *unused)729{730 struct corsairpsu_data *priv = seqf->private;731 long val;732 int ret;733 734 /*735 * The rail mode is switchable on the fly. The RAW interface can be used for this. But it736 * will not be included here, because I consider it somewhat dangerous for the health of the737 * PSU. The returned value can be a bogus one, if the PSU is in the process of switching and738 * getting of the value itself can also fail during this. Because of this every other value739 * than OCP_MULTI_RAIL can be considered as "single rail".740 */741 ret = corsairpsu_get_value(priv, PSU_CMD_OCPMODE, 0, &val);742 if (ret < 0)743 seq_puts(seqf, "N/A\n");744 else745 seq_printf(seqf, "%s\n", (val == OCP_MULTI_RAIL) ? "multi rail" : "single rail");746 747 return 0;748}749DEFINE_SHOW_ATTRIBUTE(ocpmode);750 751static void corsairpsu_debugfs_init(struct corsairpsu_data *priv)752{753 char name[32];754 755 scnprintf(name, sizeof(name), "%s-%s", DRIVER_NAME, dev_name(&priv->hdev->dev));756 757 priv->debugfs = debugfs_create_dir(name, NULL);758 debugfs_create_file("uptime", 0444, priv->debugfs, priv, &uptime_fops);759 debugfs_create_file("uptime_total", 0444, priv->debugfs, priv, &uptime_total_fops);760 debugfs_create_file("vendor", 0444, priv->debugfs, priv, &vendor_fops);761 debugfs_create_file("product", 0444, priv->debugfs, priv, &product_fops);762 debugfs_create_file("ocpmode", 0444, priv->debugfs, priv, &ocpmode_fops);763}764 765#else766 767static void corsairpsu_debugfs_init(struct corsairpsu_data *priv)768{769}770 771#endif772 773static int corsairpsu_probe(struct hid_device *hdev, const struct hid_device_id *id)774{775 struct corsairpsu_data *priv;776 int ret;777 778 priv = devm_kzalloc(&hdev->dev, sizeof(struct corsairpsu_data), GFP_KERNEL);779 if (!priv)780 return -ENOMEM;781 782 priv->cmd_buffer = devm_kmalloc(&hdev->dev, CMD_BUFFER_SIZE, GFP_KERNEL);783 if (!priv->cmd_buffer)784 return -ENOMEM;785 786 ret = hid_parse(hdev);787 if (ret)788 return ret;789 790 ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);791 if (ret)792 return ret;793 794 ret = hid_hw_open(hdev);795 if (ret)796 goto fail_and_stop;797 798 priv->hdev = hdev;799 hid_set_drvdata(hdev, priv);800 mutex_init(&priv->lock);801 init_completion(&priv->wait_completion);802 803 hid_device_io_start(hdev);804 805 ret = corsairpsu_init(priv);806 if (ret < 0) {807 dev_err(&hdev->dev, "unable to initialize device (%d)\n", ret);808 goto fail_and_stop;809 }810 811 ret = corsairpsu_fwinfo(priv);812 if (ret < 0) {813 dev_err(&hdev->dev, "unable to query firmware (%d)\n", ret);814 goto fail_and_stop;815 }816 817 corsairpsu_get_criticals(priv);818 corsairpsu_check_cmd_support(priv);819 820 priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "corsairpsu", priv,821 &corsairpsu_chip_info, NULL);822 823 if (IS_ERR(priv->hwmon_dev)) {824 ret = PTR_ERR(priv->hwmon_dev);825 goto fail_and_close;826 }827 828 corsairpsu_debugfs_init(priv);829 830 return 0;831 832fail_and_close:833 hid_hw_close(hdev);834fail_and_stop:835 hid_hw_stop(hdev);836 return ret;837}838 839static void corsairpsu_remove(struct hid_device *hdev)840{841 struct corsairpsu_data *priv = hid_get_drvdata(hdev);842 843 debugfs_remove_recursive(priv->debugfs);844 hwmon_device_unregister(priv->hwmon_dev);845 hid_hw_close(hdev);846 hid_hw_stop(hdev);847}848 849static int corsairpsu_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data,850 int size)851{852 struct corsairpsu_data *priv = hid_get_drvdata(hdev);853 854 if (completion_done(&priv->wait_completion))855 return 0;856 857 memcpy(priv->cmd_buffer, data, min(CMD_BUFFER_SIZE, size));858 complete(&priv->wait_completion);859 860 return 0;861}862 863#ifdef CONFIG_PM864static int corsairpsu_resume(struct hid_device *hdev)865{866 struct corsairpsu_data *priv = hid_get_drvdata(hdev);867 868 /* some PSUs turn off the microcontroller during standby, so a reinit is required */869 return corsairpsu_init(priv);870}871#endif872 873static const struct hid_device_id corsairpsu_idtable[] = {874 { HID_USB_DEVICE(0x1b1c, 0x1c03) }, /* Corsair HX550i */875 { HID_USB_DEVICE(0x1b1c, 0x1c04) }, /* Corsair HX650i */876 { HID_USB_DEVICE(0x1b1c, 0x1c05) }, /* Corsair HX750i */877 { HID_USB_DEVICE(0x1b1c, 0x1c06) }, /* Corsair HX850i */878 { HID_USB_DEVICE(0x1b1c, 0x1c07) }, /* Corsair HX1000i Legacy */879 { HID_USB_DEVICE(0x1b1c, 0x1c08) }, /* Corsair HX1200i Legacy */880 { HID_USB_DEVICE(0x1b1c, 0x1c09) }, /* Corsair RM550i */881 { HID_USB_DEVICE(0x1b1c, 0x1c0a) }, /* Corsair RM650i */882 { HID_USB_DEVICE(0x1b1c, 0x1c0b) }, /* Corsair RM750i */883 { HID_USB_DEVICE(0x1b1c, 0x1c0c) }, /* Corsair RM850i */884 { HID_USB_DEVICE(0x1b1c, 0x1c0d) }, /* Corsair RM1000i */885 { HID_USB_DEVICE(0x1b1c, 0x1c1e) }, /* Corsair HX1000i Series 2023 */886 { HID_USB_DEVICE(0x1b1c, 0x1c1f) }, /* Corsair HX1500i Legacy and Series 2023 */887 { HID_USB_DEVICE(0x1b1c, 0x1c23) }, /* Corsair HX1200i Series 2023 */888 { },889};890MODULE_DEVICE_TABLE(hid, corsairpsu_idtable);891 892static struct hid_driver corsairpsu_driver = {893 .name = DRIVER_NAME,894 .id_table = corsairpsu_idtable,895 .probe = corsairpsu_probe,896 .remove = corsairpsu_remove,897 .raw_event = corsairpsu_raw_event,898#ifdef CONFIG_PM899 .resume = corsairpsu_resume,900 .reset_resume = corsairpsu_resume,901#endif902};903 904static int __init corsair_init(void)905{906 return hid_register_driver(&corsairpsu_driver);907}908 909static void __exit corsair_exit(void)910{911 hid_unregister_driver(&corsairpsu_driver);912}913 914/*915 * With module_init() the driver would load before the HID bus when916 * built-in, so use late_initcall() instead.917 */918late_initcall(corsair_init);919module_exit(corsair_exit);920 921MODULE_LICENSE("GPL");922MODULE_AUTHOR("Wilken Gottwalt <wilken.gottwalt@posteo.net>");923MODULE_DESCRIPTION("Linux driver for Corsair power supplies with HID sensors interface");924