[PATCH 0/4] wifi: ath9k: cut USB round trips on channel changes

From: Nerijus Bendžiūnas

Date: Sat Aug 29 2026 - 01:44:29 EST


Every register access on the ath9k_htc devices is a synchronous WMI round
trip, so a channel change that is nearly free on PCI costs tens of
milliseconds over USB. These four patches take round trips out of that
path without changing what the hardware is asked to do.

Patch 1 writes down the eight-register limit on REG_READ_MULTI() that
ath9k_multi_regread() has always had and never stated, so patch 2 can
rely on it. Patch 2 replaces the ten-queue ath9k_hw_numtxpending() poll
in ath9k_hw_channel_change() with two multi-reads. Patch 3 wraps the
read-modify-write runs in ar5008_hw_set_delta_slope() and
ath9k_hw_start_nfcal() in the RMW buffer. Patch 4 drops the departing
channel's noise-floor readout on a fast channel change over USB.

Patches 2 and 3 sit in code that PCI runs too, but there the multi-read
is a loop of ordinary reads and the RMW buffer callbacks are not
installed, so the register traffic is unchanged. Patch 4 is gated on
ATH_USB.

Measured on an AR9271 (0cf3:9271), counting WMI commands with a debug
counter added locally around the driver's channel change:

full reset, unpatched 182 commands ~92 ms
channel change with 1-4 44 commands ~31 ms

The queue poll alone was around twenty reads per change, and the two
read-modify-write runs another ten commands.

The second row also needs two changes that are not in this series:
taking the fast channel-change path on same-band retunes, which mainline
does only for off-channel scan hops, and a mac80211 fix so a monitor
retune performs one driver channel change instead of two. Both are
separate, and I am not asking for them here. What this series
contributes on its own is the round-trip reduction that those then
benefit from; mainline as it stands sees it on scan hops.

One note on how this was checked, because it changed the series. Two
further patches that also cut round trips, skipping the PCU
re-initialisation and the WMI_SET_MODE on a fast change, measured about
twice as fast again on a channel-switch latency benchmark. They also left
the receiver dead: ath9k_host_rx_init() is what clears AR_DIAG_RX_DIS and
AR_DIAG_RX_ABORT, and without it the radio retunes correctly and hears
nothing. The latency benchmark could not see that, since it only timed
the tuning. What caught it was two AR9271s coupled over coax, one
injecting raw frames and the other capturing. Those two patches are
therefore not here. With patches 1-4 in place, the fast path delivered
90-97% of injected frames on the standard 2.4 GHz channels across
repeated runs, against 95-97% for a full reset on the same build. The
spread is the rig, not the patches: the same binary can read differently
minutes apart, and the fast path shows that drift first because it does
not recalibrate, which is the reason mainline limits it to scan hops.
Those numbers are from cards on root ports. With the receiving card two
USB hub tiers deep, fast-path reception was unreliable with and without
these patches while full resets were unaffected, so that is a property
of the fast path on this hardware rather than of the series.

Nerijus Bendžiūnas (4):
wifi: ath9k: name the register multi-read limit
wifi: ath9k: check all tx queues with one multi-read
wifi: ath9k: batch the read-modify-writes of a channel change
wifi: ath9k: skip the departing channel's noise floor on USB fast
changes

drivers/net/wireless/ath/ath9k/ar5008_phy.c | 2 ++
drivers/net/wireless/ath/ath9k/calib.c | 2 ++
drivers/net/wireless/ath/ath9k/htc_drv_init.c | 7 ++--
drivers/net/wireless/ath/ath9k/hw.c | 21 ++++++-----
drivers/net/wireless/ath/ath9k/hw.h | 7 ++++
drivers/net/wireless/ath/ath9k/mac.c | 36 +++++++++++++++++++
drivers/net/wireless/ath/ath9k/mac.h | 1 +
7 files changed, 66 insertions(+), 10 deletions(-)


base-commit: ca800a9302764c445de0da0e84d2252400a770ee
--
2.55.0