From: Johannes Berg Date: Sun, 17 May 2026 07:59:59 +0000 (+0300) Subject: wifi: iwlwifi: transport: add memory read under NIC access X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=4f479cbee68509ef56e4be310da93ec7883feb65;p=thirdparty%2Flinux.git wifi: iwlwifi: transport: add memory read under NIC access Add functions to be able to do memory read under NIC access, in order to use them later during firmware dump. These may drop and re-acquire the spinlock, but will not acquire and release the NIC access. Signed-off-by: Johannes Berg Link: https://patch.msgid.link/20260517100550.7bb1ea51c347.I91420a24fb0c481c75a2600d60e1365c15c1c5a9@changeid Signed-off-by: Miri Korenblit --- diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c index 5b44e15fe64d..0009488ca51b 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c @@ -459,6 +459,12 @@ int iwl_trans_read_mem(struct iwl_trans *trans, u32 addr, } IWL_EXPORT_SYMBOL(iwl_trans_read_mem); +int iwl_trans_read_mem_no_grab(struct iwl_trans *trans, u32 addr, + void *buf, u32 dwords) +{ + return iwl_trans_pcie_read_mem_no_grab(trans, addr, buf, dwords); +} + int iwl_trans_write_mem(struct iwl_trans *trans, u32 addr, const void *buf, int dwords) { diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 1ed6bcb7882c..3ae840e546e8 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -919,6 +919,14 @@ void iwl_trans_write_prph(struct iwl_trans *trans, u32 ofs, u32 val); int iwl_trans_read_mem(struct iwl_trans *trans, u32 addr, void *buf, int dwords); +/* + * Note the special calling convention - it's allowed to drop the + * internal transport lock and re-enable BHs temporarily, but will + * not release NIC access. + */ +int iwl_trans_read_mem_no_grab(struct iwl_trans *trans, u32 addr, + void *buf, u32 dwords); + int iwl_trans_read_config32(struct iwl_trans *trans, u32 ofs, u32 *val); @@ -934,6 +942,14 @@ void iwl_trans_debugfs_cleanup(struct iwl_trans *trans); (bufsize) / sizeof(u32)); \ }) +static inline int +iwl_trans_read_mem_bytes_no_grab(struct iwl_trans *trans, + u32 addr, void *buf, u32 bufsize) +{ + return iwl_trans_read_mem_no_grab(trans, addr, buf, + bufsize / sizeof(u32)); +} + int iwl_trans_write_imr_mem(struct iwl_trans *trans, u32 dst_addr, u64 src_addr, u32 byte_cnt); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h index 24f8714eae9f..abc0c831d1ca 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h @@ -1186,6 +1186,8 @@ u32 iwl_trans_pcie_read_prph(struct iwl_trans *trans, u32 reg); void iwl_trans_pcie_write_prph(struct iwl_trans *trans, u32 addr, u32 val); int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr, void *buf, int dwords); +int iwl_trans_pcie_read_mem_no_grab(struct iwl_trans *trans, u32 addr, + void *buf, u32 dwords); int iwl_trans_pcie_sw_reset(struct iwl_trans *trans, bool retake_ownership); struct iwl_trans_dump_data * iwl_trans_pcie_dump_data(struct iwl_trans *trans, u32 dump_mask, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c index 50342604491d..1c4ee76d8387 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c @@ -2424,6 +2424,15 @@ bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans) return false; } +static void iwl_trans_pcie_resched_with_nic_access(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + spin_unlock_bh(&trans_pcie->reg_lock); + cond_resched(); + spin_lock_bh(&trans_pcie->reg_lock); +} + void __releases(nic_access_nobh) iwl_trans_pcie_release_nic_access(struct iwl_trans *trans) { @@ -2506,6 +2515,51 @@ int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr, return 0; } +int iwl_trans_pcie_read_mem_no_grab(struct iwl_trans *trans, u32 addr, + void *buf, u32 dwords) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); +#define IWL_MAX_HW_ERRS 5 + unsigned int num_consec_hw_errors = 0; + u32 offs = 0; + u32 *vals = buf; + + lockdep_assert_held(&trans_pcie->reg_lock); + + while (offs < dwords) { + /* limit the time we spin here under lock to 1/2s */ + unsigned long end = jiffies + HZ / 2; + bool resched = false; + + iwl_write32(trans, HBUS_TARG_MEM_RADDR, + addr + 4 * offs); + + while (offs < dwords) { + vals[offs] = iwl_read32(trans, HBUS_TARG_MEM_RDAT); + + if (iwl_trans_is_hw_error_value(vals[offs])) + num_consec_hw_errors++; + else + num_consec_hw_errors = 0; + + if (num_consec_hw_errors >= IWL_MAX_HW_ERRS) + return -EIO; + + offs++; + + if (time_after(jiffies, end)) { + resched = true; + break; + } + } + + if (resched) + iwl_trans_pcie_resched_with_nic_access(trans); + } + + return 0; +} + int iwl_trans_pcie_read_config32(struct iwl_trans *trans, u32 ofs, u32 *val) {