Commit | Line | Data |
---|---|---|
0e35f63f AS |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
cdbe34da | 3 | * hwmon driver for Aquacomputer devices (D5 Next, Farbwerk, Farbwerk 360, Octo, |
aed80bb9 | 4 | * Quadro, High Flow Next) |
0e35f63f | 5 | * |
2fd3eec1 AS |
6 | * Aquacomputer devices send HID reports (with ID 0x01) every second to report |
7 | * sensor values. | |
0e35f63f AS |
8 | * |
9 | * Copyright 2021 Aleksa Savic <savicaleksa83@gmail.com> | |
229b159c | 10 | * Copyright 2022 Jack Doan <me@jackdoan.com> |
0e35f63f AS |
11 | */ |
12 | ||
752b9279 | 13 | #include <linux/crc16.h> |
0e35f63f AS |
14 | #include <linux/debugfs.h> |
15 | #include <linux/hid.h> | |
16 | #include <linux/hwmon.h> | |
17 | #include <linux/jiffies.h> | |
18 | #include <linux/module.h> | |
752b9279 | 19 | #include <linux/mutex.h> |
0e35f63f | 20 | #include <linux/seq_file.h> |
2fd3eec1 | 21 | #include <asm/unaligned.h> |
0e35f63f | 22 | |
2fd3eec1 | 23 | #define USB_VENDOR_ID_AQUACOMPUTER 0x0c70 |
229b159c | 24 | #define USB_PRODUCT_ID_FARBWERK 0xf00a |
cdbe34da | 25 | #define USB_PRODUCT_ID_QUADRO 0xf00d |
2fd3eec1 AS |
26 | #define USB_PRODUCT_ID_D5NEXT 0xf00e |
27 | #define USB_PRODUCT_ID_FARBWERK360 0xf010 | |
752b9279 | 28 | #define USB_PRODUCT_ID_OCTO 0xf011 |
aed80bb9 | 29 | #define USB_PRODUCT_ID_HIGHFLOWNEXT 0xf012 |
0e35f63f | 30 | |
aed80bb9 | 31 | enum kinds { d5next, farbwerk, farbwerk360, octo, quadro, highflownext }; |
0e35f63f | 32 | |
2fd3eec1 AS |
33 | static const char *const aqc_device_names[] = { |
34 | [d5next] = "d5next", | |
229b159c | 35 | [farbwerk] = "farbwerk", |
752b9279 | 36 | [farbwerk360] = "farbwerk360", |
cdbe34da | 37 | [octo] = "octo", |
aed80bb9 AS |
38 | [quadro] = "quadro", |
39 | [highflownext] = "highflownext" | |
2fd3eec1 | 40 | }; |
0e35f63f | 41 | |
2fd3eec1 AS |
42 | #define DRIVER_NAME "aquacomputer_d5next" |
43 | ||
44 | #define STATUS_REPORT_ID 0x01 | |
45 | #define STATUS_UPDATE_INTERVAL (2 * HZ) /* In seconds */ | |
46 | #define SERIAL_FIRST_PART 3 | |
47 | #define SERIAL_SECOND_PART 5 | |
48 | #define FIRMWARE_VERSION 13 | |
49 | ||
752b9279 AS |
50 | #define CTRL_REPORT_ID 0x03 |
51 | ||
52 | /* The HID report that the official software always sends | |
53 | * after writing values, currently same for all devices | |
54 | */ | |
55 | #define SECONDARY_CTRL_REPORT_ID 0x02 | |
56 | #define SECONDARY_CTRL_REPORT_SIZE 0x0B | |
57 | ||
58 | static u8 secondary_ctrl_report[] = { | |
59 | 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x34, 0xC6 | |
60 | }; | |
61 | ||
654c9735 AS |
62 | /* Register offsets for all Aquacomputer devices */ |
63 | #define AQC_TEMP_SENSOR_SIZE 0x02 | |
64 | #define AQC_TEMP_SENSOR_DISCONNECTED 0x7FFF | |
65 | #define AQC_FAN_PERCENT_OFFSET 0x00 | |
66 | #define AQC_FAN_VOLTAGE_OFFSET 0x02 | |
67 | #define AQC_FAN_CURRENT_OFFSET 0x04 | |
68 | #define AQC_FAN_POWER_OFFSET 0x06 | |
69 | #define AQC_FAN_SPEED_OFFSET 0x08 | |
0e35f63f | 70 | |
654c9735 AS |
71 | /* Register offsets for the D5 Next pump */ |
72 | #define D5NEXT_POWER_CYCLES 0x18 | |
73 | #define D5NEXT_COOLANT_TEMP 0x57 | |
74 | #define D5NEXT_NUM_FANS 2 | |
75 | #define D5NEXT_NUM_SENSORS 1 | |
e2769f5e AS |
76 | #define D5NEXT_NUM_VIRTUAL_SENSORS 8 |
77 | #define D5NEXT_VIRTUAL_SENSORS_START 0x3f | |
654c9735 AS |
78 | #define D5NEXT_PUMP_OFFSET 0x6c |
79 | #define D5NEXT_FAN_OFFSET 0x5f | |
80 | #define D5NEXT_5V_VOLTAGE 0x39 | |
f4caa262 | 81 | #define D5NEXT_12V_VOLTAGE 0x37 |
09e89309 | 82 | #define D5NEXT_CTRL_REPORT_SIZE 0x329 |
662d20b3 | 83 | #define D5NEXT_TEMP_CTRL_OFFSET 0x2D |
654c9735 | 84 | static u8 d5next_sensor_fan_offsets[] = { D5NEXT_PUMP_OFFSET, D5NEXT_FAN_OFFSET }; |
0e35f63f | 85 | |
09e89309 AS |
86 | /* Pump and fan speed registers in D5 Next control report (from 0-100%) */ |
87 | static u16 d5next_ctrl_fan_offsets[] = { 0x97, 0x42 }; | |
88 | ||
229b159c JD |
89 | /* Register offsets for the Farbwerk RGB controller */ |
90 | #define FARBWERK_NUM_SENSORS 4 | |
91 | #define FARBWERK_SENSOR_START 0x2f | |
229b159c | 92 | |
2fd3eec1 | 93 | /* Register offsets for the Farbwerk 360 RGB controller */ |
e2769f5e AS |
94 | #define FARBWERK360_NUM_SENSORS 4 |
95 | #define FARBWERK360_SENSOR_START 0x32 | |
96 | #define FARBWERK360_NUM_VIRTUAL_SENSORS 16 | |
97 | #define FARBWERK360_VIRTUAL_SENSORS_START 0x3a | |
662d20b3 AS |
98 | #define FARBWERK360_CTRL_REPORT_SIZE 0x682 |
99 | #define FARBWERK360_TEMP_CTRL_OFFSET 0x8 | |
0e35f63f | 100 | |
752b9279 AS |
101 | /* Register offsets for the Octo fan controller */ |
102 | #define OCTO_POWER_CYCLES 0x18 | |
103 | #define OCTO_NUM_FANS 8 | |
752b9279 AS |
104 | #define OCTO_NUM_SENSORS 4 |
105 | #define OCTO_SENSOR_START 0x3D | |
e2769f5e AS |
106 | #define OCTO_NUM_VIRTUAL_SENSORS 16 |
107 | #define OCTO_VIRTUAL_SENSORS_START 0x45 | |
654c9735 | 108 | #define OCTO_CTRL_REPORT_SIZE 0x65F |
662d20b3 | 109 | #define OCTO_TEMP_CTRL_OFFSET 0xA |
654c9735 | 110 | static u8 octo_sensor_fan_offsets[] = { 0x7D, 0x8A, 0x97, 0xA4, 0xB1, 0xBE, 0xCB, 0xD8 }; |
752b9279 AS |
111 | |
112 | /* Fan speed registers in Octo control report (from 0-100%) */ | |
113 | static u16 octo_ctrl_fan_offsets[] = { 0x5B, 0xB0, 0x105, 0x15A, 0x1AF, 0x204, 0x259, 0x2AE }; | |
114 | ||
cdbe34da AS |
115 | /* Register offsets for the Quadro fan controller */ |
116 | #define QUADRO_POWER_CYCLES 0x18 | |
117 | #define QUADRO_NUM_FANS 4 | |
118 | #define QUADRO_NUM_SENSORS 4 | |
119 | #define QUADRO_SENSOR_START 0x34 | |
e2769f5e AS |
120 | #define QUADRO_NUM_VIRTUAL_SENSORS 16 |
121 | #define QUADRO_VIRTUAL_SENSORS_START 0x3c | |
cdbe34da AS |
122 | #define QUADRO_CTRL_REPORT_SIZE 0x3c1 |
123 | #define QUADRO_FLOW_SENSOR_OFFSET 0x6e | |
662d20b3 | 124 | #define QUADRO_TEMP_CTRL_OFFSET 0xA |
cdbe34da AS |
125 | static u8 quadro_sensor_fan_offsets[] = { 0x70, 0x7D, 0x8A, 0x97 }; |
126 | ||
127 | /* Fan speed registers in Quadro control report (from 0-100%) */ | |
b7f3e965 | 128 | static u16 quadro_ctrl_fan_offsets[] = { 0x37, 0x8c, 0xe1, 0x136 }; |
cdbe34da | 129 | |
aed80bb9 AS |
130 | /* Register offsets for the High Flow Next */ |
131 | #define HIGHFLOWNEXT_NUM_SENSORS 2 | |
132 | #define HIGHFLOWNEXT_SENSOR_START 85 | |
133 | #define HIGHFLOWNEXT_FLOW 81 | |
134 | #define HIGHFLOWNEXT_WATER_QUALITY 89 | |
135 | #define HIGHFLOWNEXT_POWER 91 | |
136 | #define HIGHFLOWNEXT_CONDUCTIVITY 95 | |
137 | #define HIGHFLOWNEXT_5V_VOLTAGE 97 | |
138 | #define HIGHFLOWNEXT_5V_VOLTAGE_USB 99 | |
139 | ||
2fd3eec1 | 140 | /* Labels for D5 Next */ |
752b9279 AS |
141 | static const char *const label_d5next_temp[] = { |
142 | "Coolant temp" | |
143 | }; | |
0e35f63f | 144 | |
2fd3eec1 AS |
145 | static const char *const label_d5next_speeds[] = { |
146 | "Pump speed", | |
147 | "Fan speed" | |
148 | }; | |
0e35f63f | 149 | |
2fd3eec1 AS |
150 | static const char *const label_d5next_power[] = { |
151 | "Pump power", | |
152 | "Fan power" | |
0e35f63f AS |
153 | }; |
154 | ||
2fd3eec1 AS |
155 | static const char *const label_d5next_voltages[] = { |
156 | "Pump voltage", | |
157 | "Fan voltage", | |
f4caa262 AS |
158 | "+5V voltage", |
159 | "+12V voltage" | |
0e35f63f AS |
160 | }; |
161 | ||
2fd3eec1 AS |
162 | static const char *const label_d5next_current[] = { |
163 | "Pump current", | |
164 | "Fan current" | |
0e35f63f AS |
165 | }; |
166 | ||
cdbe34da | 167 | /* Labels for Farbwerk, Farbwerk 360 and Octo and Quadro temperature sensors */ |
2fd3eec1 AS |
168 | static const char *const label_temp_sensors[] = { |
169 | "Sensor 1", | |
170 | "Sensor 2", | |
171 | "Sensor 3", | |
172 | "Sensor 4" | |
0e35f63f AS |
173 | }; |
174 | ||
e2769f5e AS |
175 | static const char *const label_virtual_temp_sensors[] = { |
176 | "Virtual sensor 1", | |
177 | "Virtual sensor 2", | |
178 | "Virtual sensor 3", | |
179 | "Virtual sensor 4", | |
180 | "Virtual sensor 5", | |
181 | "Virtual sensor 6", | |
182 | "Virtual sensor 7", | |
183 | "Virtual sensor 8", | |
184 | "Virtual sensor 9", | |
185 | "Virtual sensor 10", | |
186 | "Virtual sensor 11", | |
187 | "Virtual sensor 12", | |
188 | "Virtual sensor 13", | |
189 | "Virtual sensor 14", | |
190 | "Virtual sensor 15", | |
191 | "Virtual sensor 16", | |
192 | }; | |
193 | ||
cdbe34da | 194 | /* Labels for Octo and Quadro (except speed) */ |
752b9279 AS |
195 | static const char *const label_fan_speed[] = { |
196 | "Fan 1 speed", | |
197 | "Fan 2 speed", | |
198 | "Fan 3 speed", | |
199 | "Fan 4 speed", | |
200 | "Fan 5 speed", | |
201 | "Fan 6 speed", | |
202 | "Fan 7 speed", | |
203 | "Fan 8 speed" | |
204 | }; | |
205 | ||
206 | static const char *const label_fan_power[] = { | |
207 | "Fan 1 power", | |
208 | "Fan 2 power", | |
209 | "Fan 3 power", | |
210 | "Fan 4 power", | |
211 | "Fan 5 power", | |
212 | "Fan 6 power", | |
213 | "Fan 7 power", | |
214 | "Fan 8 power" | |
215 | }; | |
216 | ||
217 | static const char *const label_fan_voltage[] = { | |
218 | "Fan 1 voltage", | |
219 | "Fan 2 voltage", | |
220 | "Fan 3 voltage", | |
221 | "Fan 4 voltage", | |
222 | "Fan 5 voltage", | |
223 | "Fan 6 voltage", | |
224 | "Fan 7 voltage", | |
225 | "Fan 8 voltage" | |
226 | }; | |
227 | ||
228 | static const char *const label_fan_current[] = { | |
229 | "Fan 1 current", | |
230 | "Fan 2 current", | |
231 | "Fan 3 current", | |
232 | "Fan 4 current", | |
233 | "Fan 5 current", | |
234 | "Fan 6 current", | |
235 | "Fan 7 current", | |
236 | "Fan 8 current" | |
237 | }; | |
238 | ||
cdbe34da AS |
239 | /* Labels for Quadro fan speeds */ |
240 | static const char *const label_quadro_speeds[] = { | |
241 | "Fan 1 speed", | |
242 | "Fan 2 speed", | |
243 | "Fan 3 speed", | |
244 | "Fan 4 speed", | |
245 | "Flow speed [dL/h]" | |
246 | }; | |
247 | ||
aed80bb9 AS |
248 | /* Labels for High Flow Next */ |
249 | static const char *const label_highflownext_temp_sensors[] = { | |
250 | "Coolant temp", | |
251 | "External sensor" | |
252 | }; | |
253 | ||
254 | static const char *const label_highflownext_fan_speed[] = { | |
255 | "Flow [dL/h]", | |
256 | "Water quality [%]", | |
257 | "Conductivity [nS/cm]", | |
258 | }; | |
259 | ||
260 | static const char *const label_highflownext_power[] = { | |
261 | "Dissipated power", | |
262 | }; | |
263 | ||
264 | static const char *const label_highflownext_voltage[] = { | |
265 | "+5V voltage", | |
266 | "+5V USB voltage" | |
267 | }; | |
268 | ||
2fd3eec1 | 269 | struct aqc_data { |
0e35f63f AS |
270 | struct hid_device *hdev; |
271 | struct device *hwmon_dev; | |
272 | struct dentry *debugfs; | |
752b9279 | 273 | struct mutex mutex; /* Used for locking access when reading and writing PWM values */ |
2fd3eec1 AS |
274 | enum kinds kind; |
275 | const char *name; | |
276 | ||
752b9279 AS |
277 | int buffer_size; |
278 | u8 *buffer; | |
279 | int checksum_start; | |
280 | int checksum_length; | |
281 | int checksum_offset; | |
282 | ||
654c9735 AS |
283 | int num_fans; |
284 | u8 *fan_sensor_offsets; | |
285 | u16 *fan_ctrl_offsets; | |
286 | int num_temp_sensors; | |
287 | int temp_sensor_start_offset; | |
e2769f5e AS |
288 | int num_virtual_temp_sensors; |
289 | int virtual_temp_sensor_start_offset; | |
662d20b3 | 290 | u16 temp_ctrl_offset; |
654c9735 | 291 | u16 power_cycle_count_offset; |
cdbe34da | 292 | u8 flow_sensor_offset; |
654c9735 | 293 | |
2fd3eec1 AS |
294 | /* General info, same across all devices */ |
295 | u32 serial_number[2]; | |
296 | u16 firmware_version; | |
297 | ||
654c9735 | 298 | /* How many times the device was powered on, if available */ |
2fd3eec1 AS |
299 | u32 power_cycles; |
300 | ||
301 | /* Sensor values */ | |
e2769f5e | 302 | s32 temp_input[20]; /* Max 4 physical and 16 virtual */ |
752b9279 AS |
303 | u16 speed_input[8]; |
304 | u32 power_input[8]; | |
305 | u16 voltage_input[8]; | |
306 | u16 current_input[8]; | |
307 | ||
308 | /* Label values */ | |
309 | const char *const *temp_label; | |
e2769f5e | 310 | const char *const *virtual_temp_label; |
752b9279 AS |
311 | const char *const *speed_label; |
312 | const char *const *power_label; | |
313 | const char *const *voltage_label; | |
314 | const char *const *current_label; | |
2fd3eec1 | 315 | |
0e35f63f AS |
316 | unsigned long updated; |
317 | }; | |
318 | ||
752b9279 AS |
319 | /* Converts from centi-percent */ |
320 | static int aqc_percent_to_pwm(u16 val) | |
321 | { | |
322 | return DIV_ROUND_CLOSEST(val * 255, 100 * 100); | |
323 | } | |
324 | ||
325 | /* Converts to centi-percent */ | |
326 | static int aqc_pwm_to_percent(long val) | |
327 | { | |
328 | if (val < 0 || val > 255) | |
329 | return -EINVAL; | |
330 | ||
331 | return DIV_ROUND_CLOSEST(val * 100 * 100, 255); | |
332 | } | |
333 | ||
334 | /* Expects the mutex to be locked */ | |
335 | static int aqc_get_ctrl_data(struct aqc_data *priv) | |
336 | { | |
337 | int ret; | |
338 | ||
339 | memset(priv->buffer, 0x00, priv->buffer_size); | |
340 | ret = hid_hw_raw_request(priv->hdev, CTRL_REPORT_ID, priv->buffer, priv->buffer_size, | |
341 | HID_FEATURE_REPORT, HID_REQ_GET_REPORT); | |
342 | if (ret < 0) | |
343 | ret = -ENODATA; | |
344 | ||
345 | return ret; | |
346 | } | |
347 | ||
348 | /* Expects the mutex to be locked */ | |
349 | static int aqc_send_ctrl_data(struct aqc_data *priv) | |
350 | { | |
351 | int ret; | |
352 | u16 checksum; | |
353 | ||
354 | /* Init and xorout value for CRC-16/USB is 0xffff */ | |
355 | checksum = crc16(0xffff, priv->buffer + priv->checksum_start, priv->checksum_length); | |
356 | checksum ^= 0xffff; | |
357 | ||
358 | /* Place the new checksum at the end of the report */ | |
359 | put_unaligned_be16(checksum, priv->buffer + priv->checksum_offset); | |
360 | ||
361 | /* Send the patched up report back to the device */ | |
362 | ret = hid_hw_raw_request(priv->hdev, CTRL_REPORT_ID, priv->buffer, priv->buffer_size, | |
363 | HID_FEATURE_REPORT, HID_REQ_SET_REPORT); | |
364 | if (ret < 0) | |
365 | return ret; | |
366 | ||
367 | /* The official software sends this report after every change, so do it here as well */ | |
368 | ret = hid_hw_raw_request(priv->hdev, SECONDARY_CTRL_REPORT_ID, secondary_ctrl_report, | |
369 | SECONDARY_CTRL_REPORT_SIZE, HID_FEATURE_REPORT, | |
370 | HID_REQ_SET_REPORT); | |
371 | return ret; | |
372 | } | |
373 | ||
662d20b3 AS |
374 | /* Refreshes the control buffer and stores value at offset in val */ |
375 | static int aqc_get_ctrl_val(struct aqc_data *priv, int offset, long *val) | |
752b9279 AS |
376 | { |
377 | int ret; | |
378 | ||
379 | mutex_lock(&priv->mutex); | |
380 | ||
381 | ret = aqc_get_ctrl_data(priv); | |
382 | if (ret < 0) | |
383 | goto unlock_and_return; | |
384 | ||
662d20b3 | 385 | *val = (s16)get_unaligned_be16(priv->buffer + offset); |
752b9279 AS |
386 | |
387 | unlock_and_return: | |
388 | mutex_unlock(&priv->mutex); | |
389 | return ret; | |
390 | } | |
391 | ||
392 | static int aqc_set_ctrl_val(struct aqc_data *priv, int offset, long val) | |
393 | { | |
394 | int ret; | |
395 | ||
396 | mutex_lock(&priv->mutex); | |
397 | ||
398 | ret = aqc_get_ctrl_data(priv); | |
399 | if (ret < 0) | |
400 | goto unlock_and_return; | |
401 | ||
662d20b3 | 402 | put_unaligned_be16((s16)val, priv->buffer + offset); |
752b9279 AS |
403 | |
404 | ret = aqc_send_ctrl_data(priv); | |
405 | ||
406 | unlock_and_return: | |
407 | mutex_unlock(&priv->mutex); | |
408 | return ret; | |
409 | } | |
410 | ||
411 | static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, int channel) | |
0e35f63f | 412 | { |
2fd3eec1 AS |
413 | const struct aqc_data *priv = data; |
414 | ||
415 | switch (type) { | |
416 | case hwmon_temp: | |
662d20b3 AS |
417 | if (channel < priv->num_temp_sensors) { |
418 | switch (attr) { | |
419 | case hwmon_temp_label: | |
420 | case hwmon_temp_input: | |
421 | return 0444; | |
422 | case hwmon_temp_offset: | |
423 | if (priv->temp_ctrl_offset != 0) | |
424 | return 0644; | |
425 | break; | |
426 | default: | |
427 | break; | |
428 | } | |
429 | } | |
430 | ||
e2769f5e | 431 | if (channel < priv->num_temp_sensors + priv->num_virtual_temp_sensors) |
662d20b3 AS |
432 | switch (attr) { |
433 | case hwmon_temp_label: | |
434 | case hwmon_temp_input: | |
435 | return 0444; | |
436 | default: | |
437 | break; | |
438 | } | |
2fd3eec1 | 439 | break; |
752b9279 | 440 | case hwmon_pwm: |
654c9735 | 441 | if (priv->fan_ctrl_offsets && channel < priv->num_fans) { |
752b9279 AS |
442 | switch (attr) { |
443 | case hwmon_pwm_input: | |
444 | return 0644; | |
445 | default: | |
446 | break; | |
447 | } | |
752b9279 AS |
448 | } |
449 | break; | |
2fd3eec1 | 450 | case hwmon_fan: |
cdbe34da | 451 | switch (priv->kind) { |
aed80bb9 AS |
452 | case highflownext: |
453 | /* Special case to support flow sensor, water quality and conductivity */ | |
454 | if (channel < 3) | |
455 | return 0444; | |
456 | break; | |
cdbe34da AS |
457 | case quadro: |
458 | /* Special case to support flow sensor */ | |
459 | if (channel < priv->num_fans + 1) | |
460 | return 0444; | |
461 | break; | |
462 | default: | |
463 | if (channel < priv->num_fans) | |
464 | return 0444; | |
465 | break; | |
466 | } | |
467 | break; | |
2fd3eec1 | 468 | case hwmon_power: |
aed80bb9 AS |
469 | switch (priv->kind) { |
470 | case highflownext: | |
471 | /* Special case to support one power sensor */ | |
472 | if (channel == 0) | |
473 | return 0444; | |
474 | break; | |
475 | default: | |
476 | if (channel < priv->num_fans) | |
477 | return 0444; | |
478 | break; | |
479 | } | |
480 | break; | |
2fd3eec1 | 481 | case hwmon_curr: |
654c9735 | 482 | if (channel < priv->num_fans) |
752b9279 | 483 | return 0444; |
752b9279 AS |
484 | break; |
485 | case hwmon_in: | |
486 | switch (priv->kind) { | |
487 | case d5next: | |
f4caa262 AS |
488 | /* Special case to support +5V and +12V voltage sensors */ |
489 | if (channel < priv->num_fans + 2) | |
752b9279 AS |
490 | return 0444; |
491 | break; | |
aed80bb9 AS |
492 | case highflownext: |
493 | /* Special case to support two voltage sensors */ | |
494 | if (channel < 2) | |
495 | return 0444; | |
496 | break; | |
2fd3eec1 | 497 | default: |
654c9735 AS |
498 | if (channel < priv->num_fans) |
499 | return 0444; | |
2fd3eec1 AS |
500 | break; |
501 | } | |
502 | break; | |
503 | default: | |
504 | break; | |
505 | } | |
506 | ||
507 | return 0; | |
0e35f63f AS |
508 | } |
509 | ||
2fd3eec1 AS |
510 | static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, |
511 | int channel, long *val) | |
0e35f63f | 512 | { |
752b9279 | 513 | int ret; |
2fd3eec1 | 514 | struct aqc_data *priv = dev_get_drvdata(dev); |
0e35f63f | 515 | |
2fd3eec1 | 516 | if (time_after(jiffies, priv->updated + STATUS_UPDATE_INTERVAL)) |
0e35f63f AS |
517 | return -ENODATA; |
518 | ||
519 | switch (type) { | |
520 | case hwmon_temp: | |
662d20b3 AS |
521 | switch (attr) { |
522 | case hwmon_temp_input: | |
523 | if (priv->temp_input[channel] == -ENODATA) | |
524 | return -ENODATA; | |
525 | ||
526 | *val = priv->temp_input[channel]; | |
527 | break; | |
528 | case hwmon_temp_offset: | |
529 | ret = | |
530 | aqc_get_ctrl_val(priv, priv->temp_ctrl_offset + | |
531 | channel * AQC_TEMP_SENSOR_SIZE, val); | |
532 | if (ret < 0) | |
533 | return ret; | |
2fd3eec1 | 534 | |
662d20b3 AS |
535 | *val *= 10; |
536 | break; | |
537 | default: | |
538 | break; | |
539 | } | |
0e35f63f AS |
540 | break; |
541 | case hwmon_fan: | |
542 | *val = priv->speed_input[channel]; | |
543 | break; | |
544 | case hwmon_power: | |
545 | *val = priv->power_input[channel]; | |
546 | break; | |
752b9279 | 547 | case hwmon_pwm: |
654c9735 | 548 | if (priv->fan_ctrl_offsets) { |
662d20b3 | 549 | ret = aqc_get_ctrl_val(priv, priv->fan_ctrl_offsets[channel], val); |
752b9279 AS |
550 | if (ret < 0) |
551 | return ret; | |
552 | ||
553 | *val = aqc_percent_to_pwm(ret); | |
752b9279 AS |
554 | } |
555 | break; | |
0e35f63f AS |
556 | case hwmon_in: |
557 | *val = priv->voltage_input[channel]; | |
558 | break; | |
559 | case hwmon_curr: | |
560 | *val = priv->current_input[channel]; | |
561 | break; | |
562 | default: | |
563 | return -EOPNOTSUPP; | |
564 | } | |
565 | ||
566 | return 0; | |
567 | } | |
568 | ||
2fd3eec1 AS |
569 | static int aqc_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, |
570 | int channel, const char **str) | |
0e35f63f | 571 | { |
2fd3eec1 AS |
572 | struct aqc_data *priv = dev_get_drvdata(dev); |
573 | ||
0e35f63f AS |
574 | switch (type) { |
575 | case hwmon_temp: | |
e2769f5e AS |
576 | if (channel < priv->num_temp_sensors) |
577 | *str = priv->temp_label[channel]; | |
578 | else | |
579 | *str = priv->virtual_temp_label[channel - priv->num_temp_sensors]; | |
0e35f63f AS |
580 | break; |
581 | case hwmon_fan: | |
752b9279 | 582 | *str = priv->speed_label[channel]; |
0e35f63f AS |
583 | break; |
584 | case hwmon_power: | |
752b9279 | 585 | *str = priv->power_label[channel]; |
0e35f63f AS |
586 | break; |
587 | case hwmon_in: | |
752b9279 | 588 | *str = priv->voltage_label[channel]; |
0e35f63f AS |
589 | break; |
590 | case hwmon_curr: | |
752b9279 AS |
591 | *str = priv->current_label[channel]; |
592 | break; | |
593 | default: | |
594 | return -EOPNOTSUPP; | |
595 | } | |
596 | ||
597 | return 0; | |
598 | } | |
599 | ||
600 | static int aqc_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, | |
601 | long val) | |
602 | { | |
603 | int ret, pwm_value; | |
604 | struct aqc_data *priv = dev_get_drvdata(dev); | |
605 | ||
606 | switch (type) { | |
662d20b3 AS |
607 | case hwmon_temp: |
608 | switch (attr) { | |
609 | case hwmon_temp_offset: | |
610 | /* Limit temp offset to +/- 15K as in the official software */ | |
611 | val = clamp_val(val, -15000, 15000) / 10; | |
612 | ret = | |
613 | aqc_set_ctrl_val(priv, priv->temp_ctrl_offset + | |
614 | channel * AQC_TEMP_SENSOR_SIZE, val); | |
615 | if (ret < 0) | |
616 | return ret; | |
617 | break; | |
618 | default: | |
619 | return -EOPNOTSUPP; | |
620 | } | |
621 | break; | |
752b9279 AS |
622 | case hwmon_pwm: |
623 | switch (attr) { | |
624 | case hwmon_pwm_input: | |
654c9735 | 625 | if (priv->fan_ctrl_offsets) { |
752b9279 AS |
626 | pwm_value = aqc_pwm_to_percent(val); |
627 | if (pwm_value < 0) | |
628 | return pwm_value; | |
629 | ||
654c9735 | 630 | ret = aqc_set_ctrl_val(priv, priv->fan_ctrl_offsets[channel], |
752b9279 AS |
631 | pwm_value); |
632 | if (ret < 0) | |
633 | return ret; | |
752b9279 | 634 | } |
2fd3eec1 AS |
635 | break; |
636 | default: | |
637 | break; | |
638 | } | |
0e35f63f AS |
639 | break; |
640 | default: | |
641 | return -EOPNOTSUPP; | |
642 | } | |
643 | ||
644 | return 0; | |
645 | } | |
646 | ||
2fd3eec1 AS |
647 | static const struct hwmon_ops aqc_hwmon_ops = { |
648 | .is_visible = aqc_is_visible, | |
649 | .read = aqc_read, | |
650 | .read_string = aqc_read_string, | |
752b9279 | 651 | .write = aqc_write |
0e35f63f AS |
652 | }; |
653 | ||
2fd3eec1 AS |
654 | static const struct hwmon_channel_info *aqc_info[] = { |
655 | HWMON_CHANNEL_INFO(temp, | |
662d20b3 AS |
656 | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET, |
657 | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET, | |
658 | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET, | |
659 | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET, | |
e2769f5e AS |
660 | HWMON_T_INPUT | HWMON_T_LABEL, |
661 | HWMON_T_INPUT | HWMON_T_LABEL, | |
662 | HWMON_T_INPUT | HWMON_T_LABEL, | |
663 | HWMON_T_INPUT | HWMON_T_LABEL, | |
664 | HWMON_T_INPUT | HWMON_T_LABEL, | |
665 | HWMON_T_INPUT | HWMON_T_LABEL, | |
666 | HWMON_T_INPUT | HWMON_T_LABEL, | |
667 | HWMON_T_INPUT | HWMON_T_LABEL, | |
668 | HWMON_T_INPUT | HWMON_T_LABEL, | |
669 | HWMON_T_INPUT | HWMON_T_LABEL, | |
670 | HWMON_T_INPUT | HWMON_T_LABEL, | |
671 | HWMON_T_INPUT | HWMON_T_LABEL, | |
2fd3eec1 AS |
672 | HWMON_T_INPUT | HWMON_T_LABEL, |
673 | HWMON_T_INPUT | HWMON_T_LABEL, | |
674 | HWMON_T_INPUT | HWMON_T_LABEL, | |
675 | HWMON_T_INPUT | HWMON_T_LABEL), | |
676 | HWMON_CHANNEL_INFO(fan, | |
752b9279 AS |
677 | HWMON_F_INPUT | HWMON_F_LABEL, |
678 | HWMON_F_INPUT | HWMON_F_LABEL, | |
679 | HWMON_F_INPUT | HWMON_F_LABEL, | |
680 | HWMON_F_INPUT | HWMON_F_LABEL, | |
681 | HWMON_F_INPUT | HWMON_F_LABEL, | |
682 | HWMON_F_INPUT | HWMON_F_LABEL, | |
2fd3eec1 AS |
683 | HWMON_F_INPUT | HWMON_F_LABEL, |
684 | HWMON_F_INPUT | HWMON_F_LABEL), | |
685 | HWMON_CHANNEL_INFO(power, | |
752b9279 AS |
686 | HWMON_P_INPUT | HWMON_P_LABEL, |
687 | HWMON_P_INPUT | HWMON_P_LABEL, | |
688 | HWMON_P_INPUT | HWMON_P_LABEL, | |
689 | HWMON_P_INPUT | HWMON_P_LABEL, | |
690 | HWMON_P_INPUT | HWMON_P_LABEL, | |
691 | HWMON_P_INPUT | HWMON_P_LABEL, | |
2fd3eec1 AS |
692 | HWMON_P_INPUT | HWMON_P_LABEL, |
693 | HWMON_P_INPUT | HWMON_P_LABEL), | |
752b9279 AS |
694 | HWMON_CHANNEL_INFO(pwm, |
695 | HWMON_PWM_INPUT, | |
696 | HWMON_PWM_INPUT, | |
697 | HWMON_PWM_INPUT, | |
698 | HWMON_PWM_INPUT, | |
699 | HWMON_PWM_INPUT, | |
700 | HWMON_PWM_INPUT, | |
701 | HWMON_PWM_INPUT, | |
702 | HWMON_PWM_INPUT), | |
2fd3eec1 | 703 | HWMON_CHANNEL_INFO(in, |
752b9279 AS |
704 | HWMON_I_INPUT | HWMON_I_LABEL, |
705 | HWMON_I_INPUT | HWMON_I_LABEL, | |
706 | HWMON_I_INPUT | HWMON_I_LABEL, | |
707 | HWMON_I_INPUT | HWMON_I_LABEL, | |
708 | HWMON_I_INPUT | HWMON_I_LABEL, | |
2fd3eec1 AS |
709 | HWMON_I_INPUT | HWMON_I_LABEL, |
710 | HWMON_I_INPUT | HWMON_I_LABEL, | |
0e35f63f | 711 | HWMON_I_INPUT | HWMON_I_LABEL), |
2fd3eec1 | 712 | HWMON_CHANNEL_INFO(curr, |
752b9279 AS |
713 | HWMON_C_INPUT | HWMON_C_LABEL, |
714 | HWMON_C_INPUT | HWMON_C_LABEL, | |
715 | HWMON_C_INPUT | HWMON_C_LABEL, | |
716 | HWMON_C_INPUT | HWMON_C_LABEL, | |
717 | HWMON_C_INPUT | HWMON_C_LABEL, | |
718 | HWMON_C_INPUT | HWMON_C_LABEL, | |
2fd3eec1 AS |
719 | HWMON_C_INPUT | HWMON_C_LABEL, |
720 | HWMON_C_INPUT | HWMON_C_LABEL), | |
0e35f63f AS |
721 | NULL |
722 | }; | |
723 | ||
2fd3eec1 AS |
724 | static const struct hwmon_chip_info aqc_chip_info = { |
725 | .ops = &aqc_hwmon_ops, | |
726 | .info = aqc_info, | |
0e35f63f AS |
727 | }; |
728 | ||
229b159c | 729 | static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) |
0e35f63f | 730 | { |
e2769f5e | 731 | int i, j, sensor_value; |
2fd3eec1 | 732 | struct aqc_data *priv; |
0e35f63f | 733 | |
2fd3eec1 | 734 | if (report->id != STATUS_REPORT_ID) |
0e35f63f AS |
735 | return 0; |
736 | ||
737 | priv = hid_get_drvdata(hdev); | |
738 | ||
739 | /* Info provided with every report */ | |
2fd3eec1 AS |
740 | priv->serial_number[0] = get_unaligned_be16(data + SERIAL_FIRST_PART); |
741 | priv->serial_number[1] = get_unaligned_be16(data + SERIAL_SECOND_PART); | |
742 | priv->firmware_version = get_unaligned_be16(data + FIRMWARE_VERSION); | |
0e35f63f | 743 | |
e2769f5e | 744 | /* Physical temperature sensor readings */ |
654c9735 AS |
745 | for (i = 0; i < priv->num_temp_sensors; i++) { |
746 | sensor_value = get_unaligned_be16(data + | |
747 | priv->temp_sensor_start_offset + | |
748 | i * AQC_TEMP_SENSOR_SIZE); | |
749 | if (sensor_value == AQC_TEMP_SENSOR_DISCONNECTED) | |
750 | priv->temp_input[i] = -ENODATA; | |
751 | else | |
752 | priv->temp_input[i] = sensor_value * 10; | |
753 | } | |
0e35f63f | 754 | |
e2769f5e AS |
755 | /* Virtual temperature sensor readings */ |
756 | for (j = 0; j < priv->num_virtual_temp_sensors; j++) { | |
757 | sensor_value = get_unaligned_be16(data + | |
758 | priv->virtual_temp_sensor_start_offset + | |
759 | j * AQC_TEMP_SENSOR_SIZE); | |
760 | if (sensor_value == AQC_TEMP_SENSOR_DISCONNECTED) | |
761 | priv->temp_input[i] = -ENODATA; | |
762 | else | |
763 | priv->temp_input[i] = sensor_value * 10; | |
764 | i++; | |
765 | } | |
766 | ||
654c9735 AS |
767 | /* Fan speed and related readings */ |
768 | for (i = 0; i < priv->num_fans; i++) { | |
769 | priv->speed_input[i] = | |
770 | get_unaligned_be16(data + priv->fan_sensor_offsets[i] + AQC_FAN_SPEED_OFFSET); | |
771 | priv->power_input[i] = | |
772 | get_unaligned_be16(data + priv->fan_sensor_offsets[i] + | |
773 | AQC_FAN_POWER_OFFSET) * 10000; | |
774 | priv->voltage_input[i] = | |
775 | get_unaligned_be16(data + priv->fan_sensor_offsets[i] + | |
776 | AQC_FAN_VOLTAGE_OFFSET) * 10; | |
777 | priv->current_input[i] = | |
778 | get_unaligned_be16(data + priv->fan_sensor_offsets[i] + AQC_FAN_CURRENT_OFFSET); | |
779 | } | |
0e35f63f | 780 | |
654c9735 AS |
781 | if (priv->power_cycle_count_offset != 0) |
782 | priv->power_cycles = get_unaligned_be32(data + priv->power_cycle_count_offset); | |
0e35f63f | 783 | |
654c9735 AS |
784 | /* Special-case sensor readings */ |
785 | switch (priv->kind) { | |
786 | case d5next: | |
2fd3eec1 | 787 | priv->voltage_input[2] = get_unaligned_be16(data + D5NEXT_5V_VOLTAGE) * 10; |
f4caa262 | 788 | priv->voltage_input[3] = get_unaligned_be16(data + D5NEXT_12V_VOLTAGE) * 10; |
752b9279 | 789 | break; |
cdbe34da AS |
790 | case quadro: |
791 | priv->speed_input[4] = get_unaligned_be16(data + priv->flow_sensor_offset); | |
792 | break; | |
aed80bb9 AS |
793 | case highflownext: |
794 | /* If external temp sensor is not connected, its power reading is also N/A */ | |
795 | if (priv->temp_input[1] == -ENODATA) | |
796 | priv->power_input[0] = -ENODATA; | |
797 | else | |
798 | priv->power_input[0] = | |
799 | get_unaligned_be16(data + HIGHFLOWNEXT_POWER) * 1000000; | |
800 | ||
801 | priv->voltage_input[0] = get_unaligned_be16(data + HIGHFLOWNEXT_5V_VOLTAGE) * 10; | |
802 | priv->voltage_input[1] = | |
803 | get_unaligned_be16(data + HIGHFLOWNEXT_5V_VOLTAGE_USB) * 10; | |
804 | ||
805 | priv->speed_input[0] = get_unaligned_be16(data + HIGHFLOWNEXT_FLOW); | |
806 | priv->speed_input[1] = get_unaligned_be16(data + HIGHFLOWNEXT_WATER_QUALITY); | |
807 | priv->speed_input[2] = get_unaligned_be16(data + HIGHFLOWNEXT_CONDUCTIVITY); | |
808 | break; | |
2fd3eec1 AS |
809 | default: |
810 | break; | |
811 | } | |
0e35f63f AS |
812 | |
813 | priv->updated = jiffies; | |
814 | ||
815 | return 0; | |
816 | } | |
817 | ||
818 | #ifdef CONFIG_DEBUG_FS | |
819 | ||
820 | static int serial_number_show(struct seq_file *seqf, void *unused) | |
821 | { | |
2fd3eec1 | 822 | struct aqc_data *priv = seqf->private; |
0e35f63f AS |
823 | |
824 | seq_printf(seqf, "%05u-%05u\n", priv->serial_number[0], priv->serial_number[1]); | |
825 | ||
826 | return 0; | |
827 | } | |
828 | DEFINE_SHOW_ATTRIBUTE(serial_number); | |
829 | ||
830 | static int firmware_version_show(struct seq_file *seqf, void *unused) | |
831 | { | |
2fd3eec1 | 832 | struct aqc_data *priv = seqf->private; |
0e35f63f AS |
833 | |
834 | seq_printf(seqf, "%u\n", priv->firmware_version); | |
835 | ||
836 | return 0; | |
837 | } | |
838 | DEFINE_SHOW_ATTRIBUTE(firmware_version); | |
839 | ||
840 | static int power_cycles_show(struct seq_file *seqf, void *unused) | |
841 | { | |
2fd3eec1 | 842 | struct aqc_data *priv = seqf->private; |
0e35f63f AS |
843 | |
844 | seq_printf(seqf, "%u\n", priv->power_cycles); | |
845 | ||
846 | return 0; | |
847 | } | |
848 | DEFINE_SHOW_ATTRIBUTE(power_cycles); | |
849 | ||
2fd3eec1 | 850 | static void aqc_debugfs_init(struct aqc_data *priv) |
0e35f63f | 851 | { |
2fd3eec1 | 852 | char name[64]; |
0e35f63f | 853 | |
2fd3eec1 AS |
854 | scnprintf(name, sizeof(name), "%s_%s-%s", "aquacomputer", priv->name, |
855 | dev_name(&priv->hdev->dev)); | |
0e35f63f AS |
856 | |
857 | priv->debugfs = debugfs_create_dir(name, NULL); | |
858 | debugfs_create_file("serial_number", 0444, priv->debugfs, priv, &serial_number_fops); | |
859 | debugfs_create_file("firmware_version", 0444, priv->debugfs, priv, &firmware_version_fops); | |
2fd3eec1 | 860 | |
654c9735 | 861 | if (priv->power_cycle_count_offset != 0) |
2fd3eec1 | 862 | debugfs_create_file("power_cycles", 0444, priv->debugfs, priv, &power_cycles_fops); |
0e35f63f AS |
863 | } |
864 | ||
865 | #else | |
866 | ||
2fd3eec1 | 867 | static void aqc_debugfs_init(struct aqc_data *priv) |
0e35f63f AS |
868 | { |
869 | } | |
870 | ||
871 | #endif | |
872 | ||
2fd3eec1 | 873 | static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) |
0e35f63f | 874 | { |
2fd3eec1 | 875 | struct aqc_data *priv; |
0e35f63f AS |
876 | int ret; |
877 | ||
878 | priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL); | |
879 | if (!priv) | |
880 | return -ENOMEM; | |
881 | ||
882 | priv->hdev = hdev; | |
883 | hid_set_drvdata(hdev, priv); | |
884 | ||
2fd3eec1 | 885 | priv->updated = jiffies - STATUS_UPDATE_INTERVAL; |
0e35f63f AS |
886 | |
887 | ret = hid_parse(hdev); | |
888 | if (ret) | |
889 | return ret; | |
890 | ||
891 | ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); | |
892 | if (ret) | |
893 | return ret; | |
894 | ||
895 | ret = hid_hw_open(hdev); | |
896 | if (ret) | |
897 | goto fail_and_stop; | |
898 | ||
2fd3eec1 AS |
899 | switch (hdev->product) { |
900 | case USB_PRODUCT_ID_D5NEXT: | |
901 | priv->kind = d5next; | |
752b9279 | 902 | |
654c9735 AS |
903 | priv->num_fans = D5NEXT_NUM_FANS; |
904 | priv->fan_sensor_offsets = d5next_sensor_fan_offsets; | |
09e89309 | 905 | priv->fan_ctrl_offsets = d5next_ctrl_fan_offsets; |
654c9735 AS |
906 | priv->num_temp_sensors = D5NEXT_NUM_SENSORS; |
907 | priv->temp_sensor_start_offset = D5NEXT_COOLANT_TEMP; | |
e2769f5e AS |
908 | priv->num_virtual_temp_sensors = D5NEXT_NUM_VIRTUAL_SENSORS; |
909 | priv->virtual_temp_sensor_start_offset = D5NEXT_VIRTUAL_SENSORS_START; | |
654c9735 | 910 | priv->power_cycle_count_offset = D5NEXT_POWER_CYCLES; |
09e89309 | 911 | priv->buffer_size = D5NEXT_CTRL_REPORT_SIZE; |
662d20b3 | 912 | priv->temp_ctrl_offset = D5NEXT_TEMP_CTRL_OFFSET; |
654c9735 | 913 | |
752b9279 | 914 | priv->temp_label = label_d5next_temp; |
e2769f5e | 915 | priv->virtual_temp_label = label_virtual_temp_sensors; |
752b9279 AS |
916 | priv->speed_label = label_d5next_speeds; |
917 | priv->power_label = label_d5next_power; | |
918 | priv->voltage_label = label_d5next_voltages; | |
919 | priv->current_label = label_d5next_current; | |
2fd3eec1 | 920 | break; |
229b159c JD |
921 | case USB_PRODUCT_ID_FARBWERK: |
922 | priv->kind = farbwerk; | |
923 | ||
654c9735 AS |
924 | priv->num_fans = 0; |
925 | priv->num_temp_sensors = FARBWERK_NUM_SENSORS; | |
926 | priv->temp_sensor_start_offset = FARBWERK_SENSOR_START; | |
229b159c JD |
927 | priv->temp_label = label_temp_sensors; |
928 | break; | |
2fd3eec1 AS |
929 | case USB_PRODUCT_ID_FARBWERK360: |
930 | priv->kind = farbwerk360; | |
752b9279 | 931 | |
654c9735 AS |
932 | priv->num_fans = 0; |
933 | priv->num_temp_sensors = FARBWERK360_NUM_SENSORS; | |
934 | priv->temp_sensor_start_offset = FARBWERK360_SENSOR_START; | |
e2769f5e AS |
935 | priv->num_virtual_temp_sensors = FARBWERK360_NUM_VIRTUAL_SENSORS; |
936 | priv->virtual_temp_sensor_start_offset = FARBWERK360_VIRTUAL_SENSORS_START; | |
662d20b3 AS |
937 | priv->buffer_size = FARBWERK360_CTRL_REPORT_SIZE; |
938 | priv->temp_ctrl_offset = FARBWERK360_TEMP_CTRL_OFFSET; | |
752b9279 | 939 | priv->temp_label = label_temp_sensors; |
e2769f5e | 940 | priv->virtual_temp_label = label_virtual_temp_sensors; |
752b9279 AS |
941 | break; |
942 | case USB_PRODUCT_ID_OCTO: | |
943 | priv->kind = octo; | |
654c9735 AS |
944 | |
945 | priv->num_fans = OCTO_NUM_FANS; | |
946 | priv->fan_sensor_offsets = octo_sensor_fan_offsets; | |
947 | priv->fan_ctrl_offsets = octo_ctrl_fan_offsets; | |
948 | priv->num_temp_sensors = OCTO_NUM_SENSORS; | |
949 | priv->temp_sensor_start_offset = OCTO_SENSOR_START; | |
e2769f5e AS |
950 | priv->num_virtual_temp_sensors = OCTO_NUM_VIRTUAL_SENSORS; |
951 | priv->virtual_temp_sensor_start_offset = OCTO_VIRTUAL_SENSORS_START; | |
654c9735 | 952 | priv->power_cycle_count_offset = OCTO_POWER_CYCLES; |
752b9279 | 953 | priv->buffer_size = OCTO_CTRL_REPORT_SIZE; |
662d20b3 | 954 | priv->temp_ctrl_offset = OCTO_TEMP_CTRL_OFFSET; |
752b9279 AS |
955 | |
956 | priv->temp_label = label_temp_sensors; | |
e2769f5e | 957 | priv->virtual_temp_label = label_virtual_temp_sensors; |
752b9279 AS |
958 | priv->speed_label = label_fan_speed; |
959 | priv->power_label = label_fan_power; | |
960 | priv->voltage_label = label_fan_voltage; | |
961 | priv->current_label = label_fan_current; | |
2fd3eec1 | 962 | break; |
cdbe34da AS |
963 | case USB_PRODUCT_ID_QUADRO: |
964 | priv->kind = quadro; | |
965 | ||
966 | priv->num_fans = QUADRO_NUM_FANS; | |
967 | priv->fan_sensor_offsets = quadro_sensor_fan_offsets; | |
968 | priv->fan_ctrl_offsets = quadro_ctrl_fan_offsets; | |
969 | priv->num_temp_sensors = QUADRO_NUM_SENSORS; | |
970 | priv->temp_sensor_start_offset = QUADRO_SENSOR_START; | |
e2769f5e AS |
971 | priv->num_virtual_temp_sensors = QUADRO_NUM_VIRTUAL_SENSORS; |
972 | priv->virtual_temp_sensor_start_offset = QUADRO_VIRTUAL_SENSORS_START; | |
cdbe34da AS |
973 | priv->power_cycle_count_offset = QUADRO_POWER_CYCLES; |
974 | priv->buffer_size = QUADRO_CTRL_REPORT_SIZE; | |
975 | priv->flow_sensor_offset = QUADRO_FLOW_SENSOR_OFFSET; | |
662d20b3 | 976 | priv->temp_ctrl_offset = QUADRO_TEMP_CTRL_OFFSET; |
cdbe34da AS |
977 | |
978 | priv->temp_label = label_temp_sensors; | |
e2769f5e | 979 | priv->virtual_temp_label = label_virtual_temp_sensors; |
cdbe34da AS |
980 | priv->speed_label = label_quadro_speeds; |
981 | priv->power_label = label_fan_power; | |
982 | priv->voltage_label = label_fan_voltage; | |
983 | priv->current_label = label_fan_current; | |
984 | break; | |
aed80bb9 AS |
985 | case USB_PRODUCT_ID_HIGHFLOWNEXT: |
986 | priv->kind = highflownext; | |
987 | ||
988 | priv->num_fans = 0; | |
989 | priv->num_temp_sensors = HIGHFLOWNEXT_NUM_SENSORS; | |
990 | priv->temp_sensor_start_offset = HIGHFLOWNEXT_SENSOR_START; | |
991 | priv->power_cycle_count_offset = QUADRO_POWER_CYCLES; | |
992 | ||
993 | priv->temp_label = label_highflownext_temp_sensors; | |
994 | priv->speed_label = label_highflownext_fan_speed; | |
995 | priv->power_label = label_highflownext_power; | |
996 | priv->voltage_label = label_highflownext_voltage; | |
997 | break; | |
2fd3eec1 AS |
998 | default: |
999 | break; | |
1000 | } | |
1001 | ||
654c9735 AS |
1002 | if (priv->buffer_size != 0) { |
1003 | priv->checksum_start = 0x01; | |
1004 | priv->checksum_length = priv->buffer_size - 3; | |
1005 | priv->checksum_offset = priv->buffer_size - 2; | |
1006 | } | |
1007 | ||
2fd3eec1 AS |
1008 | priv->name = aqc_device_names[priv->kind]; |
1009 | ||
752b9279 | 1010 | priv->buffer = devm_kzalloc(&hdev->dev, priv->buffer_size, GFP_KERNEL); |
8877ecb0 CJ |
1011 | if (!priv->buffer) { |
1012 | ret = -ENOMEM; | |
1013 | goto fail_and_close; | |
1014 | } | |
752b9279 AS |
1015 | |
1016 | mutex_init(&priv->mutex); | |
1017 | ||
2fd3eec1 AS |
1018 | priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, priv->name, priv, |
1019 | &aqc_chip_info, NULL); | |
0e35f63f AS |
1020 | |
1021 | if (IS_ERR(priv->hwmon_dev)) { | |
1022 | ret = PTR_ERR(priv->hwmon_dev); | |
1023 | goto fail_and_close; | |
1024 | } | |
1025 | ||
2fd3eec1 | 1026 | aqc_debugfs_init(priv); |
0e35f63f AS |
1027 | |
1028 | return 0; | |
1029 | ||
1030 | fail_and_close: | |
1031 | hid_hw_close(hdev); | |
1032 | fail_and_stop: | |
1033 | hid_hw_stop(hdev); | |
1034 | return ret; | |
1035 | } | |
1036 | ||
2fd3eec1 | 1037 | static void aqc_remove(struct hid_device *hdev) |
0e35f63f | 1038 | { |
2fd3eec1 | 1039 | struct aqc_data *priv = hid_get_drvdata(hdev); |
0e35f63f AS |
1040 | |
1041 | debugfs_remove_recursive(priv->debugfs); | |
1042 | hwmon_device_unregister(priv->hwmon_dev); | |
1043 | ||
1044 | hid_hw_close(hdev); | |
1045 | hid_hw_stop(hdev); | |
1046 | } | |
1047 | ||
2fd3eec1 AS |
1048 | static const struct hid_device_id aqc_table[] = { |
1049 | { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_D5NEXT) }, | |
229b159c | 1050 | { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_FARBWERK) }, |
2fd3eec1 | 1051 | { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_FARBWERK360) }, |
752b9279 | 1052 | { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_OCTO) }, |
cdbe34da | 1053 | { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_QUADRO) }, |
aed80bb9 | 1054 | { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_HIGHFLOWNEXT) }, |
2fd3eec1 | 1055 | { } |
0e35f63f AS |
1056 | }; |
1057 | ||
2fd3eec1 | 1058 | MODULE_DEVICE_TABLE(hid, aqc_table); |
0e35f63f | 1059 | |
2fd3eec1 | 1060 | static struct hid_driver aqc_driver = { |
0e35f63f | 1061 | .name = DRIVER_NAME, |
2fd3eec1 AS |
1062 | .id_table = aqc_table, |
1063 | .probe = aqc_probe, | |
1064 | .remove = aqc_remove, | |
1065 | .raw_event = aqc_raw_event, | |
0e35f63f AS |
1066 | }; |
1067 | ||
2fd3eec1 | 1068 | static int __init aqc_init(void) |
0e35f63f | 1069 | { |
2fd3eec1 | 1070 | return hid_register_driver(&aqc_driver); |
0e35f63f AS |
1071 | } |
1072 | ||
2fd3eec1 | 1073 | static void __exit aqc_exit(void) |
0e35f63f | 1074 | { |
2fd3eec1 | 1075 | hid_unregister_driver(&aqc_driver); |
0e35f63f AS |
1076 | } |
1077 | ||
1078 | /* Request to initialize after the HID bus to ensure it's not being loaded before */ | |
2fd3eec1 AS |
1079 | late_initcall(aqc_init); |
1080 | module_exit(aqc_exit); | |
0e35f63f AS |
1081 | |
1082 | MODULE_LICENSE("GPL"); | |
1083 | MODULE_AUTHOR("Aleksa Savic <savicaleksa83@gmail.com>"); | |
229b159c | 1084 | MODULE_AUTHOR("Jack Doan <me@jackdoan.com>"); |
2fd3eec1 | 1085 | MODULE_DESCRIPTION("Hwmon driver for Aquacomputer devices"); |