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

ath9k_hw: merge codepaths that access the cycle counter registers

The cycle counters are used by ANI to determine the amount of time that the
radio spent not receiving or transmitting. They're also used for debugging
purposes if the baseband watchdog on AR9003 detects a lockup.
In the future, we want to use these counters to determine the medium utilization
and export this information via survey. For that, we need to make sure that
the counter is only accessed from one place, which also ensures that
wraparounds won't occur at inconvenient points in time.
Signed-off-by: default avatarFelix Fietkau <nbd@openwrt.org>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 6497827f
...@@ -549,47 +549,15 @@ static u8 ath9k_hw_chan_2_clockrate_mhz(struct ath_hw *ah) ...@@ -549,47 +549,15 @@ static u8 ath9k_hw_chan_2_clockrate_mhz(struct ath_hw *ah)
static int32_t ath9k_hw_ani_get_listen_time(struct ath_hw *ah) static int32_t ath9k_hw_ani_get_listen_time(struct ath_hw *ah)
{ {
struct ar5416AniState *aniState; int32_t listen_time;
struct ath_common *common = ath9k_hw_common(ah); int32_t clock_rate;
u32 txFrameCount, rxFrameCount, cycleCount;
int32_t listenTime;
txFrameCount = REG_READ(ah, AR_TFCNT);
rxFrameCount = REG_READ(ah, AR_RFCNT);
cycleCount = REG_READ(ah, AR_CCCNT);
aniState = ah->curani;
if (aniState->cycleCount == 0 || aniState->cycleCount > cycleCount) {
listenTime = 0;
ah->stats.ast_ani_lzero++;
ath_print(common, ATH_DBG_ANI,
"1st call: aniState->cycleCount=%d\n",
aniState->cycleCount);
} else {
int32_t ccdelta = cycleCount - aniState->cycleCount;
int32_t rfdelta = rxFrameCount - aniState->rxFrameCount;
int32_t tfdelta = txFrameCount - aniState->txFrameCount;
int32_t clock_rate;
/*
* convert HW counter values to ms using mode
* specifix clock rate
*/
clock_rate = ath9k_hw_chan_2_clockrate_mhz(ah) * 1000;;
listenTime = (ccdelta - rfdelta - tfdelta) / clock_rate; ath9k_hw_update_cycle_counters(ah);
clock_rate = ath9k_hw_chan_2_clockrate_mhz(ah) * 1000;
listen_time = ah->listen_time / clock_rate;
ah->listen_time = 0;
ath_print(common, ATH_DBG_ANI, return listen_time;
"cyclecount=%d, rfcount=%d, "
"tfcount=%d, listenTime=%d CLOCK_RATE=%d\n",
ccdelta, rfdelta, tfdelta, listenTime, clock_rate);
}
aniState->cycleCount = cycleCount;
aniState->txFrameCount = txFrameCount;
aniState->rxFrameCount = rxFrameCount;
return listenTime;
} }
static void ath9k_ani_reset_old(struct ath_hw *ah, bool is_scanning) static void ath9k_ani_reset_old(struct ath_hw *ah, bool is_scanning)
...@@ -1041,45 +1009,52 @@ void ath9k_hw_disable_mib_counters(struct ath_hw *ah) ...@@ -1041,45 +1009,52 @@ void ath9k_hw_disable_mib_counters(struct ath_hw *ah)
} }
EXPORT_SYMBOL(ath9k_hw_disable_mib_counters); EXPORT_SYMBOL(ath9k_hw_disable_mib_counters);
u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hw *ah, void ath9k_hw_update_cycle_counters(struct ath_hw *ah)
u32 *rxc_pcnt,
u32 *rxf_pcnt,
u32 *txf_pcnt)
{ {
struct ath_common *common = ath9k_hw_common(ah); struct ath_cycle_counters cc;
static u32 cycles, rx_clear, rx_frame, tx_frame; bool clear;
u32 good = 1;
u32 rc = REG_READ(ah, AR_RCCNT); memcpy(&cc, &ah->cc, sizeof(cc));
u32 rf = REG_READ(ah, AR_RFCNT);
u32 tf = REG_READ(ah, AR_TFCNT);
u32 cc = REG_READ(ah, AR_CCCNT);
if (cycles == 0 || cycles > cc) { /* freeze counters */
ath_print(common, ATH_DBG_ANI, REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC);
"cycle counter wrap. ExtBusy = 0\n");
good = 0; ah->cc.cycles = REG_READ(ah, AR_CCCNT);
} else { if (ah->cc.cycles < cc.cycles) {
u32 cc_d = cc - cycles; clear = true;
u32 rc_d = rc - rx_clear; goto skip;
u32 rf_d = rf - rx_frame;
u32 tf_d = tf - tx_frame;
if (cc_d != 0) {
*rxc_pcnt = rc_d * 100 / cc_d;
*rxf_pcnt = rf_d * 100 / cc_d;
*txf_pcnt = tf_d * 100 / cc_d;
} else {
good = 0;
}
} }
cycles = cc; ah->cc.rx_clear = REG_READ(ah, AR_RCCNT);
rx_frame = rf; ah->cc.rx_frame = REG_READ(ah, AR_RFCNT);
rx_clear = rc; ah->cc.tx_frame = REG_READ(ah, AR_TFCNT);
tx_frame = tf;
/* prevent wraparound */
if (ah->cc.cycles & BIT(31))
clear = true;
#define CC_DELTA(_field, _reg) ah->cc_delta._field += ah->cc._field - cc._field
CC_DELTA(cycles, AR_CCCNT);
CC_DELTA(rx_frame, AR_RFCNT);
CC_DELTA(rx_clear, AR_RCCNT);
CC_DELTA(tx_frame, AR_TFCNT);
#undef CC_DELTA
ah->listen_time += (ah->cc.cycles - cc.cycles) -
((ah->cc.rx_frame - cc.rx_frame) +
(ah->cc.tx_frame - cc.tx_frame));
skip:
if (clear) {
REG_WRITE(ah, AR_CCCNT, 0);
REG_WRITE(ah, AR_RFCNT, 0);
REG_WRITE(ah, AR_RCCNT, 0);
REG_WRITE(ah, AR_TFCNT, 0);
memset(&ah->cc, 0, sizeof(ah->cc));
}
return good; /* unfreeze counters */
REG_WRITE(ah, AR_MIBC, 0);
} }
/* /*
......
...@@ -93,6 +93,13 @@ struct ath9k_mib_stats { ...@@ -93,6 +93,13 @@ struct ath9k_mib_stats {
u32 beacons; u32 beacons;
}; };
struct ath_cycle_counters {
u32 cycles;
u32 rx_frame;
u32 rx_clear;
u32 tx_frame;
};
/* INI default values for ANI registers */ /* INI default values for ANI registers */
struct ath9k_ani_default { struct ath9k_ani_default {
u16 m1ThreshLow; u16 m1ThreshLow;
...@@ -130,9 +137,6 @@ struct ar5416AniState { ...@@ -130,9 +137,6 @@ struct ar5416AniState {
int32_t rssiThrLow; int32_t rssiThrLow;
int32_t rssiThrHigh; int32_t rssiThrHigh;
u32 noiseFloor; u32 noiseFloor;
u32 txFrameCount;
u32 rxFrameCount;
u32 cycleCount;
u32 ofdmPhyErrCount; u32 ofdmPhyErrCount;
u32 cckPhyErrCount; u32 cckPhyErrCount;
u32 ofdmPhyErrBase; u32 ofdmPhyErrBase;
...@@ -166,8 +170,7 @@ struct ar5416Stats { ...@@ -166,8 +170,7 @@ struct ar5416Stats {
void ath9k_enable_mib_counters(struct ath_hw *ah); void ath9k_enable_mib_counters(struct ath_hw *ah);
void ath9k_hw_disable_mib_counters(struct ath_hw *ah); void ath9k_hw_disable_mib_counters(struct ath_hw *ah);
u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hw *ah, u32 *rxc_pcnt, void ath9k_hw_update_cycle_counters(struct ath_hw *ah);
u32 *rxf_pcnt, u32 *txf_pcnt);
void ath9k_hw_ani_setup(struct ath_hw *ah); void ath9k_hw_ani_setup(struct ath_hw *ah);
void ath9k_hw_ani_init(struct ath_hw *ah); void ath9k_hw_ani_init(struct ath_hw *ah);
int ath9k_hw_get_ani_channel_idx(struct ath_hw *ah, int ath9k_hw_get_ani_channel_idx(struct ath_hw *ah,
......
...@@ -1225,8 +1225,7 @@ static bool ar5008_hw_ani_control_old(struct ath_hw *ah, ...@@ -1225,8 +1225,7 @@ static bool ar5008_hw_ani_control_old(struct ath_hw *ah,
aniState->firstepLevel, aniState->firstepLevel,
aniState->listenTime); aniState->listenTime);
ath_print(common, ATH_DBG_ANI, ath_print(common, ATH_DBG_ANI,
"cycleCount=%d, ofdmPhyErrCount=%d, cckPhyErrCount=%d\n\n", "ofdmPhyErrCount=%d, cckPhyErrCount=%d\n\n",
aniState->cycleCount,
aniState->ofdmPhyErrCount, aniState->ofdmPhyErrCount,
aniState->cckPhyErrCount); aniState->cckPhyErrCount);
...@@ -1478,15 +1477,13 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, ...@@ -1478,15 +1477,13 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah,
ath_print(common, ATH_DBG_ANI, ath_print(common, ATH_DBG_ANI,
"ANI parameters: SI=%d, ofdmWS=%s FS=%d " "ANI parameters: SI=%d, ofdmWS=%s FS=%d "
"MRCcck=%s listenTime=%d CC=%d listen=%d " "MRCcck=%s listenTime=%d "
"ofdmErrs=%d cckErrs=%d\n", "ofdmErrs=%d cckErrs=%d\n",
aniState->spurImmunityLevel, aniState->spurImmunityLevel,
!aniState->ofdmWeakSigDetectOff ? "on" : "off", !aniState->ofdmWeakSigDetectOff ? "on" : "off",
aniState->firstepLevel, aniState->firstepLevel,
!aniState->mrcCCKOff ? "on" : "off", !aniState->mrcCCKOff ? "on" : "off",
aniState->listenTime, aniState->listenTime,
aniState->cycleCount,
aniState->listenTime,
aniState->ofdmPhyErrCount, aniState->ofdmPhyErrCount,
aniState->cckPhyErrCount); aniState->cckPhyErrCount);
return true; return true;
...@@ -1579,8 +1576,6 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah) ...@@ -1579,8 +1576,6 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah)
aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL_NEW; aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL_NEW;
aniState->ofdmWeakSigDetectOff = !ATH9K_ANI_USE_OFDM_WEAK_SIG; aniState->ofdmWeakSigDetectOff = !ATH9K_ANI_USE_OFDM_WEAK_SIG;
aniState->mrcCCKOff = true; /* not available on pre AR9003 */ aniState->mrcCCKOff = true; /* not available on pre AR9003 */
aniState->cycleCount = 0;
} }
static void ar5008_hw_set_nf_limits(struct ath_hw *ah) static void ar5008_hw_set_nf_limits(struct ath_hw *ah)
......
...@@ -1005,15 +1005,13 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, ...@@ -1005,15 +1005,13 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah,
ath_print(common, ATH_DBG_ANI, ath_print(common, ATH_DBG_ANI,
"ANI parameters: SI=%d, ofdmWS=%s FS=%d " "ANI parameters: SI=%d, ofdmWS=%s FS=%d "
"MRCcck=%s listenTime=%d CC=%d listen=%d " "MRCcck=%s listenTime=%d "
"ofdmErrs=%d cckErrs=%d\n", "ofdmErrs=%d cckErrs=%d\n",
aniState->spurImmunityLevel, aniState->spurImmunityLevel,
!aniState->ofdmWeakSigDetectOff ? "on" : "off", !aniState->ofdmWeakSigDetectOff ? "on" : "off",
aniState->firstepLevel, aniState->firstepLevel,
!aniState->mrcCCKOff ? "on" : "off", !aniState->mrcCCKOff ? "on" : "off",
aniState->listenTime, aniState->listenTime,
aniState->cycleCount,
aniState->listenTime,
aniState->ofdmPhyErrCount, aniState->ofdmPhyErrCount,
aniState->cckPhyErrCount); aniState->cckPhyErrCount);
return true; return true;
...@@ -1116,8 +1114,6 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah) ...@@ -1116,8 +1114,6 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah)
aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL_NEW; aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL_NEW;
aniState->ofdmWeakSigDetectOff = !ATH9K_ANI_USE_OFDM_WEAK_SIG; aniState->ofdmWeakSigDetectOff = !ATH9K_ANI_USE_OFDM_WEAK_SIG;
aniState->mrcCCKOff = !ATH9K_ANI_ENABLE_MRC_CCK; aniState->mrcCCKOff = !ATH9K_ANI_ENABLE_MRC_CCK;
aniState->cycleCount = 0;
} }
void ar9003_hw_attach_phy_ops(struct ath_hw *ah) void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
...@@ -1232,7 +1228,7 @@ void ar9003_hw_bb_watchdog_read(struct ath_hw *ah) ...@@ -1232,7 +1228,7 @@ void ar9003_hw_bb_watchdog_read(struct ath_hw *ah)
void ar9003_hw_bb_watchdog_dbg_info(struct ath_hw *ah) void ar9003_hw_bb_watchdog_dbg_info(struct ath_hw *ah)
{ {
struct ath_common *common = ath9k_hw_common(ah); struct ath_common *common = ath9k_hw_common(ah);
u32 rxc_pcnt = 0, rxf_pcnt = 0, txf_pcnt = 0, status; u32 status;
if (likely(!(common->debug_mask & ATH_DBG_RESET))) if (likely(!(common->debug_mask & ATH_DBG_RESET)))
return; return;
...@@ -1261,11 +1257,13 @@ void ar9003_hw_bb_watchdog_dbg_info(struct ath_hw *ah) ...@@ -1261,11 +1257,13 @@ void ar9003_hw_bb_watchdog_dbg_info(struct ath_hw *ah)
"** BB mode: BB_gen_controls=0x%08x **\n", "** BB mode: BB_gen_controls=0x%08x **\n",
REG_READ(ah, AR_PHY_GEN_CTRL)); REG_READ(ah, AR_PHY_GEN_CTRL));
if (ath9k_hw_GetMibCycleCountsPct(ah, &rxc_pcnt, &rxf_pcnt, &txf_pcnt)) ath9k_hw_update_cycle_counters(ah);
#define PCT(_field) (ah->cc_delta._field * 100 / ah->cc_delta.cycles)
if (ah->cc_delta.cycles)
ath_print(common, ATH_DBG_RESET, ath_print(common, ATH_DBG_RESET,
"** BB busy times: rx_clear=%d%%, " "** BB busy times: rx_clear=%d%%, "
"rx_frame=%d%%, tx_frame=%d%% **\n", "rx_frame=%d%%, tx_frame=%d%% **\n",
rxc_pcnt, rxf_pcnt, txf_pcnt); PCT(rx_clear), PCT(rx_frame), PCT(tx_frame));
ath_print(common, ATH_DBG_RESET, ath_print(common, ATH_DBG_RESET,
"==== BB update: done ====\n\n"); "==== BB update: done ====\n\n");
......
...@@ -765,6 +765,8 @@ struct ath_hw { ...@@ -765,6 +765,8 @@ struct ath_hw {
int coarse_low[5]; int coarse_low[5];
int firpwr[5]; int firpwr[5];
enum ath9k_ani_cmd ani_function; enum ath9k_ani_cmd ani_function;
struct ath_cycle_counters cc, cc_delta;
int32_t listen_time;
/* Bluetooth coexistance */ /* Bluetooth coexistance */
struct ath_btcoex_hw btcoex_hw; struct ath_btcoex_hw btcoex_hw;
......
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