From 7e3c4194f8b2b3ad31a69e6a05170f4de6b469c7 Mon Sep 17 00:00:00 2001 From: stsp Date: Fri, 9 Jul 2021 11:04:05 +0000 Subject: [PATCH] Clear the "persistence bit" on iwm(4) 9k devices during hardware init. According to iwlwifi commit messages this fixes an edge case where 9k family devices fail to resume after system suspend. See Linux commit 8954e1eb2270fa2effffd031b4839253952c76f2 --- sys/dev/pci/if_iwm.c | 51 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/sys/dev/pci/if_iwm.c b/sys/dev/pci/if_iwm.c index 40a03cb68d3..412e6ef8340 100644 --- a/sys/dev/pci/if_iwm.c +++ b/sys/dev/pci/if_iwm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_iwm.c,v 1.346 2021/07/09 10:46:56 stsp Exp $ */ +/* $OpenBSD: if_iwm.c,v 1.347 2021/07/09 11:04:05 stsp Exp $ */ /* * Copyright (c) 2014, 2016 genua gmbh @@ -254,7 +254,9 @@ int iwm_firmware_store_section(struct iwm_softc *, enum iwm_ucode_type, int iwm_set_default_calib(struct iwm_softc *, const void *); void iwm_fw_info_free(struct iwm_fw_info *); int iwm_read_firmware(struct iwm_softc *, enum iwm_ucode_type); +uint32_t iwm_read_prph_unlocked(struct iwm_softc *, uint32_t); uint32_t iwm_read_prph(struct iwm_softc *, uint32_t); +void iwm_write_prph_unlocked(struct iwm_softc *, uint32_t, uint32_t); void iwm_write_prph(struct iwm_softc *, uint32_t, uint32_t); int iwm_read_mem(struct iwm_softc *, uint32_t, void *, int); int iwm_write_mem(struct iwm_softc *, uint32_t, const void *, int); @@ -292,6 +294,7 @@ void iwm_apm_stop(struct iwm_softc *); int iwm_allow_mcast(struct iwm_softc *); void iwm_init_msix_hw(struct iwm_softc *); void iwm_conf_msix_hw(struct iwm_softc *, int); +int iwm_clear_persistence_bit(struct iwm_softc *); int iwm_start_hw(struct iwm_softc *); void iwm_stop_device(struct iwm_softc *); void iwm_nic_config(struct iwm_softc *); @@ -1006,25 +1009,37 @@ iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) } uint32_t -iwm_read_prph(struct iwm_softc *sc, uint32_t addr) +iwm_read_prph_unlocked(struct iwm_softc *sc, uint32_t addr) { - iwm_nic_assert_locked(sc); IWM_WRITE(sc, IWM_HBUS_TARG_PRPH_RADDR, ((addr & 0x000fffff) | (3 << 24))); IWM_BARRIER_READ_WRITE(sc); return IWM_READ(sc, IWM_HBUS_TARG_PRPH_RDAT); } -void -iwm_write_prph(struct iwm_softc *sc, uint32_t addr, uint32_t val) +uint32_t +iwm_read_prph(struct iwm_softc *sc, uint32_t addr) { iwm_nic_assert_locked(sc); + return iwm_read_prph_unlocked(sc, addr); +} + +void +iwm_write_prph_unlocked(struct iwm_softc *sc, uint32_t addr, uint32_t val) +{ IWM_WRITE(sc, IWM_HBUS_TARG_PRPH_WADDR, ((addr & 0x000fffff) | (3 << 24))); IWM_BARRIER_WRITE(sc); IWM_WRITE(sc, IWM_HBUS_TARG_PRPH_WDAT, val); } +void +iwm_write_prph(struct iwm_softc *sc, uint32_t addr, uint32_t val) +{ + iwm_nic_assert_locked(sc); + iwm_write_prph_unlocked(sc, addr, val); +} + void iwm_write_prph64(struct iwm_softc *sc, uint64_t addr, uint64_t val) { @@ -1961,6 +1976,26 @@ iwm_conf_msix_hw(struct iwm_softc *sc, int stopped) IWM_MSIX_HW_INT_CAUSES_REG_HAP); } +int +iwm_clear_persistence_bit(struct iwm_softc *sc) +{ + uint32_t hpm, wprot; + + hpm = iwm_read_prph_unlocked(sc, IWM_HPM_DEBUG); + if (hpm != 0xa5a5a5a0 && (hpm & IWM_HPM_PERSISTENCE_BIT)) { + wprot = iwm_read_prph_unlocked(sc, IWM_PREG_PRPH_WPROT_9000); + if (wprot & IWM_PREG_WFPM_ACCESS) { + printf("%s: cannot clear persistence bit\n", + DEVNAME(sc)); + return EPERM; + } + iwm_write_prph_unlocked(sc, IWM_HPM_DEBUG, + hpm & ~IWM_HPM_PERSISTENCE_BIT); + } + + return 0; +} + int iwm_start_hw(struct iwm_softc *sc) { @@ -1970,6 +2005,12 @@ iwm_start_hw(struct iwm_softc *sc) if (err) return err; + if (sc->sc_device_family == IWM_DEVICE_FAMILY_9000) { + err = iwm_clear_persistence_bit(sc); + if (err) + return err; + } + /* Reset the entire device */ IWM_WRITE(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_SW_RESET); DELAY(5000); -- 2.20.1