1: eb957e6001a3 = 1: 632463064df6 wifi: rtw88: download the beacon the reserved page was built with 2: 890829377d95 ! 2: 0814ec6146e7 wifi: rtw88: support channel switch in AP mode @@ Commit message the AP off the channel its stations count down to.  Signed-off-by: Mehmet Fide  + Acked-by: Ping-Ke Shih   ## drivers/net/wireless/realtek/rtw88/fw.c ##  @@ drivers/net/wireless/realtek/rtw88/fw.c: int rtw_fw_download_rsvd_page(struct rtw_dev *rtwdev) 3: 15aa0735aa4b ! 3: afb1d169efec wifi: rtw88: time the countdown downloads from the TBTT @@ drivers/net/wireless/realtek/rtw88/fw.c: bool rtw_fw_csa_active(struct rtw_dev * }   -/* paced from the first download: a late one does not delay the next */ +-static void rtw_fw_csa_queue(struct rtw_dev *rtwdev, struct ieee80211_vif *vif)  +#define RTW_CSA_TBTT_MARGIN_US 10000  + -+/* the beaconing interface is always on port 0 */  +static u64 rtw_fw_read_tsf(struct rtw_dev *rtwdev)  +{ -+ u32 hi, lo; ++ u32 hi, lo, tmp;  + -+ do { -+ hi = rtw_read32(rtwdev, REG_TSFTR + 4); ++ hi = rtw_read32(rtwdev, REG_TSFTR + 4); ++ lo = rtw_read32(rtwdev, REG_TSFTR); ++ tmp = rtw_read32(rtwdev, REG_TSFTR + 4); ++ if (tmp != hi) { ++ hi = tmp;  + lo = rtw_read32(rtwdev, REG_TSFTR); -+ } while (hi != rtw_read32(rtwdev, REG_TSFTR + 4)); ++ }  +  + return ((u64)hi << 32) | lo;  +}  + - static void rtw_fw_csa_queue(struct rtw_dev *rtwdev, struct ieee80211_vif *vif) ++static void rtw_fw_csa_queue(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, ++ bool start) { u32 interval = ieee80211_tu_to_usec(vif->bss_conf.beacon_int);  + u64 tsf = rtw_fw_read_tsf(rtwdev); s64 delay;  + u32 rem; -  -- rtwdev->csa_due = ktime_add_us(rtwdev->csa_due, interval); -- delay = ktime_us_delta(rtwdev->csa_due, ktime_get()); -+ rtwdev->csa_tbtt += interval; ++ ++ if (start) { ++ div_u64_rem(tsf, interval, &rem); ++ rtwdev->csa_tbtt = tsf - rem + interval; ++ /* a TBTT just passed may have sent the first count */ ++ if (rem < RTW_CSA_TBTT_MARGIN_US) ++ rtwdev->csa_tbtt -= interval; ++ } else { ++ rtwdev->csa_tbtt += interval; ++ } ++  + delay = rtwdev->csa_tbtt + RTW_CSA_TBTT_MARGIN_US - tsf;  + if (delay > interval + RTW_CSA_TBTT_MARGIN_US ||  + delay < -(s64)interval) { @@ drivers/net/wireless/realtek/rtw88/fw.c: bool rtw_fw_csa_active(struct rtw_dev *  + rtwdev->csa_tbtt = tsf - rem + interval;  + delay = rtwdev->csa_tbtt + RTW_CSA_TBTT_MARGIN_US - tsf;  + } +  +- rtwdev->csa_due = ktime_add_us(rtwdev->csa_due, interval); +- delay = ktime_us_delta(rtwdev->csa_due, ktime_get()); ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->csa_beacon_work, usecs_to_jiffies(max(delay, 0))); } -  - void rtw_fw_csa_start(struct rtw_dev *rtwdev, struct ieee80211_vif *vif) - { -+ u32 interval = ieee80211_tu_to_usec(vif->bss_conf.beacon_int); -+ u32 rem; -+ - lockdep_assert_held(&rtwdev->mutex); -  - if (rtwdev->csa_vif == vif) +@@ drivers/net/wireless/realtek/rtw88/fw.c: void rtw_fw_csa_start(struct rtw_dev *rtwdev, struct ieee80211_vif *vif) return;  rtwdev->csa_vif = vif;  - rtwdev->csa_due = ktime_get(); -+ rtwdev->csa_tbtt = rtw_fw_read_tsf(rtwdev); -+ div_u64_rem(rtwdev->csa_tbtt, interval, &rem); -+ rtwdev->csa_tbtt -= rem; -+ /* a TBTT that just passed may have sent the first count already */ -+ if (rem < RTW_CSA_TBTT_MARGIN_US) -+ rtwdev->csa_tbtt -= interval; - rtw_fw_csa_queue(rtwdev, vif); +- rtw_fw_csa_queue(rtwdev, vif); ++ ++ rtw_fw_csa_queue(rtwdev, vif, true); }  + void rtw_fw_csa_stop(struct rtw_dev *rtwdev, struct ieee80211_vif *vif) +@@ drivers/net/wireless/realtek/rtw88/fw.c: void rtw_fw_csa_beacon_work(struct work_struct *work) + if (!ieee80211_beacon_cntdwn_is_complete(vif, 0)) { + rtw_fw_download_rsvd_page(rtwdev); + rtw_send_rsvd_page_h2c(rtwdev); +- rtw_fw_csa_queue(rtwdev, vif); ++ rtw_fw_csa_queue(rtwdev, vif, false); + } else { + ieee80211_csa_finish(vif, 0); + }  ## drivers/net/wireless/realtek/rtw88/main.h ##  @@ drivers/net/wireless/realtek/rtw88/main.h: struct rtw_dev {