Lines Matching +full:32 +full:- +full:rail

1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * corsair-psu.c - Linux driver for Corsair power supplies with HID sensors interface
12 #include <linux/hwmon-sysfs.h>
29 * - commands are byte sized opcodes
30 * - length is the sum of all bytes of the commands/params
31 * - the micro-controller of most of these PSUs support concatenation in the request and reply,
33 * - the driver uses raw events to be accessible from userspace (though this is not really
35 * - a reply always starts with the length and command in the same order the request used it
36 * - length of the reply data is specific to the command used
37 * - some of the commands work on a rail and can be switched to a specific rail (0 = 12v,
39 * - the format of the init command 0xFE is swapped length/command bytes
40 * - parameter bytes amount and values are specific to the command (rail setting is the only
41 * one for now that uses non-zero values)
42 * - the driver supports debugfs for values not fitting into the hwmon class
43 * - not every device class (HXi or RMi) supports all commands
44 * - if configured wrong the PSU resets or shuts down, often before actually hitting the
46 * - new models like HX1500i Series 2023 have changes in the reported vendor and product
51 #define DRIVER_NAME "corsair-psu"
149 return (exp >= 0) ? (result << exp) : (result >> -exp); in corsairpsu_linear11_to_int()
152 /* the micro-controller uses percentage values to control pwm */
165 memset(priv->cmd_buffer, 0, CMD_BUFFER_SIZE); in corsairpsu_usb_cmd()
166 priv->cmd_buffer[0] = p0; in corsairpsu_usb_cmd()
167 priv->cmd_buffer[1] = p1; in corsairpsu_usb_cmd()
168 priv->cmd_buffer[2] = p2; in corsairpsu_usb_cmd()
170 reinit_completion(&priv->wait_completion); in corsairpsu_usb_cmd()
172 ret = hid_hw_output_report(priv->hdev, priv->cmd_buffer, CMD_BUFFER_SIZE); in corsairpsu_usb_cmd()
176 time = wait_for_completion_timeout(&priv->wait_completion, in corsairpsu_usb_cmd()
179 return -ETIMEDOUT; in corsairpsu_usb_cmd()
186 if (p0 != priv->cmd_buffer[0] || p1 != priv->cmd_buffer[1]) in corsairpsu_usb_cmd()
187 return -EOPNOTSUPP; in corsairpsu_usb_cmd()
190 memcpy(data, priv->cmd_buffer + 2, REPLY_SIZE); in corsairpsu_usb_cmd()
208 ret = corsairpsu_usb_cmd(priv, 3, PSU_CMD_VEND_STR, 0, priv->vendor); in corsairpsu_fwinfo()
212 ret = corsairpsu_usb_cmd(priv, 3, PSU_CMD_PROD_STR, 0, priv->product); in corsairpsu_fwinfo()
219 static int corsairpsu_request(struct corsairpsu_data *priv, u8 cmd, u8 rail, void *data) in corsairpsu_request() argument
223 mutex_lock(&priv->lock); in corsairpsu_request()
231 ret = corsairpsu_usb_cmd(priv, 2, PSU_CMD_SELECT_RAIL, rail, NULL); in corsairpsu_request()
242 mutex_unlock(&priv->lock); in corsairpsu_request()
246 static int corsairpsu_get_value(struct corsairpsu_data *priv, u8 cmd, u8 rail, long *val) in corsairpsu_get_value() argument
252 ret = corsairpsu_request(priv, cmd, rail, data); in corsairpsu_get_value()
282 * 0 = automatic mode, means the micro-controller controls the fan using a plan in corsairpsu_get_value()
286 * value 0-100, this matches the PWM manual fan speed control = PWM 1 in corsairpsu_get_value()
307 ret = -EOPNOTSUPP; in corsairpsu_get_value()
317 int rail; in corsairpsu_get_criticals() local
319 for (rail = 0; rail < TEMP_COUNT; ++rail) { in corsairpsu_get_criticals()
320 if (!corsairpsu_get_value(priv, PSU_CMD_TEMP_HCRIT, rail, &tmp)) { in corsairpsu_get_criticals()
321 priv->temp_crit_support |= BIT(rail); in corsairpsu_get_criticals()
322 priv->temp_crit[rail] = tmp; in corsairpsu_get_criticals()
326 for (rail = 0; rail < RAIL_COUNT; ++rail) { in corsairpsu_get_criticals()
327 if (!corsairpsu_get_value(priv, PSU_CMD_RAIL_VOLTS_HCRIT, rail, &tmp)) { in corsairpsu_get_criticals()
328 priv->in_crit_support |= BIT(rail); in corsairpsu_get_criticals()
329 priv->in_crit[rail] = tmp; in corsairpsu_get_criticals()
332 if (!corsairpsu_get_value(priv, PSU_CMD_RAIL_VOLTS_LCRIT, rail, &tmp)) { in corsairpsu_get_criticals()
333 priv->in_lcrit_support |= BIT(rail); in corsairpsu_get_criticals()
334 priv->in_lcrit[rail] = tmp; in corsairpsu_get_criticals()
337 if (!corsairpsu_get_value(priv, PSU_CMD_RAIL_AMPS_HCRIT, rail, &tmp)) { in corsairpsu_get_criticals()
338 priv->curr_crit_support |= BIT(rail); in corsairpsu_get_criticals()
339 priv->curr_crit[rail] = tmp; in corsairpsu_get_criticals()
348 priv->in_curr_cmd_support = !corsairpsu_get_value(priv, PSU_CMD_IN_AMPS, 0, &tmp); in corsairpsu_check_cmd_support()
360 if (channel > 0 && !(priv->temp_crit_support & BIT(channel - 1))) in corsairpsu_hwmon_temp_is_visible()
415 if (channel > 0 && !(priv->in_crit_support & BIT(channel - 1))) in corsairpsu_hwmon_in_is_visible()
419 if (channel > 0 && !(priv->in_lcrit_support & BIT(channel - 1))) in corsairpsu_hwmon_in_is_visible()
436 if (channel == 0 && !priv->in_curr_cmd_support) in corsairpsu_hwmon_curr_is_visible()
441 if (channel > 0 && !(priv->curr_crit_support & BIT(channel - 1))) in corsairpsu_hwmon_curr_is_visible()
477 int err = -EOPNOTSUPP; in corsairpsu_hwmon_temp_read()
484 *val = priv->temp_crit[channel]; in corsairpsu_hwmon_temp_read()
505 return -EOPNOTSUPP; in corsairpsu_hwmon_pwm_read()
516 return corsairpsu_get_value(priv, PSU_CMD_RAIL_WATTS, channel - 1, val); in corsairpsu_hwmon_power_read()
522 return -EOPNOTSUPP; in corsairpsu_hwmon_power_read()
527 int err = -EOPNOTSUPP; in corsairpsu_hwmon_in_read()
535 return corsairpsu_get_value(priv, PSU_CMD_RAIL_VOLTS, channel - 1, val); in corsairpsu_hwmon_in_read()
541 *val = priv->in_crit[channel - 1]; in corsairpsu_hwmon_in_read()
545 *val = priv->in_lcrit[channel - 1]; in corsairpsu_hwmon_in_read()
556 int err = -EOPNOTSUPP; in corsairpsu_hwmon_curr_read()
564 return corsairpsu_get_value(priv, PSU_CMD_RAIL_AMPS, channel - 1, val); in corsairpsu_hwmon_curr_read()
570 *val = priv->curr_crit[channel - 1]; in corsairpsu_hwmon_curr_read()
591 return -EOPNOTSUPP; in corsairpsu_hwmon_ops_read()
601 return -EOPNOTSUPP; in corsairpsu_hwmon_ops_read()
625 return -EOPNOTSUPP; in corsairpsu_hwmon_ops_read_string()
671 struct corsairpsu_data *priv = seqf->private; in print_uptime()
710 struct corsairpsu_data *priv = seqf->private; in vendor_show()
712 seq_printf(seqf, "%s\n", priv->vendor); in vendor_show()
720 struct corsairpsu_data *priv = seqf->private; in product_show()
722 seq_printf(seqf, "%s\n", priv->product); in product_show()
730 struct corsairpsu_data *priv = seqf->private; in ocpmode_show()
735 * The rail mode is switchable on the fly. The RAW interface can be used for this. But it in ocpmode_show()
739 * than OCP_MULTI_RAIL can be considered as "single rail". in ocpmode_show()
745 seq_printf(seqf, "%s\n", (val == OCP_MULTI_RAIL) ? "multi rail" : "single rail"); in ocpmode_show()
753 char name[32]; in corsairpsu_debugfs_init()
755 scnprintf(name, sizeof(name), "%s-%s", DRIVER_NAME, dev_name(&priv->hdev->dev)); in corsairpsu_debugfs_init()
757 priv->debugfs = debugfs_create_dir(name, NULL); in corsairpsu_debugfs_init()
758 debugfs_create_file("uptime", 0444, priv->debugfs, priv, &uptime_fops); in corsairpsu_debugfs_init()
759 debugfs_create_file("uptime_total", 0444, priv->debugfs, priv, &uptime_total_fops); in corsairpsu_debugfs_init()
760 debugfs_create_file("vendor", 0444, priv->debugfs, priv, &vendor_fops); in corsairpsu_debugfs_init()
761 debugfs_create_file("product", 0444, priv->debugfs, priv, &product_fops); in corsairpsu_debugfs_init()
762 debugfs_create_file("ocpmode", 0444, priv->debugfs, priv, &ocpmode_fops); in corsairpsu_debugfs_init()
778 priv = devm_kzalloc(&hdev->dev, sizeof(struct corsairpsu_data), GFP_KERNEL); in corsairpsu_probe()
780 return -ENOMEM; in corsairpsu_probe()
782 priv->cmd_buffer = devm_kmalloc(&hdev->dev, CMD_BUFFER_SIZE, GFP_KERNEL); in corsairpsu_probe()
783 if (!priv->cmd_buffer) in corsairpsu_probe()
784 return -ENOMEM; in corsairpsu_probe()
798 priv->hdev = hdev; in corsairpsu_probe()
800 mutex_init(&priv->lock); in corsairpsu_probe()
801 init_completion(&priv->wait_completion); in corsairpsu_probe()
807 dev_err(&hdev->dev, "unable to initialize device (%d)\n", ret); in corsairpsu_probe()
813 dev_err(&hdev->dev, "unable to query firmware (%d)\n", ret); in corsairpsu_probe()
820 priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "corsairpsu", priv, in corsairpsu_probe()
823 if (IS_ERR(priv->hwmon_dev)) { in corsairpsu_probe()
824 ret = PTR_ERR(priv->hwmon_dev); in corsairpsu_probe()
843 debugfs_remove_recursive(priv->debugfs); in corsairpsu_remove()
844 hwmon_device_unregister(priv->hwmon_dev); in corsairpsu_remove()
854 if (completion_done(&priv->wait_completion)) in corsairpsu_raw_event()
857 memcpy(priv->cmd_buffer, data, min(CMD_BUFFER_SIZE, size)); in corsairpsu_raw_event()
858 complete(&priv->wait_completion); in corsairpsu_raw_event()
916 * built-in, so use late_initcall() instead.