File Index
Files and directories
Sign in
FILE

nct6694-hwmon.c

File preview with syntax highlighting.

linux-6.19.14/drivers/hwmon/nct6694-hwmon.c C
  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
 74enum 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
 81struct __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
 91struct __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
106struct __packed nct6694_pwm_control {
107	u8 mal_en[2];
108	u8 mal_val[10];
109	u8 reserved[12];
110};
111
112union __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
123union __packed nct6694_hwmon_msg {
124	struct nct6694_hwmon_alarm hwmon_alarm;
125	struct nct6694_pwm_control pwm_ctrl;
126};
127
128struct 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
136static inline long in_from_reg(u8 reg)
137{
138	return reg * 16;
139}
140
141static inline u8 in_to_reg(long val)
142{
143	return DIV_ROUND_CLOSEST(val, 16);
144}
145
146static inline long temp_from_reg(s8 reg)
147{
148	return reg * 1000;
149}
150
151static 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)
166static 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
239static 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
318static 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
405static 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
469static 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
508static 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
574static 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
645static 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
694static 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
758static 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
779static 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
796static 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
850static const struct hwmon_ops nct6694_hwmon_ops = {
851	.is_visible = nct6694_is_visible,
852	.read = nct6694_read,
853	.write = nct6694_write,
854};
855
856static const struct hwmon_chip_info nct6694_chip_info = {
857	.ops = &nct6694_hwmon_ops,
858	.info = nct6694_info,
859};
860
861static 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
899static 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
937static struct platform_driver nct6694_hwmon_driver = {
938	.driver = {
939		.name	= "nct6694-hwmon",
940	},
941	.probe		= nct6694_hwmon_probe,
942};
943
944module_platform_driver(nct6694_hwmon_driver);
945
946MODULE_DESCRIPTION("USB-HWMON driver for NCT6694");
947MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>");
948MODULE_LICENSE("GPL");
949MODULE_ALIAS("platform:nct6694-hwmon");
~/ Read-only file preview.