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