Commit aea38272 authored by Felix Fietkau's avatar Felix Fietkau Committed by Kalle Valo

mt76: fix beacon timer drift

The beacon timer drifts by 1 microsecond every TBTT. After 20 minutes
with a beacon interval of 100, the drift will be almost 12 ms, enough to
cause weird issues for devices in powersave mode.

Since the beacon timer is configured in units of 1/16 TU (64 us), we
need to adjust it once every 64 beacons and only for one beacon.
Signed-off-by: default avatarFelix Fietkau <nbd@nbd.name>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent 81454b84
...@@ -120,10 +120,13 @@ struct mt76x2_dev { ...@@ -120,10 +120,13 @@ struct mt76x2_dev {
u8 beacon_mask; u8 beacon_mask;
u8 beacon_data_mask; u8 beacon_data_mask;
u32 rxfilter; u8 tbtt_count;
u16 beacon_int;
u16 chainmask; u16 chainmask;
u32 rxfilter;
struct mt76x2_calibration cal; struct mt76x2_calibration cal;
s8 target_power; s8 target_power;
......
...@@ -238,10 +238,13 @@ mt76x2_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, ...@@ -238,10 +238,13 @@ mt76x2_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
if (changed & BSS_CHANGED_BSSID) if (changed & BSS_CHANGED_BSSID)
mt76x2_mac_set_bssid(dev, mvif->idx, info->bssid); mt76x2_mac_set_bssid(dev, mvif->idx, info->bssid);
if (changed & BSS_CHANGED_BEACON_INT) if (changed & BSS_CHANGED_BEACON_INT) {
mt76_rmw_field(dev, MT_BEACON_TIME_CFG, mt76_rmw_field(dev, MT_BEACON_TIME_CFG,
MT_BEACON_TIME_CFG_INTVAL, MT_BEACON_TIME_CFG_INTVAL,
info->beacon_int << 4); info->beacon_int << 4);
dev->beacon_int = info->beacon_int;
dev->tbtt_count = 0;
}
if (changed & BSS_CHANGED_BEACON_ENABLED) { if (changed & BSS_CHANGED_BEACON_ENABLED) {
tasklet_disable(&dev->pre_tbtt_tasklet); tasklet_disable(&dev->pre_tbtt_tasklet);
......
...@@ -218,6 +218,37 @@ mt76x2_add_buffered_bc(void *priv, u8 *mac, struct ieee80211_vif *vif) ...@@ -218,6 +218,37 @@ mt76x2_add_buffered_bc(void *priv, u8 *mac, struct ieee80211_vif *vif)
data->tail[mvif->idx] = skb; data->tail[mvif->idx] = skb;
} }
static void
mt76x2_resync_beacon_timer(struct mt76x2_dev *dev)
{
u32 timer_val = dev->beacon_int << 4;
dev->tbtt_count++;
/*
* Beacon timer drifts by 1us every tick, the timer is configured
* in 1/16 TU (64us) units.
*/
if (dev->tbtt_count < 62)
return;
if (dev->tbtt_count >= 64) {
dev->tbtt_count = 0;
return;
}
/*
* The updated beacon interval takes effect after two TBTT, because
* at this point the original interval has already been loaded into
* the next TBTT_TIMER value
*/
if (dev->tbtt_count == 62)
timer_val -= 1;
mt76_rmw_field(dev, MT_BEACON_TIME_CFG,
MT_BEACON_TIME_CFG_INTVAL, timer_val);
}
void mt76x2_pre_tbtt_tasklet(unsigned long arg) void mt76x2_pre_tbtt_tasklet(unsigned long arg)
{ {
struct mt76x2_dev *dev = (struct mt76x2_dev *) arg; struct mt76x2_dev *dev = (struct mt76x2_dev *) arg;
...@@ -226,6 +257,8 @@ void mt76x2_pre_tbtt_tasklet(unsigned long arg) ...@@ -226,6 +257,8 @@ void mt76x2_pre_tbtt_tasklet(unsigned long arg)
struct sk_buff *skb; struct sk_buff *skb;
int i, nframes; int i, nframes;
mt76x2_resync_beacon_timer(dev);
data.dev = dev; data.dev = dev;
__skb_queue_head_init(&data.q); __skb_queue_head_init(&data.q);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment