Signed-off-by: Jaap Aarts <jaap.aarts1@xxxxxxxxxxx> --- drivers/hwmon/asetek_gen6.c | 190 ++++++++++++++++++++++++++++++++++-- 1 file changed, 182 insertions(+), 8 deletions(-) diff --git a/drivers/hwmon/asetek_gen6.c b/drivers/hwmon/asetek_gen6.c index b82a678717ab..5c3bd456f881 100644 --- a/drivers/hwmon/asetek_gen6.c +++ b/drivers/hwmon/asetek_gen6.c @@ -108,6 +108,50 @@ static bool check_succes(char command, char ret[SUCCES_LENGTH]) return strncmp(ret, success, SUCCES_LENGTH) == 0; } +int set_fan_rpm_curve(struct driver *cdev, struct fan_hwmon_data *fan_data, + struct curve_point point[7]) +{ + int retval; + int wrote; + int sndpipe = usb_sndbulkpipe(cdev->udev, cdev->bulk_out_endpointAddr); + int rcvpipe = usb_rcvbulkpipe(cdev->udev, cdev->bulk_in_endpointAddr); + + char *send_buf = cdev->bulk_out_buffer; + char *recv_buf = cdev->bulk_in_buffer; + memcpy(fan_data->curve, point, sizeof(fan_data->curve)); + + send_buf[0] = 0x40; + send_buf[1] = fan_data->fan_channel; + send_buf[2] = point[0].temp; + send_buf[3] = point[1].temp; + send_buf[4] = point[2].temp; + send_buf[5] = point[3].temp; + send_buf[6] = point[4].temp; + send_buf[7] = point[5].temp; + send_buf[8] = point[6].temp; + send_buf[9] = point[0].pwm; + send_buf[10] = point[1].pwm; + send_buf[11] = point[2].pwm; + send_buf[12] = point[3].pwm; + send_buf[13] = point[4].pwm; + send_buf[14] = point[5].pwm; + send_buf[15] = point[6].pwm; + + retval = usb_bulk_msg(cdev->udev, sndpipe, send_buf, 16, &wrote, 100); + if (retval != 0) + return retval; + + retval = usb_bulk_msg(cdev->udev, rcvpipe, recv_buf, 4, &wrote, 100); + if (retval != 0) + return retval; + + if (!check_succes( + send_buf[0], + recv_buf) /* || recv_buf[3] != fan_data->fan_channel */) + printk(KERN_INFO "[*] Failled setting fan curve %d,%d,%d/%d\n", + recv_buf[0], recv_buf[1], recv_buf[2], recv_buf[3]); + return 0; +} int set_fan_target_rpm(struct driver *cdev, struct fan_hwmon_data *fan_data, long val) { @@ -177,6 +221,40 @@ int get_fan_current_rpm(struct driver *cdev, struct fan_hwmon_data *fan_data, return 0; } +int set_fan_target_pwm(struct driver *cdev, struct fan_hwmon_data *fan_data, + long val) +{ + int retval; + int wrote; + int sndpipe = usb_sndbulkpipe(cdev->udev, cdev->bulk_out_endpointAddr); + int rcvpipe = usb_rcvbulkpipe(cdev->udev, cdev->bulk_in_endpointAddr); + + unsigned char *send_buf = cdev->bulk_out_buffer; + unsigned char *recv_buf = cdev->bulk_in_buffer; + + fan_data->fan_pwm_target = val; + fan_data->fan_target = 0; + + send_buf[0] = 0x42; + send_buf[1] = fan_data->fan_channel; + send_buf[3] = fan_data->fan_pwm_target; + + retval = usb_bulk_msg(cdev->udev, sndpipe, send_buf, 4, &wrote, 100); + if (retval != 0) + return retval; + + retval = usb_bulk_msg(cdev->udev, rcvpipe, recv_buf, 6, &wrote, 100000); + if (retval != 0) + return retval; + + //no error + if (!check_succes(send_buf[0], recv_buf) || + recv_buf[3] != fan_data->fan_channel) + printk(KERN_INFO "[*] Failled setting fan pwm %d,%d,%d/%d\n", + recv_buf[0], recv_buf[1], recv_buf[2], recv_buf[3]); + return 0; +} + umode_t is_visible_func(const void *d, enum hwmon_sensor_types type, u32 attr, int channel) { @@ -196,6 +274,19 @@ umode_t is_visible_func(const void *d, enum hwmon_sensor_types type, u32 attr, break; } break; + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + return 0200; + break; + case hwmon_pwm_mode: + return 0644; + break; + default: + break; + } + break; + default: break; } @@ -214,7 +305,11 @@ static int write_func(struct device *dev, enum hwmon_sensor_types type, case hwmon_fan:; switch (attr) { case hwmon_fan_target: + fan_data = data->channel_data[channel]; + if (fan_data->mode != 1) { + return -EINVAL; + } retval = usb_autopm_get_interface(cdev->interface); if (retval) @@ -229,11 +324,70 @@ static int write_func(struct device *dev, enum hwmon_sensor_types type, if (retval) goto cleanup; - break; + goto exit; default: return -EINVAL; } - break; + goto exit; + case hwmon_pwm:; + + switch (attr) { + case hwmon_pwm_input: + fan_data = data->channel_data[channel]; + if (fan_data->mode != 1) { + return -EINVAL; + } + + retval = usb_autopm_get_interface(cdev->interface); + if (retval) + goto exit; + + if (down_trylock(&cdev->limit_sem)) { + retval = -EAGAIN; + goto cleanup_interface; + } + + retval = set_fan_target_pwm(cdev, fan_data, val); + if (retval) + return retval; + + goto cleanup; + case hwmon_pwm_mode: + fan_data = data->channel_data[channel]; + + retval = usb_autopm_get_interface(cdev->interface); + if (retval) + goto exit; + + if (down_trylock(&cdev->limit_sem)) { + retval = -EAGAIN; + goto cleanup_interface; + } + fan_data->mode = val; + + if (val == 0) { + set_fan_rpm_curve(cdev, fan_data, + default_curve); + } else if (val == 1) { + if (fan_data->fan_target != 0) { + retval = set_fan_target_rpm( + cdev, fan_data, + fan_data->fan_target); + if (retval) + goto cleanup; + } else if (fan_data->fan_pwm_target != 0) { + retval = set_fan_target_pwm( + cdev, fan_data, + fan_data->fan_pwm_target); + if (retval) + goto cleanup; + } + } + goto cleanup; + default: + return -EINVAL; + } + goto exit; default: return -EINVAL; } @@ -274,19 +428,36 @@ int read_func(struct device *dev, enum hwmon_sensor_types type, u32 attr, if (retval) goto cleanup; - break; + goto cleanup; case hwmon_fan_target: fan_data = data->channel_data[channel]; + if (fan_data->mode != 1) { + *val = 0; + goto exit; + } get_fan_target_rpm(fan_data, val); - break; + goto exit; case hwmon_fan_min: *val = 200; - break; + goto exit; + default: return -EINVAL; } - break; + goto exit; + + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_mode: + fan_data = data->channel_data[channel]; + *val = fan_data->mode; + goto exit; + default: + return -EINVAL; + } + goto exit; + default: return -EINVAL; } @@ -299,8 +470,11 @@ int read_func(struct device *dev, enum hwmon_sensor_types type, u32 attr, return retval; } static const struct hwmon_channel_info *dual_fan[] = { - HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_TARGET, - HWMON_F_INPUT | HWMON_F_TARGET), + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_MIN, + HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_MIN), + HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_MODE, + HWMON_PWM_INPUT | HWMON_PWM_MODE), + NULL }; -- 2.27.0