blob: f62dd188245147caec3ac061218b47218d0ab4de [file] [log] [blame]
Thomas Gleixnerc942fdd2019-05-27 08:55:06 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +03002/*
3 * nct7904.c - driver for Nuvoton NCT7904D.
4 *
5 * Copyright (c) 2015 Kontron
6 * Author: Vadim V. Vlasov <vvlasov@dev.rtsoft.ru>
amy.shihb67b7352019-05-31 10:20:47 +00007 *
8 * Copyright (c) 2019 Advantech
9 * Author: Amy.Shih <amy.shih@advantech.com.tw>
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +030010 */
11
12#include <linux/module.h>
13#include <linux/device.h>
14#include <linux/init.h>
15#include <linux/i2c.h>
16#include <linux/mutex.h>
17#include <linux/hwmon.h>
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +030018
19#define VENDOR_ID_REG 0x7A /* Any bank */
20#define NUVOTON_ID 0x50
21#define CHIP_ID_REG 0x7B /* Any bank */
22#define NCT7904_ID 0xC5
23#define DEVICE_ID_REG 0x7C /* Any bank */
24
25#define BANK_SEL_REG 0xFF
26#define BANK_0 0x00
27#define BANK_1 0x01
28#define BANK_2 0x02
29#define BANK_3 0x03
30#define BANK_4 0x04
31#define BANK_MAX 0x04
32
33#define FANIN_MAX 12 /* Counted from 1 */
34#define VSEN_MAX 21 /* VSEN1..14, 3VDD, VBAT, V3VSB,
35 LTD (not a voltage), VSEN17..19 */
36#define FANCTL_MAX 4 /* Counted from 1 */
37#define TCPU_MAX 8 /* Counted from 1 */
38#define TEMP_MAX 4 /* Counted from 1 */
39
40#define VT_ADC_CTRL0_REG 0x20 /* Bank 0 */
41#define VT_ADC_CTRL1_REG 0x21 /* Bank 0 */
42#define VT_ADC_CTRL2_REG 0x22 /* Bank 0 */
43#define FANIN_CTRL0_REG 0x24
44#define FANIN_CTRL1_REG 0x25
45#define DTS_T_CTRL0_REG 0x26
46#define DTS_T_CTRL1_REG 0x27
47#define VT_ADC_MD_REG 0x2E
48
amy.shih486842d2019-08-07 01:38:41 +000049#define VSEN1_HV_LL_REG 0x02 /* Bank 1; 2 regs (HV/LV) per sensor */
50#define VSEN1_LV_LL_REG 0x03 /* Bank 1; 2 regs (HV/LV) per sensor */
51#define VSEN1_HV_HL_REG 0x00 /* Bank 1; 2 regs (HV/LV) per sensor */
52#define VSEN1_LV_HL_REG 0x01 /* Bank 1; 2 regs (HV/LV) per sensor */
53#define SMI_STS1_REG 0xC1 /* Bank 0; SMI Status Register */
amy.shih3b710d72019-09-12 11:33:00 +000054#define SMI_STS3_REG 0xC3 /* Bank 0; SMI Status Register */
amy.shih486842d2019-08-07 01:38:41 +000055#define SMI_STS5_REG 0xC5 /* Bank 0; SMI Status Register */
56#define SMI_STS7_REG 0xC7 /* Bank 0; SMI Status Register */
57#define SMI_STS8_REG 0xC8 /* Bank 0; SMI Status Register */
58
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +030059#define VSEN1_HV_REG 0x40 /* Bank 0; 2 regs (HV/LV) per sensor */
60#define TEMP_CH1_HV_REG 0x42 /* Bank 0; same as VSEN2_HV */
61#define LTD_HV_REG 0x62 /* Bank 0; 2 regs in VSEN range */
amy.shih486842d2019-08-07 01:38:41 +000062#define LTD_HV_HL_REG 0x44 /* Bank 1; 1 reg for LTD */
63#define LTD_LV_HL_REG 0x45 /* Bank 1; 1 reg for LTD */
64#define LTD_HV_LL_REG 0x46 /* Bank 1; 1 reg for LTD */
65#define LTD_LV_LL_REG 0x47 /* Bank 1; 1 reg for LTD */
66#define TEMP_CH1_CH_REG 0x05 /* Bank 1; 1 reg for LTD */
67#define TEMP_CH1_W_REG 0x06 /* Bank 1; 1 reg for LTD */
68#define TEMP_CH1_WH_REG 0x07 /* Bank 1; 1 reg for LTD */
69#define TEMP_CH1_C_REG 0x04 /* Bank 1; 1 reg per sensor */
70#define DTS_T_CPU1_C_REG 0x90 /* Bank 1; 1 reg per sensor */
71#define DTS_T_CPU1_CH_REG 0x91 /* Bank 1; 1 reg per sensor */
72#define DTS_T_CPU1_W_REG 0x92 /* Bank 1; 1 reg per sensor */
73#define DTS_T_CPU1_WH_REG 0x93 /* Bank 1; 1 reg per sensor */
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +030074#define FANIN1_HV_REG 0x80 /* Bank 0; 2 regs (HV/LV) per sensor */
amy.shih486842d2019-08-07 01:38:41 +000075#define FANIN1_HV_HL_REG 0x60 /* Bank 1; 2 regs (HV/LV) per sensor */
76#define FANIN1_LV_HL_REG 0x61 /* Bank 1; 2 regs (HV/LV) per sensor */
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +030077#define T_CPU1_HV_REG 0xA0 /* Bank 0; 2 regs (HV/LV) per sensor */
78
79#define PRTS_REG 0x03 /* Bank 2 */
amy.shihb67b7352019-05-31 10:20:47 +000080#define PFE_REG 0x00 /* Bank 2; PECI Function Enable */
81#define TSI_CTRL_REG 0x50 /* Bank 2; TSI Control Register */
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +030082#define FANCTL1_FMR_REG 0x00 /* Bank 3; 1 reg per channel */
83#define FANCTL1_OUT_REG 0x10 /* Bank 3; 1 reg per channel */
84
amy.shih486842d2019-08-07 01:38:41 +000085#define ENABLE_TSI BIT(1)
86
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +030087static const unsigned short normal_i2c[] = {
88 0x2d, 0x2e, I2C_CLIENT_END
89};
90
91struct nct7904_data {
92 struct i2c_client *client;
93 struct mutex bank_lock;
94 int bank_sel;
95 u32 fanin_mask;
96 u32 vsen_mask;
97 u32 tcpu_mask;
98 u8 fan_mode[FANCTL_MAX];
amy.shihb67b7352019-05-31 10:20:47 +000099 u8 enable_dts;
100 u8 has_dts;
amy.shih486842d2019-08-07 01:38:41 +0000101 u8 temp_mode; /* 0: TR mode, 1: TD mode */
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300102};
103
104/* Access functions */
Jakob Albert73ed6e22018-06-13 17:13:25 +0200105static int nct7904_bank_lock(struct nct7904_data *data, unsigned int bank)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300106{
107 int ret;
108
109 mutex_lock(&data->bank_lock);
110 if (data->bank_sel == bank)
111 return 0;
112 ret = i2c_smbus_write_byte_data(data->client, BANK_SEL_REG, bank);
113 if (ret == 0)
114 data->bank_sel = bank;
115 else
116 data->bank_sel = -1;
117 return ret;
118}
119
120static inline void nct7904_bank_release(struct nct7904_data *data)
121{
122 mutex_unlock(&data->bank_lock);
123}
124
125/* Read 1-byte register. Returns unsigned reg or -ERRNO on error. */
126static int nct7904_read_reg(struct nct7904_data *data,
Jakob Albert73ed6e22018-06-13 17:13:25 +0200127 unsigned int bank, unsigned int reg)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300128{
129 struct i2c_client *client = data->client;
130 int ret;
131
132 ret = nct7904_bank_lock(data, bank);
133 if (ret == 0)
134 ret = i2c_smbus_read_byte_data(client, reg);
135
136 nct7904_bank_release(data);
137 return ret;
138}
139
140/*
141 * Read 2-byte register. Returns register in big-endian format or
142 * -ERRNO on error.
143 */
144static int nct7904_read_reg16(struct nct7904_data *data,
Jakob Albert73ed6e22018-06-13 17:13:25 +0200145 unsigned int bank, unsigned int reg)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300146{
147 struct i2c_client *client = data->client;
148 int ret, hi;
149
150 ret = nct7904_bank_lock(data, bank);
151 if (ret == 0) {
152 ret = i2c_smbus_read_byte_data(client, reg);
153 if (ret >= 0) {
154 hi = ret;
155 ret = i2c_smbus_read_byte_data(client, reg + 1);
156 if (ret >= 0)
157 ret |= hi << 8;
158 }
159 }
160
161 nct7904_bank_release(data);
162 return ret;
163}
164
165/* Write 1-byte register. Returns 0 or -ERRNO on error. */
166static int nct7904_write_reg(struct nct7904_data *data,
Jakob Albert73ed6e22018-06-13 17:13:25 +0200167 unsigned int bank, unsigned int reg, u8 val)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300168{
169 struct i2c_client *client = data->client;
170 int ret;
171
172 ret = nct7904_bank_lock(data, bank);
173 if (ret == 0)
174 ret = i2c_smbus_write_byte_data(client, reg, val);
175
176 nct7904_bank_release(data);
177 return ret;
178}
179
Guenter Roeckd65a5102016-06-26 12:22:03 -0700180static int nct7904_read_fan(struct device *dev, u32 attr, int channel,
181 long *val)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300182{
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300183 struct nct7904_data *data = dev_get_drvdata(dev);
Guenter Roeckd65a5102016-06-26 12:22:03 -0700184 unsigned int cnt, rpm;
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300185 int ret;
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300186
Jakob Albertec1460e2018-06-13 17:13:23 +0200187 switch (attr) {
Guenter Roeckd65a5102016-06-26 12:22:03 -0700188 case hwmon_fan_input:
189 ret = nct7904_read_reg16(data, BANK_0,
190 FANIN1_HV_REG + channel * 2);
191 if (ret < 0)
192 return ret;
193 cnt = ((ret & 0xff00) >> 3) | (ret & 0x1f);
194 if (cnt == 0x1fff)
195 rpm = 0;
196 else
197 rpm = 1350000 / cnt;
198 *val = rpm;
199 return 0;
amy.shih486842d2019-08-07 01:38:41 +0000200 case hwmon_fan_min:
201 ret = nct7904_read_reg16(data, BANK_1,
202 FANIN1_HV_HL_REG + channel * 2);
203 if (ret < 0)
204 return ret;
205 cnt = ((ret & 0xff00) >> 3) | (ret & 0x1f);
206 if (cnt == 0x1fff)
207 rpm = 0;
208 else
209 rpm = 1350000 / cnt;
210 *val = rpm;
211 return 0;
212 case hwmon_fan_alarm:
213 ret = nct7904_read_reg(data, BANK_0,
amy.shih3b710d72019-09-12 11:33:00 +0000214 SMI_STS5_REG + (channel >> 3));
amy.shih486842d2019-08-07 01:38:41 +0000215 if (ret < 0)
216 return ret;
217 *val = (ret >> (channel & 0x07)) & 1;
218 return 0;
Guenter Roeckd65a5102016-06-26 12:22:03 -0700219 default:
220 return -EOPNOTSUPP;
221 }
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300222}
223
Guenter Roeckd65a5102016-06-26 12:22:03 -0700224static umode_t nct7904_fan_is_visible(const void *_data, u32 attr, int channel)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300225{
Guenter Roeckd65a5102016-06-26 12:22:03 -0700226 const struct nct7904_data *data = _data;
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300227
amy.shih486842d2019-08-07 01:38:41 +0000228 switch (attr) {
229 case hwmon_fan_input:
230 case hwmon_fan_alarm:
231 if (data->fanin_mask & (1 << channel))
232 return 0444;
233 break;
234 case hwmon_fan_min:
235 if (data->fanin_mask & (1 << channel))
236 return 0644;
237 break;
238 default:
239 break;
240 }
241
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300242 return 0;
243}
244
Guenter Roeckd65a5102016-06-26 12:22:03 -0700245static u8 nct7904_chan_to_index[] = {
246 0, /* Not used */
247 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
248 18, 19, 20, 16
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300249};
250
Guenter Roeckd65a5102016-06-26 12:22:03 -0700251static int nct7904_read_in(struct device *dev, u32 attr, int channel,
252 long *val)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300253{
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300254 struct nct7904_data *data = dev_get_drvdata(dev);
Guenter Roeckd65a5102016-06-26 12:22:03 -0700255 int ret, volt, index;
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300256
Guenter Roeckd65a5102016-06-26 12:22:03 -0700257 index = nct7904_chan_to_index[channel];
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300258
Jakob Albertec1460e2018-06-13 17:13:23 +0200259 switch (attr) {
Guenter Roeckd65a5102016-06-26 12:22:03 -0700260 case hwmon_in_input:
261 ret = nct7904_read_reg16(data, BANK_0,
262 VSEN1_HV_REG + index * 2);
263 if (ret < 0)
264 return ret;
265 volt = ((ret & 0xff00) >> 5) | (ret & 0x7);
266 if (index < 14)
267 volt *= 2; /* 0.002V scale */
268 else
269 volt *= 6; /* 0.006V scale */
270 *val = volt;
271 return 0;
amy.shih486842d2019-08-07 01:38:41 +0000272 case hwmon_in_min:
273 ret = nct7904_read_reg16(data, BANK_1,
274 VSEN1_HV_LL_REG + index * 4);
275 if (ret < 0)
276 return ret;
277 volt = ((ret & 0xff00) >> 5) | (ret & 0x7);
278 if (index < 14)
279 volt *= 2; /* 0.002V scale */
280 else
281 volt *= 6; /* 0.006V scale */
282 *val = volt;
283 return 0;
284 case hwmon_in_max:
285 ret = nct7904_read_reg16(data, BANK_1,
286 VSEN1_HV_HL_REG + index * 4);
287 if (ret < 0)
288 return ret;
289 volt = ((ret & 0xff00) >> 5) | (ret & 0x7);
290 if (index < 14)
291 volt *= 2; /* 0.002V scale */
292 else
293 volt *= 6; /* 0.006V scale */
294 *val = volt;
295 return 0;
296 case hwmon_in_alarm:
297 ret = nct7904_read_reg(data, BANK_0,
298 SMI_STS1_REG + (index >> 3));
299 if (ret < 0)
300 return ret;
301 *val = (ret >> (index & 0x07)) & 1;
302 return 0;
Guenter Roeckd65a5102016-06-26 12:22:03 -0700303 default:
304 return -EOPNOTSUPP;
305 }
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300306}
307
Guenter Roeckd65a5102016-06-26 12:22:03 -0700308static umode_t nct7904_in_is_visible(const void *_data, u32 attr, int channel)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300309{
Guenter Roeckd65a5102016-06-26 12:22:03 -0700310 const struct nct7904_data *data = _data;
311 int index = nct7904_chan_to_index[channel];
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300312
amy.shih486842d2019-08-07 01:38:41 +0000313 switch (attr) {
314 case hwmon_in_input:
315 case hwmon_in_alarm:
316 if (channel > 0 && (data->vsen_mask & BIT(index)))
317 return 0444;
318 break;
319 case hwmon_in_min:
320 case hwmon_in_max:
321 if (channel > 0 && (data->vsen_mask & BIT(index)))
322 return 0644;
323 break;
324 default:
325 break;
326 }
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300327
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300328 return 0;
329}
330
Guenter Roeckd65a5102016-06-26 12:22:03 -0700331static int nct7904_read_temp(struct device *dev, u32 attr, int channel,
332 long *val)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300333{
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300334 struct nct7904_data *data = dev_get_drvdata(dev);
Guenter Roeckd65a5102016-06-26 12:22:03 -0700335 int ret, temp;
amy.shih486842d2019-08-07 01:38:41 +0000336 unsigned int reg1, reg2, reg3;
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300337
Jakob Albertec1460e2018-06-13 17:13:23 +0200338 switch (attr) {
Guenter Roeckd65a5102016-06-26 12:22:03 -0700339 case hwmon_temp_input:
amy.shihb67b7352019-05-31 10:20:47 +0000340 if (channel == 4)
Guenter Roeckd65a5102016-06-26 12:22:03 -0700341 ret = nct7904_read_reg16(data, BANK_0, LTD_HV_REG);
amy.shihb67b7352019-05-31 10:20:47 +0000342 else if (channel < 5)
343 ret = nct7904_read_reg16(data, BANK_0,
344 TEMP_CH1_HV_REG + channel * 4);
Guenter Roeckd65a5102016-06-26 12:22:03 -0700345 else
346 ret = nct7904_read_reg16(data, BANK_0,
amy.shihb67b7352019-05-31 10:20:47 +0000347 T_CPU1_HV_REG + (channel - 5)
348 * 2);
Guenter Roeckd65a5102016-06-26 12:22:03 -0700349 if (ret < 0)
350 return ret;
351 temp = ((ret & 0xff00) >> 5) | (ret & 0x7);
352 *val = sign_extend32(temp, 10) * 125;
353 return 0;
amy.shih486842d2019-08-07 01:38:41 +0000354 case hwmon_temp_alarm:
amy.shih3b710d72019-09-12 11:33:00 +0000355 if (channel == 4) {
356 ret = nct7904_read_reg(data, BANK_0,
357 SMI_STS3_REG);
358 if (ret < 0)
359 return ret;
360 *val = (ret >> 1) & 1;
361 } else if (channel < 4) {
amy.shih486842d2019-08-07 01:38:41 +0000362 ret = nct7904_read_reg(data, BANK_0,
363 SMI_STS1_REG);
364 if (ret < 0)
365 return ret;
366 *val = (ret >> (((channel * 2) + 1) & 0x07)) & 1;
367 } else {
368 if ((channel - 5) < 4) {
369 ret = nct7904_read_reg(data, BANK_0,
370 SMI_STS7_REG +
371 ((channel - 5) >> 3));
372 if (ret < 0)
373 return ret;
374 *val = (ret >> ((channel - 5) & 0x07)) & 1;
375 } else {
376 ret = nct7904_read_reg(data, BANK_0,
377 SMI_STS8_REG +
378 ((channel - 5) >> 3));
379 if (ret < 0)
380 return ret;
381 *val = (ret >> (((channel - 5) & 0x07) - 4))
382 & 1;
383 }
384 }
385 return 0;
386 case hwmon_temp_type:
387 if (channel < 5) {
388 if ((data->tcpu_mask >> channel) & 0x01) {
389 if ((data->temp_mode >> channel) & 0x01)
390 *val = 3; /* TD */
391 else
392 *val = 4; /* TR */
393 } else {
394 *val = 0;
395 }
396 } else {
397 if ((data->has_dts >> (channel - 5)) & 0x01) {
398 if (data->enable_dts & ENABLE_TSI)
399 *val = 5; /* TSI */
400 else
401 *val = 6; /* PECI */
402 } else {
403 *val = 0;
404 }
405 }
406 return 0;
407 case hwmon_temp_max:
amy.shih4a2d7882085-06-18 15:57:19 +0000408 reg1 = LTD_HV_LL_REG;
amy.shih486842d2019-08-07 01:38:41 +0000409 reg2 = TEMP_CH1_W_REG;
410 reg3 = DTS_T_CPU1_W_REG;
411 break;
412 case hwmon_temp_max_hyst:
amy.shih4a2d7882085-06-18 15:57:19 +0000413 reg1 = LTD_LV_LL_REG;
amy.shih486842d2019-08-07 01:38:41 +0000414 reg2 = TEMP_CH1_WH_REG;
415 reg3 = DTS_T_CPU1_WH_REG;
416 break;
417 case hwmon_temp_crit:
amy.shih4a2d7882085-06-18 15:57:19 +0000418 reg1 = LTD_HV_HL_REG;
amy.shih486842d2019-08-07 01:38:41 +0000419 reg2 = TEMP_CH1_C_REG;
420 reg3 = DTS_T_CPU1_C_REG;
421 break;
422 case hwmon_temp_crit_hyst:
amy.shih4a2d7882085-06-18 15:57:19 +0000423 reg1 = LTD_LV_HL_REG;
amy.shih486842d2019-08-07 01:38:41 +0000424 reg2 = TEMP_CH1_CH_REG;
425 reg3 = DTS_T_CPU1_CH_REG;
426 break;
Guenter Roeckd65a5102016-06-26 12:22:03 -0700427 default:
428 return -EOPNOTSUPP;
429 }
amy.shih486842d2019-08-07 01:38:41 +0000430
431 if (channel == 4)
432 ret = nct7904_read_reg(data, BANK_1, reg1);
433 else if (channel < 5)
434 ret = nct7904_read_reg(data, BANK_1,
435 reg2 + channel * 8);
436 else
437 ret = nct7904_read_reg(data, BANK_1,
438 reg3 + (channel - 5) * 4);
439
440 if (ret < 0)
441 return ret;
442 *val = ret * 1000;
443 return 0;
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300444}
445
Guenter Roeckd65a5102016-06-26 12:22:03 -0700446static umode_t nct7904_temp_is_visible(const void *_data, u32 attr, int channel)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300447{
Guenter Roeckd65a5102016-06-26 12:22:03 -0700448 const struct nct7904_data *data = _data;
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300449
amy.shih486842d2019-08-07 01:38:41 +0000450 switch (attr) {
451 case hwmon_temp_input:
452 case hwmon_temp_alarm:
453 case hwmon_temp_type:
amy.shihb67b7352019-05-31 10:20:47 +0000454 if (channel < 5) {
455 if (data->tcpu_mask & BIT(channel))
Guenter Roecke590be42018-12-10 14:02:17 -0800456 return 0444;
Guenter Roeckd65a5102016-06-26 12:22:03 -0700457 } else {
amy.shihb67b7352019-05-31 10:20:47 +0000458 if (data->has_dts & BIT(channel - 5))
Guenter Roecke590be42018-12-10 14:02:17 -0800459 return 0444;
Guenter Roeckd65a5102016-06-26 12:22:03 -0700460 }
amy.shih486842d2019-08-07 01:38:41 +0000461 break;
462 case hwmon_temp_max:
463 case hwmon_temp_max_hyst:
464 case hwmon_temp_crit:
465 case hwmon_temp_crit_hyst:
466 if (channel < 5) {
467 if (data->tcpu_mask & BIT(channel))
468 return 0644;
469 } else {
470 if (data->has_dts & BIT(channel - 5))
471 return 0644;
472 }
473 break;
474 default:
475 break;
Guenter Roeckd65a5102016-06-26 12:22:03 -0700476 }
477
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300478 return 0;
479}
480
Guenter Roeckd65a5102016-06-26 12:22:03 -0700481static int nct7904_read_pwm(struct device *dev, u32 attr, int channel,
482 long *val)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300483{
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300484 struct nct7904_data *data = dev_get_drvdata(dev);
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300485 int ret;
486
Jakob Albertec1460e2018-06-13 17:13:23 +0200487 switch (attr) {
Guenter Roeckd65a5102016-06-26 12:22:03 -0700488 case hwmon_pwm_input:
489 ret = nct7904_read_reg(data, BANK_3, FANCTL1_OUT_REG + channel);
490 if (ret < 0)
491 return ret;
492 *val = ret;
493 return 0;
494 case hwmon_pwm_enable:
495 ret = nct7904_read_reg(data, BANK_3, FANCTL1_FMR_REG + channel);
496 if (ret < 0)
497 return ret;
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300498
Guenter Roeckd65a5102016-06-26 12:22:03 -0700499 *val = ret ? 2 : 1;
500 return 0;
501 default:
502 return -EOPNOTSUPP;
503 }
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300504}
505
amy.shih486842d2019-08-07 01:38:41 +0000506static int nct7904_write_temp(struct device *dev, u32 attr, int channel,
507 long val)
508{
509 struct nct7904_data *data = dev_get_drvdata(dev);
510 int ret;
511 unsigned int reg1, reg2, reg3;
512
513 val = clamp_val(val / 1000, -128, 127);
514
515 switch (attr) {
516 case hwmon_temp_max:
amy.shih4a2d7882085-06-18 15:57:19 +0000517 reg1 = LTD_HV_LL_REG;
amy.shih486842d2019-08-07 01:38:41 +0000518 reg2 = TEMP_CH1_W_REG;
519 reg3 = DTS_T_CPU1_W_REG;
520 break;
521 case hwmon_temp_max_hyst:
amy.shih4a2d7882085-06-18 15:57:19 +0000522 reg1 = LTD_LV_LL_REG;
amy.shih486842d2019-08-07 01:38:41 +0000523 reg2 = TEMP_CH1_WH_REG;
524 reg3 = DTS_T_CPU1_WH_REG;
525 break;
526 case hwmon_temp_crit:
amy.shih4a2d7882085-06-18 15:57:19 +0000527 reg1 = LTD_HV_HL_REG;
amy.shih486842d2019-08-07 01:38:41 +0000528 reg2 = TEMP_CH1_C_REG;
529 reg3 = DTS_T_CPU1_C_REG;
530 break;
531 case hwmon_temp_crit_hyst:
amy.shih4a2d7882085-06-18 15:57:19 +0000532 reg1 = LTD_LV_HL_REG;
amy.shih486842d2019-08-07 01:38:41 +0000533 reg2 = TEMP_CH1_CH_REG;
534 reg3 = DTS_T_CPU1_CH_REG;
535 break;
536 default:
537 return -EOPNOTSUPP;
538 }
539 if (channel == 4)
540 ret = nct7904_write_reg(data, BANK_1, reg1, val);
541 else if (channel < 5)
542 ret = nct7904_write_reg(data, BANK_1,
543 reg2 + channel * 8, val);
544 else
545 ret = nct7904_write_reg(data, BANK_1,
546 reg3 + (channel - 5) * 4, val);
547
548 return ret;
549}
550
551static int nct7904_write_fan(struct device *dev, u32 attr, int channel,
552 long val)
553{
554 struct nct7904_data *data = dev_get_drvdata(dev);
555 int ret;
556 u8 tmp;
557
558 switch (attr) {
559 case hwmon_fan_min:
560 if (val <= 0)
561 return -EINVAL;
562
563 val = clamp_val(DIV_ROUND_CLOSEST(1350000, val), 1, 0x1fff);
564 tmp = (val >> 5) & 0xff;
565 ret = nct7904_write_reg(data, BANK_1,
566 FANIN1_HV_HL_REG + channel * 2, tmp);
567 if (ret < 0)
568 return ret;
569 tmp = val & 0x1f;
570 ret = nct7904_write_reg(data, BANK_1,
571 FANIN1_LV_HL_REG + channel * 2, tmp);
572 return ret;
573 default:
574 return -EOPNOTSUPP;
575 }
576}
577
578static int nct7904_write_in(struct device *dev, u32 attr, int channel,
579 long val)
580{
581 struct nct7904_data *data = dev_get_drvdata(dev);
582 int ret, index, tmp;
583
584 index = nct7904_chan_to_index[channel];
585
586 if (index < 14)
587 val = val / 2; /* 0.002V scale */
588 else
589 val = val / 6; /* 0.006V scale */
590
591 val = clamp_val(val, 0, 0x7ff);
592
593 switch (attr) {
594 case hwmon_in_min:
595 tmp = nct7904_read_reg(data, BANK_1,
596 VSEN1_LV_LL_REG + index * 4);
597 if (tmp < 0)
598 return tmp;
599 tmp &= ~0x7;
600 tmp |= val & 0x7;
601 ret = nct7904_write_reg(data, BANK_1,
602 VSEN1_LV_LL_REG + index * 4, tmp);
603 if (ret < 0)
604 return ret;
605 tmp = nct7904_read_reg(data, BANK_1,
606 VSEN1_HV_LL_REG + index * 4);
607 if (tmp < 0)
608 return tmp;
609 tmp = (val >> 3) & 0xff;
610 ret = nct7904_write_reg(data, BANK_1,
611 VSEN1_HV_LL_REG + index * 4, tmp);
612 return ret;
613 case hwmon_in_max:
614 tmp = nct7904_read_reg(data, BANK_1,
615 VSEN1_LV_HL_REG + index * 4);
616 if (tmp < 0)
617 return tmp;
618 tmp &= ~0x7;
619 tmp |= val & 0x7;
620 ret = nct7904_write_reg(data, BANK_1,
621 VSEN1_LV_HL_REG + index * 4, tmp);
622 if (ret < 0)
623 return ret;
624 tmp = nct7904_read_reg(data, BANK_1,
625 VSEN1_HV_HL_REG + index * 4);
626 if (tmp < 0)
627 return tmp;
628 tmp = (val >> 3) & 0xff;
629 ret = nct7904_write_reg(data, BANK_1,
630 VSEN1_HV_HL_REG + index * 4, tmp);
631 return ret;
632 default:
633 return -EOPNOTSUPP;
634 }
635}
636
Guenter Roeckd65a5102016-06-26 12:22:03 -0700637static int nct7904_write_pwm(struct device *dev, u32 attr, int channel,
638 long val)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300639{
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300640 struct nct7904_data *data = dev_get_drvdata(dev);
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300641 int ret;
642
Jakob Albertec1460e2018-06-13 17:13:23 +0200643 switch (attr) {
Guenter Roeckd65a5102016-06-26 12:22:03 -0700644 case hwmon_pwm_input:
645 if (val < 0 || val > 255)
646 return -EINVAL;
647 ret = nct7904_write_reg(data, BANK_3, FANCTL1_OUT_REG + channel,
648 val);
649 return ret;
650 case hwmon_pwm_enable:
651 if (val < 1 || val > 2 ||
652 (val == 2 && !data->fan_mode[channel]))
653 return -EINVAL;
654 ret = nct7904_write_reg(data, BANK_3, FANCTL1_FMR_REG + channel,
655 val == 2 ? data->fan_mode[channel] : 0);
656 return ret;
657 default:
658 return -EOPNOTSUPP;
659 }
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300660}
661
Guenter Roeckd65a5102016-06-26 12:22:03 -0700662static umode_t nct7904_pwm_is_visible(const void *_data, u32 attr, int channel)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300663{
Jakob Albertec1460e2018-06-13 17:13:23 +0200664 switch (attr) {
Guenter Roeckd65a5102016-06-26 12:22:03 -0700665 case hwmon_pwm_input:
666 case hwmon_pwm_enable:
Guenter Roecke590be42018-12-10 14:02:17 -0800667 return 0644;
Guenter Roeckd65a5102016-06-26 12:22:03 -0700668 default:
669 return 0;
670 }
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300671}
672
Guenter Roeckd65a5102016-06-26 12:22:03 -0700673static int nct7904_read(struct device *dev, enum hwmon_sensor_types type,
674 u32 attr, int channel, long *val)
675{
676 switch (type) {
677 case hwmon_in:
678 return nct7904_read_in(dev, attr, channel, val);
679 case hwmon_fan:
680 return nct7904_read_fan(dev, attr, channel, val);
681 case hwmon_pwm:
682 return nct7904_read_pwm(dev, attr, channel, val);
683 case hwmon_temp:
684 return nct7904_read_temp(dev, attr, channel, val);
685 default:
686 return -EOPNOTSUPP;
687 }
688}
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300689
Guenter Roeckd65a5102016-06-26 12:22:03 -0700690static int nct7904_write(struct device *dev, enum hwmon_sensor_types type,
691 u32 attr, int channel, long val)
692{
693 switch (type) {
amy.shih486842d2019-08-07 01:38:41 +0000694 case hwmon_in:
695 return nct7904_write_in(dev, attr, channel, val);
696 case hwmon_fan:
697 return nct7904_write_fan(dev, attr, channel, val);
Guenter Roeckd65a5102016-06-26 12:22:03 -0700698 case hwmon_pwm:
699 return nct7904_write_pwm(dev, attr, channel, val);
amy.shih486842d2019-08-07 01:38:41 +0000700 case hwmon_temp:
701 return nct7904_write_temp(dev, attr, channel, val);
Guenter Roeckd65a5102016-06-26 12:22:03 -0700702 default:
703 return -EOPNOTSUPP;
704 }
705}
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300706
Guenter Roeckd65a5102016-06-26 12:22:03 -0700707static umode_t nct7904_is_visible(const void *data,
708 enum hwmon_sensor_types type,
709 u32 attr, int channel)
710{
711 switch (type) {
712 case hwmon_in:
713 return nct7904_in_is_visible(data, attr, channel);
714 case hwmon_fan:
715 return nct7904_fan_is_visible(data, attr, channel);
716 case hwmon_pwm:
717 return nct7904_pwm_is_visible(data, attr, channel);
718 case hwmon_temp:
719 return nct7904_temp_is_visible(data, attr, channel);
720 default:
721 return 0;
722 }
723}
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300724
725/* Return 0 if detection is successful, -ENODEV otherwise */
726static int nct7904_detect(struct i2c_client *client,
727 struct i2c_board_info *info)
728{
729 struct i2c_adapter *adapter = client->adapter;
730
731 if (!i2c_check_functionality(adapter,
732 I2C_FUNC_SMBUS_READ_BYTE |
733 I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
734 return -ENODEV;
735
736 /* Determine the chip type. */
737 if (i2c_smbus_read_byte_data(client, VENDOR_ID_REG) != NUVOTON_ID ||
738 i2c_smbus_read_byte_data(client, CHIP_ID_REG) != NCT7904_ID ||
Guenter Roeck6552f322015-02-27 08:23:37 -0800739 (i2c_smbus_read_byte_data(client, DEVICE_ID_REG) & 0xf0) != 0x50 ||
740 (i2c_smbus_read_byte_data(client, BANK_SEL_REG) & 0xf8) != 0x00)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300741 return -ENODEV;
742
743 strlcpy(info->type, "nct7904", I2C_NAME_SIZE);
744
745 return 0;
746}
747
Guenter Roeckd65a5102016-06-26 12:22:03 -0700748static const struct hwmon_channel_info *nct7904_info[] = {
Guenter Roeck4ec1d232019-03-29 13:26:40 -0700749 HWMON_CHANNEL_INFO(in,
amy.shih486842d2019-08-07 01:38:41 +0000750 /* dummy, skipped in is_visible */
751 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
752 HWMON_I_ALARM,
753 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
754 HWMON_I_ALARM,
755 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
756 HWMON_I_ALARM,
757 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
758 HWMON_I_ALARM,
759 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
760 HWMON_I_ALARM,
761 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
762 HWMON_I_ALARM,
763 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
764 HWMON_I_ALARM,
765 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
766 HWMON_I_ALARM,
767 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
768 HWMON_I_ALARM,
769 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
770 HWMON_I_ALARM,
771 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
772 HWMON_I_ALARM,
773 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
774 HWMON_I_ALARM,
775 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
776 HWMON_I_ALARM,
777 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
778 HWMON_I_ALARM,
779 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
780 HWMON_I_ALARM,
781 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
782 HWMON_I_ALARM,
783 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
784 HWMON_I_ALARM,
785 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
786 HWMON_I_ALARM,
787 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
788 HWMON_I_ALARM,
789 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
790 HWMON_I_ALARM,
791 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
792 HWMON_I_ALARM),
Guenter Roeck4ec1d232019-03-29 13:26:40 -0700793 HWMON_CHANNEL_INFO(fan,
amy.shih486842d2019-08-07 01:38:41 +0000794 HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
795 HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
796 HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
797 HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
798 HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
799 HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
800 HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
801 HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM),
Guenter Roeck4ec1d232019-03-29 13:26:40 -0700802 HWMON_CHANNEL_INFO(pwm,
803 HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
804 HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
805 HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
806 HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
807 HWMON_CHANNEL_INFO(temp,
amy.shih486842d2019-08-07 01:38:41 +0000808 HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
809 HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
810 HWMON_T_CRIT_HYST,
811 HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
812 HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
813 HWMON_T_CRIT_HYST,
814 HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
815 HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
816 HWMON_T_CRIT_HYST,
817 HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
818 HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
819 HWMON_T_CRIT_HYST,
820 HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
821 HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
822 HWMON_T_CRIT_HYST,
823 HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
824 HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
825 HWMON_T_CRIT_HYST,
826 HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
827 HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
828 HWMON_T_CRIT_HYST,
829 HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
830 HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
831 HWMON_T_CRIT_HYST,
832 HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
833 HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
834 HWMON_T_CRIT_HYST),
Guenter Roeckd65a5102016-06-26 12:22:03 -0700835 NULL
836};
837
838static const struct hwmon_ops nct7904_hwmon_ops = {
839 .is_visible = nct7904_is_visible,
840 .read = nct7904_read,
841 .write = nct7904_write,
842};
843
844static const struct hwmon_chip_info nct7904_chip_info = {
845 .ops = &nct7904_hwmon_ops,
846 .info = nct7904_info,
847};
848
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300849static int nct7904_probe(struct i2c_client *client,
850 const struct i2c_device_id *id)
851{
852 struct nct7904_data *data;
853 struct device *hwmon_dev;
854 struct device *dev = &client->dev;
855 int ret, i;
856 u32 mask;
amy.shihb67b7352019-05-31 10:20:47 +0000857 u8 val, bit;
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300858
859 data = devm_kzalloc(dev, sizeof(struct nct7904_data), GFP_KERNEL);
860 if (!data)
861 return -ENOMEM;
862
863 data->client = client;
864 mutex_init(&data->bank_lock);
865 data->bank_sel = -1;
866
867 /* Setup sensor groups. */
868 /* FANIN attributes */
869 ret = nct7904_read_reg16(data, BANK_0, FANIN_CTRL0_REG);
870 if (ret < 0)
871 return ret;
872 data->fanin_mask = (ret >> 8) | ((ret & 0xff) << 8);
873
874 /*
875 * VSEN attributes
876 *
877 * Note: voltage sensors overlap with external temperature
878 * sensors. So, if we ever decide to support the latter
879 * we will have to adjust 'vsen_mask' accordingly.
880 */
881 mask = 0;
882 ret = nct7904_read_reg16(data, BANK_0, VT_ADC_CTRL0_REG);
883 if (ret >= 0)
884 mask = (ret >> 8) | ((ret & 0xff) << 8);
885 ret = nct7904_read_reg(data, BANK_0, VT_ADC_CTRL2_REG);
886 if (ret >= 0)
887 mask |= (ret << 16);
888 data->vsen_mask = mask;
889
890 /* CPU_TEMP attributes */
amy.shihb67b7352019-05-31 10:20:47 +0000891 ret = nct7904_read_reg(data, BANK_0, VT_ADC_CTRL0_REG);
amy.shihb3e26062019-06-17 08:08:50 +0000892 if (ret < 0)
893 return ret;
amy.shihb67b7352019-05-31 10:20:47 +0000894
895 if ((ret & 0x6) == 0x6)
896 data->tcpu_mask |= 1; /* TR1 */
897 if ((ret & 0x18) == 0x18)
898 data->tcpu_mask |= 2; /* TR2 */
899 if ((ret & 0x20) == 0x20)
900 data->tcpu_mask |= 4; /* TR3 */
901 if ((ret & 0x80) == 0x80)
902 data->tcpu_mask |= 8; /* TR4 */
903
904 /* LTD */
905 ret = nct7904_read_reg(data, BANK_0, VT_ADC_CTRL2_REG);
amy.shihb3e26062019-06-17 08:08:50 +0000906 if (ret < 0)
907 return ret;
amy.shihb67b7352019-05-31 10:20:47 +0000908 if ((ret & 0x02) == 0x02)
909 data->tcpu_mask |= 0x10;
910
911 /* Multi-Function detecting for Volt and TR/TD */
912 ret = nct7904_read_reg(data, BANK_0, VT_ADC_MD_REG);
amy.shihb3e26062019-06-17 08:08:50 +0000913 if (ret < 0)
914 return ret;
amy.shihb67b7352019-05-31 10:20:47 +0000915
amy.shih486842d2019-08-07 01:38:41 +0000916 data->temp_mode = 0;
amy.shihb67b7352019-05-31 10:20:47 +0000917 for (i = 0; i < 4; i++) {
amy.shih539ad002019-09-18 16:48:00 +0800918 val = (ret >> (i * 2)) & 0x03;
amy.shihb67b7352019-05-31 10:20:47 +0000919 bit = (1 << i);
amy.shih539ad002019-09-18 16:48:00 +0800920 if (val == 0) {
amy.shihb67b7352019-05-31 10:20:47 +0000921 data->tcpu_mask &= ~bit;
amy.shih539ad002019-09-18 16:48:00 +0800922 } else {
923 if (val == 0x1 || val == 0x2)
924 data->temp_mode |= bit;
925 data->vsen_mask &= ~(0x06 << (i * 2));
926 }
amy.shihb67b7352019-05-31 10:20:47 +0000927 }
928
929 /* PECI */
930 ret = nct7904_read_reg(data, BANK_2, PFE_REG);
amy.shihb3e26062019-06-17 08:08:50 +0000931 if (ret < 0)
932 return ret;
amy.shihb67b7352019-05-31 10:20:47 +0000933 if (ret & 0x80) {
amy.shiha653acf2019-06-17 08:10:00 +0000934 data->enable_dts = 1; /* Enable DTS & PECI */
amy.shihb67b7352019-05-31 10:20:47 +0000935 } else {
936 ret = nct7904_read_reg(data, BANK_2, TSI_CTRL_REG);
amy.shihb3e26062019-06-17 08:08:50 +0000937 if (ret < 0)
938 return ret;
amy.shihb67b7352019-05-31 10:20:47 +0000939 if (ret & 0x80)
amy.shiha653acf2019-06-17 08:10:00 +0000940 data->enable_dts = 0x3; /* Enable DTS & TSI */
amy.shihb67b7352019-05-31 10:20:47 +0000941 }
942
943 /* Check DTS enable status */
944 if (data->enable_dts) {
amy.shihb3e26062019-06-17 08:08:50 +0000945 ret = nct7904_read_reg(data, BANK_0, DTS_T_CTRL0_REG);
946 if (ret < 0)
947 return ret;
948 data->has_dts = ret & 0xF;
amy.shih486842d2019-08-07 01:38:41 +0000949 if (data->enable_dts & ENABLE_TSI) {
amy.shihb3e26062019-06-17 08:08:50 +0000950 ret = nct7904_read_reg(data, BANK_0, DTS_T_CTRL1_REG);
951 if (ret < 0)
952 return ret;
953 data->has_dts |= (ret & 0xF) << 4;
amy.shihb67b7352019-05-31 10:20:47 +0000954 }
955 }
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300956
957 for (i = 0; i < FANCTL_MAX; i++) {
958 ret = nct7904_read_reg(data, BANK_3, FANCTL1_FMR_REG + i);
959 if (ret < 0)
960 return ret;
961 data->fan_mode[i] = ret;
962 }
963
964 hwmon_dev =
Guenter Roeckd65a5102016-06-26 12:22:03 -0700965 devm_hwmon_device_register_with_info(dev, client->name, data,
966 &nct7904_chip_info, NULL);
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300967 return PTR_ERR_OR_ZERO(hwmon_dev);
968}
969
970static const struct i2c_device_id nct7904_id[] = {
971 {"nct7904", 0},
972 {}
973};
Javier Martinez Canillas1252be92015-07-30 18:18:39 +0200974MODULE_DEVICE_TABLE(i2c, nct7904_id);
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300975
976static struct i2c_driver nct7904_driver = {
977 .class = I2C_CLASS_HWMON,
978 .driver = {
979 .name = "nct7904",
980 },
981 .probe = nct7904_probe,
982 .id_table = nct7904_id,
983 .detect = nct7904_detect,
984 .address_list = normal_i2c,
985};
986
987module_i2c_driver(nct7904_driver);
988
989MODULE_AUTHOR("Vadim V. Vlasov <vvlasov@dev.rtsoft.ru>");
990MODULE_DESCRIPTION("Hwmon driver for NUVOTON NCT7904");
991MODULE_LICENSE("GPL");