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>
This commit is contained in:
parent
e4922176e1
commit
fa4dac3e1b
|
@ -35,8 +35,7 @@ fan[1-6]_input Connected fan rpm.
|
||||||
fan[1-6]_label Shows fan type as detected by the device.
|
fan[1-6]_label Shows fan type as detected by the device.
|
||||||
fan[1-6]_target Sets fan speed target rpm.
|
fan[1-6]_target Sets fan speed target rpm.
|
||||||
When reading, it reports the last value if it was set by the driver.
|
When reading, it reports the last value if it was set by the driver.
|
||||||
Otherwise returns 0.
|
Otherwise returns an error.
|
||||||
pwm[1-6] Sets the fan speed. Values from 0-255.
|
pwm[1-6] Sets the fan speed. Values from 0-255. Can only be read if pwm
|
||||||
When reading, it reports the last value if it was set by the driver.
|
was set directly.
|
||||||
Otherwise returns 0.
|
|
||||||
======================= =====================================================================
|
======================= =====================================================================
|
||||||
|
|
|
@ -4402,7 +4402,7 @@ F: Documentation/hwmon/coretemp.rst
|
||||||
F: drivers/hwmon/coretemp.c
|
F: drivers/hwmon/coretemp.c
|
||||||
|
|
||||||
CORSAIR-CPRO HARDWARE MONITOR DRIVER
|
CORSAIR-CPRO HARDWARE MONITOR DRIVER
|
||||||
M: Marius <mail@mariuszachmann.de>
|
M: Marius Zachmann <mail@mariuszachmann.de>
|
||||||
L: linux-hwmon@vger.kernel.org
|
L: linux-hwmon@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/hwmon/corsair-cpro.c
|
F: drivers/hwmon/corsair-cpro.c
|
||||||
|
|
|
@ -36,11 +36,12 @@
|
||||||
* send: byte 1 is channel, rest zero
|
* send: byte 1 is channel, rest zero
|
||||||
* rcv: returns temp for channel in centi-degree celsius
|
* rcv: returns temp for channel in centi-degree celsius
|
||||||
* in bytes 1 and 2
|
* in bytes 1 and 2
|
||||||
* returns 17 in byte 0 if no sensor is connected
|
* returns 0x11 in byte 0 if no sensor is connected
|
||||||
*/
|
*/
|
||||||
#define CTL_GET_VOLT 0x12 /*
|
#define CTL_GET_VOLT 0x12 /*
|
||||||
* send: byte 1 is rail number: 0 = 12v, 1 = 5v, 2 = 3.3v
|
* send: byte 1 is rail number: 0 = 12v, 1 = 5v, 2 = 3.3v
|
||||||
* rcv: returns millivolt in bytes 1,2
|
* rcv: returns millivolt in bytes 1,2
|
||||||
|
* returns error 0x10 if request is invalid
|
||||||
*/
|
*/
|
||||||
#define CTL_GET_FAN_CNCT 0x20 /*
|
#define CTL_GET_FAN_CNCT 0x20 /*
|
||||||
* returns in bytes 1-6 for each fan:
|
* returns in bytes 1-6 for each fan:
|
||||||
|
@ -52,6 +53,12 @@
|
||||||
* send: byte 1 is channel, rest zero
|
* send: byte 1 is channel, rest zero
|
||||||
* rcv: returns rpm in bytes 1,2
|
* rcv: returns rpm in bytes 1,2
|
||||||
*/
|
*/
|
||||||
|
#define CTL_GET_FAN_PWM 0x22 /*
|
||||||
|
* send: byte 1 is channel, rest zero
|
||||||
|
* rcv: returns pwm in byte 1 if it was set
|
||||||
|
* returns error 0x12 if fan is controlled via
|
||||||
|
* fan_target or fan curve
|
||||||
|
*/
|
||||||
#define CTL_SET_FAN_FPWM 0x23 /*
|
#define CTL_SET_FAN_FPWM 0x23 /*
|
||||||
* set fixed pwm
|
* set fixed pwm
|
||||||
* send: byte 1 is fan number
|
* send: byte 1 is fan number
|
||||||
|
@ -73,13 +80,31 @@ struct ccp_device {
|
||||||
struct completion wait_input_report;
|
struct completion wait_input_report;
|
||||||
struct mutex mutex; /* whenever buffer is used, lock before send_usb_cmd */
|
struct mutex mutex; /* whenever buffer is used, lock before send_usb_cmd */
|
||||||
u8 *buffer;
|
u8 *buffer;
|
||||||
int pwm[6];
|
|
||||||
int target[6];
|
int target[6];
|
||||||
DECLARE_BITMAP(temp_cnct, NUM_TEMP_SENSORS);
|
DECLARE_BITMAP(temp_cnct, NUM_TEMP_SENSORS);
|
||||||
DECLARE_BITMAP(fan_cnct, NUM_FANS);
|
DECLARE_BITMAP(fan_cnct, NUM_FANS);
|
||||||
char fan_label[6][LABEL_LENGTH];
|
char fan_label[6][LABEL_LENGTH];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* converts response error in buffer to errno */
|
||||||
|
static int ccp_get_errno(struct ccp_device *ccp)
|
||||||
|
{
|
||||||
|
switch (ccp->buffer[0]) {
|
||||||
|
case 0x00: /* success */
|
||||||
|
return 0;
|
||||||
|
case 0x01: /* called invalid command */
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
case 0x10: /* called GET_VOLT / GET_TMP with invalid arguments */
|
||||||
|
return -EINVAL;
|
||||||
|
case 0x11: /* requested temps of disconnected sensors */
|
||||||
|
case 0x12: /* requested pwm of not pwm controlled channels */
|
||||||
|
return -ENODATA;
|
||||||
|
default:
|
||||||
|
hid_dbg(ccp->hdev, "unknown device response error: %d", ccp->buffer[0]);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* send command, check for error in response, response in ccp->buffer */
|
/* send command, check for error in response, response in ccp->buffer */
|
||||||
static int send_usb_cmd(struct ccp_device *ccp, u8 command, u8 byte1, u8 byte2, u8 byte3)
|
static int send_usb_cmd(struct ccp_device *ccp, u8 command, u8 byte1, u8 byte2, u8 byte3)
|
||||||
{
|
{
|
||||||
|
@ -102,13 +127,7 @@ static int send_usb_cmd(struct ccp_device *ccp, u8 command, u8 byte1, u8 byte2,
|
||||||
if (!t)
|
if (!t)
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
|
|
||||||
/* first byte of response is error code */
|
return ccp_get_errno(ccp);
|
||||||
if (ccp->buffer[0] != 0x00) {
|
|
||||||
hid_dbg(ccp->hdev, "device response error: %d", ccp->buffer[0]);
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ccp_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size)
|
static int ccp_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size)
|
||||||
|
@ -126,7 +145,7 @@ static int ccp_raw_event(struct hid_device *hdev, struct hid_report *report, u8
|
||||||
}
|
}
|
||||||
|
|
||||||
/* requests and returns single data values depending on channel */
|
/* requests and returns single data values depending on channel */
|
||||||
static int get_data(struct ccp_device *ccp, int command, int channel)
|
static int get_data(struct ccp_device *ccp, int command, int channel, bool two_byte_data)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -136,7 +155,9 @@ static int get_data(struct ccp_device *ccp, int command, int channel)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
|
|
||||||
ret = (ccp->buffer[1] << 8) + ccp->buffer[2];
|
ret = ccp->buffer[1];
|
||||||
|
if (two_byte_data)
|
||||||
|
ret = (ret << 8) + ccp->buffer[2];
|
||||||
|
|
||||||
out_unlock:
|
out_unlock:
|
||||||
mutex_unlock(&ccp->mutex);
|
mutex_unlock(&ccp->mutex);
|
||||||
|
@ -150,14 +171,14 @@ static int set_pwm(struct ccp_device *ccp, int channel, long val)
|
||||||
if (val < 0 || val > 255)
|
if (val < 0 || val > 255)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
ccp->pwm[channel] = val;
|
|
||||||
|
|
||||||
/* The Corsair Commander Pro uses values from 0-100 */
|
/* The Corsair Commander Pro uses values from 0-100 */
|
||||||
val = DIV_ROUND_CLOSEST(val * 100, 255);
|
val = DIV_ROUND_CLOSEST(val * 100, 255);
|
||||||
|
|
||||||
mutex_lock(&ccp->mutex);
|
mutex_lock(&ccp->mutex);
|
||||||
|
|
||||||
ret = send_usb_cmd(ccp, CTL_SET_FAN_FPWM, channel, val, 0);
|
ret = send_usb_cmd(ccp, CTL_SET_FAN_FPWM, channel, val, 0);
|
||||||
|
if (!ret)
|
||||||
|
ccp->target[channel] = -ENODATA;
|
||||||
|
|
||||||
mutex_unlock(&ccp->mutex);
|
mutex_unlock(&ccp->mutex);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -171,7 +192,6 @@ static int set_target(struct ccp_device *ccp, int channel, long val)
|
||||||
ccp->target[channel] = val;
|
ccp->target[channel] = val;
|
||||||
|
|
||||||
mutex_lock(&ccp->mutex);
|
mutex_lock(&ccp->mutex);
|
||||||
|
|
||||||
ret = send_usb_cmd(ccp, CTL_SET_FAN_TARGET, channel, val >> 8, val);
|
ret = send_usb_cmd(ccp, CTL_SET_FAN_TARGET, channel, val >> 8, val);
|
||||||
|
|
||||||
mutex_unlock(&ccp->mutex);
|
mutex_unlock(&ccp->mutex);
|
||||||
|
@ -210,7 +230,7 @@ static int ccp_read(struct device *dev, enum hwmon_sensor_types type,
|
||||||
case hwmon_temp:
|
case hwmon_temp:
|
||||||
switch (attr) {
|
switch (attr) {
|
||||||
case hwmon_temp_input:
|
case hwmon_temp_input:
|
||||||
ret = get_data(ccp, CTL_GET_TMP, channel);
|
ret = get_data(ccp, CTL_GET_TMP, channel, true);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
*val = ret * 10;
|
*val = ret * 10;
|
||||||
|
@ -222,7 +242,7 @@ static int ccp_read(struct device *dev, enum hwmon_sensor_types type,
|
||||||
case hwmon_fan:
|
case hwmon_fan:
|
||||||
switch (attr) {
|
switch (attr) {
|
||||||
case hwmon_fan_input:
|
case hwmon_fan_input:
|
||||||
ret = get_data(ccp, CTL_GET_FAN_RPM, channel);
|
ret = get_data(ccp, CTL_GET_FAN_RPM, channel, true);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
*val = ret;
|
*val = ret;
|
||||||
|
@ -230,6 +250,8 @@ static int ccp_read(struct device *dev, enum hwmon_sensor_types type,
|
||||||
case hwmon_fan_target:
|
case hwmon_fan_target:
|
||||||
/* how to read target values from the device is unknown */
|
/* how to read target values from the device is unknown */
|
||||||
/* driver returns last set value or 0 */
|
/* driver returns last set value or 0 */
|
||||||
|
if (ccp->target[channel] < 0)
|
||||||
|
return -ENODATA;
|
||||||
*val = ccp->target[channel];
|
*val = ccp->target[channel];
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
|
@ -239,9 +261,10 @@ static int ccp_read(struct device *dev, enum hwmon_sensor_types type,
|
||||||
case hwmon_pwm:
|
case hwmon_pwm:
|
||||||
switch (attr) {
|
switch (attr) {
|
||||||
case hwmon_pwm_input:
|
case hwmon_pwm_input:
|
||||||
/* how to read pwm values from the device is currently unknown */
|
ret = get_data(ccp, CTL_GET_FAN_PWM, channel, false);
|
||||||
/* driver returns last set value or 0 */
|
if (ret < 0)
|
||||||
*val = ccp->pwm[channel];
|
return ret;
|
||||||
|
*val = DIV_ROUND_CLOSEST(ret * 255, 100);
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -250,7 +273,7 @@ static int ccp_read(struct device *dev, enum hwmon_sensor_types type,
|
||||||
case hwmon_in:
|
case hwmon_in:
|
||||||
switch (attr) {
|
switch (attr) {
|
||||||
case hwmon_in_input:
|
case hwmon_in_input:
|
||||||
ret = get_data(ccp, CTL_GET_VOLT, channel);
|
ret = get_data(ccp, CTL_GET_VOLT, channel, true);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
*val = ret;
|
*val = ret;
|
||||||
|
@ -416,6 +439,7 @@ static int get_fan_cnct(struct ccp_device *ccp)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
set_bit(channel, ccp->fan_cnct);
|
set_bit(channel, ccp->fan_cnct);
|
||||||
|
ccp->target[channel] = -ENODATA;
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case 1:
|
case 1:
|
||||||
|
|
Loading…
Reference in New Issue