xref: /linux/drivers/hwmon/nct6694-hwmon.c (revision 546b928da0427b0d6c663cbb992bd7bfa9ac7971)
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