git: 7ce67909fa68 - main - iwm: clear persistence bit on 9000 series NICs

From: Adrian Chadd <adrian_at_FreeBSD.org>
Date: Mon, 12 Oct 2026 02:51:37 UTC
The branch main has been updated by adrian:

URL: https://cgit.FreeBSD.org/src/commit/?id=7ce67909fa683b22546b2ec8d635db1582b56bca

commit 7ce67909fa683b22546b2ec8d635db1582b56bca
Author:     Adrian Chadd <adrian@FreeBSD.org>
AuthorDate: 2026-10-12 02:50:50 +0000
Commit:     Adrian Chadd <adrian@FreeBSD.org>
CommitDate: 2026-10-12 02:50:50 +0000

    iwm: clear persistence bit on 9000 series NICs
    
    From what I've gathered, this is to make sure that the firmware
    clears state and is reloaded through hardware bring-up and
    suspend/resume.
    
    It may fix instability issues with the 9000 series NICs.
    I'll need to test it out first.
    
    Obtained from: OpenBSD a6e25c727a2214219715b51ba6af2221d7e0d674
    
    Differential Revision:  https://reviews.freebsd.org/D60078
---
 sys/dev/iwm/if_iwm_pcie_trans.c | 25 +++++++++++++++++++++++++
 sys/dev/iwm/if_iwm_pcie_trans.h |  1 +
 2 files changed, 26 insertions(+)

diff --git a/sys/dev/iwm/if_iwm_pcie_trans.c b/sys/dev/iwm/if_iwm_pcie_trans.c
index 96134b4c9d81..ce7526b0377d 100644
--- a/sys/dev/iwm/if_iwm_pcie_trans.c
+++ b/sys/dev/iwm/if_iwm_pcie_trans.c
@@ -620,6 +620,26 @@ iwm_apm_stop(struct iwm_softc *sc)
 	IWM_DPRINTF(sc, IWM_DEBUG_TRANS, "%s: iwm apm stop\n", __func__);
 }
 
+int
+iwm_clear_persistence_bit(struct iwm_softc *sc)
+{
+	uint32_t hpm, wprot;
+
+	hpm = iwm_read_prph(sc, IWM_HPM_DEBUG);
+	if (hpm != 0xa5a5a5a0 && (hpm & IWM_HPM_PERSISTENCE_BIT)) {
+		wprot = iwm_read_prph(sc, IWM_PREG_PRPH_WPROT_9000);
+		if (wprot & IWM_PREG_WFPM_ACCESS) {
+			device_printf(sc->sc_dev,
+			    "cannot clear persistence bit\n");
+			return EPERM;
+		}
+		iwm_write_prph(sc, IWM_HPM_DEBUG,
+		    hpm & ~IWM_HPM_PERSISTENCE_BIT);
+	}
+
+	return 0;
+}
+
 /* iwlwifi pcie/trans.c */
 int
 iwm_start_hw(struct iwm_softc *sc)
@@ -629,6 +649,11 @@ iwm_start_hw(struct iwm_softc *sc)
 	if ((error = iwm_prepare_card_hw(sc)) != 0)
 		return error;
 
+	if (sc->cfg->device_family == IWM_DEVICE_FAMILY_9000) {
+		if ((error = iwm_clear_persistence_bit(sc)) != 0)
+			return error;
+	}
+
 	/* Reset the entire device */
 	IWM_WRITE(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_SW_RESET);
 	DELAY(5000);
diff --git a/sys/dev/iwm/if_iwm_pcie_trans.h b/sys/dev/iwm/if_iwm_pcie_trans.h
index 8b00a3fd6a69..f55707e1c96f 100644
--- a/sys/dev/iwm/if_iwm_pcie_trans.h
+++ b/sys/dev/iwm/if_iwm_pcie_trans.h
@@ -128,6 +128,7 @@ extern	int iwm_prepare_card_hw(struct iwm_softc *sc);
 extern	void iwm_apm_config(struct iwm_softc *sc);
 extern	int iwm_apm_init(struct iwm_softc *sc);
 extern	void iwm_apm_stop(struct iwm_softc *sc);
+extern	int iwm_clear_persistence_bit(struct iwm_softc *sc);
 extern	int iwm_start_hw(struct iwm_softc *sc);
 extern	void iwm_set_pwr(struct iwm_softc *sc);
 extern	int iwm_pcie_rx_stop(struct iwm_softc *sc);