blob: ce688ab4fce2fceb57e42876702a6ce6131ff29a [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 */
54#define SMI_STS5_REG 0xC5 /* Bank 0; SMI Status Register */
55#define SMI_STS7_REG 0xC7 /* Bank 0; SMI Status Register */
56#define SMI_STS8_REG 0xC8 /* Bank 0; SMI Status Register */
57
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +030058#define VSEN1_HV_REG 0x40 /* Bank 0; 2 regs (HV/LV) per sensor */
59#define TEMP_CH1_HV_REG 0x42 /* Bank 0; same as VSEN2_HV */
60#define LTD_HV_REG 0x62 /* Bank 0; 2 regs in VSEN range */
amy.shih486842d2019-08-07 01:38:41 +000061#define LTD_HV_HL_REG 0x44 /* Bank 1; 1 reg for LTD */
62#define LTD_LV_HL_REG 0x45 /* Bank 1; 1 reg for LTD */
63#define LTD_HV_LL_REG 0x46 /* Bank 1; 1 reg for LTD */
64#define LTD_LV_LL_REG 0x47 /* Bank 1; 1 reg for LTD */
65#define TEMP_CH1_CH_REG 0x05 /* Bank 1; 1 reg for LTD */
66#define TEMP_CH1_W_REG 0x06 /* Bank 1; 1 reg for LTD */
67#define TEMP_CH1_WH_REG 0x07 /* Bank 1; 1 reg for LTD */
68#define TEMP_CH1_C_REG 0x04 /* Bank 1; 1 reg per sensor */
69#define DTS_T_CPU1_C_REG 0x90 /* Bank 1; 1 reg per sensor */
70#define DTS_T_CPU1_CH_REG 0x91 /* Bank 1; 1 reg per sensor */
71#define DTS_T_CPU1_W_REG 0x92 /* Bank 1; 1 reg per sensor */
72#define DTS_T_CPU1_WH_REG 0x93 /* Bank 1; 1 reg per sensor */
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +030073#define FANIN1_HV_REG 0x80 /* Bank 0; 2 regs (HV/LV) per sensor */
amy.shih486842d2019-08-07 01:38:41 +000074#define FANIN1_HV_HL_REG 0x60 /* Bank 1; 2 regs (HV/LV) per sensor */
75#define FANIN1_LV_HL_REG 0x61 /* Bank 1; 2 regs (HV/LV) per sensor */
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +030076#define T_CPU1_HV_REG 0xA0 /* Bank 0; 2 regs (HV/LV) per sensor */
77
78#define PRTS_REG 0x03 /* Bank 2 */
amy.shihb67b7352019-05-31 10:20:47 +000079#define PFE_REG 0x00 /* Bank 2; PECI Function Enable */
80#define TSI_CTRL_REG 0x50 /* Bank 2; TSI Control Register */
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +030081#define FANCTL1_FMR_REG 0x00 /* Bank 3; 1 reg per channel */
82#define FANCTL1_OUT_REG 0x10 /* Bank 3; 1 reg per channel */
83
amy.shih486842d2019-08-07 01:38:41 +000084#define ENABLE_TSI BIT(1)
85
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +030086static const unsigned short normal_i2c[] = {
87 0x2d, 0x2e, I2C_CLIENT_END
88};
89
90struct nct7904_data {
91 struct i2c_client *client;
92 struct mutex bank_lock;
93 int bank_sel;
94 u32 fanin_mask;
95 u32 vsen_mask;
96 u32 tcpu_mask;
97 u8 fan_mode[FANCTL_MAX];
amy.shihb67b7352019-05-31 10:20:47 +000098 u8 enable_dts;
99 u8 has_dts;
amy.shih486842d2019-08-07 01:38:41 +0000100 u8 temp_mode; /* 0: TR mode, 1: TD mode */
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300101};
102
103/* Access functions */
Jakob Albert73ed6e22018-06-13 17:13:25 +0200104static int nct7904_bank_lock(struct nct7904_data *data, unsigned int bank)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300105{
106 int ret;
107
108 mutex_lock(&data->bank_lock);
109 if (data->bank_sel == bank)
110 return 0;
111 ret = i2c_smbus_write_byte_data(data->client, BANK_SEL_REG, bank);
112 if (ret == 0)
113 data->bank_sel = bank;
114 else
115 data->bank_sel = -1;
116 return ret;
117}
118
119static inline void nct7904_bank_release(struct nct7904_data *data)
120{
121 mutex_unlock(&data->bank_lock);
122}
123
124/* Read 1-byte register. Returns unsigned reg or -ERRNO on error. */
125static int nct7904_read_reg(struct nct7904_data *data,
Jakob Albert73ed6e22018-06-13 17:13:25 +0200126 unsigned int bank, unsigned int reg)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300127{
128 struct i2c_client *client = data->client;
129 int ret;
130
131 ret = nct7904_bank_lock(data, bank);
132 if (ret == 0)
133 ret = i2c_smbus_read_byte_data(client, reg);
134
135 nct7904_bank_release(data);
136 return ret;
137}
138
139/*
140 * Read 2-byte register. Returns register in big-endian format or
141 * -ERRNO on error.
142 */
143static int nct7904_read_reg16(struct nct7904_data *data,
Jakob Albert73ed6e22018-06-13 17:13:25 +0200144 unsigned int bank, unsigned int reg)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300145{
146 struct i2c_client *client = data->client;
147 int ret, hi;
148
149 ret = nct7904_bank_lock(data, bank);
150 if (ret == 0) {
151 ret = i2c_smbus_read_byte_data(client, reg);
152 if (ret >= 0) {
153 hi = ret;
154 ret = i2c_smbus_read_byte_data(client, reg + 1);
155 if (ret >= 0)
156 ret |= hi << 8;
157 }
158 }
159
160 nct7904_bank_release(data);
161 return ret;
162}
163
164/* Write 1-byte register. Returns 0 or -ERRNO on error. */
165static int nct7904_write_reg(struct nct7904_data *data,
Jakob Albert73ed6e22018-06-13 17:13:25 +0200166 unsigned int bank, unsigned int reg, u8 val)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300167{
168 struct i2c_client *client = data->client;
169 int ret;
170
171 ret = nct7904_bank_lock(data, bank);
172 if (ret == 0)
173 ret = i2c_smbus_write_byte_data(client, reg, val);
174
175 nct7904_bank_release(data);
176 return ret;
177}
178
Guenter Roeckd65a5102016-06-26 12:22:03 -0700179static int nct7904_read_fan(struct device *dev, u32 attr, int channel,
180 long *val)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300181{
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300182 struct nct7904_data *data = dev_get_drvdata(dev);
Guenter Roeckd65a5102016-06-26 12:22:03 -0700183 unsigned int cnt, rpm;
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300184 int ret;
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300185
Jakob Albertec1460e2018-06-13 17:13:23 +0200186 switch (attr) {
Guenter Roeckd65a5102016-06-26 12:22:03 -0700187 case hwmon_fan_input:
188 ret = nct7904_read_reg16(data, BANK_0,
189 FANIN1_HV_REG + channel * 2);
190 if (ret < 0)
191 return ret;
192 cnt = ((ret & 0xff00) >> 3) | (ret & 0x1f);
193 if (cnt == 0x1fff)
194 rpm = 0;
195 else
196 rpm = 1350000 / cnt;
197 *val = rpm;
198 return 0;
amy.shih486842d2019-08-07 01:38:41 +0000199 case hwmon_fan_min:
200 ret = nct7904_read_reg16(data, BANK_1,
201 FANIN1_HV_HL_REG + channel * 2);
202 if (ret < 0)
203 return ret;
204 cnt = ((ret & 0xff00) >> 3) | (ret & 0x1f);
205 if (cnt == 0x1fff)
206 rpm = 0;
207 else
208 rpm = 1350000 / cnt;
209 *val = rpm;
210 return 0;
211 case hwmon_fan_alarm:
212 ret = nct7904_read_reg(data, BANK_0,
213 SMI_STS7_REG + (channel >> 3));
214 if (ret < 0)
215 return ret;
216 *val = (ret >> (channel & 0x07)) & 1;
217 return 0;
Guenter Roeckd65a5102016-06-26 12:22:03 -0700218 default:
219 return -EOPNOTSUPP;
220 }
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300221}
222
Guenter Roeckd65a5102016-06-26 12:22:03 -0700223static umode_t nct7904_fan_is_visible(const void *_data, u32 attr, int channel)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300224{
Guenter Roeckd65a5102016-06-26 12:22:03 -0700225 const struct nct7904_data *data = _data;
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300226
amy.shih486842d2019-08-07 01:38:41 +0000227 switch (attr) {
228 case hwmon_fan_input:
229 case hwmon_fan_alarm:
230 if (data->fanin_mask & (1 << channel))
231 return 0444;
232 break;
233 case hwmon_fan_min:
234 if (data->fanin_mask & (1 << channel))
235 return 0644;
236 break;
237 default:
238 break;
239 }
240
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300241 return 0;
242}
243
Guenter Roeckd65a5102016-06-26 12:22:03 -0700244static u8 nct7904_chan_to_index[] = {
245 0, /* Not used */
246 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
247 18, 19, 20, 16
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300248};
249
Guenter Roeckd65a5102016-06-26 12:22:03 -0700250static int nct7904_read_in(struct device *dev, u32 attr, int channel,
251 long *val)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300252{
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300253 struct nct7904_data *data = dev_get_drvdata(dev);
Guenter Roeckd65a5102016-06-26 12:22:03 -0700254 int ret, volt, index;
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300255
Guenter Roeckd65a5102016-06-26 12:22:03 -0700256 index = nct7904_chan_to_index[channel];
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300257
Jakob Albertec1460e2018-06-13 17:13:23 +0200258 switch (attr) {
Guenter Roeckd65a5102016-06-26 12:22:03 -0700259 case hwmon_in_input:
260 ret = nct7904_read_reg16(data, BANK_0,
261 VSEN1_HV_REG + index * 2);
262 if (ret < 0)
263 return ret;
264 volt = ((ret & 0xff00) >> 5) | (ret & 0x7);
265 if (index < 14)
266 volt *= 2; /* 0.002V scale */
267 else
268 volt *= 6; /* 0.006V scale */
269 *val = volt;
270 return 0;
amy.shih486842d2019-08-07 01:38:41 +0000271 case hwmon_in_min:
272 ret = nct7904_read_reg16(data, BANK_1,
273 VSEN1_HV_LL_REG + index * 4);
274 if (ret < 0)
275 return ret;
276 volt = ((ret & 0xff00) >> 5) | (ret & 0x7);
277 if (index < 14)
278 volt *= 2; /* 0.002V scale */
279 else
280 volt *= 6; /* 0.006V scale */
281 *val = volt;
282 return 0;
283 case hwmon_in_max:
284 ret = nct7904_read_reg16(data, BANK_1,
285 VSEN1_HV_HL_REG + index * 4);
286 if (ret < 0)
287 return ret;
288 volt = ((ret & 0xff00) >> 5) | (ret & 0x7);
289 if (index < 14)
290 volt *= 2; /* 0.002V scale */
291 else
292 volt *= 6; /* 0.006V scale */
293 *val = volt;
294 return 0;
295 case hwmon_in_alarm:
296 ret = nct7904_read_reg(data, BANK_0,
297 SMI_STS1_REG + (index >> 3));
298 if (ret < 0)
299 return ret;
300 *val = (ret >> (index & 0x07)) & 1;
301 return 0;
Guenter Roeckd65a5102016-06-26 12:22:03 -0700302 default:
303 return -EOPNOTSUPP;
304 }
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300305}
306
Guenter Roeckd65a5102016-06-26 12:22:03 -0700307static umode_t nct7904_in_is_visible(const void *_data, u32 attr, int channel)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300308{
Guenter Roeckd65a5102016-06-26 12:22:03 -0700309 const struct nct7904_data *data = _data;
310 int index = nct7904_chan_to_index[channel];
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300311
amy.shih486842d2019-08-07 01:38:41 +0000312 switch (attr) {
313 case hwmon_in_input:
314 case hwmon_in_alarm:
315 if (channel > 0 && (data->vsen_mask & BIT(index)))
316 return 0444;
317 break;
318 case hwmon_in_min:
319 case hwmon_in_max:
320 if (channel > 0 && (data->vsen_mask & BIT(index)))
321 return 0644;
322 break;
323 default:
324 break;
325 }
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300326
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300327 return 0;
328}
329
Guenter Roeckd65a5102016-06-26 12:22:03 -0700330static int nct7904_read_temp(struct device *dev, u32 attr, int channel,
331 long *val)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300332{
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300333 struct nct7904_data *data = dev_get_drvdata(dev);
Guenter Roeckd65a5102016-06-26 12:22:03 -0700334 int ret, temp;
amy.shih486842d2019-08-07 01:38:41 +0000335 unsigned int reg1, reg2, reg3;
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300336
Jakob Albertec1460e2018-06-13 17:13:23 +0200337 switch (attr) {
Guenter Roeckd65a5102016-06-26 12:22:03 -0700338 case hwmon_temp_input:
amy.shihb67b7352019-05-31 10:20:47 +0000339 if (channel == 4)
Guenter Roeckd65a5102016-06-26 12:22:03 -0700340 ret = nct7904_read_reg16(data, BANK_0, LTD_HV_REG);
amy.shihb67b7352019-05-31 10:20:47 +0000341 else if (channel < 5)
342 ret = nct7904_read_reg16(data, BANK_0,
343 TEMP_CH1_HV_REG + channel * 4);
Guenter Roeckd65a5102016-06-26 12:22:03 -0700344 else
345 ret = nct7904_read_reg16(data, BANK_0,
amy.shihb67b7352019-05-31 10:20:47 +0000346 T_CPU1_HV_REG + (channel - 5)
347 * 2);
Guenter Roeckd65a5102016-06-26 12:22:03 -0700348 if (ret < 0)
349 return ret;
350 temp = ((ret & 0xff00) >> 5) | (ret & 0x7);
351 *val = sign_extend32(temp, 10) * 125;
352 return 0;
amy.shih486842d2019-08-07 01:38:41 +0000353 case hwmon_temp_alarm:
354 if (channel < 5) {
355 ret = nct7904_read_reg(data, BANK_0,
356 SMI_STS1_REG);
357 if (ret < 0)
358 return ret;
359 *val = (ret >> (((channel * 2) + 1) & 0x07)) & 1;
360 } else {
361 if ((channel - 5) < 4) {
362 ret = nct7904_read_reg(data, BANK_0,
363 SMI_STS7_REG +
364 ((channel - 5) >> 3));
365 if (ret < 0)
366 return ret;
367 *val = (ret >> ((channel - 5) & 0x07)) & 1;
368 } else {
369 ret = nct7904_read_reg(data, BANK_0,
370 SMI_STS8_REG +
371 ((channel - 5) >> 3));
372 if (ret < 0)
373 return ret;
374 *val = (ret >> (((channel - 5) & 0x07) - 4))
375 & 1;
376 }
377 }
378 return 0;
379 case hwmon_temp_type:
380 if (channel < 5) {
381 if ((data->tcpu_mask >> channel) & 0x01) {
382 if ((data->temp_mode >> channel) & 0x01)
383 *val = 3; /* TD */
384 else
385 *val = 4; /* TR */
386 } else {
387 *val = 0;
388 }
389 } else {
390 if ((data->has_dts >> (channel - 5)) & 0x01) {
391 if (data->enable_dts & ENABLE_TSI)
392 *val = 5; /* TSI */
393 else
394 *val = 6; /* PECI */
395 } else {
396 *val = 0;
397 }
398 }
399 return 0;
400 case hwmon_temp_max:
amy.shih4a2d7882085-06-18 15:57:19 +0000401 reg1 = LTD_HV_LL_REG;
amy.shih486842d2019-08-07 01:38:41 +0000402 reg2 = TEMP_CH1_W_REG;
403 reg3 = DTS_T_CPU1_W_REG;
404 break;
405 case hwmon_temp_max_hyst:
amy.shih4a2d7882085-06-18 15:57:19 +0000406 reg1 = LTD_LV_LL_REG;
amy.shih486842d2019-08-07 01:38:41 +0000407 reg2 = TEMP_CH1_WH_REG;
408 reg3 = DTS_T_CPU1_WH_REG;
409 break;
410 case hwmon_temp_crit:
amy.shih4a2d7882085-06-18 15:57:19 +0000411 reg1 = LTD_HV_HL_REG;
amy.shih486842d2019-08-07 01:38:41 +0000412 reg2 = TEMP_CH1_C_REG;
413 reg3 = DTS_T_CPU1_C_REG;
414 break;
415 case hwmon_temp_crit_hyst:
amy.shih4a2d7882085-06-18 15:57:19 +0000416 reg1 = LTD_LV_HL_REG;
amy.shih486842d2019-08-07 01:38:41 +0000417 reg2 = TEMP_CH1_CH_REG;
418 reg3 = DTS_T_CPU1_CH_REG;
419 break;
Guenter Roeckd65a5102016-06-26 12:22:03 -0700420 default:
421 return -EOPNOTSUPP;
422 }
amy.shih486842d2019-08-07 01:38:41 +0000423
424 if (channel == 4)
425 ret = nct7904_read_reg(data, BANK_1, reg1);
426 else if (channel < 5)
427 ret = nct7904_read_reg(data, BANK_1,
428 reg2 + channel * 8);
429 else
430 ret = nct7904_read_reg(data, BANK_1,
431 reg3 + (channel - 5) * 4);
432
433 if (ret < 0)
434 return ret;
435 *val = ret * 1000;
436 return 0;
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300437}
438
Guenter Roeckd65a5102016-06-26 12:22:03 -0700439static umode_t nct7904_temp_is_visible(const void *_data, u32 attr, int channel)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300440{
Guenter Roeckd65a5102016-06-26 12:22:03 -0700441 const struct nct7904_data *data = _data;
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300442
amy.shih486842d2019-08-07 01:38:41 +0000443 switch (attr) {
444 case hwmon_temp_input:
445 case hwmon_temp_alarm:
446 case hwmon_temp_type:
amy.shihb67b7352019-05-31 10:20:47 +0000447 if (channel < 5) {
448 if (data->tcpu_mask & BIT(channel))
Guenter Roecke590be42018-12-10 14:02:17 -0800449 return 0444;
Guenter Roeckd65a5102016-06-26 12:22:03 -0700450 } else {
amy.shihb67b7352019-05-31 10:20:47 +0000451 if (data->has_dts & BIT(channel - 5))
Guenter Roecke590be42018-12-10 14:02:17 -0800452 return 0444;
Guenter Roeckd65a5102016-06-26 12:22:03 -0700453 }
amy.shih486842d2019-08-07 01:38:41 +0000454 break;
455 case hwmon_temp_max:
456 case hwmon_temp_max_hyst:
457 case hwmon_temp_crit:
458 case hwmon_temp_crit_hyst:
459 if (channel < 5) {
460 if (data->tcpu_mask & BIT(channel))
461 return 0644;
462 } else {
463 if (data->has_dts & BIT(channel - 5))
464 return 0644;
465 }
466 break;
467 default:
468 break;
Guenter Roeckd65a5102016-06-26 12:22:03 -0700469 }
470
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300471 return 0;
472}
473
Guenter Roeckd65a5102016-06-26 12:22:03 -0700474static int nct7904_read_pwm(struct device *dev, u32 attr, int channel,
475 long *val)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300476{
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300477 struct nct7904_data *data = dev_get_drvdata(dev);
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300478 int ret;
479
Jakob Albertec1460e2018-06-13 17:13:23 +0200480 switch (attr) {
Guenter Roeckd65a5102016-06-26 12:22:03 -0700481 case hwmon_pwm_input:
482 ret = nct7904_read_reg(data, BANK_3, FANCTL1_OUT_REG + channel);
483 if (ret < 0)
484 return ret;
485 *val = ret;
486 return 0;
487 case hwmon_pwm_enable:
488 ret = nct7904_read_reg(data, BANK_3, FANCTL1_FMR_REG + channel);
489 if (ret < 0)
490 return ret;
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300491
Guenter Roeckd65a5102016-06-26 12:22:03 -0700492 *val = ret ? 2 : 1;
493 return 0;
494 default:
495 return -EOPNOTSUPP;
496 }
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300497}
498
amy.shih486842d2019-08-07 01:38:41 +0000499static int nct7904_write_temp(struct device *dev, u32 attr, int channel,
500 long val)
501{
502 struct nct7904_data *data = dev_get_drvdata(dev);
503 int ret;
504 unsigned int reg1, reg2, reg3;
505
506 val = clamp_val(val / 1000, -128, 127);
507
508 switch (attr) {
509 case hwmon_temp_max:
amy.shih4a2d7882085-06-18 15:57:19 +0000510 reg1 = LTD_HV_LL_REG;
amy.shih486842d2019-08-07 01:38:41 +0000511 reg2 = TEMP_CH1_W_REG;
512 reg3 = DTS_T_CPU1_W_REG;
513 break;
514 case hwmon_temp_max_hyst:
amy.shih4a2d7882085-06-18 15:57:19 +0000515 reg1 = LTD_LV_LL_REG;
amy.shih486842d2019-08-07 01:38:41 +0000516 reg2 = TEMP_CH1_WH_REG;
517 reg3 = DTS_T_CPU1_WH_REG;
518 break;
519 case hwmon_temp_crit:
amy.shih4a2d7882085-06-18 15:57:19 +0000520 reg1 = LTD_HV_HL_REG;
amy.shih486842d2019-08-07 01:38:41 +0000521 reg2 = TEMP_CH1_C_REG;
522 reg3 = DTS_T_CPU1_C_REG;
523 break;
524 case hwmon_temp_crit_hyst:
amy.shih4a2d7882085-06-18 15:57:19 +0000525 reg1 = LTD_LV_HL_REG;
amy.shih486842d2019-08-07 01:38:41 +0000526 reg2 = TEMP_CH1_CH_REG;
527 reg3 = DTS_T_CPU1_CH_REG;
528 break;
529 default:
530 return -EOPNOTSUPP;
531 }
532 if (channel == 4)
533 ret = nct7904_write_reg(data, BANK_1, reg1, val);
534 else if (channel < 5)
535 ret = nct7904_write_reg(data, BANK_1,
536 reg2 + channel * 8, val);
537 else
538 ret = nct7904_write_reg(data, BANK_1,
539 reg3 + (channel - 5) * 4, val);
540
541 return ret;
542}
543
544static int nct7904_write_fan(struct device *dev, u32 attr, int channel,
545 long val)
546{
547 struct nct7904_data *data = dev_get_drvdata(dev);
548 int ret;
549 u8 tmp;
550
551 switch (attr) {
552 case hwmon_fan_min:
553 if (val <= 0)
554 return -EINVAL;
555
556 val = clamp_val(DIV_ROUND_CLOSEST(1350000, val), 1, 0x1fff);
557 tmp = (val >> 5) & 0xff;
558 ret = nct7904_write_reg(data, BANK_1,
559 FANIN1_HV_HL_REG + channel * 2, tmp);
560 if (ret < 0)
561 return ret;
562 tmp = val & 0x1f;
563 ret = nct7904_write_reg(data, BANK_1,
564 FANIN1_LV_HL_REG + channel * 2, tmp);
565 return ret;
566 default:
567 return -EOPNOTSUPP;
568 }
569}
570
571static int nct7904_write_in(struct device *dev, u32 attr, int channel,
572 long val)
573{
574 struct nct7904_data *data = dev_get_drvdata(dev);
575 int ret, index, tmp;
576
577 index = nct7904_chan_to_index[channel];
578
579 if (index < 14)
580 val = val / 2; /* 0.002V scale */
581 else
582 val = val / 6; /* 0.006V scale */
583
584 val = clamp_val(val, 0, 0x7ff);
585
586 switch (attr) {
587 case hwmon_in_min:
588 tmp = nct7904_read_reg(data, BANK_1,
589 VSEN1_LV_LL_REG + index * 4);
590 if (tmp < 0)
591 return tmp;
592 tmp &= ~0x7;
593 tmp |= val & 0x7;
594 ret = nct7904_write_reg(data, BANK_1,
595 VSEN1_LV_LL_REG + index * 4, tmp);
596 if (ret < 0)
597 return ret;
598 tmp = nct7904_read_reg(data, BANK_1,
599 VSEN1_HV_LL_REG + index * 4);
600 if (tmp < 0)
601 return tmp;
602 tmp = (val >> 3) & 0xff;
603 ret = nct7904_write_reg(data, BANK_1,
604 VSEN1_HV_LL_REG + index * 4, tmp);
605 return ret;
606 case hwmon_in_max:
607 tmp = nct7904_read_reg(data, BANK_1,
608 VSEN1_LV_HL_REG + index * 4);
609 if (tmp < 0)
610 return tmp;
611 tmp &= ~0x7;
612 tmp |= val & 0x7;
613 ret = nct7904_write_reg(data, BANK_1,
614 VSEN1_LV_HL_REG + index * 4, tmp);
615 if (ret < 0)
616 return ret;
617 tmp = nct7904_read_reg(data, BANK_1,
618 VSEN1_HV_HL_REG + index * 4);
619 if (tmp < 0)
620 return tmp;
621 tmp = (val >> 3) & 0xff;
622 ret = nct7904_write_reg(data, BANK_1,
623 VSEN1_HV_HL_REG + index * 4, tmp);
624 return ret;
625 default:
626 return -EOPNOTSUPP;
627 }
628}
629
Guenter Roeckd65a5102016-06-26 12:22:03 -0700630static int nct7904_write_pwm(struct device *dev, u32 attr, int channel,
631 long val)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300632{
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300633 struct nct7904_data *data = dev_get_drvdata(dev);
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300634 int ret;
635
Jakob Albertec1460e2018-06-13 17:13:23 +0200636 switch (attr) {
Guenter Roeckd65a5102016-06-26 12:22:03 -0700637 case hwmon_pwm_input:
638 if (val < 0 || val > 255)
639 return -EINVAL;
640 ret = nct7904_write_reg(data, BANK_3, FANCTL1_OUT_REG + channel,
641 val);
642 return ret;
643 case hwmon_pwm_enable:
644 if (val < 1 || val > 2 ||
645 (val == 2 && !data->fan_mode[channel]))
646 return -EINVAL;
647 ret = nct7904_write_reg(data, BANK_3, FANCTL1_FMR_REG + channel,
648 val == 2 ? data->fan_mode[channel] : 0);
649 return ret;
650 default:
651 return -EOPNOTSUPP;
652 }
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300653}
654
Guenter Roeckd65a5102016-06-26 12:22:03 -0700655static umode_t nct7904_pwm_is_visible(const void *_data, u32 attr, int channel)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300656{
Jakob Albertec1460e2018-06-13 17:13:23 +0200657 switch (attr) {
Guenter Roeckd65a5102016-06-26 12:22:03 -0700658 case hwmon_pwm_input:
659 case hwmon_pwm_enable:
Guenter Roecke590be42018-12-10 14:02:17 -0800660 return 0644;
Guenter Roeckd65a5102016-06-26 12:22:03 -0700661 default:
662 return 0;
663 }
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300664}
665
Guenter Roeckd65a5102016-06-26 12:22:03 -0700666static int nct7904_read(struct device *dev, enum hwmon_sensor_types type,
667 u32 attr, int channel, long *val)
668{
669 switch (type) {
670 case hwmon_in:
671 return nct7904_read_in(dev, attr, channel, val);
672 case hwmon_fan:
673 return nct7904_read_fan(dev, attr, channel, val);
674 case hwmon_pwm:
675 return nct7904_read_pwm(dev, attr, channel, val);
676 case hwmon_temp:
677 return nct7904_read_temp(dev, attr, channel, val);
678 default:
679 return -EOPNOTSUPP;
680 }
681}
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300682
Guenter Roeckd65a5102016-06-26 12:22:03 -0700683static int nct7904_write(struct device *dev, enum hwmon_sensor_types type,
684 u32 attr, int channel, long val)
685{
686 switch (type) {
amy.shih486842d2019-08-07 01:38:41 +0000687 case hwmon_in:
688 return nct7904_write_in(dev, attr, channel, val);
689 case hwmon_fan:
690 return nct7904_write_fan(dev, attr, channel, val);
Guenter Roeckd65a5102016-06-26 12:22:03 -0700691 case hwmon_pwm:
692 return nct7904_write_pwm(dev, attr, channel, val);
amy.shih486842d2019-08-07 01:38:41 +0000693 case hwmon_temp:
694 return nct7904_write_temp(dev, attr, channel, val);
Guenter Roeckd65a5102016-06-26 12:22:03 -0700695 default:
696 return -EOPNOTSUPP;
697 }
698}
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300699
Guenter Roeckd65a5102016-06-26 12:22:03 -0700700static umode_t nct7904_is_visible(const void *data,
701 enum hwmon_sensor_types type,
702 u32 attr, int channel)
703{
704 switch (type) {
705 case hwmon_in:
706 return nct7904_in_is_visible(data, attr, channel);
707 case hwmon_fan:
708 return nct7904_fan_is_visible(data, attr, channel);
709 case hwmon_pwm:
710 return nct7904_pwm_is_visible(data, attr, channel);
711 case hwmon_temp:
712 return nct7904_temp_is_visible(data, attr, channel);
713 default:
714 return 0;
715 }
716}
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300717
718/* Return 0 if detection is successful, -ENODEV otherwise */
719static int nct7904_detect(struct i2c_client *client,
720 struct i2c_board_info *info)
721{
722 struct i2c_adapter *adapter = client->adapter;
723
724 if (!i2c_check_functionality(adapter,
725 I2C_FUNC_SMBUS_READ_BYTE |
726 I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
727 return -ENODEV;
728
729 /* Determine the chip type. */
730 if (i2c_smbus_read_byte_data(client, VENDOR_ID_REG) != NUVOTON_ID ||
731 i2c_smbus_read_byte_data(client, CHIP_ID_REG) != NCT7904_ID ||
Guenter Roeck6552f322015-02-27 08:23:37 -0800732 (i2c_smbus_read_byte_data(client, DEVICE_ID_REG) & 0xf0) != 0x50 ||
733 (i2c_smbus_read_byte_data(client, BANK_SEL_REG) & 0xf8) != 0x00)
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300734 return -ENODEV;
735
736 strlcpy(info->type, "nct7904", I2C_NAME_SIZE);
737
738 return 0;
739}
740
Guenter Roeckd65a5102016-06-26 12:22:03 -0700741static const struct hwmon_channel_info *nct7904_info[] = {
Guenter Roeck4ec1d232019-03-29 13:26:40 -0700742 HWMON_CHANNEL_INFO(in,
amy.shih486842d2019-08-07 01:38:41 +0000743 /* dummy, skipped in is_visible */
744 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
745 HWMON_I_ALARM,
746 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
747 HWMON_I_ALARM,
748 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
749 HWMON_I_ALARM,
750 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
751 HWMON_I_ALARM,
752 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
753 HWMON_I_ALARM,
754 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
755 HWMON_I_ALARM,
756 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
757 HWMON_I_ALARM,
758 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
759 HWMON_I_ALARM,
760 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
761 HWMON_I_ALARM,
762 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
763 HWMON_I_ALARM,
764 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
765 HWMON_I_ALARM,
766 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
767 HWMON_I_ALARM,
768 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
769 HWMON_I_ALARM,
770 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
771 HWMON_I_ALARM,
772 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
773 HWMON_I_ALARM,
774 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
775 HWMON_I_ALARM,
776 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
777 HWMON_I_ALARM,
778 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
779 HWMON_I_ALARM,
780 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
781 HWMON_I_ALARM,
782 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
783 HWMON_I_ALARM,
784 HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
785 HWMON_I_ALARM),
Guenter Roeck4ec1d232019-03-29 13:26:40 -0700786 HWMON_CHANNEL_INFO(fan,
amy.shih486842d2019-08-07 01:38:41 +0000787 HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
788 HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
789 HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
790 HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
791 HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
792 HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
793 HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
794 HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM),
Guenter Roeck4ec1d232019-03-29 13:26:40 -0700795 HWMON_CHANNEL_INFO(pwm,
796 HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
797 HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
798 HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
799 HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
800 HWMON_CHANNEL_INFO(temp,
amy.shih486842d2019-08-07 01:38:41 +0000801 HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
802 HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
803 HWMON_T_CRIT_HYST,
804 HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
805 HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
806 HWMON_T_CRIT_HYST,
807 HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
808 HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
809 HWMON_T_CRIT_HYST,
810 HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
811 HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
812 HWMON_T_CRIT_HYST,
813 HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
814 HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
815 HWMON_T_CRIT_HYST,
816 HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
817 HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
818 HWMON_T_CRIT_HYST,
819 HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
820 HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
821 HWMON_T_CRIT_HYST,
822 HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
823 HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
824 HWMON_T_CRIT_HYST,
825 HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
826 HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
827 HWMON_T_CRIT_HYST),
Guenter Roeckd65a5102016-06-26 12:22:03 -0700828 NULL
829};
830
831static const struct hwmon_ops nct7904_hwmon_ops = {
832 .is_visible = nct7904_is_visible,
833 .read = nct7904_read,
834 .write = nct7904_write,
835};
836
837static const struct hwmon_chip_info nct7904_chip_info = {
838 .ops = &nct7904_hwmon_ops,
839 .info = nct7904_info,
840};
841
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300842static int nct7904_probe(struct i2c_client *client,
843 const struct i2c_device_id *id)
844{
845 struct nct7904_data *data;
846 struct device *hwmon_dev;
847 struct device *dev = &client->dev;
848 int ret, i;
849 u32 mask;
amy.shihb67b7352019-05-31 10:20:47 +0000850 u8 val, bit;
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300851
852 data = devm_kzalloc(dev, sizeof(struct nct7904_data), GFP_KERNEL);
853 if (!data)
854 return -ENOMEM;
855
856 data->client = client;
857 mutex_init(&data->bank_lock);
858 data->bank_sel = -1;
859
860 /* Setup sensor groups. */
861 /* FANIN attributes */
862 ret = nct7904_read_reg16(data, BANK_0, FANIN_CTRL0_REG);
863 if (ret < 0)
864 return ret;
865 data->fanin_mask = (ret >> 8) | ((ret & 0xff) << 8);
866
867 /*
868 * VSEN attributes
869 *
870 * Note: voltage sensors overlap with external temperature
871 * sensors. So, if we ever decide to support the latter
872 * we will have to adjust 'vsen_mask' accordingly.
873 */
874 mask = 0;
875 ret = nct7904_read_reg16(data, BANK_0, VT_ADC_CTRL0_REG);
876 if (ret >= 0)
877 mask = (ret >> 8) | ((ret & 0xff) << 8);
878 ret = nct7904_read_reg(data, BANK_0, VT_ADC_CTRL2_REG);
879 if (ret >= 0)
880 mask |= (ret << 16);
881 data->vsen_mask = mask;
882
883 /* CPU_TEMP attributes */
amy.shihb67b7352019-05-31 10:20:47 +0000884 ret = nct7904_read_reg(data, BANK_0, VT_ADC_CTRL0_REG);
amy.shihb3e26062019-06-17 08:08:50 +0000885 if (ret < 0)
886 return ret;
amy.shihb67b7352019-05-31 10:20:47 +0000887
888 if ((ret & 0x6) == 0x6)
889 data->tcpu_mask |= 1; /* TR1 */
890 if ((ret & 0x18) == 0x18)
891 data->tcpu_mask |= 2; /* TR2 */
892 if ((ret & 0x20) == 0x20)
893 data->tcpu_mask |= 4; /* TR3 */
894 if ((ret & 0x80) == 0x80)
895 data->tcpu_mask |= 8; /* TR4 */
896
897 /* LTD */
898 ret = nct7904_read_reg(data, BANK_0, VT_ADC_CTRL2_REG);
amy.shihb3e26062019-06-17 08:08:50 +0000899 if (ret < 0)
900 return ret;
amy.shihb67b7352019-05-31 10:20:47 +0000901 if ((ret & 0x02) == 0x02)
902 data->tcpu_mask |= 0x10;
903
904 /* Multi-Function detecting for Volt and TR/TD */
905 ret = nct7904_read_reg(data, BANK_0, VT_ADC_MD_REG);
amy.shihb3e26062019-06-17 08:08:50 +0000906 if (ret < 0)
907 return ret;
amy.shihb67b7352019-05-31 10:20:47 +0000908
amy.shih486842d2019-08-07 01:38:41 +0000909 data->temp_mode = 0;
amy.shihb67b7352019-05-31 10:20:47 +0000910 for (i = 0; i < 4; i++) {
911 val = (ret & (0x03 << i)) >> (i * 2);
912 bit = (1 << i);
913 if (val == 0)
914 data->tcpu_mask &= ~bit;
amy.shih486842d2019-08-07 01:38:41 +0000915 else if (val == 0x1 || val == 0x2)
916 data->temp_mode |= bit;
amy.shihb67b7352019-05-31 10:20:47 +0000917 }
918
919 /* PECI */
920 ret = nct7904_read_reg(data, BANK_2, PFE_REG);
amy.shihb3e26062019-06-17 08:08:50 +0000921 if (ret < 0)
922 return ret;
amy.shihb67b7352019-05-31 10:20:47 +0000923 if (ret & 0x80) {
amy.shiha653acf2019-06-17 08:10:00 +0000924 data->enable_dts = 1; /* Enable DTS & PECI */
amy.shihb67b7352019-05-31 10:20:47 +0000925 } else {
926 ret = nct7904_read_reg(data, BANK_2, TSI_CTRL_REG);
amy.shihb3e26062019-06-17 08:08:50 +0000927 if (ret < 0)
928 return ret;
amy.shihb67b7352019-05-31 10:20:47 +0000929 if (ret & 0x80)
amy.shiha653acf2019-06-17 08:10:00 +0000930 data->enable_dts = 0x3; /* Enable DTS & TSI */
amy.shihb67b7352019-05-31 10:20:47 +0000931 }
932
933 /* Check DTS enable status */
934 if (data->enable_dts) {
amy.shihb3e26062019-06-17 08:08:50 +0000935 ret = nct7904_read_reg(data, BANK_0, DTS_T_CTRL0_REG);
936 if (ret < 0)
937 return ret;
938 data->has_dts = ret & 0xF;
amy.shih486842d2019-08-07 01:38:41 +0000939 if (data->enable_dts & ENABLE_TSI) {
amy.shihb3e26062019-06-17 08:08:50 +0000940 ret = nct7904_read_reg(data, BANK_0, DTS_T_CTRL1_REG);
941 if (ret < 0)
942 return ret;
943 data->has_dts |= (ret & 0xF) << 4;
amy.shihb67b7352019-05-31 10:20:47 +0000944 }
945 }
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300946
947 for (i = 0; i < FANCTL_MAX; i++) {
948 ret = nct7904_read_reg(data, BANK_3, FANCTL1_FMR_REG + i);
949 if (ret < 0)
950 return ret;
951 data->fan_mode[i] = ret;
952 }
953
954 hwmon_dev =
Guenter Roeckd65a5102016-06-26 12:22:03 -0700955 devm_hwmon_device_register_with_info(dev, client->name, data,
956 &nct7904_chip_info, NULL);
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300957 return PTR_ERR_OR_ZERO(hwmon_dev);
958}
959
960static const struct i2c_device_id nct7904_id[] = {
961 {"nct7904", 0},
962 {}
963};
Javier Martinez Canillas1252be92015-07-30 18:18:39 +0200964MODULE_DEVICE_TABLE(i2c, nct7904_id);
Vadim V. Vlasov9c947d22015-02-27 16:16:00 +0300965
966static struct i2c_driver nct7904_driver = {
967 .class = I2C_CLASS_HWMON,
968 .driver = {
969 .name = "nct7904",
970 },
971 .probe = nct7904_probe,
972 .id_table = nct7904_id,
973 .detect = nct7904_detect,
974 .address_list = normal_i2c,
975};
976
977module_i2c_driver(nct7904_driver);
978
979MODULE_AUTHOR("Vadim V. Vlasov <vvlasov@dev.rtsoft.ru>");
980MODULE_DESCRIPTION("Hwmon driver for NUVOTON NCT7904");
981MODULE_LICENSE("GPL");