Clear the "persistence bit" on iwx(4) devices during hardware init.
authorstsp <stsp@openbsd.org>
Sun, 18 Jul 2021 12:39:16 +0000 (12:39 +0000)
committerstsp <stsp@openbsd.org>
Sun, 18 Jul 2021 12:39:16 +0000 (12:39 +0000)
According to iwlwifi commit messages this fixes an edge case where
devices fail to resume after system suspend.
See Linux commit 8954e1eb2270fa2effffd031b4839253952c76f2

Same fix was made for iwm(4) in CVS commit x0XTNdEmudy5oBR4

sys/dev/pci/if_iwx.c

index 332d49f..b8aa252 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if_iwx.c,v 1.67 2021/07/18 12:21:49 stsp Exp $        */
+/*     $OpenBSD: if_iwx.c,v 1.68 2021/07/18 12:39:16 stsp Exp $        */
 
 /*
  * Copyright (c) 2014, 2016 genua gmbh <info@genua.de>
@@ -248,7 +248,9 @@ int iwx_firmware_store_section(struct iwx_softc *, enum iwx_ucode_type,
 int    iwx_set_default_calib(struct iwx_softc *, const void *);
 void   iwx_fw_info_free(struct iwx_fw_info *);
 int    iwx_read_firmware(struct iwx_softc *);
+uint32_t iwx_read_prph_unlocked(struct iwx_softc *, uint32_t);
 uint32_t iwx_read_prph(struct iwx_softc *, uint32_t);
+void   iwx_write_prph_unlocked(struct iwx_softc *, uint32_t, uint32_t);
 void   iwx_write_prph(struct iwx_softc *, uint32_t, uint32_t);
 int    iwx_read_mem(struct iwx_softc *, uint32_t, void *, int);
 int    iwx_write_mem(struct iwx_softc *, uint32_t, const void *, int);
@@ -287,6 +289,7 @@ void        iwx_apm_stop(struct iwx_softc *);
 int    iwx_allow_mcast(struct iwx_softc *);
 void   iwx_init_msix_hw(struct iwx_softc *);
 void   iwx_conf_msix_hw(struct iwx_softc *, int);
+int    iwx_clear_persistence_bit(struct iwx_softc *);
 int    iwx_start_hw(struct iwx_softc *);
 void   iwx_stop_device(struct iwx_softc *);
 void   iwx_nic_config(struct iwx_softc *);
@@ -1344,25 +1347,37 @@ iwx_read_firmware(struct iwx_softc *sc)
 }
 
 uint32_t
-iwx_read_prph(struct iwx_softc *sc, uint32_t addr)
+iwx_read_prph_unlocked(struct iwx_softc *sc, uint32_t addr)
 {
-       iwx_nic_assert_locked(sc);
        IWX_WRITE(sc,
            IWX_HBUS_TARG_PRPH_RADDR, ((addr & 0x000fffff) | (3 << 24)));
        IWX_BARRIER_READ_WRITE(sc);
        return IWX_READ(sc, IWX_HBUS_TARG_PRPH_RDAT);
 }
 
-void
-iwx_write_prph(struct iwx_softc *sc, uint32_t addr, uint32_t val)
+uint32_t
+iwx_read_prph(struct iwx_softc *sc, uint32_t addr)
 {
        iwx_nic_assert_locked(sc);
+       return iwx_read_prph_unlocked(sc, addr);
+}
+
+void
+iwx_write_prph_unlocked(struct iwx_softc *sc, uint32_t addr, uint32_t val)
+{
        IWX_WRITE(sc,
            IWX_HBUS_TARG_PRPH_WADDR, ((addr & 0x000fffff) | (3 << 24)));
        IWX_BARRIER_WRITE(sc);
        IWX_WRITE(sc, IWX_HBUS_TARG_PRPH_WDAT, val);
 }
 
+void
+iwx_write_prph(struct iwx_softc *sc, uint32_t addr, uint32_t val)
+{
+       iwx_nic_assert_locked(sc);
+       iwx_write_prph_unlocked(sc, addr, val);
+}
+
 void
 iwx_write_prph64(struct iwx_softc *sc, uint64_t addr, uint64_t val)
 {
@@ -2215,6 +2230,26 @@ iwx_conf_msix_hw(struct iwx_softc *sc, int stopped)
            IWX_MSIX_HW_INT_CAUSES_REG_HAP);
 }
 
+int
+iwx_clear_persistence_bit(struct iwx_softc *sc)
+{
+       uint32_t hpm, wprot;
+
+       hpm = iwx_read_prph_unlocked(sc, IWX_HPM_DEBUG);
+       if (hpm != 0xa5a5a5a0 && (hpm & IWX_PERSISTENCE_BIT)) {
+               wprot = iwx_read_prph_unlocked(sc, IWX_PREG_PRPH_WPROT_22000);
+               if (wprot & IWX_PREG_WFPM_ACCESS) {
+                       printf("%s: cannot clear persistence bit\n",
+                           DEVNAME(sc));
+                       return EPERM;
+               }
+               iwx_write_prph_unlocked(sc, IWX_HPM_DEBUG,
+                   hpm & ~IWX_PERSISTENCE_BIT);
+       }
+
+       return 0;
+}
+
 int
 iwx_start_hw(struct iwx_softc *sc)
 {
@@ -2225,6 +2260,10 @@ iwx_start_hw(struct iwx_softc *sc)
        if (err)
                return err;
 
+       err = iwx_clear_persistence_bit(sc);
+       if (err)
+               return err;
+
        /* Reset the entire device */
        IWX_SETBITS(sc, IWX_CSR_RESET, IWX_CSR_RESET_REG_FLAG_SW_RESET);
        DELAY(5000);