Merge tag 'hwmon-for-v6.10' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck...
[linux-2.6-block.git] / drivers / hwmon / it87.c
index fbe86cec605538043c8cc0abc470df09e4cd2810..e233aafa8856c94c3fde3502732fcda2bd3bf130 100644 (file)
@@ -117,7 +117,7 @@ static inline void superio_select(int ioreg, int ldn)
        outb(ldn, ioreg + 1);
 }
 
-static inline int superio_enter(int ioreg)
+static inline int superio_enter(int ioreg, bool noentry)
 {
        /*
         * Try to reserve ioreg and ioreg + 1 for exclusive access.
@@ -125,7 +125,8 @@ static inline int superio_enter(int ioreg)
        if (!request_muxed_region(ioreg, 2, DRVNAME))
                return -EBUSY;
 
-       __superio_enter(ioreg);
+       if (!noentry)
+               __superio_enter(ioreg);
        return 0;
 }
 
@@ -320,7 +321,7 @@ struct it87_devices {
  * second SIO address. Never exit configuration mode on these
  * chips to avoid the problem.
  */
-#define FEAT_CONF_NOEXIT       BIT(19) /* Chip should not exit conf mode */
+#define FEAT_NOCONF            BIT(19) /* Chip conf mode enabled on startup */
 #define FEAT_FOUR_FANS         BIT(20) /* Supports four fans */
 #define FEAT_FOUR_PWM          BIT(21) /* Supports four fan controls */
 #define FEAT_FOUR_TEMP         BIT(22)
@@ -452,7 +453,7 @@ static const struct it87_devices it87_devices[] = {
                .model = "IT8790E",
                .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
                  | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL
-                 | FEAT_PWM_FREQ2 | FEAT_FANCTL_ONOFF | FEAT_CONF_NOEXIT,
+                 | FEAT_PWM_FREQ2 | FEAT_FANCTL_ONOFF | FEAT_NOCONF,
                .peci_mask = 0x07,
        },
        [it8792] = {
@@ -461,7 +462,7 @@ static const struct it87_devices it87_devices[] = {
                .features = FEAT_NEWER_AUTOPWM | FEAT_16BIT_FANS
                  | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI
                  | FEAT_10_9MV_ADC | FEAT_IN7_INTERNAL | FEAT_FANCTL_ONOFF
-                 | FEAT_CONF_NOEXIT,
+                 | FEAT_NOCONF,
                .peci_mask = 0x07,
                .old_peci_mask = 0x02,  /* Actually reports PCH */
        },
@@ -507,7 +508,7 @@ static const struct it87_devices it87_devices[] = {
                .features = FEAT_NEWER_AUTOPWM | FEAT_16BIT_FANS
                  | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI
                  | FEAT_10_9MV_ADC | FEAT_IN7_INTERNAL | FEAT_FANCTL_ONOFF
-                 | FEAT_CONF_NOEXIT,
+                 | FEAT_NOCONF,
                .peci_mask = 0x07,
                .old_peci_mask = 0x02,  /* Actually reports PCH */
        },
@@ -544,7 +545,7 @@ static const struct it87_devices it87_devices[] = {
 #define has_four_temp(data)    ((data)->features & FEAT_FOUR_TEMP)
 #define has_six_temp(data)     ((data)->features & FEAT_SIX_TEMP)
 #define has_vin3_5v(data)      ((data)->features & FEAT_VIN3_5V)
-#define has_conf_noexit(data)  ((data)->features & FEAT_CONF_NOEXIT)
+#define has_noconf(data)       ((data)->features & FEAT_NOCONF)
 #define has_scaling(data)      ((data)->features & (FEAT_12MV_ADC | \
                                                     FEAT_10_9MV_ADC))
 #define has_fanctl_onoff(data) ((data)->features & FEAT_FANCTL_ONOFF)
@@ -742,13 +743,13 @@ static int smbus_disable(struct it87_data *data)
        int err;
 
        if (data->smbus_bitmap) {
-               err = superio_enter(data->sioaddr);
+               err = superio_enter(data->sioaddr, has_noconf(data));
                if (err)
                        return err;
                superio_select(data->sioaddr, PME);
                superio_outb(data->sioaddr, IT87_SPECIAL_CFG_REG,
                             data->ec_special_config & ~data->smbus_bitmap);
-               superio_exit(data->sioaddr, has_conf_noexit(data));
+               superio_exit(data->sioaddr, has_noconf(data));
        }
        return 0;
 }
@@ -758,14 +759,14 @@ static int smbus_enable(struct it87_data *data)
        int err;
 
        if (data->smbus_bitmap) {
-               err = superio_enter(data->sioaddr);
+               err = superio_enter(data->sioaddr, has_noconf(data));
                if (err)
                        return err;
 
                superio_select(data->sioaddr, PME);
                superio_outb(data->sioaddr, IT87_SPECIAL_CFG_REG,
                             data->ec_special_config);
-               superio_exit(data->sioaddr, has_conf_noexit(data));
+               superio_exit(data->sioaddr, has_noconf(data));
        }
        return 0;
 }
@@ -2666,6 +2667,27 @@ static const struct attribute_group it87_group_auto_pwm = {
        .is_visible = it87_auto_pwm_is_visible,
 };
 
+/*
+ * Original explanation:
+ * On various Gigabyte AM4 boards (AB350, AX370), the second Super-IO chip
+ * (IT8792E) needs to be in configuration mode before accessing the first
+ * due to a bug in IT8792E which otherwise results in LPC bus access errors.
+ * This needs to be done before accessing the first Super-IO chip since
+ * the second chip may have been accessed prior to loading this driver.
+ *
+ * The problem is also reported to affect IT8795E, which is used on X299 boards
+ * and has the same chip ID as IT8792E (0x8733). It also appears to affect
+ * systems with IT8790E, which is used on some Z97X-Gaming boards as well as
+ * Z87X-OC.
+ *
+ * From other information supplied:
+ * ChipIDs 0x8733, 0x8695 (early ID for IT87952E) and 0x8790 are initialized
+ * and left in configuration mode, and entering and/or exiting configuration
+ * mode is what causes the crash.
+ *
+ * The recommendation is to look up the chipID before doing any mode swap
+ * and then act accordingly.
+ */
 /* SuperIO detection - will change isa_address if a chip is found */
 static int __init it87_find(int sioaddr, unsigned short *address,
                            struct it87_sio_data *sio_data, int chip_cnt)
@@ -2673,16 +2695,25 @@ static int __init it87_find(int sioaddr, unsigned short *address,
        int err;
        u16 chip_type;
        const struct it87_devices *config = NULL;
+       bool enabled = false;
 
-       err = superio_enter(sioaddr);
+       /* First step, lock memory but don't enter configuration mode */
+       err = superio_enter(sioaddr, true);
        if (err)
                return err;
 
        err = -ENODEV;
        chip_type = superio_inw(sioaddr, DEVID);
-       /* check first for a valid chip before forcing chip id */
-       if (chip_type == 0xffff)
-               goto exit;
+       /* Check for a valid chip before forcing chip id */
+       if (chip_type == 0xffff) {
+               /* Enter configuration mode */
+               __superio_enter(sioaddr);
+               enabled = true;
+               /* and then try again */
+               chip_type = superio_inw(sioaddr, DEVID);
+               if (chip_type == 0xffff)
+                       goto exit;
+       }
 
        if (force_id_cnt == 1) {
                /* If only one value given use for all chips */
@@ -2766,6 +2797,18 @@ static int __init it87_find(int sioaddr, unsigned short *address,
 
        config = &it87_devices[sio_data->type];
 
+       /*
+        * If previously we didn't enter configuration mode and it isn't a
+        * chip we know is initialised in configuration mode, then enter
+        * configuration mode.
+        *
+        * I don't know if any such chips can exist but be defensive.
+        */
+       if (!enabled && !has_noconf(config)) {
+               __superio_enter(sioaddr);
+               enabled = true;
+       }
+
        superio_select(sioaddr, PME);
        if (!(superio_inb(sioaddr, IT87_ACT_REG) & 0x01)) {
                pr_info("Device (chip %s ioreg 0x%x) not activated, skipping\n",
@@ -3143,7 +3186,7 @@ static int __init it87_find(int sioaddr, unsigned short *address,
        }
 
 exit:
-       superio_exit(sioaddr, config ? has_conf_noexit(config) : false);
+       superio_exit(sioaddr, !enabled);
        return err;
 }
 
@@ -3520,7 +3563,7 @@ static void it87_resume_sio(struct platform_device *pdev)
        if (!data->need_in7_reroute)
                return;
 
-       err = superio_enter(data->sioaddr);
+       err = superio_enter(data->sioaddr, has_noconf(data));
        if (err) {
                dev_warn(&pdev->dev,
                         "Unable to enter Super I/O to reroute in7 (%d)",
@@ -3540,7 +3583,7 @@ static void it87_resume_sio(struct platform_device *pdev)
                             reg2c);
        }
 
-       superio_exit(data->sioaddr, has_conf_noexit(data));
+       superio_exit(data->sioaddr, has_noconf(data));
 }
 
 static int it87_resume(struct device *dev)
@@ -3640,27 +3683,6 @@ static int it87_dmi_cb(const struct dmi_system_id *dmi_entry)
        return 1;
 }
 
-/*
- * On various Gigabyte AM4 boards (AB350, AX370), the second Super-IO chip
- * (IT8792E) needs to be in configuration mode before accessing the first
- * due to a bug in IT8792E which otherwise results in LPC bus access errors.
- * This needs to be done before accessing the first Super-IO chip since
- * the second chip may have been accessed prior to loading this driver.
- *
- * The problem is also reported to affect IT8795E, which is used on X299 boards
- * and has the same chip ID as IT8792E (0x8733). It also appears to affect
- * systems with IT8790E, which is used on some Z97X-Gaming boards as well as
- * Z87X-OC.
- * DMI entries for those systems will be added as they become available and
- * as the problem is confirmed to affect those boards.
- */
-static int it87_sio_force(const struct dmi_system_id *dmi_entry)
-{
-       __superio_enter(REG_4E);
-
-       return it87_dmi_cb(dmi_entry);
-};
-
 /*
  * On the Shuttle SN68PT, FAN_CTL2 is apparently not
  * connected to a fan, but to something else. One user
@@ -3683,34 +3705,7 @@ static struct it87_dmi_data nvidia_fn68pt = {
                .driver_data = data, \
        }
 
-#define IT87_DMI_MATCH_GBT(name, cb, data) \
-       IT87_DMI_MATCH_VND("Gigabyte Technology Co., Ltd.", name, cb, data)
-
 static const struct dmi_system_id it87_dmi_table[] __initconst = {
-       IT87_DMI_MATCH_GBT("AB350", it87_sio_force, NULL),
-               /* ? + IT8792E/IT8795E */
-       IT87_DMI_MATCH_GBT("AX370", it87_sio_force, NULL),
-               /* ? + IT8792E/IT8795E */
-       IT87_DMI_MATCH_GBT("Z97X-Gaming G1", it87_sio_force, NULL),
-               /* ? + IT8790E */
-       IT87_DMI_MATCH_GBT("TRX40 AORUS XTREME", it87_sio_force, NULL),
-               /* IT8688E + IT8792E/IT8795E */
-       IT87_DMI_MATCH_GBT("Z390 AORUS ULTRA-CF", it87_sio_force, NULL),
-               /* IT8688E + IT8792E/IT8795E */
-       IT87_DMI_MATCH_GBT("B550 AORUS PRO AC", it87_sio_force, NULL),
-               /* IT8688E + IT8792E/IT8795E */
-       IT87_DMI_MATCH_GBT("X570 AORUS MASTER", it87_sio_force, NULL),
-               /* IT8688E + IT8792E/IT8795E */
-       IT87_DMI_MATCH_GBT("X570 AORUS PRO", it87_sio_force, NULL),
-               /* IT8688E + IT8792E/IT8795E */
-       IT87_DMI_MATCH_GBT("X570 AORUS PRO WIFI", it87_sio_force, NULL),
-               /* IT8688E + IT8792E/IT8795E */
-       IT87_DMI_MATCH_GBT("X570S AERO G", it87_sio_force, NULL),
-               /* IT8689E + IT87952E */
-       IT87_DMI_MATCH_GBT("Z690 AORUS PRO DDR4", it87_sio_force, NULL),
-               /* IT8689E + IT87952E */
-       IT87_DMI_MATCH_GBT("Z690 AORUS PRO", it87_sio_force, NULL),
-               /* IT8689E + IT87952E */
        IT87_DMI_MATCH_VND("nVIDIA", "FN68PT", it87_dmi_cb, &nvidia_fn68pt),
        { }