Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

hwmon: (corsair-cpro) add reading pwm values

This adds the possibility for reading pwm values.
These can not be read if the device is controlled via
fan_target or a fan curve and will return an error in
this case. Since an error is expected, this adds some
rudimentary error handling.

Changes:
- add CTL_GET_FAN_PWM and use it via get_data
- pwm returns -ENODATA if the device returns error 0x12
- fan_target now returns -ENODATA when the driver is
started or a pwm value is set.
- add ccp_get_errno to determine errno from device error.
- get_data now has a parameter to determine whether
to read one or two bytes of data.
- update documentation
- fix missing surname in MAINTAINERS

Signed-off-by: Marius Zachmann <mail@mariuszachmann.de>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>

authored by

Marius Zachmann and committed by
Guenter Roeck
fa4dac3e e4922176

+48 -25
+3 -4
Documentation/hwmon/corsair-cpro.rst
··· 35 35 fan[1-6]_label Shows fan type as detected by the device. 36 36 fan[1-6]_target Sets fan speed target rpm. 37 37 When reading, it reports the last value if it was set by the driver. 38 - Otherwise returns 0. 39 - pwm[1-6] Sets the fan speed. Values from 0-255. 40 - When reading, it reports the last value if it was set by the driver. 41 - Otherwise returns 0. 38 + Otherwise returns an error. 39 + pwm[1-6] Sets the fan speed. Values from 0-255. Can only be read if pwm 40 + was set directly. 42 41 ======================= =====================================================================
+1 -1
MAINTAINERS
··· 4402 4402 F: drivers/hwmon/coretemp.c 4403 4403 4404 4404 CORSAIR-CPRO HARDWARE MONITOR DRIVER 4405 - M: Marius <mail@mariuszachmann.de> 4405 + M: Marius Zachmann <mail@mariuszachmann.de> 4406 4406 L: linux-hwmon@vger.kernel.org 4407 4407 S: Maintained 4408 4408 F: drivers/hwmon/corsair-cpro.c
+44 -20
drivers/hwmon/corsair-cpro.c
··· 36 36 * send: byte 1 is channel, rest zero 37 37 * rcv: returns temp for channel in centi-degree celsius 38 38 * in bytes 1 and 2 39 - * returns 17 in byte 0 if no sensor is connected 39 + * returns 0x11 in byte 0 if no sensor is connected 40 40 */ 41 41 #define CTL_GET_VOLT 0x12 /* 42 42 * send: byte 1 is rail number: 0 = 12v, 1 = 5v, 2 = 3.3v 43 43 * rcv: returns millivolt in bytes 1,2 44 + * returns error 0x10 if request is invalid 44 45 */ 45 46 #define CTL_GET_FAN_CNCT 0x20 /* 46 47 * returns in bytes 1-6 for each fan: ··· 52 51 #define CTL_GET_FAN_RPM 0x21 /* 53 52 * send: byte 1 is channel, rest zero 54 53 * rcv: returns rpm in bytes 1,2 54 + */ 55 + #define CTL_GET_FAN_PWM 0x22 /* 56 + * send: byte 1 is channel, rest zero 57 + * rcv: returns pwm in byte 1 if it was set 58 + * returns error 0x12 if fan is controlled via 59 + * fan_target or fan curve 55 60 */ 56 61 #define CTL_SET_FAN_FPWM 0x23 /* 57 62 * set fixed pwm ··· 80 73 struct completion wait_input_report; 81 74 struct mutex mutex; /* whenever buffer is used, lock before send_usb_cmd */ 82 75 u8 *buffer; 83 - int pwm[6]; 84 76 int target[6]; 85 77 DECLARE_BITMAP(temp_cnct, NUM_TEMP_SENSORS); 86 78 DECLARE_BITMAP(fan_cnct, NUM_FANS); 87 79 char fan_label[6][LABEL_LENGTH]; 88 80 }; 81 + 82 + /* converts response error in buffer to errno */ 83 + static int ccp_get_errno(struct ccp_device *ccp) 84 + { 85 + switch (ccp->buffer[0]) { 86 + case 0x00: /* success */ 87 + return 0; 88 + case 0x01: /* called invalid command */ 89 + return -EOPNOTSUPP; 90 + case 0x10: /* called GET_VOLT / GET_TMP with invalid arguments */ 91 + return -EINVAL; 92 + case 0x11: /* requested temps of disconnected sensors */ 93 + case 0x12: /* requested pwm of not pwm controlled channels */ 94 + return -ENODATA; 95 + default: 96 + hid_dbg(ccp->hdev, "unknown device response error: %d", ccp->buffer[0]); 97 + return -EIO; 98 + } 99 + } 89 100 90 101 /* send command, check for error in response, response in ccp->buffer */ 91 102 static int send_usb_cmd(struct ccp_device *ccp, u8 command, u8 byte1, u8 byte2, u8 byte3) ··· 127 102 if (!t) 128 103 return -ETIMEDOUT; 129 104 130 - /* first byte of response is error code */ 131 - if (ccp->buffer[0] != 0x00) { 132 - hid_dbg(ccp->hdev, "device response error: %d", ccp->buffer[0]); 133 - return -EIO; 134 - } 135 - 136 - return 0; 105 + return ccp_get_errno(ccp); 137 106 } 138 107 139 108 static int ccp_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) ··· 145 126 } 146 127 147 128 /* requests and returns single data values depending on channel */ 148 - static int get_data(struct ccp_device *ccp, int command, int channel) 129 + static int get_data(struct ccp_device *ccp, int command, int channel, bool two_byte_data) 149 130 { 150 131 int ret; 151 132 ··· 155 136 if (ret) 156 137 goto out_unlock; 157 138 158 - ret = (ccp->buffer[1] << 8) + ccp->buffer[2]; 139 + ret = ccp->buffer[1]; 140 + if (two_byte_data) 141 + ret = (ret << 8) + ccp->buffer[2]; 159 142 160 143 out_unlock: 161 144 mutex_unlock(&ccp->mutex); ··· 171 150 if (val < 0 || val > 255) 172 151 return -EINVAL; 173 152 174 - ccp->pwm[channel] = val; 175 - 176 153 /* The Corsair Commander Pro uses values from 0-100 */ 177 154 val = DIV_ROUND_CLOSEST(val * 100, 255); 178 155 179 156 mutex_lock(&ccp->mutex); 180 157 181 158 ret = send_usb_cmd(ccp, CTL_SET_FAN_FPWM, channel, val, 0); 159 + if (!ret) 160 + ccp->target[channel] = -ENODATA; 182 161 183 162 mutex_unlock(&ccp->mutex); 184 163 return ret; ··· 192 171 ccp->target[channel] = val; 193 172 194 173 mutex_lock(&ccp->mutex); 195 - 196 174 ret = send_usb_cmd(ccp, CTL_SET_FAN_TARGET, channel, val >> 8, val); 197 175 198 176 mutex_unlock(&ccp->mutex); ··· 230 210 case hwmon_temp: 231 211 switch (attr) { 232 212 case hwmon_temp_input: 233 - ret = get_data(ccp, CTL_GET_TMP, channel); 213 + ret = get_data(ccp, CTL_GET_TMP, channel, true); 234 214 if (ret < 0) 235 215 return ret; 236 216 *val = ret * 10; ··· 242 222 case hwmon_fan: 243 223 switch (attr) { 244 224 case hwmon_fan_input: 245 - ret = get_data(ccp, CTL_GET_FAN_RPM, channel); 225 + ret = get_data(ccp, CTL_GET_FAN_RPM, channel, true); 246 226 if (ret < 0) 247 227 return ret; 248 228 *val = ret; ··· 250 230 case hwmon_fan_target: 251 231 /* how to read target values from the device is unknown */ 252 232 /* driver returns last set value or 0 */ 233 + if (ccp->target[channel] < 0) 234 + return -ENODATA; 253 235 *val = ccp->target[channel]; 254 236 return 0; 255 237 default: ··· 261 239 case hwmon_pwm: 262 240 switch (attr) { 263 241 case hwmon_pwm_input: 264 - /* how to read pwm values from the device is currently unknown */ 265 - /* driver returns last set value or 0 */ 266 - *val = ccp->pwm[channel]; 242 + ret = get_data(ccp, CTL_GET_FAN_PWM, channel, false); 243 + if (ret < 0) 244 + return ret; 245 + *val = DIV_ROUND_CLOSEST(ret * 255, 100); 267 246 return 0; 268 247 default: 269 248 break; ··· 273 250 case hwmon_in: 274 251 switch (attr) { 275 252 case hwmon_in_input: 276 - ret = get_data(ccp, CTL_GET_VOLT, channel); 253 + ret = get_data(ccp, CTL_GET_VOLT, channel, true); 277 254 if (ret < 0) 278 255 return ret; 279 256 *val = ret; ··· 439 416 continue; 440 417 441 418 set_bit(channel, ccp->fan_cnct); 419 + ccp->target[channel] = -ENODATA; 442 420 443 421 switch (mode) { 444 422 case 1: