1: 236f85ca5b53 ! 1: 22344f2fa3f1 wifi: b43: keep the N-PHY TX power control from drifting while idle @@ Commit message load bit is set as frames go out, so keep it set until OFDM frames have been sent for a while. Until then, the base indices stay where they were loaded instead of drifting. Forget the indices when the power - control is set up again or the channel changes. + control is set up again or the channel changes. Only sample them once + OFDM frames have been sent without a break for 200 ms, so the loop has + settled first, and suspend the MAC around the register accesses.  With this, the base indices stay at 75-79 over idle periods, and all pings after them get through. Throughput is unchanged. @@ drivers/net/wireless/broadcom/b43/main.c: static void b43_tx_work(struct work_st else  ## drivers/net/wireless/broadcom/b43/phy_n.c ## -@@ drivers/net/wireless/broadcom/b43/phy_n.c: static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable) - b43_nphy_stay_in_carrier_search(dev, 1); +@@ drivers/net/wireless/broadcom/b43/phy_n.c: static void b43_nphy_iq_cal_gain_params(struct b43_wldev *dev, u16 core, + * Tx and Rx + **************************************************/  - nphy->txpwrctrl = enable; ++/* ++ * Forget the remembered TX power control base indices. Also move the last ++ * OFDM transmission into the past, so the next one is not taken as part of ++ * ongoing traffic: jiffies starts at -300 s on 32-bit, so a zero timestamp ++ * would look recent for the first five minutes after boot. ++ */ ++static void b43_nphy_txpctl_bidx_reset(struct b43_wldev *dev) ++{ ++ struct b43_phy_n *nphy = dev->phy.n; ++  + nphy->txpctl_bidx_valid = false; ++ nphy->txpctl_last_tx = jiffies - HZ;  + if (nphy->txpctl_bidx_loaded) {  + b43_phy_write(dev, B43_NPHY_TXPCTL_BIDX, 0);  + nphy->txpctl_bidx_loaded = false;  + } ++} ++ + /* https://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrCtrlEnable */ + static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable) + { +@@ drivers/net/wireless/broadcom/b43/phy_n.c: static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable) + b43_nphy_stay_in_carrier_search(dev, 1); +  + nphy->txpwrctrl = enable; ++ b43_nphy_txpctl_bidx_reset(dev); if (!enable) { if (dev->phy.rev >= 3 && (b43_phy_read(dev, B43_NPHY_TXPCTL_CMD) & @@ drivers/net/wireless/broadcom/b43/phy_n.c: static void b43_nphy_channel_setup(st  + * them back when sending starts again after a pause. The base indices  + * only take the loaded values while the load bit is set as frames go  + * out, so it stays set until frames have been sent for a while. Until -+ * then, the base indices stay where they were loaded. ++ * then, the base indices stay where they were loaded. Only sample them ++ * once OFDM frames have been sent without a break for a while, so the ++ * loop has settled on them first. ++ * ++ * The MAC is suspended only around the register accesses, which happen ++ * at most every 20 ms while frames are sent.  + */  +void b43_nphy_txpctl_ofdm_tx(struct b43_wldev *dev)  +{  + struct b43_phy_n *nphy = dev->phy.n;  + unsigned long now = jiffies; ++ bool load = false, unload = false, sample = false;  + u16 c1, c2;  +  + if (dev->phy.rev < 3 || !nphy->txpwrctrl)  + return;  +  + if (nphy->txpctl_bidx_valid && -+ time_after(now, nphy->txpctl_last_tx + HZ / 2)) { ++ time_after(now, nphy->txpctl_last_tx + HZ / 2)) ++ load = true; ++ else if (nphy->txpctl_bidx_loaded) ++ unload = time_after(now, nphy->txpctl_last_sample + HZ / 50); ++ else if (time_after_eq(now, nphy->txpctl_last_tx + HZ / 20)) ++ /* Traffic starts again, give the loop time to settle */ ++ nphy->txpctl_last_sample = now; ++ else ++ sample = time_after(now, nphy->txpctl_last_sample + HZ / 5); ++ ++ nphy->txpctl_last_tx = now; ++ if (!load && !unload && !sample) ++ return; ++ ++ b43_mac_suspend(dev); ++ if (nphy->hang_avoid) ++ b43_nphy_stay_in_carrier_search(dev, 1); ++ ++ if (load) {  + b43_phy_write(dev, B43_NPHY_TXPCTL_BIDX,  + B43_NPHY_TXPCTL_BIDX_LOAD | nphy->txpctl_bidx);  + nphy->txpctl_bidx_loaded = true; -+ nphy->txpctl_last_sample = now; -+ } else if (nphy->txpctl_bidx_loaded) { -+ if (time_after(now, nphy->txpctl_last_sample + HZ / 50)) { -+ b43_phy_write(dev, B43_NPHY_TXPCTL_BIDX, -+ nphy->txpctl_bidx); -+ nphy->txpctl_bidx_loaded = false; -+ nphy->txpctl_last_sample = now; -+ } -+ } else if (time_before(now, nphy->txpctl_last_tx + HZ / 20) && -+ (!nphy->txpctl_bidx_valid || -+ time_after(now, nphy->txpctl_last_sample + HZ / 5))) { ++ } else if (unload) { ++ b43_phy_write(dev, B43_NPHY_TXPCTL_BIDX, nphy->txpctl_bidx); ++ nphy->txpctl_bidx_loaded = false; ++ } else {  + c1 = b43_phy_read(dev, B43_NPHY_C1_TXPCTL_STAT);  + c2 = b43_phy_read(dev, B43_NPHY_C2_TXPCTL_STAT);  + nphy->txpctl_bidx = (c1 & B43_NPHY_TXPCTL_STAT_BIDX) >>  + B43_NPHY_TXPCTL_STAT_BIDX_SHIFT;  + nphy->txpctl_bidx |= c2 & B43_NPHY_TXPCTL_STAT_BIDX;  + nphy->txpctl_bidx_valid = true; -+ nphy->txpctl_last_sample = now;  + }  + -+ nphy->txpctl_last_tx = now; ++ if (nphy->hang_avoid) ++ b43_nphy_stay_in_carrier_search(dev, 0); ++ b43_mac_enable(dev); ++ ++ nphy->txpctl_last_sample = now;  +}  + /* https://bcm-v4.sipsolutions.net/802.11/PHY/N/SetChanspec */ @@ drivers/net/wireless/broadcom/b43/phy_n.c: static int b43_nphy_set_channel(struc /* Channel is set later in common code, but we need to set it on our own to let this function's subcalls work properly. */ phy->channel = channel->hw_value; -+ dev->phy.n->txpctl_bidx_valid = false; -+ if (dev->phy.n->txpctl_bidx_loaded) { -+ b43_phy_write(dev, B43_NPHY_TXPCTL_BIDX, 0); -+ dev->phy.n->txpctl_bidx_loaded = false; -+ } ++ b43_nphy_txpctl_bidx_reset(dev);  #if 0 if (b43_channel_type_is_40mhz(phy->channel_type) != @@ drivers/net/wireless/broadcom/b43/phy_n.h: struct b43_phy_n { struct b43_phy_operations; extern const struct b43_phy_operations b43_phyops_n;  ++#ifdef CONFIG_B43_PHY_N  +void b43_nphy_txpctl_ofdm_tx(struct b43_wldev *dev); ++#else ++static inline void b43_nphy_txpctl_ofdm_tx(struct b43_wldev *dev) ++{ ++} ++#endif  + #endif /* B43_NPHY_H_ */