diff mbox series

hwmon: (nct7904) Add extra sysfs support for fan, voltage and tempearture. Fix the incorrect value of tcpu_mask in nct7904_data struct.

Message ID 20190530091758.1331-1-Amy.Shih@advantech.com.tw (mailing list archive)
State Changes Requested
Headers show
Series hwmon: (nct7904) Add extra sysfs support for fan, voltage and tempearture. Fix the incorrect value of tcpu_mask in nct7904_data struct. | expand

Commit Message

Amy.Shih@advantech.com.tw May 30, 2019, 9:17 a.m. UTC
From: "amy.shih" <amy.shih@advantech.com.tw>

This patch do 3 jobs as described in below:

1. NCT-7904D also supports reading of channel limitation registers
and SMI status registers for fan, voltage and tempearture monitoring,
add below sysfs nodes:

-fan[1-*]_min
-fan[1-*]_alarm
-in[0-*]_min
-in[0-*]_max
-in[0-*]_alarm
-temp[1-*]_max
-temp[1-*]_max_hyst
-temp[1-*]_emergency
-temp[1-*]_emergency_hyst
-temp[1-*]_alarm

2. Add the temp[1-*]_type sysfs to show type of tempearture sensor is
thermal diode, thermistor, AMD SB-TSI or Intel PECI.

3. Detect the multi-function of voltage, thermal diode and thermistor
from register VT_ADC_MD_REG to set value of tcpu_mask in nct7904_data
struct, set temp[1-5]_input the input values TEMP_CH1~4 and LTD of
temperature. Set temp[6~13]_input the input values of DTS temperature
that correspond to sensors TCPU1~8.

Signed-off-by: amy.shih <amy.shih@advantech.com.tw>
---
 drivers/hwmon/nct7904.c | 527 ++++++++++++++++++++++++++++++++++++----
 1 file changed, 474 insertions(+), 53 deletions(-)
diff mbox series

Patch

diff --git a/drivers/hwmon/nct7904.c b/drivers/hwmon/nct7904.c
index 04516789b070..a7592305f297 100644
--- a/drivers/hwmon/nct7904.c
+++ b/drivers/hwmon/nct7904.c
@@ -4,6 +4,9 @@ 
  * Copyright (c) 2015 Kontron
  * Author: Vadim V. Vlasov <vvlasov@dev.rtsoft.ru>
  *
+ * Copyright (c) 2019 Advantech
+ * Author: Amy.Shih <amy.shih@advantech.com.tw>
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -52,13 +55,38 @@ 
 #define DTS_T_CTRL1_REG		0x27
 #define VT_ADC_MD_REG		0x2E
 
+#define VSEN1_HV_LL_REG		0x02	/* Bank 1; 2 regs (HV/LV) per sensor */
+#define VSEN1_LV_LL_REG		0x03	/* Bank 1; 2 regs (HV/LV) per sensor */
+#define VSEN1_HV_HL_REG		0x00	/* Bank 1; 2 regs (HV/LV) per sensor */
+#define VSEN1_LV_HL_REG		0x01	/* Bank 1; 2 regs (HV/LV) per sensor */
+#define SMI_STS1_REG		0xC1	/* Bank 0; SMI Status Register */
+#define SMI_STS5_REG		0xC5	/* Bank 0; SMI Status Register */
+#define SMI_STS7_REG		0xC7	/* Bank 0; SMI Status Register */
+#define SMI_STS8_REG		0xC8	/* Bank 0; SMI Status Register */
+
 #define VSEN1_HV_REG		0x40	/* Bank 0; 2 regs (HV/LV) per sensor */
 #define TEMP_CH1_HV_REG		0x42	/* Bank 0; same as VSEN2_HV */
 #define LTD_HV_REG		0x62	/* Bank 0; 2 regs in VSEN range */
+#define LTD_HV_HL_REG		0x44	/* Bank 1; 1 reg for LTD */
+#define LTD_LV_HL_REG		0x45	/* Bank 1; 1 reg for LTD */
+#define LTD_HV_LL_REG		0x46	/* Bank 1; 1 reg for LTD */
+#define LTD_LV_LL_REG		0x47	/* Bank 1; 1 reg for LTD */
+#define TEMP_CH1_CH_REG		0x05	/* Bank 1; 1 reg for LTD */
+#define TEMP_CH1_W_REG		0x06	/* Bank 1; 1 reg for LTD */
+#define TEMP_CH1_WH_REG		0x07	/* Bank 1; 1 reg for LTD */
+#define TEMP_CH1_C_REG		0x04	/* Bank 1; 1 reg per sensor */
+#define DTS_T_CPU1_C_REG	0x90	/* Bank 1; 1 reg per sensor */
+#define DTS_T_CPU1_CH_REG	0x91	/* Bank 1; 1 reg per sensor */
+#define DTS_T_CPU1_W_REG	0x92	/* Bank 1; 1 reg per sensor */
+#define DTS_T_CPU1_WH_REG	0x93	/* Bank 1; 1 reg per sensor */
 #define FANIN1_HV_REG		0x80	/* Bank 0; 2 regs (HV/LV) per sensor */
+#define FANIN1_HV_HL_REG	0x60	/* Bank 1; 2 regs (HV/LV) per sensor */
+#define FANIN1_LV_HL_REG	0x61	/* Bank 1; 2 regs (HV/LV) per sensor */
 #define T_CPU1_HV_REG		0xA0	/* Bank 0; 2 regs (HV/LV) per sensor */
 
 #define PRTS_REG		0x03	/* Bank 2 */
+#define PFE_REG			0x00	/* Bank 2; PECI Function Enable */
+#define TSI_CTRL_REG		0x50	/* Bank 2; TSI Control Register */
 #define FANCTL1_FMR_REG		0x00	/* Bank 3; 1 reg per channel */
 #define FANCTL1_OUT_REG		0x10	/* Bank 3; 1 reg per channel */
 
@@ -74,6 +102,9 @@  struct nct7904_data {
 	u32 vsen_mask;
 	u32 tcpu_mask;
 	u8 fan_mode[FANCTL_MAX];
+	u8 enable_dts;
+	u8 has_dts;
+	u8 temp_mode; /* 0: TR mode, 1: TD mode */
 };
 
 /* Access functions */
@@ -172,6 +203,25 @@  static int nct7904_read_fan(struct device *dev, u32 attr, int channel,
 			rpm = 1350000 / cnt;
 		*val = rpm;
 		return 0;
+	case hwmon_fan_min:
+		ret = nct7904_read_reg16(data, BANK_1,
+					 FANIN1_HV_HL_REG + channel * 2);
+		if (ret < 0)
+			return ret;
+		cnt = ((ret & 0xff00) >> 3) | (ret & 0x1f);
+		if (cnt == 0x1fff)
+			rpm = 0;
+		else
+			rpm = 1350000 / cnt;
+		*val = rpm;
+		return 0;
+	case hwmon_fan_alarm:
+		ret = nct7904_read_reg(data, BANK_0,
+					 SMI_STS7_REG + (channel >> 3));
+		if (ret < 0)
+			return ret;
+		*val = (ret >> (channel & 0x07)) & 1;
+		return 0;
 	default:
 		return -EOPNOTSUPP;
 	}
@@ -181,8 +231,18 @@  static umode_t nct7904_fan_is_visible(const void *_data, u32 attr, int channel)
 {
 	const struct nct7904_data *data = _data;
 
-	if (attr == hwmon_fan_input && data->fanin_mask & (1 << channel))
-		return 0444;
+	switch (attr) {
+	case hwmon_fan_input:
+	case hwmon_fan_alarm:
+		if (data->fanin_mask & (1 << channel))
+			return 0444;
+	case hwmon_fan_min:
+		if (data->fanin_mask & (1 << channel))
+			return 0644;
+	default:
+		break;
+	}
+
 	return 0;
 }
 
@@ -213,6 +273,37 @@  static int nct7904_read_in(struct device *dev, u32 attr, int channel,
 			volt *= 6; /* 0.006V scale */
 		*val = volt;
 		return 0;
+	case hwmon_in_min:
+		ret = nct7904_read_reg16(data, BANK_1,
+					 VSEN1_HV_LL_REG + index * 4);
+		if (ret < 0)
+			return ret;
+		volt = ((ret & 0xff00) >> 5) | (ret & 0x7);
+		if (index < 14)
+			volt *= 2; /* 0.002V scale */
+		else
+			volt *= 6; /* 0.006V scale */
+		*val = volt;
+		return 0;
+	case hwmon_in_max:
+		ret = nct7904_read_reg16(data, BANK_1,
+					 VSEN1_HV_HL_REG + index * 4);
+		if (ret < 0)
+			return ret;
+		volt = ((ret & 0xff00) >> 5) | (ret & 0x7);
+		if (index < 14)
+			volt *= 2; /* 0.002V scale */
+		else
+			volt *= 6; /* 0.006V scale */
+		*val = volt;
+		return 0;
+	case hwmon_in_alarm:
+		ret = nct7904_read_reg(data, BANK_0,
+					 SMI_STS1_REG + (index >> 3));
+		if (ret < 0)
+			return ret;
+		*val = (ret >> (index & 0x07)) & 1;
+		return 0;
 	default:
 		return -EOPNOTSUPP;
 	}
@@ -223,9 +314,18 @@  static umode_t nct7904_in_is_visible(const void *_data, u32 attr, int channel)
 	const struct nct7904_data *data = _data;
 	int index = nct7904_chan_to_index[channel];
 
-	if (channel > 0 && attr == hwmon_in_input &&
-	    (data->vsen_mask & BIT(index)))
-		return 0444;
+	switch (attr) {
+	case hwmon_in_input:
+	case hwmon_in_alarm:
+		if (channel > 0 && (data->vsen_mask & BIT(index)))
+			return 0444;
+	case hwmon_in_min:
+	case hwmon_in_max:
+		if (channel > 0 && (data->vsen_mask & BIT(index)))
+			return 0644;
+	default:
+		break;
+	}
 
 	return 0;
 }
@@ -235,36 +335,137 @@  static int nct7904_read_temp(struct device *dev, u32 attr, int channel,
 {
 	struct nct7904_data *data = dev_get_drvdata(dev);
 	int ret, temp;
+	unsigned int reg1, reg2, reg3;
 
 	switch (attr) {
 	case hwmon_temp_input:
-		if (channel == 0)
+		if (channel == 4)
 			ret = nct7904_read_reg16(data, BANK_0, LTD_HV_REG);
+		else if (channel < 5)
+			ret = nct7904_read_reg16(data, BANK_0,
+					TEMP_CH1_HV_REG + channel * 4);
 		else
 			ret = nct7904_read_reg16(data, BANK_0,
-					T_CPU1_HV_REG + (channel - 1) * 2);
+					T_CPU1_HV_REG + ((channel - 5) * 2));
 		if (ret < 0)
 			return ret;
 		temp = ((ret & 0xff00) >> 5) | (ret & 0x7);
 		*val = sign_extend32(temp, 10) * 125;
 		return 0;
+	case hwmon_temp_alarm:
+		if (channel < 5) {
+			ret = nct7904_read_reg(data, BANK_0,
+					SMI_STS1_REG);
+			if (ret < 0)
+				return ret;
+			*val = (ret >> (channel & 0x07)) & 1;
+		} else {
+			if ((channel - 5) < 4) {
+				ret = nct7904_read_reg(data, BANK_0,
+					SMI_STS7_REG + ((channel - 5) >> 3));
+				if (ret < 0)
+					return ret;
+				*val = (ret >> ((channel - 5) & 0x07)) & 1;
+			} else {
+				ret = nct7904_read_reg(data, BANK_0,
+					SMI_STS8_REG + ((channel - 5) >> 3));
+				if (ret < 0)
+					return ret;
+				*val = (ret >> (((channel - 5) & 0x07) - 4))
+							& 1;
+			}
+		}
+		return 0;
+	case hwmon_temp_type:
+		if (channel < 5) {
+			if ((data->tcpu_mask >> channel) & 0x01) {
+				if ((data->temp_mode >> channel) & 0x01)
+					*val = 3; //TD
+				else
+					*val = 4; //TR
+			} else {
+				*val = 0;
+			}
+		} else {
+			if (data->enable_dts == 0)
+				*val = 0;
+			if ((data->has_dts >> (channel - 5)) & 0x01) {
+				if (data->enable_dts & 2)
+					*val = 5; //TSI
+				else
+					*val = 6; //PECI
+			} else {
+				*val = 0;
+			}
+		}
+		return 0;
+	case hwmon_temp_max:
+		reg1 = LTD_HV_HL_REG;
+		reg2 = TEMP_CH1_C_REG;
+		reg3 = DTS_T_CPU1_C_REG;
+		break;
+	case hwmon_temp_max_hyst:
+		reg1 = LTD_LV_HL_REG;
+		reg2 = TEMP_CH1_CH_REG;
+		reg3 = DTS_T_CPU1_CH_REG;
+		break;
+	case hwmon_temp_emergency:
+		reg1 = LTD_HV_LL_REG;
+		reg2 = TEMP_CH1_W_REG;
+		reg3 = DTS_T_CPU1_W_REG;
+		break;
+	case hwmon_temp_emergency_hyst:
+		reg1 = LTD_LV_LL_REG;
+		reg2 = TEMP_CH1_WH_REG;
+		reg3 = DTS_T_CPU1_WH_REG;
+		break;
 	default:
 		return -EOPNOTSUPP;
 	}
+
+	if (channel == 4)
+		ret = nct7904_read_reg(data, BANK_1, reg1);
+	else if (channel < 5)
+		ret = nct7904_read_reg(data, BANK_1,
+			reg2 + channel * 8);
+	else
+		ret = nct7904_read_reg(data, BANK_1,
+			reg3 + (channel - 5) * 4);
+
+	if (ret < 0)
+		return ret;
+	*val = ret * 1000;
+	return 0;
 }
 
 static umode_t nct7904_temp_is_visible(const void *_data, u32 attr, int channel)
 {
 	const struct nct7904_data *data = _data;
 
-	if (attr == hwmon_temp_input) {
-		if (channel == 0) {
-			if (data->vsen_mask & BIT(17))
+	switch (attr) {
+	case hwmon_temp_input:
+	case hwmon_temp_alarm:
+	case hwmon_temp_type:
+		if (channel < 5) {
+			if (data->tcpu_mask & BIT(channel))
 				return 0444;
 		} else {
-			if (data->tcpu_mask & BIT(channel - 1))
+			if (data->has_dts & BIT(channel - 5))
 				return 0444;
 		}
+	case hwmon_temp_max:
+	case hwmon_temp_max_hyst:
+	case hwmon_temp_emergency:
+	case hwmon_temp_emergency_hyst:
+		if (channel < 5) {
+			if (data->tcpu_mask & BIT(channel))
+				return 0644;
+		} else {
+			if (data->has_dts & BIT(channel - 5))
+				return 0644;
+		}
+	default:
+		break;
 	}
 
 	return 0;
@@ -295,6 +496,130 @@  static int nct7904_read_pwm(struct device *dev, u32 attr, int channel,
 	}
 }
 
+static int nct7904_write_temp(struct device *dev, u32 attr, int channel,
+			     long val)
+{
+	struct nct7904_data *data = dev_get_drvdata(dev);
+	int ret, index;
+	unsigned int reg1, reg2, reg3;
+
+	index = nct7904_chan_to_index[channel];
+	val = clamp_val(val / 1000, -128, 127);
+
+	switch (attr) {
+	case hwmon_temp_max:
+		reg1 = LTD_HV_HL_REG;
+		reg2 = TEMP_CH1_C_REG;
+		reg3 = DTS_T_CPU1_C_REG;
+		break;
+	case hwmon_temp_max_hyst:
+		reg1 = LTD_LV_HL_REG;
+		reg2 = TEMP_CH1_CH_REG;
+		reg3 = DTS_T_CPU1_CH_REG;
+		break;
+	case hwmon_temp_emergency:
+		reg1 = LTD_HV_LL_REG;
+		reg2 = TEMP_CH1_W_REG;
+		reg3 = DTS_T_CPU1_W_REG;
+		break;
+	case hwmon_temp_emergency_hyst:
+		reg1 = LTD_LV_LL_REG;
+		reg2 = TEMP_CH1_WH_REG;
+		reg3 = DTS_T_CPU1_WH_REG;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+	if (channel == 4)
+		ret = nct7904_write_reg(data, BANK_1, reg1, val);
+	else if (channel < 5)
+		ret = nct7904_write_reg(data, BANK_1,
+			reg2 + channel * 8, val);
+	else
+		ret = nct7904_write_reg(data, BANK_1,
+			reg3 + (channel - 5) * 4, val);
+
+	return ret;
+}
+
+static int nct7904_write_fan(struct device *dev, u32 attr, int channel,
+			     long val)
+{
+	struct nct7904_data *data = dev_get_drvdata(dev);
+	int ret;
+	u8 tmp;
+
+	switch (attr) {
+	case hwmon_fan_min:
+		if (val <= 0)
+			return 0x1fff;
+
+		val = clamp_val((1350000 + (val >> 1)) / val, 1, 0x1fff);
+		tmp = (val >> 5) & 0xff;
+		ret = nct7904_write_reg(data, BANK_1,
+					 FANIN1_HV_HL_REG + channel * 2, tmp);
+
+		tmp = val & 0x1f;
+		ret = nct7904_write_reg(data, BANK_1,
+					 FANIN1_LV_HL_REG + channel * 2, tmp);
+		return ret;
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static int nct7904_write_in(struct device *dev, u32 attr, int channel,
+			     long val)
+{
+	struct nct7904_data *data = dev_get_drvdata(dev);
+	int ret, index;
+	u8 tmp;
+
+	index = nct7904_chan_to_index[channel];
+
+	if (index < 14)
+		val = val/2; /* 0.002V scale */
+	else
+		val = val/6; /* 0.006V scale */
+
+	val = clamp_val(val, 0, 0x7ff); /* Bit 15 is sign bit */
+
+	switch (attr) {
+	case hwmon_in_min:
+		tmp = nct7904_read_reg(data, BANK_1,
+					 VSEN1_LV_LL_REG + index * 4);
+
+		tmp &= ~0x7;
+		tmp |= val & 0x7;
+		ret = nct7904_write_reg(data, BANK_1,
+					 VSEN1_LV_LL_REG + index * 4, tmp);
+
+		tmp = nct7904_read_reg(data, BANK_1,
+					 VSEN1_HV_LL_REG + index * 4);
+		tmp = (val >> 3) & 0xff;
+		ret = nct7904_write_reg(data, BANK_1,
+					 VSEN1_HV_LL_REG + index * 4, tmp);
+		return ret;
+	case hwmon_in_max:
+		tmp = nct7904_read_reg(data, BANK_1,
+					 VSEN1_LV_HL_REG + index * 4);
+
+		tmp &= ~0x7;
+		tmp |= val & 0x7;
+		ret = nct7904_write_reg(data, BANK_1,
+					 VSEN1_LV_HL_REG + index * 4, tmp);
+
+		tmp = nct7904_read_reg(data, BANK_1,
+					 VSEN1_HV_HL_REG + index * 4);
+		tmp = (val >> 3) & 0xff;
+		ret = nct7904_write_reg(data, BANK_1,
+					 VSEN1_HV_HL_REG + index * 4, tmp);
+		return ret;
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
 static int nct7904_write_pwm(struct device *dev, u32 attr, int channel,
 			     long val)
 {
@@ -352,8 +677,14 @@  static int nct7904_write(struct device *dev, enum hwmon_sensor_types type,
 			 u32 attr, int channel, long val)
 {
 	switch (type) {
+	case hwmon_in:
+		return nct7904_write_in(dev, attr, channel, val);
+	case hwmon_fan:
+		return nct7904_write_fan(dev, attr, channel, val);
 	case hwmon_pwm:
 		return nct7904_write_pwm(dev, attr, channel, val);
+	case hwmon_temp:
+		return nct7904_write_temp(dev, attr, channel, val);
 	default:
 		return -EOPNOTSUPP;
 	}
@@ -402,51 +733,91 @@  static int nct7904_detect(struct i2c_client *client,
 
 static const struct hwmon_channel_info *nct7904_info[] = {
 	HWMON_CHANNEL_INFO(in,
-			   HWMON_I_INPUT, /* dummy, skipped in is_visible */
-			   HWMON_I_INPUT,
-			   HWMON_I_INPUT,
-			   HWMON_I_INPUT,
-			   HWMON_I_INPUT,
-			   HWMON_I_INPUT,
-			   HWMON_I_INPUT,
-			   HWMON_I_INPUT,
-			   HWMON_I_INPUT,
-			   HWMON_I_INPUT,
-			   HWMON_I_INPUT,
-			   HWMON_I_INPUT,
-			   HWMON_I_INPUT,
-			   HWMON_I_INPUT,
-			   HWMON_I_INPUT,
-			   HWMON_I_INPUT,
-			   HWMON_I_INPUT,
-			   HWMON_I_INPUT,
-			   HWMON_I_INPUT,
-			   HWMON_I_INPUT,
-			   HWMON_I_INPUT),
+			   /* dummy, skipped in is_visible */
+			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
+			   HWMON_I_ALARM,
+			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
+			   HWMON_I_ALARM,
+			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
+			   HWMON_I_ALARM,
+			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
+			   HWMON_I_ALARM,
+			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
+			   HWMON_I_ALARM,
+			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
+			   HWMON_I_ALARM,
+			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
+			   HWMON_I_ALARM,
+			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
+			   HWMON_I_ALARM,
+			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
+			   HWMON_I_ALARM,
+			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
+			   HWMON_I_ALARM,
+			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
+			   HWMON_I_ALARM,
+			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
+			   HWMON_I_ALARM,
+			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
+			   HWMON_I_ALARM,
+			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
+			   HWMON_I_ALARM,
+			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
+			   HWMON_I_ALARM,
+			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
+			   HWMON_I_ALARM,
+			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
+			   HWMON_I_ALARM,
+			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
+			   HWMON_I_ALARM,
+			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
+			   HWMON_I_ALARM,
+			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
+			   HWMON_I_ALARM,
+			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
+			   HWMON_I_ALARM),
 	HWMON_CHANNEL_INFO(fan,
-			   HWMON_F_INPUT,
-			   HWMON_F_INPUT,
-			   HWMON_F_INPUT,
-			   HWMON_F_INPUT,
-			   HWMON_F_INPUT,
-			   HWMON_F_INPUT,
-			   HWMON_F_INPUT,
-			   HWMON_F_INPUT),
+			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
+			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
+			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
+			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
+			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
+			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
+			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
+			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM),
 	HWMON_CHANNEL_INFO(pwm,
 			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
 			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
 			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
 			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
 	HWMON_CHANNEL_INFO(temp,
-			   HWMON_T_INPUT,
-			   HWMON_T_INPUT,
-			   HWMON_T_INPUT,
-			   HWMON_T_INPUT,
-			   HWMON_T_INPUT,
-			   HWMON_T_INPUT,
-			   HWMON_T_INPUT,
-			   HWMON_T_INPUT,
-			   HWMON_T_INPUT),
+			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
+			   HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_EMERGENCY |
+			   HWMON_T_EMERGENCY_HYST,
+			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
+			   HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_EMERGENCY |
+			   HWMON_T_EMERGENCY_HYST,
+			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
+			   HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_EMERGENCY |
+			   HWMON_T_EMERGENCY_HYST,
+			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
+			   HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_EMERGENCY |
+			   HWMON_T_EMERGENCY_HYST,
+			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
+			   HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_EMERGENCY |
+			   HWMON_T_EMERGENCY_HYST,
+			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
+			   HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_EMERGENCY |
+			   HWMON_T_EMERGENCY_HYST,
+			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
+			   HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_EMERGENCY |
+			   HWMON_T_EMERGENCY_HYST,
+			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
+			   HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_EMERGENCY |
+			   HWMON_T_EMERGENCY_HYST,
+			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
+			   HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_EMERGENCY |
+			   HWMON_T_EMERGENCY_HYST),
 	NULL
 };
 
@@ -469,6 +840,7 @@  static int nct7904_probe(struct i2c_client *client,
 	struct device *dev = &client->dev;
 	int ret, i;
 	u32 mask;
+	u8 val, bit;
 
 	data = devm_kzalloc(dev, sizeof(struct nct7904_data), GFP_KERNEL);
 	if (!data)
@@ -502,10 +874,59 @@  static int nct7904_probe(struct i2c_client *client,
 	data->vsen_mask = mask;
 
 	/* CPU_TEMP attributes */
-	ret = nct7904_read_reg16(data, BANK_0, DTS_T_CTRL0_REG);
-	if (ret < 0)
-		return ret;
-	data->tcpu_mask = ((ret >> 8) & 0xf) | ((ret & 0xf) << 4);
+	ret = nct7904_read_reg(data, BANK_0, VT_ADC_CTRL0_REG);
+
+	if ((ret & 0x6) == 0x6)
+		data->tcpu_mask |= 1; //TR1
+	if ((ret & 0x18) == 0x18)
+		data->tcpu_mask |= 2; //TR2
+	if ((ret & 0x20) == 0x20)
+		data->tcpu_mask |= 4; //TR3
+	if ((ret & 0x80) == 0x80)
+		data->tcpu_mask |= 8; //TR4
+
+	/* LTD */
+	ret = nct7904_read_reg(data, BANK_0, VT_ADC_CTRL2_REG);
+	if ((ret & 0x02) == 0x02)
+		data->tcpu_mask |= 0x10;
+
+	/* Multi-Function detecting for Volt and TR/TD */
+	ret = nct7904_read_reg(data, BANK_0, VT_ADC_MD_REG);
+
+	data->temp_mode = 0;
+	for (i = 0; i < 4; i++) {
+		val = (ret & (0x03 << i)) >> (i * 2);
+		bit = (1 << i);
+		if (val == 0)
+			data->tcpu_mask &= ~bit;
+		else if (val == 0x1 || val == 0x2)
+			data->temp_mode |= bit;
+	}
+
+	/* PECI */
+	ret = nct7904_read_reg(data, BANK_2, PFE_REG);
+	if (ret & 0x80) {
+		data->enable_dts = 1; //Enable DTS & PECI
+	} else {
+		ret = nct7904_read_reg(data, BANK_2, TSI_CTRL_REG);
+		if (ret & 0x80)
+			data->enable_dts = 0x3; //Enable DTS & TSI
+		else
+			data->enable_dts = 0;
+	}
+
+	/* Check DTS enable status */
+	if (data->enable_dts == 0) {
+		data->has_dts = 0;
+	} else {
+		data->has_dts =
+			nct7904_read_reg(data, BANK_0, DTS_T_CTRL0_REG) & 0xF;
+		if (data->enable_dts & 0x2) {
+			data->has_dts |=
+			(nct7904_read_reg(data, BANK_0, DTS_T_CTRL1_REG) & 0xF)
+								<< 4;
+		}
+	}
 
 	for (i = 0; i < FANCTL_MAX; i++) {
 		ret = nct7904_read_reg(data, BANK_3, FANCTL1_FMR_REG + i);