On the USB devices a register read is a WMI round trip, and ath9k_regread() reports a timeout as -1. The spectral trigger reads AR_RX_FILTER and AR_PHY_ERR back through ath9k_hw_getrxfilter(), ORs in the PHY error bits and writes both registers. A timed-out read stores 0xffffffff in one of them, the device forwards every frame and PHY error to the host, and the full RX ring drops the FFT reports the scan was started for. Add ath9k_hw_enable_rxfilter(), which sets the requested filter bits, the matching AR_PHY_ERR bits and AR_RXCFG_ZLFDMA with REG_SET_BIT() and reads nothing back, and use it in the spectral trigger. It sets bits in AR_PHY_ERR where ath9k_hw_setrxfilter() overwrites the register. With firmware 1.4 and later REG_SET_BIT() is one read-modify-write done by the firmware; older firmware still reads from the host and is not helped. Fixes: e93d083f42a1 ("ath9k: add spectral scan feature") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-fable-5-1 Signed-off-by: Nerijus Bendžiūnas --- Changes in v2: - Describe the whole of ath9k_hw_enable_rxfilter(): it also sets the AR_PHY_ERR bits and AR_RXCFG_ZLFDMA, and sets bits in AR_PHY_ERR where ath9k_hw_setrxfilter() overwrites it. Say that the fix does not help firmware older than 1.4. - Add Assisted-by, rewrite the commit message, rebase onto ath-next. No code change. .../net/wireless/ath/ath9k/common-spectral.c | 7 ++--- drivers/net/wireless/ath/ath9k/hw.c | 29 +++++++++++++++++++ drivers/net/wireless/ath/ath9k/hw.h | 1 + 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/common-spectral.c b/drivers/net/wireless/ath/ath9k/common-spectral.c index ca01a07f6630..73c1eb4ebe0e 100644 --- a/drivers/net/wireless/ath/ath9k/common-spectral.c +++ b/drivers/net/wireless/ath/ath9k/common-spectral.c @@ -716,7 +716,6 @@ void ath9k_cmn_spectral_scan_trigger(struct ath_common *common, struct ath_spec_scan_priv *spec_priv) { struct ath_hw *ah = spec_priv->ah; - u32 rxfilter; if (IS_ENABLED(CONFIG_ATH9K_TX99)) return; @@ -730,10 +729,8 @@ void ath9k_cmn_spectral_scan_trigger(struct ath_common *common, return; ath_ps_ops(common)->wakeup(common); - rxfilter = ath9k_hw_getrxfilter(ah); - ath9k_hw_setrxfilter(ah, rxfilter | - ATH9K_RX_FILTER_PHYRADAR | - ATH9K_RX_FILTER_PHYERR); + ath9k_hw_enable_rxfilter(ah, ATH9K_RX_FILTER_PHYRADAR | + ATH9K_RX_FILTER_PHYERR); /* TODO: usually this should not be necessary, but for some reason * (or in some mode?) the trigger must be called after the diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index e08ab73fcacb..caa221ce0d32 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -2905,6 +2905,35 @@ void ath9k_hw_setrxfilter(struct ath_hw *ah, u32 bits) } EXPORT_SYMBOL(ath9k_hw_setrxfilter); +/* + * Turn extra bits on in the RX filter without reading the current value back. + * + * On USB a register read is a WMI round trip that can time out, and + * ath9k_regread() reports that failure as -1, which is indistinguishable from + * a genuine all-ones read. Feeding it into a read-modify-write stores + * 0xffffffff in AR_RX_FILTER, enabling every filter bit at once, and the + * device then floods the host with every frame and PHY error it sees. Set only + * the requested bits so that no read is involved. + */ +void ath9k_hw_enable_rxfilter(struct ath_hw *ah, u32 bits) +{ + u32 phybits = 0; + + if (bits & ATH9K_RX_FILTER_PHYRADAR) + phybits |= AR_PHY_ERR_RADAR; + if (bits & ATH9K_RX_FILTER_PHYERR) + phybits |= AR_PHY_ERR_OFDM_TIMING | AR_PHY_ERR_CCK_TIMING; + + REG_SET_BIT(ah, AR_RX_FILTER, bits); + + if (phybits) { + REG_SET_BIT(ah, AR_PHY_ERR, phybits); + /* PHY errors are reported in zero length frames. */ + REG_SET_BIT(ah, AR_RXCFG, AR_RXCFG_ZLFDMA); + } +} +EXPORT_SYMBOL(ath9k_hw_enable_rxfilter); + bool ath9k_hw_phy_disable(struct ath_hw *ah) { if (ath9k_hw_mci_is_enabled(ah)) diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index b942b8303d8f..f102f73a0114 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -1055,6 +1055,7 @@ void ath9k_hw_get_channel_centers(struct ath_hw *ah, struct chan_centers *centers); u32 ath9k_hw_getrxfilter(struct ath_hw *ah); void ath9k_hw_setrxfilter(struct ath_hw *ah, u32 bits); +void ath9k_hw_enable_rxfilter(struct ath_hw *ah, u32 bits); bool ath9k_hw_phy_disable(struct ath_hw *ah); bool ath9k_hw_disable(struct ath_hw *ah); void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit, bool test); base-commit: 1d8e73163ef933624341075f576e2f36ef9133f7 -- 2.55.0