]> git.sur5r.net Git - groeck-nct6775/blobdiff - nct6775.c
Add driver removal notice
[groeck-nct6775] / nct6775.c
index caa972d7ff16d408b393cc5e5c136b6efde26ad1..bb2cf23b10623b82224f263e7ff36ad65605bd24 100644 (file)
--- a/nct6775.c
+++ b/nct6775.c
@@ -5,7 +5,7 @@
  * Copyright (C) 2012  Guenter Roeck <linux@roeck-us.net>
  *
  * Derived from w83627ehf driver
- * Copyright (C) 2005-2012  Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2005-2012  Jean Delvare <jdelvare@suse.de>
  * Copyright (C) 2006  Yuan Mu (Winbond),
  *                    Rudolf Marek <r.marek@assembler.cz>
  *                    David Hubbard <david.c.hubbard@gmail.com>
  * nct6776f     9      5       3       6+3    0xc330 0xc1    0x5ca3
  * nct6779d    15      5       5       2+6    0xc560 0xc1    0x5ca3
  * nct6791d    15      6       6       2+6    0xc800 0xc1    0x5ca3
+ * 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 };
+enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793,
+            nct6795, nct6796 };
 
 /* used to set data->name = nct6775_device_names[data->sio_kind] */
 static const char * const nct6775_device_names[] = {
@@ -71,6 +79,22 @@ static const char * const nct6775_device_names[] = {
        "nct6776",
        "nct6779",
        "nct6791",
+       "nct6792",
+       "nct6793",
+       "nct6795",
+       "nct6796",
+};
+
+static const char * const nct6775_sio_names[] __initconst = {
+       "NCT6106D",
+       "NCT6775F",
+       "NCT6776D/F",
+       "NCT6779D",
+       "NCT6791D",
+       "NCT6792D",
+       "NCT6793D",
+       "NCT6795D",
+       "NCT6796D",
 };
 
 static unsigned short force_id;
@@ -90,6 +114,7 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal");
 #define NCT6775_LD_ACPI                0x0a
 #define NCT6775_LD_HWM         0x0b
 #define NCT6775_LD_VID         0x0d
+#define NCT6775_LD_12          0x12
 
 #define SIO_REG_LDSEL          0x07    /* Logical device select */
 #define SIO_REG_DEVID          0x20    /* Device ID (2 bytes) */
@@ -101,6 +126,10 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal");
 #define SIO_NCT6776_ID         0xc330
 #define SIO_NCT6779_ID         0xc560
 #define SIO_NCT6791_ID         0xc800
+#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 };
@@ -177,7 +206,9 @@ 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
 
 /* Common and NCT6775 specific data */
 
@@ -246,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 };
@@ -288,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 };
@@ -304,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))
@@ -314,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[] = {
        "",
@@ -342,15 +373,31 @@ static const char *const nct6775_temp_label[] = {
        "PCH_DIM3_TEMP"
 };
 
-static const u16 NCT6775_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6775_temp_label) - 1]
-       = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x661, 0x662, 0x664 };
+#define NCT6775_TEMP_MASK      0x001ffffe
+
+static const u16 NCT6775_REG_TEMP_ALTERNATE[32] = {
+       [13] = 0x661,
+       [14] = 0x662,
+       [15] = 0x664,
+};
 
-static const u16 NCT6775_REG_TEMP_CRIT[ARRAY_SIZE(nct6775_temp_label) - 1]
-       = { 0, 0, 0, 0, 0xa00, 0xa01, 0xa02, 0xa03, 0xa04, 0xa05, 0xa06,
-           0xa07 };
+static const u16 NCT6775_REG_TEMP_CRIT[32] = {
+       [4] = 0xa00,
+       [5] = 0xa01,
+       [6] = 0xa02,
+       [7] = 0xa03,
+       [8] = 0xa04,
+       [9] = 0xa05,
+       [10] = 0xa06,
+       [11] = 0xa07
+};
 
 /* NCT6776 specific data */
 
+/* STEP_UP_TIME and STEP_DOWN_TIME regs are swapped for all chips but NCT6775 */
+#define NCT6776_REG_FAN_STEP_UP_TIME NCT6775_REG_FAN_STEP_DOWN_TIME
+#define NCT6776_REG_FAN_STEP_DOWN_TIME NCT6775_REG_FAN_STEP_UP_TIME
+
 static const s8 NCT6776_ALARM_BITS[] = {
        0, 1, 2, 3, 8, 21, 20, 16,      /* in0.. in7 */
        17, -1, -1, -1, -1, -1, -1,     /* in8..in14 */
@@ -372,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 };
@@ -412,11 +461,18 @@ static const char *const nct6776_temp_label[] = {
        "BYTE_TEMP"
 };
 
-static const u16 NCT6776_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6776_temp_label) - 1]
-       = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x401, 0x402, 0x404 };
+#define NCT6776_TEMP_MASK      0x007ffffe
+
+static const u16 NCT6776_REG_TEMP_ALTERNATE[32] = {
+       [14] = 0x401,
+       [15] = 0x402,
+       [16] = 0x404,
+};
 
-static const u16 NCT6776_REG_TEMP_CRIT[ARRAY_SIZE(nct6776_temp_label) - 1]
-       = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x709, 0x70a };
+static const u16 NCT6776_REG_TEMP_CRIT[32] = {
+       [11] = 0x709,
+       [12] = 0x70a,
+};
 
 /* NCT6779 specific data */
 
@@ -446,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 };
@@ -495,28 +551,38 @@ static const char *const nct6779_temp_label[] = {
        "PCH_DIM1_TEMP",
        "PCH_DIM2_TEMP",
        "PCH_DIM3_TEMP",
-       "BYTE_TEMP"
+       "BYTE_TEMP",
+       "",
+       "",
+       "",
+       "",
+       "Virtual_TEMP"
 };
 
-static const u16 NCT6779_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6779_temp_label) - 1]
+#define NCT6779_TEMP_MASK      0x07ffff7e
+#define NCT6791_TEMP_MASK      0x87ffff7e
+
+static const u16 NCT6779_REG_TEMP_ALTERNATE[32]
        = { 0x490, 0x491, 0x492, 0x493, 0x494, 0x495, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0,
            0, 0x400, 0x401, 0x402, 0x404, 0x405, 0x406, 0x407,
            0x408, 0 };
 
-static const u16 NCT6779_REG_TEMP_CRIT[ARRAY_SIZE(nct6779_temp_label) - 1]
-       = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x709, 0x70a };
+static const u16 NCT6779_REG_TEMP_CRIT[32] = {
+       [15] = 0x709,
+       [16] = 0x70a,
+};
 
 /* NCT6791 specific data */
 
 #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 };
@@ -530,6 +596,160 @@ static const s8 NCT6791_ALARM_BITS[] = {
        4, 5, 13, -1, -1, -1,           /* temp1..temp6 */
        12, 9 };                        /* intrusion0, intrusion1 */
 
+/* NCT6792/NCT6793 specific data */
+
+static const u16 NCT6792_REG_TEMP_MON[] = {
+       0x73, 0x75, 0x77, 0x79, 0x7b, 0x7d };
+static const u16 NCT6792_REG_BEEP[NUM_REG_BEEP] = {
+       0xb2, 0xb3, 0xb4, 0xb5, 0xbf };
+
+static const char *const nct6792_temp_label[] = {
+       "",
+       "SYSTIN",
+       "CPUTIN",
+       "AUXTIN0",
+       "AUXTIN1",
+       "AUXTIN2",
+       "AUXTIN3",
+       "",
+       "SMBUSMASTER 0",
+       "SMBUSMASTER 1",
+       "SMBUSMASTER 2",
+       "SMBUSMASTER 3",
+       "SMBUSMASTER 4",
+       "SMBUSMASTER 5",
+       "SMBUSMASTER 6",
+       "SMBUSMASTER 7",
+       "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_TEMP",
+       "PECI Agent 0 Calibration",
+       "PECI Agent 1 Calibration",
+       "",
+       "",
+       "Virtual_TEMP"
+};
+
+#define NCT6792_TEMP_MASK      0x9fffff7e
+
+static const char *const nct6793_temp_label[] = {
+       "",
+       "SYSTIN",
+       "CPUTIN",
+       "AUXTIN0",
+       "AUXTIN1",
+       "AUXTIN2",
+       "AUXTIN3",
+       "",
+       "SMBUSMASTER 0",
+       "SMBUSMASTER 1",
+       "",
+       "",
+       "",
+       "",
+       "",
+       "",
+       "PECI Agent 0",
+       "PECI Agent 1",
+       "PCH_CHIP_CPU_MAX_TEMP",
+       "PCH_CHIP_TEMP",
+       "PCH_CPU_TEMP",
+       "PCH_MCH_TEMP",
+       "Agent0 Dimm0 ",
+       "Agent0 Dimm1",
+       "Agent1 Dimm0",
+       "Agent1 Dimm1",
+       "BYTE_TEMP0",
+       "BYTE_TEMP1",
+       "PECI Agent 0 Calibration",
+       "PECI Agent 1 Calibration",
+       "",
+       "Virtual_TEMP"
+};
+
+#define NCT6793_TEMP_MASK      0xbfff037e
+
+static const char *const nct6795_temp_label[] = {
+       "",
+       "SYSTIN",
+       "CPUTIN",
+       "AUXTIN0",
+       "AUXTIN1",
+       "AUXTIN2",
+       "AUXTIN3",
+       "",
+       "SMBUSMASTER 0",
+       "SMBUSMASTER 1",
+       "SMBUSMASTER 2",
+       "SMBUSMASTER 3",
+       "SMBUSMASTER 4",
+       "SMBUSMASTER 5",
+       "SMBUSMASTER 6",
+       "SMBUSMASTER 7",
+       "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 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 */
 
@@ -625,11 +845,16 @@ static const s8 NCT6106_BEEP_BITS[] = {
        34, -1                          /* intrusion0, intrusion1 */
 };
 
-static const u16 NCT6106_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6776_temp_label) - 1]
-       = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x51, 0x52, 0x54 };
+static const u16 NCT6106_REG_TEMP_ALTERNATE[32] = {
+       [14] = 0x51,
+       [15] = 0x52,
+       [16] = 0x54,
+};
 
-static const u16 NCT6106_REG_TEMP_CRIT[ARRAY_SIZE(nct6776_temp_label) - 1]
-       = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x204, 0x205 };
+static const u16 NCT6106_REG_TEMP_CRIT[32] = {
+       [11] = 0x204,
+       [12] = 0x205,
+};
 
 static enum pwm_enable reg_to_pwm_enable(int pwm, int mode)
 {
@@ -704,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);
 }
 
 /*
@@ -736,11 +961,11 @@ struct nct6775_data {
        enum kinds kind;
        const char *name;
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
        struct device *hwmon_dev;
-       struct attribute_group *group_in;
-       struct attribute_group *group_fan;
-       struct attribute_group *group_temp;
-       struct attribute_group *group_pwm;
+#endif
+
+       const struct attribute_group *groups[6];
 
        u16 reg_temp[5][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst,
                                    * 3=temp_crit, 4=temp_lcrit
@@ -748,7 +973,7 @@ struct nct6775_data {
        u8 temp_src[NUM_TEMP];
        u16 reg_temp_config[NUM_TEMP];
        const char * const *temp_label;
-       int temp_label_num;
+       u32 temp_mask;
 
        u16 REG_CONFIG;
        u16 REG_VBAT;
@@ -876,12 +1101,12 @@ struct nct6775_data {
        u16 have_temp;
        u16 have_temp_fixed;
        u16 have_in;
-#ifdef CONFIG_PM
+
        /* Remember extra register values over suspend/resume */
        u8 vbat;
        u8 fandiv1;
        u8 fandiv2;
-#endif
+       u8 sio_reg_enable;
 };
 
 struct nct6775_sio_data {
@@ -889,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 {
@@ -945,7 +1252,8 @@ struct sensor_template_group {
 };
 
 static struct attribute_group *
-nct6775_create_attr_group(struct device *dev, struct sensor_template_group *tg,
+nct6775_create_attr_group(struct device *dev,
+                         const struct sensor_template_group *tg,
                          int repeat)
 {
        struct attribute_group *group;
@@ -954,7 +1262,7 @@ nct6775_create_attr_group(struct device *dev, struct sensor_template_group *tg,
        struct sensor_device_attribute_2 *a2;
        struct attribute **attrs;
        struct sensor_device_template **t;
-       int err, i, j, count;
+       int i, count;
 
        if (repeat <= 0)
                return ERR_PTR(-EINVAL);
@@ -985,11 +1293,12 @@ nct6775_create_attr_group(struct device *dev, struct sensor_template_group *tg,
 
        for (i = 0; i < repeat; i++) {
                t = tg->templates;
-               for (j = 0; *t != NULL; j++) {
+               while (*t != NULL) {
                        snprintf(su->name, sizeof(su->name),
                                 (*t)->dev_attr.attr.name, tg->base + i);
                        if ((*t)->s2) {
                                a2 = &su->u.a2;
+                               sysfs_attr_init(&a2->dev_attr.attr);
                                a2->dev_attr.attr.name = su->name;
                                a2->nr = (*t)->u.s.nr + i;
                                a2->index = (*t)->u.s.index;
@@ -1000,6 +1309,7 @@ nct6775_create_attr_group(struct device *dev, struct sensor_template_group *tg,
                                *attrs = &a2->dev_attr.attr;
                        } else {
                                a = &su->u.a1;
+                               sysfs_attr_init(&a->dev_attr.attr);
                                a->dev_attr.attr.name = su->name;
                                a->index = (*t)->u.index + i;
                                a->dev_attr.attr.mode =
@@ -1014,10 +1324,6 @@ nct6775_create_attr_group(struct device *dev, struct sensor_template_group *tg,
                }
        }
 
-       err = sysfs_create_group(&dev->kobj, group);
-       if (err)
-               return ERR_PTR(-ENOMEM);
-
        return group;
 }
 
@@ -1052,13 +1358,18 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg)
                  reg == 0x73 || reg == 0x75 || reg == 0x77;
        case nct6779:
        case nct6791:
+       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 == 0x7b || reg == 0x7d;
        }
        return false;
 }
@@ -1072,6 +1383,7 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg)
 static inline void nct6775_set_bank(struct nct6775_data *data, u16 reg)
 {
        u8 bank = reg >> 8;
+
        if (data->bank != bank) {
                outb_p(NCT6775_REG_BANK, data->addr + ADDR_REG_OFFSET);
                outb_p(bank, data->addr + DATA_REG_OFFSET);
@@ -1172,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;
 }
 
@@ -1194,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;
@@ -1217,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],
@@ -1252,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)
@@ -1283,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++) {
@@ -1309,6 +1621,7 @@ static void nct6775_update_pwm(struct device *dev)
                if (!data->target_speed_tolerance[i] ||
                    data->pwm_enable[i] == speed_cruise) {
                        u8 t = fanmodecfg & 0x0f;
+
                        if (data->REG_TOLERANCE_H) {
                                t |= (nct6775_read_value(data,
                                      data->REG_TOLERANCE_H[i]) & 0x70) >> 1;
@@ -1326,19 +1639,20 @@ static void nct6775_update_pwm(struct device *dev)
                if (reg & 0x80)
                        data->pwm[2][i] = 0;
 
-               if (data->REG_WEIGHT_TEMP_SEL[i]) {
-                       reg = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[i]);
-                       data->pwm_weight_temp_sel[i] = reg & 0x1f;
-                       /* If weight is disabled, report weight source as 0 */
-                       if (j == 1 && !(reg & 0x80))
-                               data->pwm_weight_temp_sel[i] = 0;
+               if (!data->REG_WEIGHT_TEMP_SEL[i])
+                       continue;
 
-                       /* Weight temp data */
-                       for (j = 0; j < ARRAY_SIZE(data->weight_temp); j++) {
-                               data->weight_temp[j][i]
-                                 = nct6775_read_value(data,
-                                               data->REG_WEIGHT_TEMP[j][i]);
-                       }
+               reg = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[i]);
+               data->pwm_weight_temp_sel[i] = reg & 0x1f;
+               /* If weight is disabled, report weight source as 0 */
+               if (j == 1 && !(reg & 0x80))
+                       data->pwm_weight_temp_sel[i] = 0;
+
+               /* Weight temp data */
+               for (j = 0; j < ARRAY_SIZE(data->weight_temp); j++) {
+                       data->weight_temp[j][i]
+                         = nct6775_read_value(data,
+                                              data->REG_WEIGHT_TEMP[j][i]);
                }
        }
 }
@@ -1351,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++) {
@@ -1399,6 +1713,10 @@ static void nct6775_update_pwm_limits(struct device *dev)
                case nct6106:
                case nct6779:
                case nct6791:
+               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)
@@ -1426,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,
@@ -1441,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] =
@@ -1463,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])
@@ -1471,7 +1789,8 @@ static struct nct6775_data *nct6775_update_device(struct device *dev)
                                          = nct6775_read_temp(data,
                                                data->reg_temp[j][i]);
                        }
-                       if (!(data->have_temp_fixed & (1 << i)))
+                       if (i >= NUM_TEMP_FIXED ||
+                           !(data->have_temp_fixed & BIT(i)))
                                continue;
                        data->temp_offset[i]
                          = nct6775_read_value(data, data->REG_TEMP_OFFSET[i]);
@@ -1480,6 +1799,7 @@ static struct nct6775_data *nct6775_update_device(struct device *dev)
                data->alarms = 0;
                for (i = 0; i < NUM_REG_ALARM; i++) {
                        u8 alarm;
+
                        if (!data->REG_ALARM[i])
                                continue;
                        alarm = nct6775_read_value(data, data->REG_ALARM[i]);
@@ -1489,6 +1809,7 @@ static struct nct6775_data *nct6775_update_device(struct device *dev)
                data->beeps = 0;
                for (i = 0; i < NUM_REG_BEEP; i++) {
                        u8 beep;
+
                        if (!data->REG_BEEP[i])
                                continue;
                        beep = nct6775_read_value(data, data->REG_BEEP[i]);
@@ -1511,8 +1832,9 @@ show_in_reg(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct nct6775_data *data = nct6775_update_device(dev);
        struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
-       int nr = sattr->nr;
        int index = sattr->index;
+       int nr = sattr->nr;
+
        return sprintf(buf, "%ld\n", in_from_reg(data->in[nr][index], nr));
 }
 
@@ -1522,10 +1844,12 @@ store_in_reg(struct device *dev, struct device_attribute *attr, const char *buf,
 {
        struct nct6775_data *data = dev_get_drvdata(dev);
        struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
-       int nr = sattr->nr;
        int index = sattr->index;
+       int nr = sattr->nr;
        unsigned long val;
-       int err = kstrtoul(buf, 10, &val);
+       int err;
+
+       err = kstrtoul(buf, 10, &val);
        if (err < 0)
                return err;
        mutex_lock(&data->update_lock);
@@ -1542,6 +1866,7 @@ show_alarm(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);
        int nr = data->ALARM_BITS[sattr->index];
+
        return sprintf(buf, "%u\n",
                       (unsigned int)((data->alarms >> nr) & 0x01));
 }
@@ -1559,7 +1884,7 @@ static int find_temp_source(struct nct6775_data *data, int index, int count)
                if (src == source)
                        return nr;
        }
-       return -1;
+       return -ENODEV;
 }
 
 static ssize_t
@@ -1577,6 +1902,7 @@ show_temp_alarm(struct device *dev, struct device_attribute *attr, char *buf)
        nr = find_temp_source(data, sattr->index, data->num_temp_alarms);
        if (nr >= 0) {
                int bit = data->ALARM_BITS[nr + TEMP_ALARM_BASE];
+
                alarm = (data->alarms >> bit) & 0x01;
        }
        return sprintf(buf, "%u\n", alarm);
@@ -1602,8 +1928,9 @@ store_beep(struct device *dev, struct device_attribute *attr, const char *buf,
        int nr = data->BEEP_BITS[sattr->index];
        int regindex = nr >> 3;
        unsigned long val;
+       int err;
 
-       int err = kstrtoul(buf, 10, &val);
+       err = kstrtoul(buf, 10, &val);
        if (err < 0)
                return err;
        if (val > 1)
@@ -1636,6 +1963,7 @@ show_temp_beep(struct device *dev, struct device_attribute *attr, char *buf)
        nr = find_temp_source(data, sattr->index, data->num_temp_beeps);
        if (nr >= 0) {
                int bit = data->BEEP_BITS[nr + TEMP_ALARM_BASE];
+
                beep = (data->beeps >> bit) & 0x01;
        }
        return sprintf(buf, "%u\n", beep);
@@ -1649,8 +1977,9 @@ store_temp_beep(struct device *dev, struct device_attribute *attr,
        struct nct6775_data *data = dev_get_drvdata(dev);
        int nr, bit, regindex;
        unsigned long val;
+       int err;
 
-       int err = kstrtoul(buf, 10, &val);
+       err = kstrtoul(buf, 10, &val);
        if (err < 0)
                return err;
        if (val > 1)
@@ -1658,7 +1987,7 @@ store_temp_beep(struct device *dev, struct device_attribute *attr,
 
        nr = find_temp_source(data, sattr->index, data->num_temp_beeps);
        if (nr < 0)
-               return -ENODEV;
+               return nr;
 
        bit = data->BEEP_BITS[nr + TEMP_ALARM_BASE];
        regindex = bit >> 3;
@@ -1682,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;
@@ -1711,7 +2040,7 @@ static struct sensor_device_template *nct6775_attributes_in_template[] = {
        NULL
 };
 
-static struct sensor_template_group nct6775_in_template_group = {
+static const struct sensor_template_group nct6775_in_template_group = {
        .templates = nct6775_attributes_in_template,
        .is_visible = nct6775_in_is_visible,
 };
@@ -1722,6 +2051,7 @@ show_fan(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);
        int nr = sattr->index;
+
        return sprintf(buf, "%d\n", data->rpm[nr]);
 }
 
@@ -1731,6 +2061,7 @@ show_fan_min(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);
        int nr = sattr->index;
+
        return sprintf(buf, "%d\n",
                       data->fan_from_reg_min(data->fan_min[nr],
                                              data->fan_div[nr]));
@@ -1742,6 +2073,7 @@ show_fan_div(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);
        int nr = sattr->index;
+
        return sprintf(buf, "%u\n", div_from_reg(data->fan_div[nr]));
 }
 
@@ -1753,9 +2085,9 @@ store_fan_min(struct device *dev, struct device_attribute *attr,
        struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
        int nr = sattr->index;
        unsigned long val;
-       int err;
        unsigned int reg;
        u8 new_div;
+       int err;
 
        err = kstrtoul(buf, 10, &val);
        if (err < 0)
@@ -1789,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));
@@ -1799,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));
@@ -1886,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;
@@ -1927,7 +2261,7 @@ static struct sensor_device_template *nct6775_attributes_fan_template[] = {
        NULL
 };
 
-static struct sensor_template_group nct6775_fan_template_group = {
+static const struct sensor_template_group nct6775_fan_template_group = {
        .templates = nct6775_attributes_fan_template,
        .is_visible = nct6775_fan_is_visible,
        .base = 1,
@@ -1939,6 +2273,7 @@ show_temp_label(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);
        int nr = sattr->index;
+
        return sprintf(buf, "%s\n", data->temp_label[data->temp_src[nr]]);
 }
 
@@ -2015,6 +2350,7 @@ show_temp_type(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);
        int nr = sattr->index;
+
        return sprintf(buf, "%d\n", (int)data->temp_type[nr]);
 }
 
@@ -2069,7 +2405,10 @@ 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)
                return 0;
 
        if (nr == 2 && find_temp_source(data, temp, data->num_temp_alarms) < 0)
@@ -2091,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;
@@ -2134,7 +2473,7 @@ static struct sensor_device_template *nct6775_attributes_temp_template[] = {
        NULL
 };
 
-static struct sensor_template_group nct6775_temp_template_group = {
+static const struct sensor_template_group nct6775_temp_template_group = {
        .templates = nct6775_attributes_temp_template,
        .is_visible = nct6775_temp_is_visible,
        .base = 1,
@@ -2146,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
@@ -2167,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;
        }
@@ -2178,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);
@@ -2360,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;
@@ -2396,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);
@@ -2438,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;
 
@@ -2739,6 +3078,7 @@ store_fan_time(struct device *dev, struct device_attribute *attr,
        return count;
 }
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
 static ssize_t
 show_name(struct device *dev, struct device_attribute *attr, char *buf)
 {
@@ -2748,6 +3088,7 @@ show_name(struct device *dev, struct device_attribute *attr, char *buf)
 }
 
 static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+#endif
 
 static ssize_t
 show_auto_pwm(struct device *dev, struct device_attribute *attr, char *buf)
@@ -2807,6 +3148,10 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr,
                case nct6106:
                case nct6779:
                case nct6791:
+               case nct6792:
+               case nct6793:
+               case nct6795:
+               case nct6796:
                        nct6775_write_value(data, data->REG_CRITICAL_PWM[nr],
                                            val);
                        reg = nct6775_read_value(data,
@@ -2879,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 */
@@ -3004,20 +3349,21 @@ static struct sensor_device_template *nct6775_attributes_pwm_template[] = {
        NULL
 };
 
-static struct sensor_template_group nct6775_pwm_template_group = {
+static const struct sensor_template_group nct6775_pwm_template_group = {
        .templates = nct6775_attributes_pwm_template,
        .is_visible = nct6775_pwm_is_visible,
        .base = 1,
 };
 
 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 */
 
@@ -3078,16 +3424,16 @@ static umode_t nct6775_other_is_visible(struct kobject *kobj,
        struct device *dev = container_of(kobj, struct device, kobj);
        struct nct6775_data *data = dev_get_drvdata(dev);
 
-       if (index == 1 && !data->have_vid)
+       if (index == 0 && !data->have_vid)
                return 0;
 
-       if (index == 2 || index == 3) {
-               if (data->ALARM_BITS[INTRUSION_ALARM_BASE + index - 2] < 0)
+       if (index == 1 || index == 2) {
+               if (data->ALARM_BITS[INTRUSION_ALARM_BASE + index - 1] < 0)
                        return 0;
        }
 
-       if (index == 4 || index == 5) {
-               if (data->BEEP_BITS[INTRUSION_ALARM_BASE + index - 4] < 0)
+       if (index == 3 || index == 4) {
+               if (data->BEEP_BITS[INTRUSION_ALARM_BASE + index - 3] < 0)
                        return 0;
        }
 
@@ -3100,14 +3446,15 @@ static umode_t nct6775_other_is_visible(struct kobject *kobj,
  * Any change in order or content must be matched.
  */
 static struct attribute *nct6775_attributes_other[] = {
+       &dev_attr_cpu0_vid.attr,                                /* 0 */
+       &sensor_dev_attr_intrusion0_alarm.dev_attr.attr,        /* 1 */
+       &sensor_dev_attr_intrusion1_alarm.dev_attr.attr,        /* 2 */
+       &sensor_dev_attr_intrusion0_beep.dev_attr.attr,         /* 3 */
+       &sensor_dev_attr_intrusion1_beep.dev_attr.attr,         /* 4 */
+       &sensor_dev_attr_beep_enable.dev_attr.attr,             /* 5 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
        &dev_attr_name.attr,
-       &dev_attr_cpu0_vid.attr,                                /* 1 */
-       &sensor_dev_attr_intrusion0_alarm.dev_attr.attr,        /* 2 */
-       &sensor_dev_attr_intrusion1_alarm.dev_attr.attr,        /* 3 */
-       &sensor_dev_attr_intrusion0_beep.dev_attr.attr,         /* 4 */
-       &sensor_dev_attr_intrusion1_beep.dev_attr.attr,         /* 5 */
-       &sensor_dev_attr_beep_enable.dev_attr.attr,             /* 6 */
-
+#endif
        NULL
 };
 
@@ -3116,27 +3463,6 @@ static const struct attribute_group nct6775_group_other = {
        .is_visible = nct6775_other_is_visible,
 };
 
-/*
- * Driver and device management
- */
-
-static void nct6775_device_remove_files(struct device *dev)
-{
-       struct nct6775_data *data = dev_get_drvdata(dev);
-
-       if (data->group_pwm)
-               sysfs_remove_group(&dev->kobj, data->group_pwm);
-       if (data->group_in)
-               sysfs_remove_group(&dev->kobj, data->group_in);
-       if (data->group_fan)
-               sysfs_remove_group(&dev->kobj, data->group_fan);
-       if (data->group_temp)
-               sysfs_remove_group(&dev->kobj, data->group_temp);
-
-       sysfs_remove_group(&dev->kobj, &nct6775_group_other);
-}
-
-/* Get the monitoring functions started */
 static inline void nct6775_init_device(struct nct6775_data *data)
 {
        int i;
@@ -3151,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;
@@ -3169,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]
@@ -3182,95 +3508,161 @@ 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;
 
+       /* Store SIO_REG_ENABLE for use during resume */
+       superio_select(sioreg, NCT6775_LD_HWM);
+       data->sio_reg_enable = superio_inb(sioreg, SIO_REG_ENABLE);
+
        /* fan4 and fan5 share some pins with the GPIO and serial flash */
        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;
 
-               superio_select(sioreg, NCT6775_LD_HWM);
-               regval = superio_inb(sioreg, SIO_REG_ENABLE);
+               board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
+               board_name = dmi_get_system_info(DMI_BOARD_NAME);
 
-               if (regval & 0x80)
+               if (board_name && board_vendor &&
+                   !strcmp(board_vendor, "ASRock")) {
+                       /*
+                        * Auxiliary fan monitoring is not enabled on ASRock
+                        * Z77 Pro4-M if booted in UEFI Ultra-FastBoot mode.
+                        * Observed with BIOS version 2.00.
+                        */
+                       if (!strcmp(board_name, "Z77 Pro4-M")) {
+                               if ((data->sio_reg_enable & 0xe0) != 0xe0) {
+                                       data->sio_reg_enable |= 0xe0;
+                                       superio_outb(sioreg, SIO_REG_ENABLE,
+                                                    data->sio_reg_enable);
+                               }
+                       }
+               }
+
+               if (data->sio_reg_enable & 0x80)
                        fan3pin = gpok;
                else
                        fan3pin = !(superio_inb(sioreg, 0x24) & 0x40);
 
-               if (regval & 0x40)
+               if (data->sio_reg_enable & 0x40)
                        fan4pin = gpok;
                else
                        fan4pin = superio_inb(sioreg, 0x1C) & 0x01;
 
-               if (regval & 0x20)
+               if (data->sio_reg_enable & 0x20)
                        fan5pin = gpok;
                else
                        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;
+       } else {
+               /* NCT6779D, NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D */
+               int regval_1b, regval_2a, regval_2f;
+               bool dsw_en;
 
-               fan4pin = false;
-               fan4min = false;
-               fan5pin = false;
-               fan6pin = false;
-               pwm4pin = false;
-               pwm5pin = false;
-               pwm6pin = false;
-       } else {        /* NCT6779D or NCT6791D */
                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));
 
-               fan4min = fan4pin;
+               regval = superio_inb(sioreg, 0x2d);
+               switch (data->kind) {
+               case nct6791:
+               case nct6792:
+                       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 & BIT(7);
+
+                       if (!fan5pin)
+                               fan5pin = regval_1b & BIT(5);
+
+                       superio_select(sioreg, NCT6775_LD_12);
+                       if (data->kind != nct6796) {
+                               int regval_eb = superio_inb(sioreg, 0xeb);
+
+                               if (!dsw_en) {
+                                       fan6pin = regval & BIT(1);
+                                       pwm6pin = regval & BIT(0);
+                               }
 
-               if (data->kind == nct6791) {
-                       regval = superio_inb(sioreg, 0x2d);
-                       fan6pin = (regval & (1 << 1));
-                       pwm6pin = (regval & (1 << 0));
-               } else {        /* NCT6779D */
-                       fan6pin = false;
-                       pwm6pin = false;
+                               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 */
+                       break;
                }
+
+               fan4min = fan4pin;
        }
 
        /* 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,
@@ -3286,23 +3678,22 @@ 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 (src >= data->temp_label_num ||
-                   !strlen(data->temp_label[src]))
+               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);
        }
 }
 
 static int nct6775_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
-       struct nct6775_sio_data *sio_data = dev->platform_data;
+       struct nct6775_sio_data *sio_data = dev_get_platdata(dev);
        struct nct6775_data *data;
        struct resource *res;
        int i, s, err = 0;
@@ -3313,6 +3704,8 @@ static int nct6775_probe(struct platform_device *pdev)
        int num_reg_temp, num_reg_temp_mon;
        u8 cr2a;
        struct attribute_group *group;
+       struct device *hwmon_dev;
+       int num_attr_groups = 0;
 
        res = platform_get_resource(pdev, IORESOURCE_IO, 0);
        if (!devm_request_region(&pdev->dev, res->start, IOREGION_LENGTH,
@@ -3345,7 +3738,7 @@ static int nct6775_probe(struct platform_device *pdev)
                data->fan_from_reg_min = fan_from_reg13;
 
                data->temp_label = nct6776_temp_label;
-               data->temp_label_num = ARRAY_SIZE(nct6776_temp_label);
+               data->temp_mask = NCT6776_TEMP_MASK;
 
                data->REG_VBAT = NCT6106_REG_VBAT;
                data->REG_DIODE = NCT6106_REG_DIODE;
@@ -3423,7 +3816,7 @@ static int nct6775_probe(struct platform_device *pdev)
                data->speed_tolerance_limit = 15;
 
                data->temp_label = nct6775_temp_label;
-               data->temp_label_num = ARRAY_SIZE(nct6775_temp_label);
+               data->temp_mask = NCT6775_TEMP_MASK;
 
                data->REG_CONFIG = NCT6775_REG_CONFIG;
                data->REG_VBAT = NCT6775_REG_VBAT;
@@ -3495,7 +3888,7 @@ static int nct6775_probe(struct platform_device *pdev)
                data->speed_tolerance_limit = 63;
 
                data->temp_label = nct6776_temp_label;
-               data->temp_label_num = ARRAY_SIZE(nct6776_temp_label);
+               data->temp_mask = NCT6776_TEMP_MASK;
 
                data->REG_CONFIG = NCT6775_REG_CONFIG;
                data->REG_VBAT = NCT6775_REG_VBAT;
@@ -3511,8 +3904,8 @@ static int nct6775_probe(struct platform_device *pdev)
                data->REG_FAN_PULSES = NCT6776_REG_FAN_PULSES;
                data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT;
                data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME;
-               data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME;
-               data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME;
+               data->REG_FAN_TIME[1] = NCT6776_REG_FAN_STEP_UP_TIME;
+               data->REG_FAN_TIME[2] = NCT6776_REG_FAN_STEP_DOWN_TIME;
                data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H;
                data->REG_PWM[0] = NCT6775_REG_PWM;
                data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT;
@@ -3567,7 +3960,7 @@ static int nct6775_probe(struct platform_device *pdev)
                data->speed_tolerance_limit = 63;
 
                data->temp_label = nct6779_temp_label;
-               data->temp_label_num = ARRAY_SIZE(nct6779_temp_label);
+               data->temp_mask = NCT6779_TEMP_MASK;
 
                data->REG_CONFIG = NCT6775_REG_CONFIG;
                data->REG_VBAT = NCT6775_REG_VBAT;
@@ -3583,8 +3976,8 @@ static int nct6775_probe(struct platform_device *pdev)
                data->REG_FAN_PULSES = NCT6779_REG_FAN_PULSES;
                data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT;
                data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME;
-               data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME;
-               data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME;
+               data->REG_FAN_TIME[1] = NCT6776_REG_FAN_STEP_UP_TIME;
+               data->REG_FAN_TIME[2] = NCT6776_REG_FAN_STEP_DOWN_TIME;
                data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H;
                data->REG_PWM[0] = NCT6775_REG_PWM;
                data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT;
@@ -3625,8 +4018,12 @@ static int nct6775_probe(struct platform_device *pdev)
 
                break;
        case nct6791:
+       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;
@@ -3642,8 +4039,29 @@ static int nct6775_probe(struct platform_device *pdev)
                data->tolerance_mask = 0x07;
                data->speed_tolerance_limit = 63;
 
-               data->temp_label = nct6779_temp_label;
-               data->temp_label_num = ARRAY_SIZE(nct6779_temp_label);
+               switch (data->kind) {
+               default:
+               case nct6791:
+                       data->temp_label = nct6779_temp_label;
+                       data->temp_mask = NCT6791_TEMP_MASK;
+                       break;
+               case nct6792:
+                       data->temp_label = nct6792_temp_label;
+                       data->temp_mask = NCT6792_TEMP_MASK;
+                       break;
+               case nct6793:
+                       data->temp_label = nct6793_temp_label;
+                       data->temp_mask = NCT6793_TEMP_MASK;
+                       break;
+               case nct6795:
+                       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;
                data->REG_VBAT = NCT6775_REG_VBAT;
@@ -3659,8 +4077,8 @@ static int nct6775_probe(struct platform_device *pdev)
                data->REG_FAN_PULSES = NCT6779_REG_FAN_PULSES;
                data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT;
                data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME;
-               data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME;
-               data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME;
+               data->REG_FAN_TIME[1] = NCT6776_REG_FAN_STEP_UP_TIME;
+               data->REG_FAN_TIME[2] = NCT6776_REG_FAN_STEP_DOWN_TIME;
                data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H;
                data->REG_PWM[0] = NCT6775_REG_PWM;
                data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT;
@@ -3687,12 +4105,20 @@ static int nct6775_probe(struct platform_device *pdev)
                data->REG_WEIGHT_TEMP[1] = NCT6791_REG_WEIGHT_TEMP_STEP_TOL;
                data->REG_WEIGHT_TEMP[2] = NCT6791_REG_WEIGHT_TEMP_BASE;
                data->REG_ALARM = NCT6791_REG_ALARM;
-               data->REG_BEEP = NCT6776_REG_BEEP;
+               if (data->kind == nct6791)
+                       data->REG_BEEP = NCT6776_REG_BEEP;
+               else
+                       data->REG_BEEP = NCT6792_REG_BEEP;
 
                reg_temp = NCT6779_REG_TEMP;
-               reg_temp_mon = NCT6779_REG_TEMP_MON;
                num_reg_temp = ARRAY_SIZE(NCT6779_REG_TEMP);
-               num_reg_temp_mon = ARRAY_SIZE(NCT6779_REG_TEMP_MON);
+               if (data->kind == nct6791) {
+                       reg_temp_mon = NCT6779_REG_TEMP_MON;
+                       num_reg_temp_mon = ARRAY_SIZE(NCT6779_REG_TEMP_MON);
+               } else {
+                       reg_temp_mon = NCT6792_REG_TEMP_MON;
+                       num_reg_temp_mon = ARRAY_SIZE(NCT6792_REG_TEMP_MON);
+               }
                reg_temp_over = NCT6779_REG_TEMP_OVER;
                reg_temp_hyst = NCT6779_REG_TEMP_HYST;
                reg_temp_config = NCT6779_REG_TEMP_CONFIG;
@@ -3703,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;
 
        /*
@@ -3721,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);
        }
 
        /*
@@ -3741,23 +4167,22 @@ 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 (src >= data->temp_label_num ||
-                   !strlen(data->temp_label[src])) {
+               if (!(data->temp_mask & BIT(src))) {
                        dev_info(dev,
                                 "Invalid temperature source %d at index %d, source register 0x%x, temp register 0x%x\n",
                                 src, i, data->REG_TEMP_SOURCE[i], reg_temp[i]);
                        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];
@@ -3777,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];
@@ -3802,25 +4227,34 @@ static int nct6775_probe(struct platform_device *pdev)
                        continue;
 
                src = nct6775_read_value(data, data->REG_TEMP_SEL[i]) & 0x1f;
-               if (!src || (mask & (1 << src)))
+               if (!src)
                        continue;
 
-               if (src >= data->temp_label_num ||
-                   !strlen(data->temp_label[src])) {
+               if (!(data->temp_mask & BIT(src))) {
                        dev_info(dev,
                                 "Invalid temperature source %d at index %d, source register 0x%x, temp register 0x%x\n",
-                                src, i, data->REG_TEMP_SEL[i], reg_temp_mon[i]);
+                                src, i, data->REG_TEMP_SEL[i],
+                                reg_temp_mon[i]);
                        continue;
                }
 
-               mask |= 1 << src;
+               /*
+                * For virtual temperature sources, the 'virtual' temperature
+                * for each fan reflects a different temperature, and there
+                * are no duplicates.
+                */
+               if (src != TEMP_SOURCE_VIRTUAL) {
+                       if (mask & BIT(src))
+                               continue;
+                       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;
@@ -3830,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++;
@@ -3843,16 +4277,18 @@ static int nct6775_probe(struct platform_device *pdev)
         * The temperature is already monitored if the respective bit in <mask>
         * is set.
         */
-       for (i = 0; i < data->temp_label_num - 1; i++) {
+       for (i = 0; i < 32; 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];
@@ -3865,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++;
@@ -3890,6 +4326,10 @@ static int nct6775_probe(struct platform_device *pdev)
        case nct6106:
        case nct6779:
        case nct6791:
+       case nct6792:
+       case nct6793:
+       case nct6795:
+       case nct6796:
                break;
        }
 
@@ -3921,6 +4361,10 @@ static int nct6775_probe(struct platform_device *pdev)
                        tmp |= 0x3e;
                        break;
                case nct6791:
+               case nct6792:
+               case nct6793:
+               case nct6795:
+               case nct6796:
                        tmp |= 0x7e;
                        break;
                }
@@ -3940,65 +4384,76 @@ static int nct6775_probe(struct platform_device *pdev)
        /* Register sysfs hooks */
        group = nct6775_create_attr_group(dev, &nct6775_pwm_template_group,
                                          data->pwm_num);
-       if (IS_ERR(group)) {
-               err = PTR_ERR(group);
-               goto exit_remove;
-       }
-       data->group_pwm = group;
+       if (IS_ERR(group))
+               return PTR_ERR(group);
+
+       data->groups[num_attr_groups++] = group;
 
        group = nct6775_create_attr_group(dev, &nct6775_in_template_group,
                                          fls(data->have_in));
-       if (IS_ERR(group)) {
-               err = PTR_ERR(group);
-               goto exit_remove;
-       }
-       data->group_in = group;
+       if (IS_ERR(group))
+               return PTR_ERR(group);
+
+       data->groups[num_attr_groups++] = group;
 
        group = nct6775_create_attr_group(dev, &nct6775_fan_template_group,
                                          fls(data->has_fan));
-       if (IS_ERR(group)) {
-               err = PTR_ERR(group);
-               goto exit_remove;
-       }
-       data->group_fan = group;
+       if (IS_ERR(group))
+               return PTR_ERR(group);
+
+       data->groups[num_attr_groups++] = group;
 
        group = nct6775_create_attr_group(dev, &nct6775_temp_template_group,
                                          fls(data->have_temp));
-       if (IS_ERR(group)) {
-               err = PTR_ERR(group);
-               goto exit_remove;
-       }
-       data->group_temp = group;
+       if (IS_ERR(group))
+               return PTR_ERR(group);
 
-       err = sysfs_create_group(&dev->kobj, &nct6775_group_other);
-       if (err)
-               goto exit_remove;
+       data->groups[num_attr_groups++] = group;
+       data->groups[num_attr_groups++] = &nct6775_group_other;
 
-       data->hwmon_dev = hwmon_device_register(dev);
-       if (IS_ERR(data->hwmon_dev)) {
-               err = PTR_ERR(data->hwmon_dev);
-               goto exit_remove;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
+       err = sysfs_create_groups(&dev->kobj, data->groups);
+       if (err < 0)
+               return err;
+       hwmon_dev = hwmon_device_register(dev);
+       if (IS_ERR(hwmon_dev)) {
+               sysfs_remove_groups(&dev->kobj, data->groups);
+               return PTR_ERR(hwmon_dev);
        }
-
-       return 0;
-
-exit_remove:
-       nct6775_device_remove_files(dev);
-       return err;
+       data->hwmon_dev = hwmon_dev;
+#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);
 }
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
 static int nct6775_remove(struct platform_device *pdev)
 {
        struct nct6775_data *data = platform_get_drvdata(pdev);
 
        hwmon_device_unregister(data->hwmon_dev);
-       nct6775_device_remove_files(&pdev->dev);
-
+       sysfs_remove_groups(&pdev->dev.kobj, data->groups);
        return 0;
 }
+#endif
 
-#ifdef CONFIG_PM
-static int nct6775_suspend(struct device *dev)
+static void nct6791_enable_io_mapping(int sioaddr)
+{
+       int val;
+
+       val = superio_inb(sioaddr, NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE);
+       if (val & 0x10) {
+               pr_info("Enabling hardware monitor logical device mappings.\n");
+               superio_outb(sioaddr, NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE,
+                            val & ~0x10);
+       }
+}
+
+static int __maybe_unused nct6775_suspend(struct device *dev)
 {
        struct nct6775_data *data = nct6775_update_device(dev);
 
@@ -4013,35 +4468,35 @@ static int nct6775_suspend(struct device *dev)
        return 0;
 }
 
-static int nct6775_resume(struct device *dev)
+static int __maybe_unused nct6775_resume(struct device *dev)
 {
        struct nct6775_data *data = dev_get_drvdata(dev);
+       int sioreg = data->sioreg;
        int i, j, err = 0;
+       u8 reg;
 
        mutex_lock(&data->update_lock);
        data->bank = 0xff;              /* Force initial bank selection */
 
-       if (data->kind == nct6791) {
-               int val;
+       err = superio_enter(sioreg);
+       if (err)
+               goto abort;
+
+       superio_select(sioreg, NCT6775_LD_HWM);
+       reg = superio_inb(sioreg, SIO_REG_ENABLE);
+       if (reg != data->sio_reg_enable)
+               superio_outb(sioreg, SIO_REG_ENABLE, data->sio_reg_enable);
 
-               err = superio_enter(data->sioreg);
-               if (err)
-                       goto abort;
-
-               val = superio_inb(data->sioreg,
-                                 NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE);
-               if (val & 0x10) {
-                       pr_info("Re-enabling hardware monitor logical device mappings.\n");
-                       superio_outb(data->sioreg,
-                                    NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE,
-                                    val & ~0x10);
-               }
-               superio_exit(data->sioreg);
-       }
+       if (data->kind == nct6791 || data->kind == nct6792 ||
+           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],
@@ -4051,7 +4506,7 @@ static int 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],
@@ -4059,7 +4514,7 @@ static int 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++)
@@ -4080,35 +4535,21 @@ abort:
        data->valid = false;
        mutex_unlock(&data->update_lock);
 
-       return 0;
+       return err;
 }
 
-static const struct dev_pm_ops nct6775_dev_pm_ops = {
-       .suspend = nct6775_suspend,
-       .resume = nct6775_resume,
-};
-
-#define NCT6775_DEV_PM_OPS     (&nct6775_dev_pm_ops)
-#else
-#define NCT6775_DEV_PM_OPS     NULL
-#endif /* CONFIG_PM */
+static SIMPLE_DEV_PM_OPS(nct6775_dev_pm_ops, nct6775_suspend, nct6775_resume);
 
 static struct platform_driver nct6775_driver = {
        .driver = {
                .owner  = THIS_MODULE,
                .name   = DRVNAME,
-               .pm     = NCT6775_DEV_PM_OPS,
+               .pm     = &nct6775_dev_pm_ops,
        },
        .probe          = nct6775_probe,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
        .remove         = nct6775_remove,
-};
-
-static const char * const nct6775_sio_names[] __initconst = {
-       "NCT6106D",
-       "NCT6775F",
-       "NCT6776D/F",
-       "NCT6779D",
-       "NCT6791D",
+#endif
 };
 
 /* nct6775_find() looks for a '627 in the Super-I/O config space */
@@ -4122,11 +4563,11 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
        if (err)
                return err;
 
-       if (force_id)
+       val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8) |
+               superio_inb(sioaddr, SIO_REG_DEVID + 1);
+       if (force_id && val != 0xffff)
                val = force_id;
-       else
-               val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8)
-                   | superio_inb(sioaddr, SIO_REG_DEVID + 1);
+
        switch (val & SIO_ID_MASK) {
        case SIO_NCT6106_ID:
                sio_data->kind = nct6106;
@@ -4143,6 +4584,18 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
        case SIO_NCT6791_ID:
                sio_data->kind = nct6791;
                break;
+       case SIO_NCT6792_ID:
+               sio_data->kind = nct6792;
+               break;
+       case SIO_NCT6793_ID:
+               sio_data->kind = nct6793;
+               break;
+       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);
@@ -4167,15 +4620,11 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
                pr_warn("Forcibly enabling Super-I/O. Sensor is probably unusable.\n");
                superio_outb(sioaddr, SIO_REG_ENABLE, val | 0x01);
        }
-       if (sio_data->kind == nct6791) {
-               val = superio_inb(sioaddr, NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE);
-               if (val & 0x10) {
-                       pr_info("Enabling hardware monitor logical device mappings.\n");
-                       superio_outb(sioaddr,
-                                    NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE,
-                                    val & ~0x10);
-               }
-       }
+
+       if (sio_data->kind == nct6791 || sio_data->kind == nct6792 ||
+           sio_data->kind == nct6793 || sio_data->kind == nct6795 ||
+           sio_data->kind == nct6796)
+               nct6791_enable_io_mapping(sioaddr);
 
        superio_exit(sioaddr);
        pr_info("Found %s or compatible chip at %#x:%#x\n",
@@ -4188,7 +4637,7 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
 /*
  * when Super-I/O functions move to a separate file, the Super-I/O
  * bus will manage the lifetime of the device and this module will only keep
- * track of the nct6775 driver. But since we platform_device_alloc(), we
+ * track of the nct6775 driver. But since we use platform_device_alloc(), we
  * must keep track of the device
  */
 static struct platform_device *pdev[2];
@@ -4223,7 +4672,7 @@ static int __init sensors_nct6775_init(void)
                pdev[i] = platform_device_alloc(DRVNAME, address);
                if (!pdev[i]) {
                        err = -ENOMEM;
-                       goto exit_device_put;
+                       goto exit_device_unregister;
                }
 
                err = platform_device_add_data(pdev[i], &sio_data,
@@ -4261,9 +4710,11 @@ static int __init sensors_nct6775_init(void)
        return 0;
 
 exit_device_put:
-       for (i = 0; i < ARRAY_SIZE(pdev); i++) {
+       platform_device_put(pdev[i]);
+exit_device_unregister:
+       while (--i >= 0) {
                if (pdev[i])
-                       platform_device_put(pdev[i]);
+                       platform_device_unregister(pdev[i]);
        }
 exit_unregister:
        platform_driver_unregister(&nct6775_driver);
@@ -4282,7 +4733,7 @@ static void __exit sensors_nct6775_exit(void)
 }
 
 MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
-MODULE_DESCRIPTION("NCT6775F/NCT6776F/NCT6779D driver");
+MODULE_DESCRIPTION("Driver for NCT6775F and compatible chips");
 MODULE_LICENSE("GPL");
 
 module_init(sensors_nct6775_init);