]> git.sur5r.net Git - groeck-nct6775/blobdiff - nct6775.c
Add driver removal notice
[groeck-nct6775] / nct6775.c
index a6f3b73acb154bf4fabe6fe1ce6dbf52fbdfc85f..bb2cf23b10623b82224f263e7ff36ad65605bd24 100644 (file)
--- a/nct6775.c
+++ b/nct6775.c
@@ -41,6 +41,7 @@
  * nct6792d    15      6       6       2+6    0xc910 0xc1    0x5ca3
  * nct6793d    15      6       6       2+6    0xd120 0xc1    0x5ca3
  * nct6795d    14      6       6       2+6    0xd350 0xc1    0x5ca3
+ * nct6796d    14      7       7       2+6    0xd420 0xc1    0x5ca3
  *
  * #temp lists the number of monitored temperature sources (first value) plus
  * the number of directly connectable temperature sensors (second value).
 #include <linux/err.h>
 #include <linux/mutex.h>
 #include <linux/acpi.h>
+#include <linux/bitops.h>
 #include <linux/dmi.h>
 #include <linux/io.h>
+#include <linux/debugfs.h>
 #include "lm75.h"
 #include "compat.h"
 
 #define USE_ALTERNATE
 
 enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793,
-            nct6795 };
+            nct6795, nct6796 };
 
 /* used to set data->name = nct6775_device_names[data->sio_kind] */
 static const char * const nct6775_device_names[] = {
@@ -79,6 +82,7 @@ static const char * const nct6775_device_names[] = {
        "nct6792",
        "nct6793",
        "nct6795",
+       "nct6796",
 };
 
 static const char * const nct6775_sio_names[] __initconst = {
@@ -90,6 +94,7 @@ static const char * const nct6775_sio_names[] __initconst = {
        "NCT6792D",
        "NCT6793D",
        "NCT6795D",
+       "NCT6796D",
 };
 
 static unsigned short force_id;
@@ -124,6 +129,7 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal");
 #define SIO_NCT6792_ID         0xc910
 #define SIO_NCT6793_ID         0xd120
 #define SIO_NCT6795_ID         0xd350
+#define SIO_NCT6796_ID         0xd420
 #define SIO_ID_MASK            0xFFF0
 
 enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 };
@@ -200,7 +206,7 @@ superio_exit(int ioreg)
 #define NUM_REG_ALARM  7       /* Max number of alarm registers */
 #define NUM_REG_BEEP   5       /* Max number of beep registers */
 
-#define NUM_FAN                6
+#define NUM_FAN                7
 
 #define TEMP_SOURCE_VIRTUAL    0x1f
 
@@ -271,26 +277,26 @@ static const u8 NCT6775_PWM_MODE_MASK[] = { 0x01, 0x02, 0x01 };
 /* Advanced Fan control, some values are common for all fans */
 
 static const u16 NCT6775_REG_TARGET[] = {
-       0x101, 0x201, 0x301, 0x801, 0x901, 0xa01 };
+       0x101, 0x201, 0x301, 0x801, 0x901, 0xa01, 0xb01 };
 static const u16 NCT6775_REG_FAN_MODE[] = {
-       0x102, 0x202, 0x302, 0x802, 0x902, 0xa02 };
+       0x102, 0x202, 0x302, 0x802, 0x902, 0xa02, 0xb02 };
 static const u16 NCT6775_REG_FAN_STEP_DOWN_TIME[] = {
-       0x103, 0x203, 0x303, 0x803, 0x903, 0xa03 };
+       0x103, 0x203, 0x303, 0x803, 0x903, 0xa03, 0xb03 };
 static const u16 NCT6775_REG_FAN_STEP_UP_TIME[] = {
-       0x104, 0x204, 0x304, 0x804, 0x904, 0xa04 };
+       0x104, 0x204, 0x304, 0x804, 0x904, 0xa04, 0xb04 };
 static const u16 NCT6775_REG_FAN_STOP_OUTPUT[] = {
-       0x105, 0x205, 0x305, 0x805, 0x905, 0xa05 };
+       0x105, 0x205, 0x305, 0x805, 0x905, 0xa05, 0xb05 };
 static const u16 NCT6775_REG_FAN_START_OUTPUT[] = {
-       0x106, 0x206, 0x306, 0x806, 0x906, 0xa06 };
+       0x106, 0x206, 0x306, 0x806, 0x906, 0xa06, 0xb06 };
 static const u16 NCT6775_REG_FAN_MAX_OUTPUT[] = { 0x10a, 0x20a, 0x30a };
 static const u16 NCT6775_REG_FAN_STEP_OUTPUT[] = { 0x10b, 0x20b, 0x30b };
 
 static const u16 NCT6775_REG_FAN_STOP_TIME[] = {
-       0x107, 0x207, 0x307, 0x807, 0x907, 0xa07 };
+       0x107, 0x207, 0x307, 0x807, 0x907, 0xa07, 0xb07 };
 static const u16 NCT6775_REG_PWM[] = {
-       0x109, 0x209, 0x309, 0x809, 0x909, 0xa09 };
+       0x109, 0x209, 0x309, 0x809, 0x909, 0xa09, 0xb09 };
 static const u16 NCT6775_REG_PWM_READ[] = {
-       0x01, 0x03, 0x11, 0x13, 0x15, 0xa09 };
+       0x01, 0x03, 0x11, 0x13, 0x15, 0xa09, 0xb09 };
 
 static const u16 NCT6775_REG_FAN[] = { 0x630, 0x632, 0x634, 0x636, 0x638 };
 static const u16 NCT6775_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d };
@@ -313,7 +319,7 @@ static const u16 NCT6775_REG_TEMP_SOURCE[ARRAY_SIZE(NCT6775_REG_TEMP)] = {
        0x621, 0x622, 0x623, 0x624, 0x625, 0x626 };
 
 static const u16 NCT6775_REG_TEMP_SEL[] = {
-       0x100, 0x200, 0x300, 0x800, 0x900, 0xa00 };
+       0x100, 0x200, 0x300, 0x800, 0x900, 0xa00, 0xb00 };
 
 static const u16 NCT6775_REG_WEIGHT_TEMP_SEL[] = {
        0x139, 0x239, 0x339, 0x839, 0x939, 0xa39 };
@@ -329,9 +335,9 @@ static const u16 NCT6775_REG_WEIGHT_TEMP_BASE[] = {
 static const u16 NCT6775_REG_TEMP_OFFSET[] = { 0x454, 0x455, 0x456 };
 
 static const u16 NCT6775_REG_AUTO_TEMP[] = {
-       0x121, 0x221, 0x321, 0x821, 0x921, 0xa21 };
+       0x121, 0x221, 0x321, 0x821, 0x921, 0xa21, 0xb21 };
 static const u16 NCT6775_REG_AUTO_PWM[] = {
-       0x127, 0x227, 0x327, 0x827, 0x927, 0xa27 };
+       0x127, 0x227, 0x327, 0x827, 0x927, 0xa27, 0xb27 };
 
 #define NCT6775_AUTO_TEMP(data, nr, p) ((data)->REG_AUTO_TEMP[nr] + (p))
 #define NCT6775_AUTO_PWM(data, nr, p)  ((data)->REG_AUTO_PWM[nr] + (p))
@@ -339,9 +345,9 @@ static const u16 NCT6775_REG_AUTO_PWM[] = {
 static const u16 NCT6775_REG_CRITICAL_ENAB[] = { 0x134, 0x234, 0x334 };
 
 static const u16 NCT6775_REG_CRITICAL_TEMP[] = {
-       0x135, 0x235, 0x335, 0x835, 0x935, 0xa35 };
+       0x135, 0x235, 0x335, 0x835, 0x935, 0xa35, 0xb35 };
 static const u16 NCT6775_REG_CRITICAL_TEMP_TOLERANCE[] = {
-       0x138, 0x238, 0x338, 0x838, 0x938, 0xa38 };
+       0x138, 0x238, 0x338, 0x838, 0x938, 0xa38, 0xb38 };
 
 static const char *const nct6775_temp_label[] = {
        "",
@@ -413,13 +419,15 @@ static const s8 NCT6776_BEEP_BITS[] = {
        30, 31 };                       /* intrusion0, intrusion1 */
 
 static const u16 NCT6776_REG_TOLERANCE_H[] = {
-       0x10c, 0x20c, 0x30c, 0x80c, 0x90c, 0xa0c };
+       0x10c, 0x20c, 0x30c, 0x80c, 0x90c, 0xa0c, 0xb0c };
 
 static const u8 NCT6776_REG_PWM_MODE[] = { 0x04, 0, 0, 0, 0, 0 };
 static const u8 NCT6776_PWM_MODE_MASK[] = { 0x01, 0, 0, 0, 0, 0 };
 
-static const u16 NCT6776_REG_FAN_MIN[] = { 0x63a, 0x63c, 0x63e, 0x640, 0x642 };
-static const u16 NCT6776_REG_FAN_PULSES[] = { 0x644, 0x645, 0x646, 0, 0 };
+static const u16 NCT6776_REG_FAN_MIN[] = {
+       0x63a, 0x63c, 0x63e, 0x640, 0x642, 0x64a, 0x64c };
+static const u16 NCT6776_REG_FAN_PULSES[] = {
+       0x644, 0x645, 0x646, 0x647, 0x648, 0x649, 0 };
 
 static const u16 NCT6776_REG_WEIGHT_DUTY_BASE[] = {
        0x13e, 0x23e, 0x33e, 0x83e, 0x93e, 0xa3e };
@@ -494,15 +502,15 @@ static const s8 NCT6779_BEEP_BITS[] = {
        30, 31 };                       /* intrusion0, intrusion1 */
 
 static const u16 NCT6779_REG_FAN[] = {
-       0x4b0, 0x4b2, 0x4b4, 0x4b6, 0x4b8, 0x4ba };
+       0x4b0, 0x4b2, 0x4b4, 0x4b6, 0x4b8, 0x4ba, 0x660 };
 static const u16 NCT6779_REG_FAN_PULSES[] = {
-       0x644, 0x645, 0x646, 0x647, 0x648, 0x649 };
+       0x644, 0x645, 0x646, 0x647, 0x648, 0x649, 0 };
 
 static const u16 NCT6779_REG_CRITICAL_PWM_ENABLE[] = {
-       0x136, 0x236, 0x336, 0x836, 0x936, 0xa36 };
+       0x136, 0x236, 0x336, 0x836, 0x936, 0xa36, 0xb36 };
 #define NCT6779_CRITICAL_PWM_ENABLE_MASK       0x01
 static const u16 NCT6779_REG_CRITICAL_PWM[] = {
-       0x137, 0x237, 0x337, 0x837, 0x937, 0xa37 };
+       0x137, 0x237, 0x337, 0x837, 0x937, 0xa37, 0xb37 };
 
 static const u16 NCT6779_REG_TEMP[] = { 0x27, 0x150 };
 static const u16 NCT6779_REG_TEMP_MON[] = { 0x73, 0x75, 0x77, 0x79, 0x7b };
@@ -569,12 +577,12 @@ static const u16 NCT6779_REG_TEMP_CRIT[32] = {
 
 #define NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE    0x28
 
-static const u16 NCT6791_REG_WEIGHT_TEMP_SEL[6] = { 0, 0x239 };
-static const u16 NCT6791_REG_WEIGHT_TEMP_STEP[6] = { 0, 0x23a };
-static const u16 NCT6791_REG_WEIGHT_TEMP_STEP_TOL[6] = { 0, 0x23b };
-static const u16 NCT6791_REG_WEIGHT_DUTY_STEP[6] = { 0, 0x23c };
-static const u16 NCT6791_REG_WEIGHT_TEMP_BASE[6] = { 0, 0x23d };
-static const u16 NCT6791_REG_WEIGHT_DUTY_BASE[6] = { 0, 0x23e };
+static const u16 NCT6791_REG_WEIGHT_TEMP_SEL[NUM_FAN] = { 0, 0x239 };
+static const u16 NCT6791_REG_WEIGHT_TEMP_STEP[NUM_FAN] = { 0, 0x23a };
+static const u16 NCT6791_REG_WEIGHT_TEMP_STEP_TOL[NUM_FAN] = { 0, 0x23b };
+static const u16 NCT6791_REG_WEIGHT_DUTY_STEP[NUM_FAN] = { 0, 0x23c };
+static const u16 NCT6791_REG_WEIGHT_TEMP_BASE[NUM_FAN] = { 0, 0x23d };
+static const u16 NCT6791_REG_WEIGHT_DUTY_BASE[NUM_FAN] = { 0, 0x23e };
 
 static const u16 NCT6791_REG_ALARM[NUM_REG_ALARM] = {
        0x459, 0x45A, 0x45B, 0x568, 0x45D };
@@ -706,6 +714,43 @@ static const char *const nct6795_temp_label[] = {
 
 #define NCT6795_TEMP_MASK      0xbfffff7e
 
+static const char *const nct6796_temp_label[] = {
+       "",
+       "SYSTIN",
+       "CPUTIN",
+       "AUXTIN0",
+       "AUXTIN1",
+       "AUXTIN2",
+       "AUXTIN3",
+       "AUXTIN4",
+       "SMBUSMASTER 0",
+       "SMBUSMASTER 1",
+       "",
+       "",
+       "",
+       "",
+       "",
+       "",
+       "PECI Agent 0",
+       "PECI Agent 1",
+       "PCH_CHIP_CPU_MAX_TEMP",
+       "PCH_CHIP_TEMP",
+       "PCH_CPU_TEMP",
+       "PCH_MCH_TEMP",
+       "PCH_DIM0_TEMP",
+       "PCH_DIM1_TEMP",
+       "PCH_DIM2_TEMP",
+       "PCH_DIM3_TEMP",
+       "BYTE_TEMP0",
+       "BYTE_TEMP1",
+       "PECI Agent 0 Calibration",
+       "PECI Agent 1 Calibration",
+       "",
+       "Virtual_TEMP"
+};
+
+#define NCT6796_TEMP_MASK      0xbfff03fe
+
 /* NCT6102D/NCT6106D specific data */
 
 #define NCT6106_REG_VBAT       0x318
@@ -884,7 +929,7 @@ static u16 fan_to_reg(u32 fan, unsigned int divreg)
 static inline unsigned int
 div_from_reg(u8 reg)
 {
-       return 1 << reg;
+       return BIT(reg);
 }
 
 /*
@@ -1069,6 +1114,88 @@ struct nct6775_sio_data {
        enum kinds kind;
 };
 
+#ifdef CONFIG_DEBUG_FS
+
+static u16 nct6775_read_temp(struct nct6775_data *data, u16 reg);
+static u16 nct6775_read_value(struct nct6775_data *data, u16 reg);
+
+static const char *temp_attr_names[5] = {
+       "input",
+       "max",
+       "hyst",
+       "crit",
+       "lcrit",
+};
+
+static int nct6775_seq_show(struct seq_file *s, void *v)
+{
+       struct device *dev = (struct device *)s->private;
+       struct nct6775_data *data = dev_get_drvdata(dev);
+       int i, j;
+
+       seq_printf(s, "Temperatures:\n");
+       for (i = 0; i < NUM_TEMP; i++) {
+               if (!(data->have_temp & BIT(i)))
+                       continue;
+               seq_printf(s, "  temp%d [source %d, %s]:\n", i + 1,
+                          data->temp_src[i],
+                          data->temp_label[data->temp_src[i]]);
+               for (j = 0; j < ARRAY_SIZE(data->reg_temp); j++) {
+                       if (data->reg_temp[j][i]) {
+                               seq_printf(s, "    %s: reg=0x%x val=0x%x cached 0x%x\n",
+                                          temp_attr_names[j],
+                                          data->reg_temp[j][i],
+                                          nct6775_read_temp(data, data->reg_temp[j][i]),
+                                          (u16)data->temp[j][i]);
+                       }
+               }
+       }
+       seq_printf(s, "Temperature sources:\n");
+       for (i = 0; i < data->num_temp_alarms; i++) {
+               seq_printf(s, "  index %d register 0x%x: val=0x%x\n",
+                          i, data->REG_TEMP_SOURCE[i],
+                          nct6775_read_value(data, data->REG_TEMP_SOURCE[i]) & 0x1f);
+       }
+       return 0;
+}
+
+static int nct6775_debug_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, nct6775_seq_show, inode->i_private);
+}
+
+static const struct file_operations nct6775_debug_operations = {
+       .open           = nct6775_debug_open,
+       .llseek         = seq_lseek,
+       .read           = seq_read,
+       .release        = single_release,
+};
+
+static void nct6775_debugfs_exit(void *data)
+{
+        debugfs_remove_recursive(data);
+}
+
+static int nct6775_debugfs_init(struct device *dev)
+{
+       struct dentry *rootdir;
+
+       rootdir = debugfs_create_dir(dev_name(dev), NULL);
+       if (!rootdir)
+               return -ENOMEM;
+
+       devm_add_action(dev, nct6775_debugfs_exit, rootdir);
+
+       debugfs_create_file("registers", S_IFREG | 0444, rootdir,
+                           dev, &nct6775_debug_operations);
+
+       return 0;
+}
+
+#else
+static int nct6775_debugfs_init(struct device *dev) { return 0; }
+#endif
+
 struct sensor_device_template {
        struct device_attribute dev_attr;
        union {
@@ -1234,11 +1361,13 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg)
        case nct6792:
        case nct6793:
        case nct6795:
+       case nct6796:
                return reg == 0x150 || reg == 0x153 || reg == 0x155 ||
                  ((reg & 0xfff0) == 0x4b0 && (reg & 0x000f) < 0x0b) ||
                  reg == 0x402 ||
                  reg == 0x63a || reg == 0x63c || reg == 0x63e ||
-                 reg == 0x640 || reg == 0x642 ||
+                 reg == 0x640 || reg == 0x642 || reg == 0x64a ||
+                 reg == 0x64c || reg == 0x660 ||
                  reg == 0x73 || reg == 0x75 || reg == 0x77 || reg == 0x79 ||
                  reg == 0x7b || reg == 0x7d;
        }
@@ -1355,7 +1484,7 @@ static void nct6775_update_fan_div(struct nct6775_data *data)
        data->fan_div[1] = (i & 0x70) >> 4;
        i = nct6775_read_value(data, NCT6775_REG_FANDIV2);
        data->fan_div[2] = i & 0x7;
-       if (data->has_fan & (1 << 3))
+       if (data->has_fan & BIT(3))
                data->fan_div[3] = (i & 0x70) >> 4;
 }
 
@@ -1377,7 +1506,7 @@ static void nct6775_init_fan_div(struct nct6775_data *data)
         * We'll compute a better divider later on.
         */
        for (i = 0; i < ARRAY_SIZE(data->fan_div); i++) {
-               if (!(data->has_fan & (1 << i)))
+               if (!(data->has_fan & BIT(i)))
                        continue;
                if (data->fan_div[i] == 0) {
                        data->fan_div[i] = 7;
@@ -1400,7 +1529,7 @@ static void nct6775_init_fan_common(struct device *dev,
         * prevents the unnecessary warning when fanX_min is reported as 0.
         */
        for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) {
-               if (data->has_fan_min & (1 << i)) {
+               if (data->has_fan_min & BIT(i)) {
                        reg = nct6775_read_value(data, data->REG_FAN_MIN[i]);
                        if (!reg)
                                nct6775_write_value(data, data->REG_FAN_MIN[i],
@@ -1435,7 +1564,7 @@ static void nct6775_select_fan_div(struct device *dev,
                        div_from_reg(fan_div));
 
                /* Preserve min limit if possible */
-               if (data->has_fan_min & (1 << nr)) {
+               if (data->has_fan_min & BIT(nr)) {
                        fan_min = data->fan_min[nr];
                        if (fan_div > data->fan_div[nr]) {
                                if (fan_min != 255 && fan_min > 1)
@@ -1466,13 +1595,13 @@ static void nct6775_update_pwm(struct device *dev)
        bool duty_is_dc;
 
        for (i = 0; i < data->pwm_num; i++) {
-               if (!(data->has_pwm & (1 << i)))
+               if (!(data->has_pwm & BIT(i)))
                        continue;
 
                duty_is_dc = data->REG_PWM_MODE[i] &&
                  (nct6775_read_value(data, data->REG_PWM_MODE[i])
                   & data->PWM_MODE_MASK[i]);
-               data->pwm_mode[i] = duty_is_dc;
+               data->pwm_mode[i] = !duty_is_dc;
 
                fanmodecfg = nct6775_read_value(data, data->REG_FAN_MODE[i]);
                for (j = 0; j < ARRAY_SIZE(data->REG_PWM); j++) {
@@ -1536,7 +1665,7 @@ static void nct6775_update_pwm_limits(struct device *dev)
        u16 reg_t;
 
        for (i = 0; i < data->pwm_num; i++) {
-               if (!(data->has_pwm & (1 << i)))
+               if (!(data->has_pwm & BIT(i)))
                        continue;
 
                for (j = 0; j < ARRAY_SIZE(data->fan_time); j++) {
@@ -1587,6 +1716,7 @@ static void nct6775_update_pwm_limits(struct device *dev)
                case nct6792:
                case nct6793:
                case nct6795:
+               case nct6796:
                        reg = nct6775_read_value(data,
                                        data->REG_CRITICAL_PWM_ENABLE[i]);
                        if (reg & data->CRITICAL_PWM_ENABLE_MASK)
@@ -1614,7 +1744,7 @@ static struct nct6775_data *nct6775_update_device(struct device *dev)
 
                /* Measured voltages and limits */
                for (i = 0; i < data->in_num; i++) {
-                       if (!(data->have_in & (1 << i)))
+                       if (!(data->have_in & BIT(i)))
                                continue;
 
                        data->in[i][0] = nct6775_read_value(data,
@@ -1629,14 +1759,14 @@ static struct nct6775_data *nct6775_update_device(struct device *dev)
                for (i = 0; i < ARRAY_SIZE(data->rpm); i++) {
                        u16 reg;
 
-                       if (!(data->has_fan & (1 << i)))
+                       if (!(data->has_fan & BIT(i)))
                                continue;
 
                        reg = nct6775_read_value(data, data->REG_FAN[i]);
                        data->rpm[i] = data->fan_from_reg(reg,
                                                          data->fan_div[i]);
 
-                       if (data->has_fan_min & (1 << i))
+                       if (data->has_fan_min & BIT(i))
                                data->fan_min[i] = nct6775_read_value(data,
                                           data->REG_FAN_MIN[i]);
                        data->fan_pulses[i] =
@@ -1651,7 +1781,7 @@ static struct nct6775_data *nct6775_update_device(struct device *dev)
 
                /* Measured temperatures and limits */
                for (i = 0; i < NUM_TEMP; i++) {
-                       if (!(data->have_temp & (1 << i)))
+                       if (!(data->have_temp & BIT(i)))
                                continue;
                        for (j = 0; j < ARRAY_SIZE(data->reg_temp); j++) {
                                if (data->reg_temp[j][i])
@@ -1660,7 +1790,7 @@ static struct nct6775_data *nct6775_update_device(struct device *dev)
                                                data->reg_temp[j][i]);
                        }
                        if (i >= NUM_TEMP_FIXED ||
-                           !(data->have_temp_fixed & (1 << i)))
+                           !(data->have_temp_fixed & BIT(i)))
                                continue;
                        data->temp_offset[i]
                          = nct6775_read_value(data, data->REG_TEMP_OFFSET[i]);
@@ -1881,7 +2011,7 @@ static umode_t nct6775_in_is_visible(struct kobject *kobj,
        struct nct6775_data *data = dev_get_drvdata(dev);
        int in = index / 5;     /* voltage index */
 
-       if (!(data->have_in & (1 << in)))
+       if (!(data->have_in & BIT(in)))
                return 0;
 
        return attr->mode;
@@ -1991,7 +2121,7 @@ store_fan_min(struct device *dev, struct device_attribute *attr,
                 * even with the highest divider (128)
                 */
                data->fan_min[nr] = 254;
-               new_div = 7; /* 128 == (1 << 7) */
+               new_div = 7; /* 128 == BIT(7) */
                dev_warn(dev,
                         "fan%u low limit %lu below minimum %u, set to minimum\n",
                         nr + 1, val, data->fan_from_reg_min(254, 7));
@@ -2001,7 +2131,7 @@ store_fan_min(struct device *dev, struct device_attribute *attr,
                 * even with the lowest divider (1)
                 */
                data->fan_min[nr] = 1;
-               new_div = 0; /* 1 == (1 << 0) */
+               new_div = 0; /* 1 == BIT(0) */
                dev_warn(dev,
                         "fan%u low limit %lu above maximum %u, set to maximum\n",
                         nr + 1, val, data->fan_from_reg_min(1, 0));
@@ -2088,14 +2218,16 @@ static umode_t nct6775_fan_is_visible(struct kobject *kobj,
        int fan = index / 6;    /* fan index */
        int nr = index % 6;     /* attribute index */
 
-       if (!(data->has_fan & (1 << fan)))
+       if (!(data->has_fan & BIT(fan)))
                return 0;
 
        if (nr == 1 && data->ALARM_BITS[FAN_ALARM_BASE + fan] == -1)
                return 0;
        if (nr == 2 && data->BEEP_BITS[FAN_ALARM_BASE + fan] == -1)
                return 0;
-       if (nr == 4 && !(data->has_fan_min & (1 << fan)))
+       if (nr == 3 && !data->REG_FAN_PULSES[fan])
+               return 0;
+       if (nr == 4 && !(data->has_fan_min & BIT(fan)))
                return 0;
        if (nr == 5 && data->kind != nct6775)
                return 0;
@@ -2273,7 +2405,7 @@ static umode_t nct6775_temp_is_visible(struct kobject *kobj,
        int temp = index / 10;  /* temp index */
        int nr = index % 10;    /* attribute index */
 
-       if (!(data->have_temp & (1 << temp)))
+       if (!(data->have_temp & BIT(temp)))
                return 0;
 
        if (nr == 1 && !data->temp_label)
@@ -2298,7 +2430,7 @@ static umode_t nct6775_temp_is_visible(struct kobject *kobj,
                return 0;
 
        /* offset and type only apply to fixed sensors */
-       if (nr > 7 && !(data->have_temp_fixed & (1 << temp)))
+       if (nr > 7 && !(data->have_temp_fixed & BIT(temp)))
                return 0;
 
        return attr->mode;
@@ -2353,7 +2485,7 @@ show_pwm_mode(struct device *dev, struct device_attribute *attr, char *buf)
        struct nct6775_data *data = nct6775_update_device(dev);
        struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
 
-       return sprintf(buf, "%d\n", !data->pwm_mode[sattr->index]);
+       return sprintf(buf, "%d\n", data->pwm_mode[sattr->index]);
 }
 
 static ssize_t
@@ -2374,9 +2506,9 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr,
        if (val > 1)
                return -EINVAL;
 
-       /* Setting DC mode is not supported for all chips/channels */
+       /* Setting DC mode (0) is not supported for all chips/channels */
        if (data->REG_PWM_MODE[nr] == 0) {
-               if (val)
+               if (!val)
                        return -EINVAL;
                return count;
        }
@@ -2385,7 +2517,7 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr,
        data->pwm_mode[nr] = val;
        reg = nct6775_read_value(data, data->REG_PWM_MODE[nr]);
        reg &= ~data->PWM_MODE_MASK[nr];
-       if (val)
+       if (!val)
                reg |= data->PWM_MODE_MASK[nr];
        nct6775_write_value(data, data->REG_PWM_MODE[nr], reg);
        mutex_unlock(&data->update_lock);
@@ -2567,7 +2699,7 @@ show_pwm_temp_sel_common(struct nct6775_data *data, char *buf, int src)
        int i, sel = 0;
 
        for (i = 0; i < NUM_TEMP; i++) {
-               if (!(data->have_temp & (1 << i)))
+               if (!(data->have_temp & BIT(i)))
                        continue;
                if (src == data->temp_src[i]) {
                        sel = i + 1;
@@ -2603,7 +2735,7 @@ store_pwm_temp_sel(struct device *dev, struct device_attribute *attr,
                return err;
        if (val == 0 || val > NUM_TEMP)
                return -EINVAL;
-       if (!(data->have_temp & (1 << (val - 1))) || !data->temp_src[val - 1])
+       if (!(data->have_temp & BIT(val - 1)) || !data->temp_src[val - 1])
                return -EINVAL;
 
        mutex_lock(&data->update_lock);
@@ -2645,7 +2777,7 @@ store_pwm_weight_temp_sel(struct device *dev, struct device_attribute *attr,
                return err;
        if (val > NUM_TEMP)
                return -EINVAL;
-       if (val && (!(data->have_temp & (1 << (val - 1))) ||
+       if (val && (!(data->have_temp & BIT(val - 1)) ||
                    !data->temp_src[val - 1]))
                return -EINVAL;
 
@@ -3019,6 +3151,7 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr,
                case nct6792:
                case nct6793:
                case nct6795:
+               case nct6796:
                        nct6775_write_value(data, data->REG_CRITICAL_PWM[nr],
                                            val);
                        reg = nct6775_read_value(data,
@@ -3091,7 +3224,7 @@ static umode_t nct6775_pwm_is_visible(struct kobject *kobj,
        int pwm = index / 36;   /* pwm index */
        int nr = index % 36;    /* attribute index */
 
-       if (!(data->has_pwm & (1 << pwm)))
+       if (!(data->has_pwm & BIT(pwm)))
                return 0;
 
        if ((nr >= 14 && nr <= 18) || nr == 21)   /* weight */
@@ -3223,14 +3356,14 @@ static const struct sensor_template_group nct6775_pwm_template_group = {
 };
 
 static ssize_t
-show_vid(struct device *dev, struct device_attribute *attr, char *buf)
+cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct nct6775_data *data = dev_get_drvdata(dev);
 
        return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
 }
 
-static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
+static DEVICE_ATTR_RO(cpu0_vid);
 
 /* Case open detection */
 
@@ -3344,7 +3477,7 @@ static inline void nct6775_init_device(struct nct6775_data *data)
 
        /* Enable temperature sensors if needed */
        for (i = 0; i < NUM_TEMP; i++) {
-               if (!(data->have_temp & (1 << i)))
+               if (!(data->have_temp & BIT(i)))
                        continue;
                if (!data->reg_temp_config[i])
                        continue;
@@ -3362,7 +3495,7 @@ static inline void nct6775_init_device(struct nct6775_data *data)
        diode = nct6775_read_value(data, data->REG_DIODE);
 
        for (i = 0; i < data->temp_fixed_num; i++) {
-               if (!(data->have_temp_fixed & (1 << i)))
+               if (!(data->have_temp_fixed & BIT(i)))
                        continue;
                if ((tmp & (data->DIODE_MASK << i)))    /* diode */
                        data->temp_type[i]
@@ -3375,8 +3508,10 @@ static inline void nct6775_init_device(struct nct6775_data *data)
 static void
 nct6775_check_fan_inputs(struct nct6775_data *data)
 {
-       bool fan3pin, fan4pin, fan4min, fan5pin, fan6pin;
-       bool pwm3pin, pwm4pin, pwm5pin, pwm6pin;
+       bool fan3pin = false, fan4pin = false, fan4min = false;
+       bool fan5pin = false, fan6pin = false, fan7pin = false;
+       bool pwm3pin = false, pwm4pin = false, pwm5pin = false;
+       bool pwm6pin = false, pwm7pin = false;
        int sioreg = data->sioreg;
        int regval;
 
@@ -3388,17 +3523,11 @@ nct6775_check_fan_inputs(struct nct6775_data *data)
        if (data->kind == nct6775) {
                regval = superio_inb(sioreg, 0x2c);
 
-               fan3pin = regval & (1 << 6);
-               pwm3pin = regval & (1 << 7);
+               fan3pin = regval & BIT(6);
+               pwm3pin = regval & BIT(7);
 
                /* On NCT6775, fan4 shares pins with the fdc interface */
                fan4pin = !(superio_inb(sioreg, 0x2A) & 0x80);
-               fan4min = false;
-               fan5pin = false;
-               fan6pin = false;
-               pwm4pin = false;
-               pwm5pin = false;
-               pwm6pin = false;
        } else if (data->kind == nct6776) {
                bool gpok = superio_inb(sioreg, 0x27) & 0x80;
                const char *board_vendor, *board_name;
@@ -3438,70 +3567,89 @@ nct6775_check_fan_inputs(struct nct6775_data *data)
                        fan5pin = superio_inb(sioreg, 0x1C) & 0x02;
 
                fan4min = fan4pin;
-               fan6pin = false;
                pwm3pin = fan3pin;
-               pwm4pin = false;
-               pwm5pin = false;
-               pwm6pin = false;
        } else if (data->kind == nct6106) {
                regval = superio_inb(sioreg, 0x24);
                fan3pin = !(regval & 0x80);
                pwm3pin = regval & 0x08;
-
-               fan4pin = false;
-               fan4min = false;
-               fan5pin = false;
-               fan6pin = false;
-               pwm4pin = false;
-               pwm5pin = false;
-               pwm6pin = false;
-       } else {        /* NCT6779D, NCT6791D, NCT6792D, NCT6793D, NCT6795D */
-               int regval_1b, regval_2a, regval_eb;
+       } else {
+               /* NCT6779D, NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D */
+               int regval_1b, regval_2a, regval_2f;
+               bool dsw_en;
 
                regval = superio_inb(sioreg, 0x1c);
 
-               fan3pin = !(regval & (1 << 5));
-               fan4pin = !(regval & (1 << 6));
-               fan5pin = !(regval & (1 << 7));
+               fan3pin = !(regval & BIT(5));
+               fan4pin = !(regval & BIT(6));
+               fan5pin = !(regval & BIT(7));
 
-               pwm3pin = !(regval & (1 << 0));
-               pwm4pin = !(regval & (1 << 1));
-               pwm5pin = !(regval & (1 << 2));
+               pwm3pin = !(regval & BIT(0));
+               pwm4pin = !(regval & BIT(1));
+               pwm5pin = !(regval & BIT(2));
 
                regval = superio_inb(sioreg, 0x2d);
                switch (data->kind) {
                case nct6791:
                case nct6792:
-                       fan6pin = regval & (1 << 1);
-                       pwm6pin = regval & (1 << 0);
+                       fan6pin = regval & BIT(1);
+                       pwm6pin = regval & BIT(0);
                        break;
                case nct6793:
                case nct6795:
+               case nct6796:
                        regval_1b = superio_inb(sioreg, 0x1b);
                        regval_2a = superio_inb(sioreg, 0x2a);
+                       regval_2f = superio_inb(sioreg, 0x2f);
+                       dsw_en = regval_2f & BIT(3);
 
                        if (!pwm5pin)
-                               pwm5pin = regval & (1 << 7);
-                       fan6pin = regval & (1 << 1);
-                       pwm6pin = regval & (1 << 0);
+                               pwm5pin = regval & BIT(7);
+
                        if (!fan5pin)
-                               fan5pin = regval_1b & (1 << 5);
+                               fan5pin = regval_1b & BIT(5);
 
                        superio_select(sioreg, NCT6775_LD_12);
-                       regval_eb = superio_inb(sioreg, 0xeb);
-                       if (!fan5pin)
-                               fan5pin = regval_eb & (1 << 5);
-                       if (!pwm5pin)
-                               pwm5pin = (regval_eb & (1 << 4)) &&
-                                          !(regval_2a & (1 << 0));
-                       if (!fan6pin)
-                               fan6pin = regval_eb & (1 << 3);
-                       if (!pwm6pin)
-                               pwm6pin = regval_eb & (1 << 2);
+                       if (data->kind != nct6796) {
+                               int regval_eb = superio_inb(sioreg, 0xeb);
+
+                               if (!dsw_en) {
+                                       fan6pin = regval & BIT(1);
+                                       pwm6pin = regval & BIT(0);
+                               }
+
+                               if (!fan5pin)
+                                       fan5pin = regval_eb & BIT(5);
+                               if (!pwm5pin)
+                                       pwm5pin = (regval_eb & BIT(4)) &&
+                                               !(regval_2a & BIT(0));
+                               if (!fan6pin)
+                                       fan6pin = regval_eb & BIT(3);
+                               if (!pwm6pin)
+                                       pwm6pin = regval_eb & BIT(2);
+                       }
+
+                       if (data->kind == nct6795 || data->kind == nct6796) {
+                               int regval_ed = superio_inb(sioreg, 0xed);
+
+                               if (!fan6pin)
+                                       fan6pin = (regval_2a & BIT(4)) &&
+                                         (!dsw_en ||
+                                          (dsw_en && (regval_ed & BIT(4))));
+                               if (!pwm6pin)
+                                       pwm6pin = (regval_2a & BIT(3)) &&
+                                         (regval_ed & BIT(2));
+                       }
+
+                       if (data->kind == nct6796) {
+                               int regval_1d = superio_inb(sioreg, 0x1d);
+                               int regval_2b = superio_inb(sioreg, 0x2b);
+
+                               fan7pin = !(regval_2b & BIT(2));
+                               pwm7pin = !(regval_1d & (BIT(2) | BIT(3)));
+                       }
+
                        break;
                default:        /* NCT6779D */
-                       fan6pin = false;
-                       pwm6pin = false;
                        break;
                }
 
@@ -3510,11 +3658,11 @@ nct6775_check_fan_inputs(struct nct6775_data *data)
 
        /* fan 1 and 2 (0x03) are always present */
        data->has_fan = 0x03 | (fan3pin << 2) | (fan4pin << 3) |
-               (fan5pin << 4) | (fan6pin << 5);
+               (fan5pin << 4) | (fan6pin << 5) | (fan7pin << 6);
        data->has_fan_min = 0x03 | (fan3pin << 2) | (fan4min << 3) |
-               (fan5pin << 4);
+               (fan5pin << 4) | (fan6pin << 5) | (fan7pin << 6);
        data->has_pwm = 0x03 | (pwm3pin << 2) | (pwm4pin << 3) |
-               (pwm5pin << 4) | (pwm6pin << 5);
+               (pwm5pin << 4) | (pwm6pin << 5) | (pwm7pin << 6);
 }
 
 static void add_temp_sensors(struct nct6775_data *data, const u16 *regp,
@@ -3530,15 +3678,15 @@ static void add_temp_sensors(struct nct6775_data *data, const u16 *regp,
                        continue;
                src = nct6775_read_value(data, regp[i]);
                src &= 0x1f;
-               if (!src || (*mask & (1 << src)))
+               if (!src || (*mask & BIT(src)))
                        continue;
                if (!(data->temp_mask & BIT(src)))
                        continue;
 
                index = __ffs(*available);
                nct6775_write_value(data, data->REG_TEMP_SOURCE[index], src);
-               *available &= ~(1 << index);
-               *mask |= 1 << src;
+               *available &= ~BIT(index);
+               *mask |= BIT(src);
        }
 }
 
@@ -3873,8 +4021,9 @@ static int nct6775_probe(struct platform_device *pdev)
        case nct6792:
        case nct6793:
        case nct6795:
+       case nct6796:
                data->in_num = 15;
-               data->pwm_num = 6;
+               data->pwm_num = (data->kind == nct6796) ? 7 : 6;
                data->auto_pwm_num = 4;
                data->has_fan_div = false;
                data->temp_fixed_num = 6;
@@ -3908,6 +4057,10 @@ static int nct6775_probe(struct platform_device *pdev)
                        data->temp_label = nct6795_temp_label;
                        data->temp_mask = NCT6795_TEMP_MASK;
                        break;
+               case nct6796:
+                       data->temp_label = nct6796_temp_label;
+                       data->temp_mask = NCT6796_TEMP_MASK;
+                       break;
                }
 
                data->REG_CONFIG = NCT6775_REG_CONFIG;
@@ -3976,7 +4129,7 @@ static int nct6775_probe(struct platform_device *pdev)
        default:
                return -ENODEV;
        }
-       data->have_in = (1 << data->in_num) - 1;
+       data->have_in = BIT(data->in_num) - 1;
        data->have_temp = 0;
 
        /*
@@ -3994,10 +4147,10 @@ static int nct6775_probe(struct platform_device *pdev)
                        continue;
 
                src = nct6775_read_value(data, data->REG_TEMP_SOURCE[i]) & 0x1f;
-               if (!src || (mask & (1 << src)))
-                       available |= 1 << i;
+               if (!src || (mask & BIT(src)))
+                       available |= BIT(i);
 
-               mask |= 1 << src;
+               mask |= BIT(src);
        }
 
        /*
@@ -4014,7 +4167,7 @@ static int nct6775_probe(struct platform_device *pdev)
                        continue;
 
                src = nct6775_read_value(data, data->REG_TEMP_SOURCE[i]) & 0x1f;
-               if (!src || (mask & (1 << src)))
+               if (!src || (mask & BIT(src)))
                        continue;
 
                if (!(data->temp_mask & BIT(src))) {
@@ -4024,12 +4177,12 @@ static int nct6775_probe(struct platform_device *pdev)
                        continue;
                }
 
-               mask |= 1 << src;
+               mask |= BIT(src);
 
                /* Use fixed index for SYSTIN(1), CPUTIN(2), AUXTIN(3) */
                if (src <= data->temp_fixed_num) {
-                       data->have_temp |= 1 << (src - 1);
-                       data->have_temp_fixed |= 1 << (src - 1);
+                       data->have_temp |= BIT(src - 1);
+                       data->have_temp_fixed |= BIT(src - 1);
                        data->reg_temp[0][src - 1] = reg_temp[i];
                        data->reg_temp[1][src - 1] = reg_temp_over[i];
                        data->reg_temp[2][src - 1] = reg_temp_hyst[i];
@@ -4049,7 +4202,7 @@ static int nct6775_probe(struct platform_device *pdev)
                        continue;
 
                /* Use dynamic index for other sources */
-               data->have_temp |= 1 << s;
+               data->have_temp |= BIT(s);
                data->reg_temp[0][s] = reg_temp[i];
                data->reg_temp[1][s] = reg_temp_over[i];
                data->reg_temp[2][s] = reg_temp_hyst[i];
@@ -4091,17 +4244,17 @@ static int nct6775_probe(struct platform_device *pdev)
                 * are no duplicates.
                 */
                if (src != TEMP_SOURCE_VIRTUAL) {
-                       if (mask & (1 << src))
+                       if (mask & BIT(src))
                                continue;
-                       mask |= 1 << src;
+                       mask |= BIT(src);
                }
 
                /* Use fixed index for SYSTIN(1), CPUTIN(2), AUXTIN(3) */
                if (src <= data->temp_fixed_num) {
-                       if (data->have_temp & (1 << (src - 1)))
+                       if (data->have_temp & BIT(src - 1))
                                continue;
-                       data->have_temp |= 1 << (src - 1);
-                       data->have_temp_fixed |= 1 << (src - 1);
+                       data->have_temp |= BIT(src - 1);
+                       data->have_temp_fixed |= BIT(src - 1);
                        data->reg_temp[0][src - 1] = reg_temp_mon[i];
                        data->temp_src[src - 1] = src;
                        continue;
@@ -4111,7 +4264,7 @@ static int nct6775_probe(struct platform_device *pdev)
                        continue;
 
                /* Use dynamic index for other sources */
-               data->have_temp |= 1 << s;
+               data->have_temp |= BIT(s);
                data->reg_temp[0][s] = reg_temp_mon[i];
                data->temp_src[s] = src;
                s++;
@@ -4125,17 +4278,17 @@ static int nct6775_probe(struct platform_device *pdev)
         * is set.
         */
        for (i = 0; i < 32; i++) {
-               if (!(data->temp_mask & BIT(i)))
+               if (!(data->temp_mask & BIT(i + 1)))
                        continue;
                if (!reg_temp_alternate[i])
                        continue;
-               if (mask & (1 << (i + 1)))
+               if (mask & BIT(i + 1))
                        continue;
                if (i < data->temp_fixed_num) {
-                       if (data->have_temp & (1 << i))
+                       if (data->have_temp & BIT(i))
                                continue;
-                       data->have_temp |= 1 << i;
-                       data->have_temp_fixed |= 1 << i;
+                       data->have_temp |= BIT(i);
+                       data->have_temp_fixed |= BIT(i);
                        data->reg_temp[0][i] = reg_temp_alternate[i];
                        if (i < num_reg_temp) {
                                data->reg_temp[1][i] = reg_temp_over[i];
@@ -4148,7 +4301,7 @@ static int nct6775_probe(struct platform_device *pdev)
                if (s >= NUM_TEMP)      /* Abort if no more space */
                        break;
 
-               data->have_temp |= 1 << s;
+               data->have_temp |= BIT(s);
                data->reg_temp[0][s] = reg_temp_alternate[i];
                data->temp_src[s] = i + 1;
                s++;
@@ -4176,6 +4329,7 @@ static int nct6775_probe(struct platform_device *pdev)
        case nct6792:
        case nct6793:
        case nct6795:
+       case nct6796:
                break;
        }
 
@@ -4210,6 +4364,7 @@ static int nct6775_probe(struct platform_device *pdev)
                case nct6792:
                case nct6793:
                case nct6795:
+               case nct6796:
                        tmp |= 0x7e;
                        break;
                }
@@ -4269,6 +4424,8 @@ static int nct6775_probe(struct platform_device *pdev)
 #else
        hwmon_dev = devm_hwmon_device_register_with_groups(dev, data->name,
                                                           data, data->groups);
+       if (!IS_ERR(hwmon_dev))
+               nct6775_debugfs_init(hwmon_dev);
 #endif
        return PTR_ERR_OR_ZERO(hwmon_dev);
 }
@@ -4331,14 +4488,15 @@ static int __maybe_unused nct6775_resume(struct device *dev)
                superio_outb(sioreg, SIO_REG_ENABLE, data->sio_reg_enable);
 
        if (data->kind == nct6791 || data->kind == nct6792 ||
-           data->kind == nct6793 || data->kind == nct6795)
+           data->kind == nct6793 || data->kind == nct6795 ||
+           data->kind == nct6796)
                nct6791_enable_io_mapping(sioreg);
 
        superio_exit(sioreg);
 
        /* Restore limits */
        for (i = 0; i < data->in_num; i++) {
-               if (!(data->have_in & (1 << i)))
+               if (!(data->have_in & BIT(i)))
                        continue;
 
                nct6775_write_value(data, data->REG_IN_MINMAX[0][i],
@@ -4348,7 +4506,7 @@ static int __maybe_unused nct6775_resume(struct device *dev)
        }
 
        for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) {
-               if (!(data->has_fan_min & (1 << i)))
+               if (!(data->has_fan_min & BIT(i)))
                        continue;
 
                nct6775_write_value(data, data->REG_FAN_MIN[i],
@@ -4356,7 +4514,7 @@ static int __maybe_unused nct6775_resume(struct device *dev)
        }
 
        for (i = 0; i < NUM_TEMP; i++) {
-               if (!(data->have_temp & (1 << i)))
+               if (!(data->have_temp & BIT(i)))
                        continue;
 
                for (j = 1; j < ARRAY_SIZE(data->reg_temp); j++)
@@ -4435,6 +4593,9 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
        case SIO_NCT6795_ID:
                sio_data->kind = nct6795;
                break;
+       case SIO_NCT6796_ID:
+               sio_data->kind = nct6796;
+               break;
        default:
                if (val != 0xffff)
                        pr_debug("unsupported chip ID: 0x%04x\n", val);
@@ -4461,7 +4622,8 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
        }
 
        if (sio_data->kind == nct6791 || sio_data->kind == nct6792 ||
-           sio_data->kind == nct6793 || sio_data->kind == nct6795)
+           sio_data->kind == nct6793 || sio_data->kind == nct6795 ||
+           sio_data->kind == nct6796)
                nct6791_enable_io_mapping(sioaddr);
 
        superio_exit(sioaddr);