From: Felix Fietkau <nbd@openwrt.org>
Date: Mon, 21 Mar 2011 02:22:17 +0000 (+0000)
Subject: ath9k: improve precision of beacon timer calculations
X-Git-Url: http://git.cdn.openwrt.org/?a=commitdiff_plain;h=4838baefa7667e53f715082dacf332e8d1e854c6;p=openwrt%2Fstaging%2Faparcar.git

ath9k: improve precision of beacon timer calculations

SVN-Revision: 26248
---

diff --git a/package/mac80211/patches/561-ath9k_fix_staggered_beacons.patch b/package/mac80211/patches/561-ath9k_fix_staggered_beacons.patch
new file mode 100644
index 0000000000..08f91258f7
--- /dev/null
+++ b/package/mac80211/patches/561-ath9k_fix_staggered_beacons.patch
@@ -0,0 +1,317 @@
+--- a/drivers/net/wireless/ath/ath9k/beacon.c
++++ b/drivers/net/wireless/ath/ath9k/beacon.c
+@@ -57,8 +57,8 @@ int ath_beaconq_config(struct ath_softc 
+ 
+ /*
+  *  Associates the beacon frame buffer with a transmit descriptor.  Will set
+- *  up all required antenna switch parameters, rate codes, and channel flags.
+- *  Beacons are always sent out at the lowest rate, and are not retried.
++ *  up rate codes, and channel flags. Beacons are always sent out at the
++ *  lowest rate, and are not retried.
+ */
+ static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp,
+ 			     struct ath_buf *bf, int rateidx)
+@@ -68,7 +68,7 @@ static void ath_beacon_setup(struct ath_
+ 	struct ath_common *common = ath9k_hw_common(ah);
+ 	struct ath_desc *ds;
+ 	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;
+ 	u8 rate = 0;
+ 
+@@ -76,12 +76,6 @@ static void ath_beacon_setup(struct ath_
+ 	flags = ATH9K_TXDESC_NOACK;
+ 
+ 	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];
+ 	rate = sband->bitrates[rateidx].hw_value;
+@@ -278,7 +272,7 @@ int ath_beacon_alloc(struct ath_softc *s
+ 		return -ENOMEM;
+ 
+ 	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. */
+ 	if (avp->av_bslot > 0) {
+ 		u64 tsfadjust;
+@@ -294,8 +288,8 @@ int ath_beacon_alloc(struct ath_softc *s
+ 		 * adjustment. Other slots are adjusted to get the timestamp
+ 		 * close to the TBTT for the BSS.
+ 		 */
+-		tsfadjust = intval * avp->av_bslot / ATH_BCBUF;
+-		avp->tsf_adjust = cpu_to_le64(TU_TO_USEC(tsfadjust));
++		tsfadjust = TU_TO_USEC(intval * avp->av_bslot) / ATH_BCBUF;
++		avp->tsf_adjust = cpu_to_le64(tsfadjust);
+ 
+ 		ath_dbg(common, ATH_DBG_BEACON,
+ 			"stagger beacons, bslot %d intval %u tsfadjust %llu\n",
+@@ -401,8 +395,9 @@ void ath_beacon_tasklet(unsigned long da
+ 	intval = cur_conf->beacon_interval ? : ATH_DEFAULT_BINTVAL;
+ 
+ 	tsf = ath9k_hw_gettsf64(ah);
+-	tsftu = TSF_TO_TU(tsf>>32, tsf);
+-	slot = ((tsftu % intval) * ATH_BCBUF) / intval;
++	tsf += TU_TO_USEC(ah->config.sw_beacon_response_time);
++	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
+ 	 * not require TSF adjustment and other slots adding
+@@ -415,7 +410,7 @@ void ath_beacon_tasklet(unsigned long da
+ 
+ 	ath_dbg(common, ATH_DBG_BEACON,
+ 		"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;
+ 	if (vif) {
+@@ -463,13 +458,17 @@ static void ath9k_beacon_init(struct ath
+ 			      u32 next_beacon,
+ 			      u32 beacon_period)
+ {
+-	if (beacon_period & ATH9K_BEACON_RESET_TSF)
++	if (sc->sc_flags & SC_OP_TSF_RESET) {
+ 		ath9k_ps_wakeup(sc);
++		ath9k_hw_reset_tsf(sc->sc_ah);
++	}
+ 
+ 	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);
++		sc->sc_flags &= ~SC_OP_TSF_RESET;
++	}
+ }
+ 
+ /*
+@@ -484,18 +483,14 @@ static void ath_beacon_config_ap(struct 
+ 	u32 nexttbtt, intval;
+ 
+ 	/* 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 */
+ 	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
+ 	 * prepare beacon frames.
+ 	 */
+-	intval |= ATH9K_BEACON_ENA;
+ 	ah->imask |= ATH9K_INT_SWBA;
+ 	ath_beaconq_config(sc);
+ 
+@@ -505,11 +500,6 @@ static void ath_beacon_config_ap(struct 
+ 	ath9k_beacon_init(sc, nexttbtt, intval);
+ 	sc->beacon.bmisscnt = 0;
+ 	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(stru
+ {
+ 	struct ath_hw *ah = sc->sc_ah;
+ 	struct ath_common *common = ath9k_hw_common(ah);
+-	u64 tsf;
+-	u32 tsftu, intval, nexttbtt;
+-
+-	intval = conf->beacon_interval & ATH9K_BEACON_PERIOD;
+-
++	u32 tsf, delta, intval, nexttbtt;
+ 
+-	/* Pull nexttbtt forward to reflect the current TSF */
++	tsf = ath9k_hw_gettsf32(ah) + TU_TO_USEC(FUDGE);
++	intval = TU_TO_USEC(conf->beacon_interval & ATH9K_BEACON_PERIOD);
+ 
+-	nexttbtt = TSF_TO_TU(sc->beacon.bc_tstamp >> 32, sc->beacon.bc_tstamp);
+-	if (nexttbtt == 0)
+-                nexttbtt = intval;
+-        else if (intval)
+-                nexttbtt = roundup(nexttbtt, intval);
+-
+-	tsf = ath9k_hw_gettsf64(ah);
+-	tsftu = TSF_TO_TU((u32)(tsf>>32), (u32)tsf) + FUDGE;
+-	do {
+-		nexttbtt += intval;
+-	} while (nexttbtt < tsftu);
++	if (!sc->beacon.bc_tstamp)
++		nexttbtt = tsf + intval;
++	else {
++		if (tsf > sc->beacon.bc_tstamp)
++			delta = (tsf - sc->beacon.bc_tstamp);
++		else
++			delta = (tsf + 1 + (~0U - sc->beacon.bc_tstamp));
++		nexttbtt = tsf + roundup(delta, intval);
++	}
+ 
+ 	ath_dbg(common, ATH_DBG_BEACON,
+ 		"IBSS nexttbtt %u intval %u (%u)\n",
+@@ -672,7 +657,6 @@ static void ath_beacon_config_adhoc(stru
+ 	 * if we need to manually prepare beacon frames.  Otherwise we use a
+ 	 * self-linked tx descriptor and let the hardware deal with things.
+ 	 */
+-	intval |= ATH9K_BEACON_ENA;
+ 	ah->imask |= ATH9K_INT_SWBA;
+ 
+ 	ath_beaconq_config(sc);
+--- a/drivers/net/wireless/ath/ath9k/hw.c
++++ b/drivers/net/wireless/ath/ath9k/hw.c
+@@ -1706,21 +1706,15 @@ void ath9k_hw_beaconinit(struct ath_hw *
+ 	case NL80211_IFTYPE_MESH_POINT:
+ 		REG_SET_BIT(ah, AR_TXCFG,
+ 			    AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY);
+-		REG_WRITE(ah, AR_NEXT_NDP_TIMER,
+-			  TU_TO_USEC(next_beacon +
+-				     (ah->atim_window ? ah->
+-				      atim_window : 1)));
++		REG_WRITE(ah, AR_NEXT_NDP_TIMER, next_beacon +
++			  TU_TO_USEC(ah->atim_window ? ah->atim_window : 1));
+ 		flags |= AR_NDP_TIMER_EN;
+ 	case NL80211_IFTYPE_AP:
+-		REG_WRITE(ah, AR_NEXT_TBTT_TIMER, TU_TO_USEC(next_beacon));
+-		REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT,
+-			  TU_TO_USEC(next_beacon -
+-				     ah->config.
+-				     dma_beacon_response_time));
+-		REG_WRITE(ah, AR_NEXT_SWBA,
+-			  TU_TO_USEC(next_beacon -
+-				     ah->config.
+-				     sw_beacon_response_time));
++		REG_WRITE(ah, AR_NEXT_TBTT_TIMER, next_beacon);
++		REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT, next_beacon -
++			  TU_TO_USEC(ah->config.dma_beacon_response_time));
++		REG_WRITE(ah, AR_NEXT_SWBA, next_beacon -
++			  TU_TO_USEC(ah->config.sw_beacon_response_time));
+ 		flags |=
+ 			AR_TBTT_TIMER_EN | AR_DBA_TIMER_EN | AR_SWBA_TIMER_EN;
+ 		break;
+@@ -1732,18 +1726,13 @@ void ath9k_hw_beaconinit(struct ath_hw *
+ 		break;
+ 	}
+ 
+-	REG_WRITE(ah, AR_BEACON_PERIOD, TU_TO_USEC(beacon_period));
+-	REG_WRITE(ah, AR_DMA_BEACON_PERIOD, TU_TO_USEC(beacon_period));
+-	REG_WRITE(ah, AR_SWBA_PERIOD, TU_TO_USEC(beacon_period));
+-	REG_WRITE(ah, AR_NDP_PERIOD, TU_TO_USEC(beacon_period));
++	REG_WRITE(ah, AR_BEACON_PERIOD, beacon_period);
++	REG_WRITE(ah, AR_DMA_BEACON_PERIOD, beacon_period);
++	REG_WRITE(ah, AR_SWBA_PERIOD, beacon_period);
++	REG_WRITE(ah, AR_NDP_PERIOD, beacon_period);
+ 
+ 	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);
+ }
+ EXPORT_SYMBOL(ath9k_hw_beaconinit);
+@@ -2404,10 +2393,11 @@ static u32 rightmost_index(struct ath_ge
+ 	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);
+ }
++EXPORT_SYMBOL(ath9k_hw_gettsf32);
+ 
+ struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah,
+ 					  void (*trigger)(void *),
+--- a/drivers/net/wireless/ath/ath9k/ath9k.h
++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
+@@ -392,7 +392,7 @@ struct ath_beacon {
+ 	u32 beaconq;
+ 	u32 bmisscnt;
+ 	u32 ast_be_xmit;
+-	u64 bc_tstamp;
++	u32 bc_tstamp;
+ 	struct ieee80211_vif *bslot[ATH_BCBUF];
+ 	int slottime;
+ 	int slotupdate;
+--- a/drivers/net/wireless/ath/ath9k/hw.h
++++ b/drivers/net/wireless/ath/ath9k/hw.h
+@@ -412,8 +412,6 @@ struct ath9k_beacon_state {
+ 	u32 bs_nextdtim;
+ 	u32 bs_intval;
+ #define ATH9K_BEACON_PERIOD       0x0000ffff
+-#define ATH9K_BEACON_ENA          0x00800000
+-#define ATH9K_BEACON_RESET_TSF    0x01000000
+ #define ATH9K_TSFOOR_THRESHOLD    0x00004240 /* 16k us */
+ 	u32 bs_dtimperiod;
+ 	u16 bs_cfpperiod;
+@@ -927,6 +925,7 @@ void ath9k_hw_setopmode(struct ath_hw *a
+ void ath9k_hw_setmcastfilter(struct ath_hw *ah, u32 filter0, u32 filter1);
+ void ath9k_hw_setbssidmask(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);
+ void ath9k_hw_settsf64(struct ath_hw *ah, u64 tsf64);
+ void ath9k_hw_reset_tsf(struct ath_hw *ah);
+--- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
++++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
+@@ -155,7 +155,7 @@ static void ath9k_htc_beacon_config_ap(s
+ 	nexttbtt = intval;
+ 
+ 	if (priv->op_flags & OP_TSF_RESET) {
+-		intval |= ATH9K_BEACON_RESET_TSF;
++		ath9k_hw_reset_tsf(priv->ah);
+ 		priv->op_flags &= ~OP_TSF_RESET;
+ 	} else {
+ 		/*
+@@ -168,8 +168,6 @@ static void ath9k_htc_beacon_config_ap(s
+ 		} while (nexttbtt < tsftu);
+ 	}
+ 
+-	intval |= ATH9K_BEACON_ENA;
+-
+ 	if (priv->op_flags & OP_ENABLE_BEACON)
+ 		imask |= ATH9K_INT_SWBA;
+ 
+@@ -178,7 +176,7 @@ static void ath9k_htc_beacon_config_ap(s
+ 		bss_conf->beacon_interval, nexttbtt, imask);
+ 
+ 	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;
+ 	htc_imask = cpu_to_be32(imask);
+ 	WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask);
+@@ -207,7 +205,6 @@ static void ath9k_htc_beacon_config_adho
+ 		nexttbtt += intval;
+ 	} while (nexttbtt < tsftu);
+ 
+-	intval |= ATH9K_BEACON_ENA;
+ 	if (priv->op_flags & OP_ENABLE_BEACON)
+ 		imask |= ATH9K_INT_SWBA;
+ 
+@@ -216,7 +213,7 @@ static void ath9k_htc_beacon_config_adho
+ 		bss_conf->beacon_interval, nexttbtt, imask);
+ 
+ 	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;
+ 	htc_imask = cpu_to_be32(imask);
+ 	WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask);