Commit dd347f2f authored by Felix Fietkau's avatar Felix Fietkau Committed by John W. Linville

ath9k: fix beacon timer handling issues

AP mode beacon timers in ath9k are configured in milliseconds, which breaks
when increasing ATH_BCBUF to 8 instead of 4 (due to rounding errors).
Since the hardware timers are actually configured in microseconds, it's
better to let the driver use that unit directly.

To be able to do that, the beacon interval parameter abuse for passing
certain flags needs to be removed. This is easy to do, because those flags
are completely unnecessary anyway. ATH9K_BEACON_ENA is ignored,
ATH9K_BEACON_RESET_TSF can be replaced with calling ath9k_hw_reset_tsf
from the driver directly.
Signed-off-by: default avatarFelix Fietkau <nbd@openwrt.org>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent f39de992
...@@ -386,7 +386,7 @@ struct ath_beacon { ...@@ -386,7 +386,7 @@ struct ath_beacon {
u32 beaconq; u32 beaconq;
u32 bmisscnt; u32 bmisscnt;
u32 ast_be_xmit; u32 ast_be_xmit;
u64 bc_tstamp; u32 bc_tstamp;
struct ieee80211_vif *bslot[ATH_BCBUF]; struct ieee80211_vif *bslot[ATH_BCBUF];
int slottime; int slottime;
int slotupdate; int slotupdate;
......
...@@ -57,8 +57,8 @@ int ath_beaconq_config(struct ath_softc *sc) ...@@ -57,8 +57,8 @@ int ath_beaconq_config(struct ath_softc *sc)
/* /*
* Associates the beacon frame buffer with a transmit descriptor. Will set * Associates the beacon frame buffer with a transmit descriptor. Will set
* up all required antenna switch parameters, rate codes, and channel flags. * up rate codes, and channel flags. Beacons are always sent out at the
* Beacons are always sent out at the lowest rate, and are not retried. * lowest rate, and are not retried.
*/ */
static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp, static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp,
struct ath_buf *bf, int rateidx) struct ath_buf *bf, int rateidx)
...@@ -68,7 +68,7 @@ static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp, ...@@ -68,7 +68,7 @@ static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp,
struct ath_common *common = ath9k_hw_common(ah); struct ath_common *common = ath9k_hw_common(ah);
struct ath_desc *ds; struct ath_desc *ds;
struct ath9k_11n_rate_series series[4]; struct ath9k_11n_rate_series series[4];
int flags, antenna, ctsrate = 0, ctsduration = 0; int flags, ctsrate = 0, ctsduration = 0;
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
u8 rate = 0; u8 rate = 0;
...@@ -76,12 +76,6 @@ static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp, ...@@ -76,12 +76,6 @@ static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp,
flags = ATH9K_TXDESC_NOACK; flags = ATH9K_TXDESC_NOACK;
ds->ds_link = 0; ds->ds_link = 0;
/*
* Switch antenna every beacon.
* Should only switch every beacon period, not for every SWBA
* XXX assumes two antennae
*/
antenna = ((sc->beacon.ast_be_xmit / sc->nbcnvifs) & 1 ? 2 : 1);
sband = &sc->sbands[common->hw->conf.channel->band]; sband = &sc->sbands[common->hw->conf.channel->band];
rate = sband->bitrates[rateidx].hw_value; rate = sband->bitrates[rateidx].hw_value;
...@@ -278,7 +272,7 @@ int ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_vif *vif) ...@@ -278,7 +272,7 @@ int ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_vif *vif)
return -ENOMEM; return -ENOMEM;
tstamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp; tstamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp;
sc->beacon.bc_tstamp = le64_to_cpu(tstamp); sc->beacon.bc_tstamp = (u32) le64_to_cpu(tstamp);
/* Calculate a TSF adjustment factor required for staggered beacons. */ /* Calculate a TSF adjustment factor required for staggered beacons. */
if (avp->av_bslot > 0) { if (avp->av_bslot > 0) {
u64 tsfadjust; u64 tsfadjust;
...@@ -294,8 +288,8 @@ int ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_vif *vif) ...@@ -294,8 +288,8 @@ int ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_vif *vif)
* adjustment. Other slots are adjusted to get the timestamp * adjustment. Other slots are adjusted to get the timestamp
* close to the TBTT for the BSS. * close to the TBTT for the BSS.
*/ */
tsfadjust = intval * avp->av_bslot / ATH_BCBUF; tsfadjust = TU_TO_USEC(intval * avp->av_bslot) / ATH_BCBUF;
avp->tsf_adjust = cpu_to_le64(TU_TO_USEC(tsfadjust)); avp->tsf_adjust = cpu_to_le64(tsfadjust);
ath_dbg(common, ATH_DBG_BEACON, ath_dbg(common, ATH_DBG_BEACON,
"stagger beacons, bslot %d intval %u tsfadjust %llu\n", "stagger beacons, bslot %d intval %u tsfadjust %llu\n",
...@@ -401,8 +395,9 @@ void ath_beacon_tasklet(unsigned long data) ...@@ -401,8 +395,9 @@ void ath_beacon_tasklet(unsigned long data)
intval = cur_conf->beacon_interval ? : ATH_DEFAULT_BINTVAL; intval = cur_conf->beacon_interval ? : ATH_DEFAULT_BINTVAL;
tsf = ath9k_hw_gettsf64(ah); tsf = ath9k_hw_gettsf64(ah);
tsftu = TSF_TO_TU(tsf>>32, tsf); tsf += TU_TO_USEC(ah->config.sw_beacon_response_time);
slot = ((tsftu % intval) * ATH_BCBUF) / intval; tsftu = TSF_TO_TU((tsf * ATH_BCBUF) >>32, tsf * ATH_BCBUF);
slot = (tsftu % (intval * ATH_BCBUF)) / intval;
/* /*
* Reverse the slot order to get slot 0 on the TBTT offset that does * Reverse the slot order to get slot 0 on the TBTT offset that does
* not require TSF adjustment and other slots adding * not require TSF adjustment and other slots adding
...@@ -415,7 +410,7 @@ void ath_beacon_tasklet(unsigned long data) ...@@ -415,7 +410,7 @@ void ath_beacon_tasklet(unsigned long data)
ath_dbg(common, ATH_DBG_BEACON, ath_dbg(common, ATH_DBG_BEACON,
"slot %d [tsf %llu tsftu %u intval %u] vif %p\n", "slot %d [tsf %llu tsftu %u intval %u] vif %p\n",
slot, tsf, tsftu, intval, vif); slot, tsf, tsftu / ATH_BCBUF, intval, vif);
bfaddr = 0; bfaddr = 0;
if (vif) { if (vif) {
...@@ -463,13 +458,17 @@ static void ath9k_beacon_init(struct ath_softc *sc, ...@@ -463,13 +458,17 @@ static void ath9k_beacon_init(struct ath_softc *sc,
u32 next_beacon, u32 next_beacon,
u32 beacon_period) u32 beacon_period)
{ {
if (beacon_period & ATH9K_BEACON_RESET_TSF) if (sc->sc_flags & SC_OP_TSF_RESET) {
ath9k_ps_wakeup(sc); ath9k_ps_wakeup(sc);
ath9k_hw_reset_tsf(sc->sc_ah);
}
ath9k_hw_beaconinit(sc->sc_ah, next_beacon, beacon_period); ath9k_hw_beaconinit(sc->sc_ah, next_beacon, beacon_period);
if (beacon_period & ATH9K_BEACON_RESET_TSF) if (sc->sc_flags & SC_OP_TSF_RESET) {
ath9k_ps_restore(sc); ath9k_ps_restore(sc);
sc->sc_flags &= ~SC_OP_TSF_RESET;
}
} }
/* /*
...@@ -484,18 +483,14 @@ static void ath_beacon_config_ap(struct ath_softc *sc, ...@@ -484,18 +483,14 @@ static void ath_beacon_config_ap(struct ath_softc *sc,
u32 nexttbtt, intval; u32 nexttbtt, intval;
/* NB: the beacon interval is kept internally in TU's */ /* NB: the beacon interval is kept internally in TU's */
intval = conf->beacon_interval & ATH9K_BEACON_PERIOD; intval = TU_TO_USEC(conf->beacon_interval & ATH9K_BEACON_PERIOD);
intval /= ATH_BCBUF; /* for staggered beacons */ intval /= ATH_BCBUF; /* for staggered beacons */
nexttbtt = intval; nexttbtt = intval;
if (sc->sc_flags & SC_OP_TSF_RESET)
intval |= ATH9K_BEACON_RESET_TSF;
/* /*
* In AP mode we enable the beacon timers and SWBA interrupts to * In AP mode we enable the beacon timers and SWBA interrupts to
* prepare beacon frames. * prepare beacon frames.
*/ */
intval |= ATH9K_BEACON_ENA;
ah->imask |= ATH9K_INT_SWBA; ah->imask |= ATH9K_INT_SWBA;
ath_beaconq_config(sc); ath_beaconq_config(sc);
...@@ -505,11 +500,6 @@ static void ath_beacon_config_ap(struct ath_softc *sc, ...@@ -505,11 +500,6 @@ static void ath_beacon_config_ap(struct ath_softc *sc,
ath9k_beacon_init(sc, nexttbtt, intval); ath9k_beacon_init(sc, nexttbtt, intval);
sc->beacon.bmisscnt = 0; sc->beacon.bmisscnt = 0;
ath9k_hw_set_interrupts(ah, ah->imask); ath9k_hw_set_interrupts(ah, ah->imask);
/* Clear the reset TSF flag, so that subsequent beacon updation
will not reset the HW TSF. */
sc->sc_flags &= ~SC_OP_TSF_RESET;
} }
/* /*
...@@ -643,25 +633,20 @@ static void ath_beacon_config_adhoc(struct ath_softc *sc, ...@@ -643,25 +633,20 @@ static void ath_beacon_config_adhoc(struct ath_softc *sc,
{ {
struct ath_hw *ah = sc->sc_ah; struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah); struct ath_common *common = ath9k_hw_common(ah);
u64 tsf; u32 tsf, delta, intval, nexttbtt;
u32 tsftu, intval, nexttbtt;
intval = conf->beacon_interval & ATH9K_BEACON_PERIOD;
tsf = ath9k_hw_gettsf32(ah) + TU_TO_USEC(FUDGE);
intval = TU_TO_USEC(conf->beacon_interval & ATH9K_BEACON_PERIOD);
/* Pull nexttbtt forward to reflect the current TSF */ if (!sc->beacon.bc_tstamp)
nexttbtt = tsf + intval;
nexttbtt = TSF_TO_TU(sc->beacon.bc_tstamp >> 32, sc->beacon.bc_tstamp); else {
if (nexttbtt == 0) if (tsf > sc->beacon.bc_tstamp)
nexttbtt = intval; delta = (tsf - sc->beacon.bc_tstamp);
else if (intval) else
nexttbtt = roundup(nexttbtt, intval); delta = (tsf + 1 + (~0U - sc->beacon.bc_tstamp));
nexttbtt = tsf + roundup(delta, intval);
tsf = ath9k_hw_gettsf64(ah); }
tsftu = TSF_TO_TU((u32)(tsf>>32), (u32)tsf) + FUDGE;
do {
nexttbtt += intval;
} while (nexttbtt < tsftu);
ath_dbg(common, ATH_DBG_BEACON, ath_dbg(common, ATH_DBG_BEACON,
"IBSS nexttbtt %u intval %u (%u)\n", "IBSS nexttbtt %u intval %u (%u)\n",
...@@ -672,7 +657,6 @@ static void ath_beacon_config_adhoc(struct ath_softc *sc, ...@@ -672,7 +657,6 @@ static void ath_beacon_config_adhoc(struct ath_softc *sc,
* if we need to manually prepare beacon frames. Otherwise we use a * if we need to manually prepare beacon frames. Otherwise we use a
* self-linked tx descriptor and let the hardware deal with things. * self-linked tx descriptor and let the hardware deal with things.
*/ */
intval |= ATH9K_BEACON_ENA;
ah->imask |= ATH9K_INT_SWBA; ah->imask |= ATH9K_INT_SWBA;
ath_beaconq_config(sc); ath_beaconq_config(sc);
......
...@@ -155,7 +155,7 @@ static void ath9k_htc_beacon_config_ap(struct ath9k_htc_priv *priv, ...@@ -155,7 +155,7 @@ static void ath9k_htc_beacon_config_ap(struct ath9k_htc_priv *priv,
nexttbtt = intval; nexttbtt = intval;
if (priv->op_flags & OP_TSF_RESET) { if (priv->op_flags & OP_TSF_RESET) {
intval |= ATH9K_BEACON_RESET_TSF; ath9k_hw_reset_tsf(priv->ah);
priv->op_flags &= ~OP_TSF_RESET; priv->op_flags &= ~OP_TSF_RESET;
} else { } else {
/* /*
...@@ -168,8 +168,6 @@ static void ath9k_htc_beacon_config_ap(struct ath9k_htc_priv *priv, ...@@ -168,8 +168,6 @@ static void ath9k_htc_beacon_config_ap(struct ath9k_htc_priv *priv,
} while (nexttbtt < tsftu); } while (nexttbtt < tsftu);
} }
intval |= ATH9K_BEACON_ENA;
if (priv->op_flags & OP_ENABLE_BEACON) if (priv->op_flags & OP_ENABLE_BEACON)
imask |= ATH9K_INT_SWBA; imask |= ATH9K_INT_SWBA;
...@@ -178,7 +176,7 @@ static void ath9k_htc_beacon_config_ap(struct ath9k_htc_priv *priv, ...@@ -178,7 +176,7 @@ static void ath9k_htc_beacon_config_ap(struct ath9k_htc_priv *priv,
bss_conf->beacon_interval, nexttbtt, imask); bss_conf->beacon_interval, nexttbtt, imask);
WMI_CMD(WMI_DISABLE_INTR_CMDID); WMI_CMD(WMI_DISABLE_INTR_CMDID);
ath9k_hw_beaconinit(priv->ah, nexttbtt, intval); ath9k_hw_beaconinit(priv->ah, TU_TO_USEC(nexttbtt), TU_TO_USEC(intval));
priv->bmiss_cnt = 0; priv->bmiss_cnt = 0;
htc_imask = cpu_to_be32(imask); htc_imask = cpu_to_be32(imask);
WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask); WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask);
...@@ -207,7 +205,6 @@ static void ath9k_htc_beacon_config_adhoc(struct ath9k_htc_priv *priv, ...@@ -207,7 +205,6 @@ static void ath9k_htc_beacon_config_adhoc(struct ath9k_htc_priv *priv,
nexttbtt += intval; nexttbtt += intval;
} while (nexttbtt < tsftu); } while (nexttbtt < tsftu);
intval |= ATH9K_BEACON_ENA;
if (priv->op_flags & OP_ENABLE_BEACON) if (priv->op_flags & OP_ENABLE_BEACON)
imask |= ATH9K_INT_SWBA; imask |= ATH9K_INT_SWBA;
...@@ -216,7 +213,7 @@ static void ath9k_htc_beacon_config_adhoc(struct ath9k_htc_priv *priv, ...@@ -216,7 +213,7 @@ static void ath9k_htc_beacon_config_adhoc(struct ath9k_htc_priv *priv,
bss_conf->beacon_interval, nexttbtt, imask); bss_conf->beacon_interval, nexttbtt, imask);
WMI_CMD(WMI_DISABLE_INTR_CMDID); WMI_CMD(WMI_DISABLE_INTR_CMDID);
ath9k_hw_beaconinit(priv->ah, nexttbtt, intval); ath9k_hw_beaconinit(priv->ah, TU_TO_USEC(nexttbtt), TU_TO_USEC(intval));
priv->bmiss_cnt = 0; priv->bmiss_cnt = 0;
htc_imask = cpu_to_be32(imask); htc_imask = cpu_to_be32(imask);
WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask); WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask);
......
...@@ -1697,21 +1697,15 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period) ...@@ -1697,21 +1697,15 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period)
case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_MESH_POINT:
REG_SET_BIT(ah, AR_TXCFG, REG_SET_BIT(ah, AR_TXCFG,
AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY); AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY);
REG_WRITE(ah, AR_NEXT_NDP_TIMER, REG_WRITE(ah, AR_NEXT_NDP_TIMER, next_beacon +
TU_TO_USEC(next_beacon + TU_TO_USEC(ah->atim_window ? ah->atim_window : 1));
(ah->atim_window ? ah->
atim_window : 1)));
flags |= AR_NDP_TIMER_EN; flags |= AR_NDP_TIMER_EN;
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
REG_WRITE(ah, AR_NEXT_TBTT_TIMER, TU_TO_USEC(next_beacon)); REG_WRITE(ah, AR_NEXT_TBTT_TIMER, next_beacon);
REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT, REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT, next_beacon -
TU_TO_USEC(next_beacon - TU_TO_USEC(ah->config.dma_beacon_response_time));
ah->config. REG_WRITE(ah, AR_NEXT_SWBA, next_beacon -
dma_beacon_response_time)); TU_TO_USEC(ah->config.sw_beacon_response_time));
REG_WRITE(ah, AR_NEXT_SWBA,
TU_TO_USEC(next_beacon -
ah->config.
sw_beacon_response_time));
flags |= flags |=
AR_TBTT_TIMER_EN | AR_DBA_TIMER_EN | AR_SWBA_TIMER_EN; AR_TBTT_TIMER_EN | AR_DBA_TIMER_EN | AR_SWBA_TIMER_EN;
break; break;
...@@ -1723,18 +1717,13 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period) ...@@ -1723,18 +1717,13 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period)
break; break;
} }
REG_WRITE(ah, AR_BEACON_PERIOD, TU_TO_USEC(beacon_period)); REG_WRITE(ah, AR_BEACON_PERIOD, beacon_period);
REG_WRITE(ah, AR_DMA_BEACON_PERIOD, TU_TO_USEC(beacon_period)); REG_WRITE(ah, AR_DMA_BEACON_PERIOD, beacon_period);
REG_WRITE(ah, AR_SWBA_PERIOD, TU_TO_USEC(beacon_period)); REG_WRITE(ah, AR_SWBA_PERIOD, beacon_period);
REG_WRITE(ah, AR_NDP_PERIOD, TU_TO_USEC(beacon_period)); REG_WRITE(ah, AR_NDP_PERIOD, beacon_period);
REGWRITE_BUFFER_FLUSH(ah); REGWRITE_BUFFER_FLUSH(ah);
beacon_period &= ~ATH9K_BEACON_ENA;
if (beacon_period & ATH9K_BEACON_RESET_TSF) {
ath9k_hw_reset_tsf(ah);
}
REG_SET_BIT(ah, AR_TIMER_MODE, flags); REG_SET_BIT(ah, AR_TIMER_MODE, flags);
} }
EXPORT_SYMBOL(ath9k_hw_beaconinit); EXPORT_SYMBOL(ath9k_hw_beaconinit);
...@@ -2395,10 +2384,11 @@ static u32 rightmost_index(struct ath_gen_timer_table *timer_table, u32 *mask) ...@@ -2395,10 +2384,11 @@ static u32 rightmost_index(struct ath_gen_timer_table *timer_table, u32 *mask)
return timer_table->gen_timer_index[b]; return timer_table->gen_timer_index[b];
} }
static u32 ath9k_hw_gettsf32(struct ath_hw *ah) u32 ath9k_hw_gettsf32(struct ath_hw *ah)
{ {
return REG_READ(ah, AR_TSF_L32); return REG_READ(ah, AR_TSF_L32);
} }
EXPORT_SYMBOL(ath9k_hw_gettsf32);
struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah, struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah,
void (*trigger)(void *), void (*trigger)(void *),
......
...@@ -416,8 +416,6 @@ struct ath9k_beacon_state { ...@@ -416,8 +416,6 @@ struct ath9k_beacon_state {
u32 bs_nextdtim; u32 bs_nextdtim;
u32 bs_intval; u32 bs_intval;
#define ATH9K_BEACON_PERIOD 0x0000ffff #define ATH9K_BEACON_PERIOD 0x0000ffff
#define ATH9K_BEACON_ENA 0x00800000
#define ATH9K_BEACON_RESET_TSF 0x01000000
#define ATH9K_TSFOOR_THRESHOLD 0x00004240 /* 16k us */ #define ATH9K_TSFOOR_THRESHOLD 0x00004240 /* 16k us */
u32 bs_dtimperiod; u32 bs_dtimperiod;
u16 bs_cfpperiod; u16 bs_cfpperiod;
...@@ -930,6 +928,7 @@ void ath9k_hw_setopmode(struct ath_hw *ah); ...@@ -930,6 +928,7 @@ void ath9k_hw_setopmode(struct ath_hw *ah);
void ath9k_hw_setmcastfilter(struct ath_hw *ah, u32 filter0, u32 filter1); void ath9k_hw_setmcastfilter(struct ath_hw *ah, u32 filter0, u32 filter1);
void ath9k_hw_setbssidmask(struct ath_hw *ah); void ath9k_hw_setbssidmask(struct ath_hw *ah);
void ath9k_hw_write_associd(struct ath_hw *ah); void ath9k_hw_write_associd(struct ath_hw *ah);
u32 ath9k_hw_gettsf32(struct ath_hw *ah);
u64 ath9k_hw_gettsf64(struct ath_hw *ah); u64 ath9k_hw_gettsf64(struct ath_hw *ah);
void ath9k_hw_settsf64(struct ath_hw *ah, u64 tsf64); void ath9k_hw_settsf64(struct ath_hw *ah, u64 tsf64);
void ath9k_hw_reset_tsf(struct ath_hw *ah); void ath9k_hw_reset_tsf(struct ath_hw *ah);
......
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