1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Nuvoton NCT6694 HWMON driver based on USB interface.
4 *
5 * Copyright (C) 2025 Nuvoton Technology Corp.
6 */
7
8 #include <linux/bits.h>
9 #include <linux/bitfield.h>
10 #include <linux/hwmon.h>
11 #include <linux/kernel.h>
12 #include <linux/mfd/core.h>
13 #include <linux/mfd/nct6694.h>
14 #include <linux/module.h>
15 #include <linux/platform_device.h>
16 #include <linux/slab.h>
17
18 /*
19 * USB command module type for NCT6694 report channel
20 * This defines the module type used for communication with the NCT6694
21 * report channel over the USB interface.
22 */
23 #define NCT6694_RPT_MOD 0xFF
24
25 /* Report channel */
26 /*
27 * The report channel is used to report the status of the hardware monitor
28 * devices, such as voltage, temperature, fan speed, and PWM.
29 */
30 #define NCT6694_VIN_IDX(x) (0x00 + (x))
31 #define NCT6694_TIN_IDX(x) \
32 ({ typeof(x) (_x) = (x); \
33 ((_x) < 10) ? (0x10 + ((_x) * 2)) : \
34 (0x30 + (((_x) - 10) * 2)); })
35 #define NCT6694_FIN_IDX(x) (0x50 + ((x) * 2))
36 #define NCT6694_PWM_IDX(x) (0x70 + (x))
37 #define NCT6694_VIN_STS(x) (0x68 + (x))
38 #define NCT6694_TIN_STS(x) (0x6A + (x))
39 #define NCT6694_FIN_STS(x) (0x6E + (x))
40
41 /*
42 * USB command module type for NCT6694 HWMON controller.
43 * This defines the module type used for communication with the NCT6694
44 * HWMON controller over the USB interface.
45 */
46 #define NCT6694_HWMON_MOD 0x00
47
48 /* Command 00h - Hardware Monitor Control */
49 #define NCT6694_HWMON_CONTROL 0x00
50 #define NCT6694_HWMON_CONTROL_SEL 0x00
51
52 /* Command 02h - Alarm Control */
53 #define NCT6694_HWMON_ALARM 0x02
54 #define NCT6694_HWMON_ALARM_SEL 0x00
55
56 /*
57 * USB command module type for NCT6694 PWM controller.
58 * This defines the module type used for communication with the NCT6694
59 * PWM controller over the USB interface.
60 */
61 #define NCT6694_PWM_MOD 0x01
62
63 /* PWM Command - Manual Control */
64 #define NCT6694_PWM_CONTROL 0x01
65 #define NCT6694_PWM_CONTROL_SEL 0x00
66
67 #define NCT6694_FREQ_FROM_REG(reg) ((reg) * 25000 / 255)
68 #define NCT6694_FREQ_TO_REG(val) \
69 (DIV_ROUND_CLOSEST(clamp_val((val), 100, 25000) * 255, 25000))
70
71 #define NCT6694_LSB_REG_MASK GENMASK(7, 5)
72 #define NCT6694_TIN_HYST_MASK GENMASK(7, 5)
73
74 enum nct6694_hwmon_temp_mode {
75 NCT6694_HWMON_TWOTIME_IRQ = 0,
76 NCT6694_HWMON_ONETIME_IRQ,
77 NCT6694_HWMON_REALTIME_IRQ,
78 NCT6694_HWMON_COMPARE_IRQ,
79 };
80
81 struct __packed nct6694_hwmon_control {
82 u8 vin_en[2];
83 u8 tin_en[2];
84 u8 fin_en[2];
85 u8 pwm_en[2];
86 u8 reserved1[40];
87 u8 pwm_freq[10];
88 u8 reserved2[6];
89 };
90
91 struct __packed nct6694_hwmon_alarm {
92 u8 smi_ctrl;
93 u8 reserved1[15];
94 struct {
95 u8 hl;
96 u8 ll;
97 } vin_limit[16];
98 struct {
99 u8 hyst;
100 s8 hl;
101 } tin_cfg[32];
102 __be16 fin_ll[10];
103 u8 reserved2[4];
104 };
105
106 struct __packed nct6694_pwm_control {
107 u8 mal_en[2];
108 u8 mal_val[10];
109 u8 reserved[12];
110 };
111
112 union __packed nct6694_hwmon_rpt {
113 u8 vin;
114 struct {
115 u8 msb;
116 u8 lsb;
117 } tin;
118 __be16 fin;
119 u8 pwm;
120 u8 status;
121 };
122
123 union __packed nct6694_hwmon_msg {
124 struct nct6694_hwmon_alarm hwmon_alarm;
125 struct nct6694_pwm_control pwm_ctrl;
126 };
127
128 struct nct6694_hwmon_data {
129 struct nct6694 *nct6694;
130 struct mutex lock;
131 struct nct6694_hwmon_control hwmon_en;
132 union nct6694_hwmon_rpt *rpt;
133 union nct6694_hwmon_msg *msg;
134 };
135
in_from_reg(u8 reg)136 static inline long in_from_reg(u8 reg)
137 {
138 return reg * 16;
139 }
140
in_to_reg(long val)141 static inline u8 in_to_reg(long val)
142 {
143 return DIV_ROUND_CLOSEST(val, 16);
144 }
145
temp_from_reg(s8 reg)146 static inline long temp_from_reg(s8 reg)
147 {
148 return reg * 1000;
149 }
150
temp_to_reg(long val)151 static inline s8 temp_to_reg(long val)
152 {
153 return DIV_ROUND_CLOSEST(val, 1000);
154 }
155
156 #define NCT6694_HWMON_IN_CONFIG (HWMON_I_INPUT | HWMON_I_ENABLE | \
157 HWMON_I_MAX | HWMON_I_MIN | \
158 HWMON_I_ALARM)
159 #define NCT6694_HWMON_TEMP_CONFIG (HWMON_T_INPUT | HWMON_T_ENABLE | \
160 HWMON_T_MAX | HWMON_T_MAX_HYST | \
161 HWMON_T_MAX_ALARM)
162 #define NCT6694_HWMON_FAN_CONFIG (HWMON_F_INPUT | HWMON_F_ENABLE | \
163 HWMON_F_MIN | HWMON_F_MIN_ALARM)
164 #define NCT6694_HWMON_PWM_CONFIG (HWMON_PWM_INPUT | HWMON_PWM_ENABLE | \
165 HWMON_PWM_FREQ)
166 static const struct hwmon_channel_info *nct6694_info[] = {
167 HWMON_CHANNEL_INFO(in,
168 NCT6694_HWMON_IN_CONFIG, /* VIN0 */
169 NCT6694_HWMON_IN_CONFIG, /* VIN1 */
170 NCT6694_HWMON_IN_CONFIG, /* VIN2 */
171 NCT6694_HWMON_IN_CONFIG, /* VIN3 */
172 NCT6694_HWMON_IN_CONFIG, /* VIN5 */
173 NCT6694_HWMON_IN_CONFIG, /* VIN6 */
174 NCT6694_HWMON_IN_CONFIG, /* VIN7 */
175 NCT6694_HWMON_IN_CONFIG, /* VIN14 */
176 NCT6694_HWMON_IN_CONFIG, /* VIN15 */
177 NCT6694_HWMON_IN_CONFIG, /* VIN16 */
178 NCT6694_HWMON_IN_CONFIG, /* VBAT */
179 NCT6694_HWMON_IN_CONFIG, /* VSB */
180 NCT6694_HWMON_IN_CONFIG, /* AVSB */
181 NCT6694_HWMON_IN_CONFIG, /* VCC */
182 NCT6694_HWMON_IN_CONFIG, /* VHIF */
183 NCT6694_HWMON_IN_CONFIG), /* VTT */
184
185 HWMON_CHANNEL_INFO(temp,
186 NCT6694_HWMON_TEMP_CONFIG, /* THR1 */
187 NCT6694_HWMON_TEMP_CONFIG, /* THR2 */
188 NCT6694_HWMON_TEMP_CONFIG, /* THR14 */
189 NCT6694_HWMON_TEMP_CONFIG, /* THR15 */
190 NCT6694_HWMON_TEMP_CONFIG, /* THR16 */
191 NCT6694_HWMON_TEMP_CONFIG, /* TDP0 */
192 NCT6694_HWMON_TEMP_CONFIG, /* TDP1 */
193 NCT6694_HWMON_TEMP_CONFIG, /* TDP2 */
194 NCT6694_HWMON_TEMP_CONFIG, /* TDP3 */
195 NCT6694_HWMON_TEMP_CONFIG, /* TDP4 */
196 NCT6694_HWMON_TEMP_CONFIG, /* DTIN0 */
197 NCT6694_HWMON_TEMP_CONFIG, /* DTIN1 */
198 NCT6694_HWMON_TEMP_CONFIG, /* DTIN2 */
199 NCT6694_HWMON_TEMP_CONFIG, /* DTIN3 */
200 NCT6694_HWMON_TEMP_CONFIG, /* DTIN4 */
201 NCT6694_HWMON_TEMP_CONFIG, /* DTIN5 */
202 NCT6694_HWMON_TEMP_CONFIG, /* DTIN6 */
203 NCT6694_HWMON_TEMP_CONFIG, /* DTIN7 */
204 NCT6694_HWMON_TEMP_CONFIG, /* DTIN8 */
205 NCT6694_HWMON_TEMP_CONFIG, /* DTIN9 */
206 NCT6694_HWMON_TEMP_CONFIG, /* DTIN10 */
207 NCT6694_HWMON_TEMP_CONFIG, /* DTIN11 */
208 NCT6694_HWMON_TEMP_CONFIG, /* DTIN12 */
209 NCT6694_HWMON_TEMP_CONFIG, /* DTIN13 */
210 NCT6694_HWMON_TEMP_CONFIG, /* DTIN14 */
211 NCT6694_HWMON_TEMP_CONFIG), /* DTIN15 */
212
213 HWMON_CHANNEL_INFO(fan,
214 NCT6694_HWMON_FAN_CONFIG, /* FIN0 */
215 NCT6694_HWMON_FAN_CONFIG, /* FIN1 */
216 NCT6694_HWMON_FAN_CONFIG, /* FIN2 */
217 NCT6694_HWMON_FAN_CONFIG, /* FIN3 */
218 NCT6694_HWMON_FAN_CONFIG, /* FIN4 */
219 NCT6694_HWMON_FAN_CONFIG, /* FIN5 */
220 NCT6694_HWMON_FAN_CONFIG, /* FIN6 */
221 NCT6694_HWMON_FAN_CONFIG, /* FIN7 */
222 NCT6694_HWMON_FAN_CONFIG, /* FIN8 */
223 NCT6694_HWMON_FAN_CONFIG), /* FIN9 */
224
225 HWMON_CHANNEL_INFO(pwm,
226 NCT6694_HWMON_PWM_CONFIG, /* PWM0 */
227 NCT6694_HWMON_PWM_CONFIG, /* PWM1 */
228 NCT6694_HWMON_PWM_CONFIG, /* PWM2 */
229 NCT6694_HWMON_PWM_CONFIG, /* PWM3 */
230 NCT6694_HWMON_PWM_CONFIG, /* PWM4 */
231 NCT6694_HWMON_PWM_CONFIG, /* PWM5 */
232 NCT6694_HWMON_PWM_CONFIG, /* PWM6 */
233 NCT6694_HWMON_PWM_CONFIG, /* PWM7 */
234 NCT6694_HWMON_PWM_CONFIG, /* PWM8 */
235 NCT6694_HWMON_PWM_CONFIG), /* PWM9 */
236 NULL
237 };
238
nct6694_in_read(struct device * dev,u32 attr,int channel,long * val)239 static int nct6694_in_read(struct device *dev, u32 attr, int channel,
240 long *val)
241 {
242 struct nct6694_hwmon_data *data = dev_get_drvdata(dev);
243 struct nct6694_cmd_header cmd_hd;
244 unsigned char vin_en;
245 int ret;
246
247 guard(mutex)(&data->lock);
248
249 switch (attr) {
250 case hwmon_in_enable:
251 vin_en = data->hwmon_en.vin_en[(channel / 8)];
252 *val = !!(vin_en & BIT(channel % 8));
253
254 return 0;
255 case hwmon_in_input:
256 cmd_hd = (struct nct6694_cmd_header) {
257 .mod = NCT6694_RPT_MOD,
258 .offset = cpu_to_le16(NCT6694_VIN_IDX(channel)),
259 .len = cpu_to_le16(sizeof(data->rpt->vin))
260 };
261 ret = nct6694_read_msg(data->nct6694, &cmd_hd,
262 &data->rpt->vin);
263 if (ret)
264 return ret;
265
266 *val = in_from_reg(data->rpt->vin);
267
268 return 0;
269 case hwmon_in_max:
270 cmd_hd = (struct nct6694_cmd_header) {
271 .mod = NCT6694_HWMON_MOD,
272 .cmd = NCT6694_HWMON_ALARM,
273 .sel = NCT6694_HWMON_ALARM_SEL,
274 .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))
275 };
276 ret = nct6694_read_msg(data->nct6694, &cmd_hd,
277 &data->msg->hwmon_alarm);
278 if (ret)
279 return ret;
280
281 *val = in_from_reg(data->msg->hwmon_alarm.vin_limit[channel].hl);
282
283 return 0;
284 case hwmon_in_min:
285 cmd_hd = (struct nct6694_cmd_header) {
286 .mod = NCT6694_HWMON_MOD,
287 .cmd = NCT6694_HWMON_ALARM,
288 .sel = NCT6694_HWMON_ALARM_SEL,
289 .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))
290 };
291 ret = nct6694_read_msg(data->nct6694, &cmd_hd,
292 &data->msg->hwmon_alarm);
293 if (ret)
294 return ret;
295
296 *val = in_from_reg(data->msg->hwmon_alarm.vin_limit[channel].ll);
297
298 return 0;
299 case hwmon_in_alarm:
300 cmd_hd = (struct nct6694_cmd_header) {
301 .mod = NCT6694_RPT_MOD,
302 .offset = cpu_to_le16(NCT6694_VIN_STS(channel / 8)),
303 .len = cpu_to_le16(sizeof(data->rpt->status))
304 };
305 ret = nct6694_read_msg(data->nct6694, &cmd_hd,
306 &data->rpt->status);
307 if (ret)
308 return ret;
309
310 *val = !!(data->rpt->status & BIT(channel % 8));
311
312 return 0;
313 default:
314 return -EOPNOTSUPP;
315 }
316 }
317
nct6694_temp_read(struct device * dev,u32 attr,int channel,long * val)318 static int nct6694_temp_read(struct device *dev, u32 attr, int channel,
319 long *val)
320 {
321 struct nct6694_hwmon_data *data = dev_get_drvdata(dev);
322 struct nct6694_cmd_header cmd_hd;
323 unsigned char temp_en, temp_hyst;
324 signed char temp_max;
325 int ret, temp_raw;
326
327 guard(mutex)(&data->lock);
328
329 switch (attr) {
330 case hwmon_temp_enable:
331 temp_en = data->hwmon_en.tin_en[channel / 8];
332 *val = !!(temp_en & BIT(channel % 8));
333
334 return 0;
335 case hwmon_temp_input:
336 cmd_hd = (struct nct6694_cmd_header) {
337 .mod = NCT6694_RPT_MOD,
338 .offset = cpu_to_le16(NCT6694_TIN_IDX(channel)),
339 .len = cpu_to_le16(sizeof(data->rpt->tin))
340 };
341 ret = nct6694_read_msg(data->nct6694, &cmd_hd,
342 &data->rpt->tin);
343 if (ret)
344 return ret;
345
346 temp_raw = data->rpt->tin.msb << 3;
347 temp_raw |= FIELD_GET(NCT6694_LSB_REG_MASK, data->rpt->tin.lsb);
348
349 /* Real temperature(milli degrees Celsius) = temp_raw * 1000 * 0.125 */
350 *val = sign_extend32(temp_raw, 10) * 125;
351
352 return 0;
353 case hwmon_temp_max:
354 cmd_hd = (struct nct6694_cmd_header) {
355 .mod = NCT6694_HWMON_MOD,
356 .cmd = NCT6694_HWMON_ALARM,
357 .sel = NCT6694_HWMON_ALARM_SEL,
358 .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))
359 };
360 ret = nct6694_read_msg(data->nct6694, &cmd_hd,
361 &data->msg->hwmon_alarm);
362 if (ret)
363 return ret;
364
365 *val = temp_from_reg(data->msg->hwmon_alarm.tin_cfg[channel].hl);
366
367 return 0;
368 case hwmon_temp_max_hyst:
369 cmd_hd = (struct nct6694_cmd_header) {
370 .mod = NCT6694_HWMON_MOD,
371 .cmd = NCT6694_HWMON_ALARM,
372 .sel = NCT6694_HWMON_ALARM_SEL,
373 .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))
374 };
375 ret = nct6694_read_msg(data->nct6694, &cmd_hd,
376 &data->msg->hwmon_alarm);
377 if (ret)
378 return ret;
379
380 temp_max = data->msg->hwmon_alarm.tin_cfg[channel].hl;
381 temp_hyst = FIELD_GET(NCT6694_TIN_HYST_MASK,
382 data->msg->hwmon_alarm.tin_cfg[channel].hyst);
383 *val = temp_from_reg(temp_max - temp_hyst);
384
385 return 0;
386 case hwmon_temp_max_alarm:
387 cmd_hd = (struct nct6694_cmd_header) {
388 .mod = NCT6694_RPT_MOD,
389 .offset = cpu_to_le16(NCT6694_TIN_STS(channel / 8)),
390 .len = cpu_to_le16(sizeof(data->rpt->status))
391 };
392 ret = nct6694_read_msg(data->nct6694, &cmd_hd,
393 &data->rpt->status);
394 if (ret)
395 return ret;
396
397 *val = !!(data->rpt->status & BIT(channel % 8));
398
399 return 0;
400 default:
401 return -EOPNOTSUPP;
402 }
403 }
404
nct6694_fan_read(struct device * dev,u32 attr,int channel,long * val)405 static int nct6694_fan_read(struct device *dev, u32 attr, int channel,
406 long *val)
407 {
408 struct nct6694_hwmon_data *data = dev_get_drvdata(dev);
409 struct nct6694_cmd_header cmd_hd;
410 unsigned char fanin_en;
411 int ret;
412
413 guard(mutex)(&data->lock);
414
415 switch (attr) {
416 case hwmon_fan_enable:
417 fanin_en = data->hwmon_en.fin_en[channel / 8];
418 *val = !!(fanin_en & BIT(channel % 8));
419
420 return 0;
421 case hwmon_fan_input:
422 cmd_hd = (struct nct6694_cmd_header) {
423 .mod = NCT6694_RPT_MOD,
424 .offset = cpu_to_le16(NCT6694_FIN_IDX(channel)),
425 .len = cpu_to_le16(sizeof(data->rpt->fin))
426 };
427 ret = nct6694_read_msg(data->nct6694, &cmd_hd,
428 &data->rpt->fin);
429 if (ret)
430 return ret;
431
432 *val = be16_to_cpu(data->rpt->fin);
433
434 return 0;
435 case hwmon_fan_min:
436 cmd_hd = (struct nct6694_cmd_header) {
437 .mod = NCT6694_HWMON_MOD,
438 .cmd = NCT6694_HWMON_ALARM,
439 .sel = NCT6694_HWMON_ALARM_SEL,
440 .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))
441 };
442 ret = nct6694_read_msg(data->nct6694, &cmd_hd,
443 &data->msg->hwmon_alarm);
444 if (ret)
445 return ret;
446
447 *val = be16_to_cpu(data->msg->hwmon_alarm.fin_ll[channel]);
448
449 return 0;
450 case hwmon_fan_min_alarm:
451 cmd_hd = (struct nct6694_cmd_header) {
452 .mod = NCT6694_RPT_MOD,
453 .offset = cpu_to_le16(NCT6694_FIN_STS(channel / 8)),
454 .len = cpu_to_le16(sizeof(data->rpt->status))
455 };
456 ret = nct6694_read_msg(data->nct6694, &cmd_hd,
457 &data->rpt->status);
458 if (ret)
459 return ret;
460
461 *val = !!(data->rpt->status & BIT(channel % 8));
462
463 return 0;
464 default:
465 return -EOPNOTSUPP;
466 }
467 }
468
nct6694_pwm_read(struct device * dev,u32 attr,int channel,long * val)469 static int nct6694_pwm_read(struct device *dev, u32 attr, int channel,
470 long *val)
471 {
472 struct nct6694_hwmon_data *data = dev_get_drvdata(dev);
473 struct nct6694_cmd_header cmd_hd;
474 unsigned char pwm_en;
475 int ret;
476
477 guard(mutex)(&data->lock);
478
479 switch (attr) {
480 case hwmon_pwm_enable:
481 pwm_en = data->hwmon_en.pwm_en[channel / 8];
482 *val = !!(pwm_en & BIT(channel % 8));
483
484 return 0;
485 case hwmon_pwm_input:
486 cmd_hd = (struct nct6694_cmd_header) {
487 .mod = NCT6694_RPT_MOD,
488 .offset = cpu_to_le16(NCT6694_PWM_IDX(channel)),
489 .len = cpu_to_le16(sizeof(data->rpt->pwm))
490 };
491 ret = nct6694_read_msg(data->nct6694, &cmd_hd,
492 &data->rpt->pwm);
493 if (ret)
494 return ret;
495
496 *val = data->rpt->pwm;
497
498 return 0;
499 case hwmon_pwm_freq:
500 *val = NCT6694_FREQ_FROM_REG(data->hwmon_en.pwm_freq[channel]);
501
502 return 0;
503 default:
504 return -EOPNOTSUPP;
505 }
506 }
507
nct6694_in_write(struct device * dev,u32 attr,int channel,long val)508 static int nct6694_in_write(struct device *dev, u32 attr, int channel,
509 long val)
510 {
511 struct nct6694_hwmon_data *data = dev_get_drvdata(dev);
512 struct nct6694_cmd_header cmd_hd;
513 int ret;
514
515 guard(mutex)(&data->lock);
516
517 switch (attr) {
518 case hwmon_in_enable:
519 if (val == 0)
520 data->hwmon_en.vin_en[channel / 8] &= ~BIT(channel % 8);
521 else if (val == 1)
522 data->hwmon_en.vin_en[channel / 8] |= BIT(channel % 8);
523 else
524 return -EINVAL;
525
526 cmd_hd = (struct nct6694_cmd_header) {
527 .mod = NCT6694_HWMON_MOD,
528 .cmd = NCT6694_HWMON_CONTROL,
529 .sel = NCT6694_HWMON_CONTROL_SEL,
530 .len = cpu_to_le16(sizeof(data->hwmon_en))
531 };
532
533 return nct6694_write_msg(data->nct6694, &cmd_hd,
534 &data->hwmon_en);
535 case hwmon_in_max:
536 cmd_hd = (struct nct6694_cmd_header) {
537 .mod = NCT6694_HWMON_MOD,
538 .cmd = NCT6694_HWMON_ALARM,
539 .sel = NCT6694_HWMON_ALARM_SEL,
540 .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))
541 };
542 ret = nct6694_read_msg(data->nct6694, &cmd_hd,
543 &data->msg->hwmon_alarm);
544 if (ret)
545 return ret;
546
547 val = clamp_val(val, 0, 2032);
548 data->msg->hwmon_alarm.vin_limit[channel].hl = in_to_reg(val);
549
550 return nct6694_write_msg(data->nct6694, &cmd_hd,
551 &data->msg->hwmon_alarm);
552 case hwmon_in_min:
553 cmd_hd = (struct nct6694_cmd_header) {
554 .mod = NCT6694_HWMON_MOD,
555 .cmd = NCT6694_HWMON_ALARM,
556 .sel = NCT6694_HWMON_ALARM_SEL,
557 .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))
558 };
559 ret = nct6694_read_msg(data->nct6694, &cmd_hd,
560 &data->msg->hwmon_alarm);
561 if (ret)
562 return ret;
563
564 val = clamp_val(val, 0, 2032);
565 data->msg->hwmon_alarm.vin_limit[channel].ll = in_to_reg(val);
566
567 return nct6694_write_msg(data->nct6694, &cmd_hd,
568 &data->msg->hwmon_alarm);
569 default:
570 return -EOPNOTSUPP;
571 }
572 }
573
nct6694_temp_write(struct device * dev,u32 attr,int channel,long val)574 static int nct6694_temp_write(struct device *dev, u32 attr, int channel,
575 long val)
576 {
577 struct nct6694_hwmon_data *data = dev_get_drvdata(dev);
578 struct nct6694_cmd_header cmd_hd;
579 unsigned char temp_hyst;
580 signed char temp_max;
581 int ret;
582
583 guard(mutex)(&data->lock);
584
585 switch (attr) {
586 case hwmon_temp_enable:
587 if (val == 0)
588 data->hwmon_en.tin_en[channel / 8] &= ~BIT(channel % 8);
589 else if (val == 1)
590 data->hwmon_en.tin_en[channel / 8] |= BIT(channel % 8);
591 else
592 return -EINVAL;
593
594 cmd_hd = (struct nct6694_cmd_header) {
595 .mod = NCT6694_HWMON_MOD,
596 .cmd = NCT6694_HWMON_CONTROL,
597 .sel = NCT6694_HWMON_CONTROL_SEL,
598 .len = cpu_to_le16(sizeof(data->hwmon_en))
599 };
600
601 return nct6694_write_msg(data->nct6694, &cmd_hd,
602 &data->hwmon_en);
603 case hwmon_temp_max:
604 cmd_hd = (struct nct6694_cmd_header) {
605 .mod = NCT6694_HWMON_MOD,
606 .cmd = NCT6694_HWMON_ALARM,
607 .sel = NCT6694_HWMON_ALARM_SEL,
608 .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))
609 };
610 ret = nct6694_read_msg(data->nct6694, &cmd_hd,
611 &data->msg->hwmon_alarm);
612 if (ret)
613 return ret;
614
615 val = clamp_val(val, -127000, 127000);
616 data->msg->hwmon_alarm.tin_cfg[channel].hl = temp_to_reg(val);
617
618 return nct6694_write_msg(data->nct6694, &cmd_hd,
619 &data->msg->hwmon_alarm);
620 case hwmon_temp_max_hyst:
621 cmd_hd = (struct nct6694_cmd_header) {
622 .mod = NCT6694_HWMON_MOD,
623 .cmd = NCT6694_HWMON_ALARM,
624 .sel = NCT6694_HWMON_ALARM_SEL,
625 .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))
626 };
627 ret = nct6694_read_msg(data->nct6694, &cmd_hd,
628 &data->msg->hwmon_alarm);
629
630 val = clamp_val(val, -127000, 127000);
631 temp_max = data->msg->hwmon_alarm.tin_cfg[channel].hl;
632 temp_hyst = temp_max - temp_to_reg(val);
633 temp_hyst = clamp_val(temp_hyst, 0, 7);
634 data->msg->hwmon_alarm.tin_cfg[channel].hyst =
635 (data->msg->hwmon_alarm.tin_cfg[channel].hyst & ~NCT6694_TIN_HYST_MASK) |
636 FIELD_PREP(NCT6694_TIN_HYST_MASK, temp_hyst);
637
638 return nct6694_write_msg(data->nct6694, &cmd_hd,
639 &data->msg->hwmon_alarm);
640 default:
641 return -EOPNOTSUPP;
642 }
643 }
644
nct6694_fan_write(struct device * dev,u32 attr,int channel,long val)645 static int nct6694_fan_write(struct device *dev, u32 attr, int channel,
646 long val)
647 {
648 struct nct6694_hwmon_data *data = dev_get_drvdata(dev);
649 struct nct6694_cmd_header cmd_hd;
650 int ret;
651
652 guard(mutex)(&data->lock);
653
654 switch (attr) {
655 case hwmon_fan_enable:
656 if (val == 0)
657 data->hwmon_en.fin_en[channel / 8] &= ~BIT(channel % 8);
658 else if (val == 1)
659 data->hwmon_en.fin_en[channel / 8] |= BIT(channel % 8);
660 else
661 return -EINVAL;
662
663 cmd_hd = (struct nct6694_cmd_header) {
664 .mod = NCT6694_HWMON_MOD,
665 .cmd = NCT6694_HWMON_CONTROL,
666 .sel = NCT6694_HWMON_CONTROL_SEL,
667 .len = cpu_to_le16(sizeof(data->hwmon_en))
668 };
669
670 return nct6694_write_msg(data->nct6694, &cmd_hd,
671 &data->hwmon_en);
672 case hwmon_fan_min:
673 cmd_hd = (struct nct6694_cmd_header) {
674 .mod = NCT6694_HWMON_MOD,
675 .cmd = NCT6694_HWMON_ALARM,
676 .sel = NCT6694_HWMON_ALARM_SEL,
677 .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))
678 };
679 ret = nct6694_read_msg(data->nct6694, &cmd_hd,
680 &data->msg->hwmon_alarm);
681 if (ret)
682 return ret;
683
684 val = clamp_val(val, 1, 65535);
685 data->msg->hwmon_alarm.fin_ll[channel] = cpu_to_be16(val);
686
687 return nct6694_write_msg(data->nct6694, &cmd_hd,
688 &data->msg->hwmon_alarm);
689 default:
690 return -EOPNOTSUPP;
691 }
692 }
693
nct6694_pwm_write(struct device * dev,u32 attr,int channel,long val)694 static int nct6694_pwm_write(struct device *dev, u32 attr, int channel,
695 long val)
696 {
697 struct nct6694_hwmon_data *data = dev_get_drvdata(dev);
698 struct nct6694_cmd_header cmd_hd;
699 int ret;
700
701 guard(mutex)(&data->lock);
702
703 switch (attr) {
704 case hwmon_pwm_enable:
705 if (val == 0)
706 data->hwmon_en.pwm_en[channel / 8] &= ~BIT(channel % 8);
707 else if (val == 1)
708 data->hwmon_en.pwm_en[channel / 8] |= BIT(channel % 8);
709 else
710 return -EINVAL;
711
712 cmd_hd = (struct nct6694_cmd_header) {
713 .mod = NCT6694_HWMON_MOD,
714 .cmd = NCT6694_HWMON_CONTROL,
715 .sel = NCT6694_HWMON_CONTROL_SEL,
716 .len = cpu_to_le16(sizeof(data->hwmon_en))
717 };
718
719 return nct6694_write_msg(data->nct6694, &cmd_hd,
720 &data->hwmon_en);
721 case hwmon_pwm_input:
722 if (val < 0 || val > 255)
723 return -EINVAL;
724
725 cmd_hd = (struct nct6694_cmd_header) {
726 .mod = NCT6694_PWM_MOD,
727 .cmd = NCT6694_PWM_CONTROL,
728 .sel = NCT6694_PWM_CONTROL_SEL,
729 .len = cpu_to_le16(sizeof(data->msg->pwm_ctrl))
730 };
731
732 ret = nct6694_read_msg(data->nct6694, &cmd_hd,
733 &data->msg->pwm_ctrl);
734 if (ret)
735 return ret;
736
737 data->msg->pwm_ctrl.mal_val[channel] = val;
738
739 return nct6694_write_msg(data->nct6694, &cmd_hd,
740 &data->msg->pwm_ctrl);
741 case hwmon_pwm_freq:
742 cmd_hd = (struct nct6694_cmd_header) {
743 .mod = NCT6694_HWMON_MOD,
744 .cmd = NCT6694_HWMON_CONTROL,
745 .sel = NCT6694_HWMON_CONTROL_SEL,
746 .len = cpu_to_le16(sizeof(data->hwmon_en))
747 };
748
749 data->hwmon_en.pwm_freq[channel] = NCT6694_FREQ_TO_REG(val);
750
751 return nct6694_write_msg(data->nct6694, &cmd_hd,
752 &data->hwmon_en);
753 default:
754 return -EOPNOTSUPP;
755 }
756 }
757
nct6694_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)758 static int nct6694_read(struct device *dev, enum hwmon_sensor_types type,
759 u32 attr, int channel, long *val)
760 {
761 switch (type) {
762 case hwmon_in:
763 /* in mV */
764 return nct6694_in_read(dev, attr, channel, val);
765 case hwmon_temp:
766 /* in mC */
767 return nct6694_temp_read(dev, attr, channel, val);
768 case hwmon_fan:
769 /* in RPM */
770 return nct6694_fan_read(dev, attr, channel, val);
771 case hwmon_pwm:
772 /* in value 0~255 */
773 return nct6694_pwm_read(dev, attr, channel, val);
774 default:
775 return -EOPNOTSUPP;
776 }
777 }
778
nct6694_write(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long val)779 static int nct6694_write(struct device *dev, enum hwmon_sensor_types type,
780 u32 attr, int channel, long val)
781 {
782 switch (type) {
783 case hwmon_in:
784 return nct6694_in_write(dev, attr, channel, val);
785 case hwmon_temp:
786 return nct6694_temp_write(dev, attr, channel, val);
787 case hwmon_fan:
788 return nct6694_fan_write(dev, attr, channel, val);
789 case hwmon_pwm:
790 return nct6694_pwm_write(dev, attr, channel, val);
791 default:
792 return -EOPNOTSUPP;
793 }
794 }
795
nct6694_is_visible(const void * data,enum hwmon_sensor_types type,u32 attr,int channel)796 static umode_t nct6694_is_visible(const void *data,
797 enum hwmon_sensor_types type,
798 u32 attr, int channel)
799 {
800 switch (type) {
801 case hwmon_in:
802 switch (attr) {
803 case hwmon_in_enable:
804 case hwmon_in_max:
805 case hwmon_in_min:
806 return 0644;
807 case hwmon_in_alarm:
808 case hwmon_in_input:
809 return 0444;
810 default:
811 return 0;
812 }
813 case hwmon_temp:
814 switch (attr) {
815 case hwmon_temp_enable:
816 case hwmon_temp_max:
817 case hwmon_temp_max_hyst:
818 return 0644;
819 case hwmon_temp_input:
820 case hwmon_temp_max_alarm:
821 return 0444;
822 default:
823 return 0;
824 }
825 case hwmon_fan:
826 switch (attr) {
827 case hwmon_fan_enable:
828 case hwmon_fan_min:
829 return 0644;
830 case hwmon_fan_input:
831 case hwmon_fan_min_alarm:
832 return 0444;
833 default:
834 return 0;
835 }
836 case hwmon_pwm:
837 switch (attr) {
838 case hwmon_pwm_enable:
839 case hwmon_pwm_freq:
840 case hwmon_pwm_input:
841 return 0644;
842 default:
843 return 0;
844 }
845 default:
846 return 0;
847 }
848 }
849
850 static const struct hwmon_ops nct6694_hwmon_ops = {
851 .is_visible = nct6694_is_visible,
852 .read = nct6694_read,
853 .write = nct6694_write,
854 };
855
856 static const struct hwmon_chip_info nct6694_chip_info = {
857 .ops = &nct6694_hwmon_ops,
858 .info = nct6694_info,
859 };
860
nct6694_hwmon_init(struct nct6694_hwmon_data * data)861 static int nct6694_hwmon_init(struct nct6694_hwmon_data *data)
862 {
863 struct nct6694_cmd_header cmd_hd = {
864 .mod = NCT6694_HWMON_MOD,
865 .cmd = NCT6694_HWMON_CONTROL,
866 .sel = NCT6694_HWMON_CONTROL_SEL,
867 .len = cpu_to_le16(sizeof(data->hwmon_en))
868 };
869 int ret;
870
871 /*
872 * Record each Hardware Monitor Channel enable status
873 * and PWM frequency register
874 */
875 ret = nct6694_read_msg(data->nct6694, &cmd_hd,
876 &data->hwmon_en);
877 if (ret)
878 return ret;
879
880 cmd_hd = (struct nct6694_cmd_header) {
881 .mod = NCT6694_HWMON_MOD,
882 .cmd = NCT6694_HWMON_ALARM,
883 .sel = NCT6694_HWMON_ALARM_SEL,
884 .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))
885 };
886
887 /* Select hwmon device alarm mode */
888 ret = nct6694_read_msg(data->nct6694, &cmd_hd,
889 &data->msg->hwmon_alarm);
890 if (ret)
891 return ret;
892
893 data->msg->hwmon_alarm.smi_ctrl = NCT6694_HWMON_REALTIME_IRQ;
894
895 return nct6694_write_msg(data->nct6694, &cmd_hd,
896 &data->msg->hwmon_alarm);
897 }
898
nct6694_hwmon_probe(struct platform_device * pdev)899 static int nct6694_hwmon_probe(struct platform_device *pdev)
900 {
901 struct nct6694_hwmon_data *data;
902 struct nct6694 *nct6694 = dev_get_drvdata(pdev->dev.parent);
903 struct device *hwmon_dev;
904 int ret;
905
906 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
907 if (!data)
908 return -ENOMEM;
909
910 data->rpt = devm_kzalloc(&pdev->dev, sizeof(union nct6694_hwmon_rpt),
911 GFP_KERNEL);
912 if (!data->rpt)
913 return -ENOMEM;
914
915 data->msg = devm_kzalloc(&pdev->dev, sizeof(union nct6694_hwmon_msg),
916 GFP_KERNEL);
917 if (!data->msg)
918 return -ENOMEM;
919
920 data->nct6694 = nct6694;
921 ret = devm_mutex_init(&pdev->dev, &data->lock);
922 if (ret)
923 return ret;
924
925 ret = nct6694_hwmon_init(data);
926 if (ret)
927 return ret;
928
929 /* Register hwmon device to HWMON framework */
930 hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,
931 "nct6694", data,
932 &nct6694_chip_info,
933 NULL);
934 return PTR_ERR_OR_ZERO(hwmon_dev);
935 }
936
937 static struct platform_driver nct6694_hwmon_driver = {
938 .driver = {
939 .name = "nct6694-hwmon",
940 },
941 .probe = nct6694_hwmon_probe,
942 };
943
944 module_platform_driver(nct6694_hwmon_driver);
945
946 MODULE_DESCRIPTION("USB-HWMON driver for NCT6694");
947 MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>");
948 MODULE_LICENSE("GPL");
949 MODULE_ALIAS("platform:nct6694-hwmon");
950