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