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