brintos

brintos / linux-shallow public Read only

0
0
Text · 15.4 KiB · 41d1927 Raw
514 lines · c
1/*2 * Copyright (c) 2008-2011 Atheros Communications Inc.3 *4 * Permission to use, copy, modify, and/or distribute this software for any5 * purpose with or without fee is hereby granted, provided that the above6 * copyright notice and this permission notice appear in all copies.7 *8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.15 */16 17#include <linux/kernel.h>18#include <linux/export.h>19#include "hw.h"20#include "hw-ops.h"21 22struct ani_ofdm_level_entry {23	int spur_immunity_level;24	int fir_step_level;25	int ofdm_weak_signal_on;26};27 28/* values here are relative to the INI */29 30/*31 * Legend:32 *33 * SI: Spur immunity34 * FS: FIR Step35 * WS: OFDM / CCK Weak Signal detection36 * MRC-CCK: Maximal Ratio Combining for CCK37 */38 39static const struct ani_ofdm_level_entry ofdm_level_table[] = {40	/* SI  FS  WS */41	{  0,  0,  1  }, /* lvl 0 */42	{  1,  1,  1  }, /* lvl 1 */43	{  2,  2,  1  }, /* lvl 2 */44	{  3,  2,  1  }, /* lvl 3  (default) */45	{  4,  3,  1  }, /* lvl 4 */46	{  5,  4,  1  }, /* lvl 5 */47	{  6,  5,  1  }, /* lvl 6 */48	{  7,  6,  1  }, /* lvl 7 */49	{  7,  7,  1  }, /* lvl 8 */50	{  7,  8,  0  }  /* lvl 9 */51};52#define ATH9K_ANI_OFDM_NUM_LEVEL \53	ARRAY_SIZE(ofdm_level_table)54#define ATH9K_ANI_OFDM_MAX_LEVEL \55	(ATH9K_ANI_OFDM_NUM_LEVEL-1)56#define ATH9K_ANI_OFDM_DEF_LEVEL \57	3 /* default level - matches the INI settings */58 59/*60 * MRC (Maximal Ratio Combining) has always been used with multi-antenna ofdm.61 * With OFDM for single stream you just add up all antenna inputs, you're62 * only interested in what you get after FFT. Signal alignment is also not63 * required for OFDM because any phase difference adds up in the frequency64 * domain.65 *66 * MRC requires extra work for use with CCK. You need to align the antenna67 * signals from the different antenna before you can add the signals together.68 * You need alignment of signals as CCK is in time domain, so addition can cancel69 * your signal completely if phase is 180 degrees (think of adding sine waves).70 * You also need to remove noise before the addition and this is where ANI71 * MRC CCK comes into play. One of the antenna inputs may be stronger but72 * lower SNR, so just adding after alignment can be dangerous.73 *74 * Regardless of alignment in time, the antenna signals add constructively after75 * FFT and improve your reception. For more information:76 *77 * https://en.wikipedia.org/wiki/Maximal-ratio_combining78 */79 80struct ani_cck_level_entry {81	int fir_step_level;82	int mrc_cck_on;83};84 85static const struct ani_cck_level_entry cck_level_table[] = {86	/* FS  MRC-CCK  */87	{  0,  1  }, /* lvl 0 */88	{  1,  1  }, /* lvl 1 */89	{  2,  1  }, /* lvl 2  (default) */90	{  3,  1  }, /* lvl 3 */91	{  4,  0  }, /* lvl 4 */92	{  5,  0  }, /* lvl 5 */93	{  6,  0  }, /* lvl 6 */94	{  7,  0  }, /* lvl 7 (only for high rssi) */95	{  8,  0  }  /* lvl 8 (only for high rssi) */96};97 98#define ATH9K_ANI_CCK_NUM_LEVEL \99	ARRAY_SIZE(cck_level_table)100#define ATH9K_ANI_CCK_MAX_LEVEL \101	(ATH9K_ANI_CCK_NUM_LEVEL-1)102#define ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI \103	(ATH9K_ANI_CCK_NUM_LEVEL-3)104#define ATH9K_ANI_CCK_DEF_LEVEL \105	2 /* default level - matches the INI settings */106 107static void ath9k_hw_update_mibstats(struct ath_hw *ah,108				     struct ath9k_mib_stats *stats)109{110	u32 addr[5] = {AR_RTS_OK, AR_RTS_FAIL, AR_ACK_FAIL,111		       AR_FCS_FAIL, AR_BEACON_CNT};112	u32 data[5];113 114	REG_READ_MULTI(ah, &addr[0], &data[0], 5);115	/* AR_RTS_OK */116	stats->rts_good += data[0];117	/* AR_RTS_FAIL */118	stats->rts_bad += data[1];119	/* AR_ACK_FAIL */120	stats->ackrcv_bad += data[2];121	/* AR_FCS_FAIL */122	stats->fcs_bad += data[3];123	/* AR_BEACON_CNT */124	stats->beacons += data[4];125}126 127static void ath9k_ani_restart(struct ath_hw *ah)128{129	struct ar5416AniState *aniState = &ah->ani;130 131	aniState->listenTime = 0;132 133	ENABLE_REGWRITE_BUFFER(ah);134 135	REG_WRITE(ah, AR_PHY_ERR_1, 0);136	REG_WRITE(ah, AR_PHY_ERR_2, 0);137	REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);138	REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);139 140	REGWRITE_BUFFER_FLUSH(ah);141 142	ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);143 144	aniState->ofdmPhyErrCount = 0;145	aniState->cckPhyErrCount = 0;146}147 148/* Adjust the OFDM Noise Immunity Level */149static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel,150				  bool scan)151{152	struct ar5416AniState *aniState = &ah->ani;153	struct ath_common *common = ath9k_hw_common(ah);154	const struct ani_ofdm_level_entry *entry_ofdm;155	const struct ani_cck_level_entry *entry_cck;156	bool weak_sig;157 158	ath_dbg(common, ANI, "**** ofdmlevel %d=>%d, rssi=%d[lo=%d hi=%d]\n",159		aniState->ofdmNoiseImmunityLevel,160		immunityLevel, BEACON_RSSI(ah),161		ATH9K_ANI_RSSI_THR_LOW,162		ATH9K_ANI_RSSI_THR_HIGH);163 164	if (AR_SREV_9100(ah) && immunityLevel < ATH9K_ANI_OFDM_DEF_LEVEL)165		immunityLevel = ATH9K_ANI_OFDM_DEF_LEVEL;166 167	if (!scan)168		aniState->ofdmNoiseImmunityLevel = immunityLevel;169 170	entry_ofdm = &ofdm_level_table[aniState->ofdmNoiseImmunityLevel];171	entry_cck = &cck_level_table[aniState->cckNoiseImmunityLevel];172 173	if (aniState->spurImmunityLevel != entry_ofdm->spur_immunity_level)174		ath9k_hw_ani_control(ah,175				     ATH9K_ANI_SPUR_IMMUNITY_LEVEL,176				     entry_ofdm->spur_immunity_level);177 178	if (aniState->firstepLevel != entry_ofdm->fir_step_level &&179	    entry_ofdm->fir_step_level >= entry_cck->fir_step_level)180		ath9k_hw_ani_control(ah,181				     ATH9K_ANI_FIRSTEP_LEVEL,182				     entry_ofdm->fir_step_level);183 184	weak_sig = entry_ofdm->ofdm_weak_signal_on;185	if (ah->opmode == NL80211_IFTYPE_STATION &&186	    BEACON_RSSI(ah) <= ATH9K_ANI_RSSI_THR_HIGH)187		weak_sig = true;188	/*189	 * Newer chipsets are better at dealing with high PHY error counts -190	 * keep weak signal detection enabled when no RSSI threshold is191	 * available to determine if it is needed (mode != STA)192	 */193	else if (AR_SREV_9300_20_OR_LATER(ah) &&194		 ah->opmode != NL80211_IFTYPE_STATION)195		weak_sig = true;196 197	/* Older chipsets are more sensitive to high PHY error counts */198	else if (!AR_SREV_9300_20_OR_LATER(ah) &&199		 aniState->ofdmNoiseImmunityLevel >= 8)200		weak_sig = false;201 202	if (aniState->ofdmWeakSigDetect != weak_sig)203		ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,204				     weak_sig);205 206	if (!AR_SREV_9300_20_OR_LATER(ah))207		return;208 209	if (aniState->ofdmNoiseImmunityLevel >= ATH9K_ANI_OFDM_DEF_LEVEL) {210		ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH;211		ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW_ABOVE_INI;212	} else {213		ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH_BELOW_INI;214		ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW;215	}216}217 218static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah)219{220	struct ar5416AniState *aniState = &ah->ani;221 222	if (aniState->ofdmNoiseImmunityLevel < ATH9K_ANI_OFDM_MAX_LEVEL)223		ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel + 1, false);224}225 226/*227 * Set the ANI settings to match an CCK level.228 */229static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel,230				 bool scan)231{232	struct ar5416AniState *aniState = &ah->ani;233	struct ath_common *common = ath9k_hw_common(ah);234	const struct ani_ofdm_level_entry *entry_ofdm;235	const struct ani_cck_level_entry *entry_cck;236 237	ath_dbg(common, ANI, "**** ccklevel %d=>%d, rssi=%d[lo=%d hi=%d]\n",238		aniState->cckNoiseImmunityLevel, immunityLevel,239		BEACON_RSSI(ah), ATH9K_ANI_RSSI_THR_LOW,240		ATH9K_ANI_RSSI_THR_HIGH);241 242	if (AR_SREV_9100(ah) && immunityLevel < ATH9K_ANI_CCK_DEF_LEVEL)243		immunityLevel = ATH9K_ANI_CCK_DEF_LEVEL;244 245	if (ah->opmode == NL80211_IFTYPE_STATION &&246	    BEACON_RSSI(ah) <= ATH9K_ANI_RSSI_THR_LOW &&247	    immunityLevel > ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI)248		immunityLevel = ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI;249 250	if (!scan)251		aniState->cckNoiseImmunityLevel = immunityLevel;252 253	entry_ofdm = &ofdm_level_table[aniState->ofdmNoiseImmunityLevel];254	entry_cck = &cck_level_table[aniState->cckNoiseImmunityLevel];255 256	if (aniState->firstepLevel != entry_cck->fir_step_level &&257	    entry_cck->fir_step_level >= entry_ofdm->fir_step_level)258		ath9k_hw_ani_control(ah,259				     ATH9K_ANI_FIRSTEP_LEVEL,260				     entry_cck->fir_step_level);261 262	/* Skip MRC CCK for pre AR9003 families */263	if (!AR_SREV_9300_20_OR_LATER(ah) || AR_SREV_9485(ah) ||264	    AR_SREV_9565(ah) || AR_SREV_9561(ah))265		return;266 267	if (aniState->mrcCCK != entry_cck->mrc_cck_on)268		ath9k_hw_ani_control(ah,269				     ATH9K_ANI_MRC_CCK,270				     entry_cck->mrc_cck_on);271}272 273static void ath9k_hw_ani_cck_err_trigger(struct ath_hw *ah)274{275	struct ar5416AniState *aniState = &ah->ani;276 277	if (aniState->cckNoiseImmunityLevel < ATH9K_ANI_CCK_MAX_LEVEL)278		ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel + 1,279				     false);280}281 282/*283 * only lower either OFDM or CCK errors per turn284 * we lower the other one next time285 */286static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah)287{288	struct ar5416AniState *aniState = &ah->ani;289 290	/* lower OFDM noise immunity */291	if (aniState->ofdmNoiseImmunityLevel > 0 &&292	    (aniState->ofdmsTurn || aniState->cckNoiseImmunityLevel == 0)) {293		ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel - 1,294				      false);295		return;296	}297 298	/* lower CCK noise immunity */299	if (aniState->cckNoiseImmunityLevel > 0)300		ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel - 1,301				     false);302}303 304/*305 * Restore the ANI parameters in the HAL and reset the statistics.306 * This routine should be called for every hardware reset and for307 * every channel change.308 */309void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)310{311	struct ar5416AniState *aniState = &ah->ani;312	struct ath9k_channel *chan = ah->curchan;313	struct ath_common *common = ath9k_hw_common(ah);314	int ofdm_nil, cck_nil;315 316	if (!chan)317		return;318 319	BUG_ON(aniState == NULL);320	ah->stats.ast_ani_reset++;321 322	ofdm_nil = max_t(int, ATH9K_ANI_OFDM_DEF_LEVEL,323			 aniState->ofdmNoiseImmunityLevel);324	cck_nil = max_t(int, ATH9K_ANI_CCK_DEF_LEVEL,325			 aniState->cckNoiseImmunityLevel);326 327	if (is_scanning ||328	    (ah->opmode != NL80211_IFTYPE_STATION &&329	     ah->opmode != NL80211_IFTYPE_ADHOC)) {330		/*331		 * If we're scanning or in AP mode, the defaults (ini)332		 * should be in place. For an AP we assume the historical333		 * levels for this channel are probably outdated so start334		 * from defaults instead.335		 */336		if (aniState->ofdmNoiseImmunityLevel !=337		    ATH9K_ANI_OFDM_DEF_LEVEL ||338		    aniState->cckNoiseImmunityLevel !=339		    ATH9K_ANI_CCK_DEF_LEVEL) {340			ath_dbg(common, ANI,341				"Restore defaults: opmode %u chan %d Mhz is_scanning=%d ofdm:%d cck:%d\n",342				ah->opmode,343				chan->channel,344				is_scanning,345				aniState->ofdmNoiseImmunityLevel,346				aniState->cckNoiseImmunityLevel);347 348			ofdm_nil = ATH9K_ANI_OFDM_DEF_LEVEL;349			cck_nil = ATH9K_ANI_CCK_DEF_LEVEL;350		}351	} else {352		/*353		 * restore historical levels for this channel354		 */355		ath_dbg(common, ANI,356			"Restore history: opmode %u chan %d Mhz is_scanning=%d ofdm:%d cck:%d\n",357			ah->opmode,358			chan->channel,359			is_scanning,360			aniState->ofdmNoiseImmunityLevel,361			aniState->cckNoiseImmunityLevel);362	}363	ath9k_hw_set_ofdm_nil(ah, ofdm_nil, is_scanning);364	ath9k_hw_set_cck_nil(ah, cck_nil, is_scanning);365 366	ath9k_ani_restart(ah);367}368 369static bool ath9k_hw_ani_read_counters(struct ath_hw *ah)370{371	struct ath_common *common = ath9k_hw_common(ah);372	struct ar5416AniState *aniState = &ah->ani;373	u32 phyCnt1, phyCnt2;374	int32_t listenTime;375 376	ath_hw_cycle_counters_update(common);377	listenTime = ath_hw_get_listen_time(common);378 379	if (listenTime <= 0) {380		ah->stats.ast_ani_lneg_or_lzero++;381		ath9k_ani_restart(ah);382		return false;383	}384 385	aniState->listenTime += listenTime;386 387	ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);388 389	phyCnt1 = REG_READ(ah, AR_PHY_ERR_1);390	phyCnt2 = REG_READ(ah, AR_PHY_ERR_2);391 392	ah->stats.ast_ani_ofdmerrs += phyCnt1 - aniState->ofdmPhyErrCount;393	aniState->ofdmPhyErrCount = phyCnt1;394 395	ah->stats.ast_ani_cckerrs += phyCnt2 - aniState->cckPhyErrCount;396	aniState->cckPhyErrCount = phyCnt2;397 398	return true;399}400 401void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan)402{403	struct ar5416AniState *aniState = &ah->ani;404	struct ath_common *common = ath9k_hw_common(ah);405	u32 ofdmPhyErrRate, cckPhyErrRate;406 407	if (!ath9k_hw_ani_read_counters(ah))408		return;409 410	ofdmPhyErrRate = aniState->ofdmPhyErrCount * 1000 /411			 aniState->listenTime;412	cckPhyErrRate =  aniState->cckPhyErrCount * 1000 /413			 aniState->listenTime;414 415	ath_dbg(common, ANI,416		"listenTime=%d OFDM:%d errs=%d/s CCK:%d errs=%d/s ofdm_turn=%d\n",417		aniState->listenTime,418		aniState->ofdmNoiseImmunityLevel,419		ofdmPhyErrRate, aniState->cckNoiseImmunityLevel,420		cckPhyErrRate, aniState->ofdmsTurn);421 422	if (aniState->listenTime > ah->aniperiod) {423		if (cckPhyErrRate < ah->config.cck_trig_low &&424		    ofdmPhyErrRate < ah->config.ofdm_trig_low) {425			ath9k_hw_ani_lower_immunity(ah);426			aniState->ofdmsTurn = !aniState->ofdmsTurn;427		} else if (ofdmPhyErrRate > ah->config.ofdm_trig_high) {428			ath9k_hw_ani_ofdm_err_trigger(ah);429			aniState->ofdmsTurn = false;430		} else if (cckPhyErrRate > ah->config.cck_trig_high) {431			ath9k_hw_ani_cck_err_trigger(ah);432			aniState->ofdmsTurn = true;433		} else434			return;435			436		ath9k_ani_restart(ah);437	}438}439EXPORT_SYMBOL(ath9k_hw_ani_monitor);440 441void ath9k_enable_mib_counters(struct ath_hw *ah)442{443	struct ath_common *common = ath9k_hw_common(ah);444 445	ath_dbg(common, ANI, "Enable MIB counters\n");446 447	ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);448 449	ENABLE_REGWRITE_BUFFER(ah);450 451	REG_WRITE(ah, AR_FILT_OFDM, 0);452	REG_WRITE(ah, AR_FILT_CCK, 0);453	REG_WRITE(ah, AR_MIBC,454		  ~(AR_MIBC_COW | AR_MIBC_FMC | AR_MIBC_CMC | AR_MIBC_MCS)455		  & 0x0f);456	REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);457	REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);458 459	REGWRITE_BUFFER_FLUSH(ah);460}461 462/* Freeze the MIB counters, get the stats and then clear them */463void ath9k_hw_disable_mib_counters(struct ath_hw *ah)464{465	struct ath_common *common = ath9k_hw_common(ah);466 467	ath_dbg(common, ANI, "Disable MIB counters\n");468 469	REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC);470	ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);471	REG_WRITE(ah, AR_MIBC, AR_MIBC_CMC);472	REG_WRITE(ah, AR_FILT_OFDM, 0);473	REG_WRITE(ah, AR_FILT_CCK, 0);474}475EXPORT_SYMBOL(ath9k_hw_disable_mib_counters);476 477void ath9k_hw_ani_init(struct ath_hw *ah)478{479	struct ath_common *common = ath9k_hw_common(ah);480	struct ar5416AniState *ani = &ah->ani;481 482	ath_dbg(common, ANI, "Initialize ANI\n");483 484	if (AR_SREV_9300_20_OR_LATER(ah)) {485		ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH;486		ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW;487		ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH;488		ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW;489	} else {490		ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH_OLD;491		ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW_OLD;492		ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH_OLD;493		ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW_OLD;494	}495 496	ani->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;497	ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL;498	ani->mrcCCK = AR_SREV_9300_20_OR_LATER(ah) ? true : false;499	ani->ofdmsTurn = true;500	ani->ofdmWeakSigDetect = true;501	ani->cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL;502	ani->ofdmNoiseImmunityLevel = ATH9K_ANI_OFDM_DEF_LEVEL;503 504	/*505	 * since we expect some ongoing maintenance on the tables, let's sanity506	 * check here default level should not modify INI setting.507	 */508	ah->aniperiod = ATH9K_ANI_PERIOD;509	ah->config.ani_poll_interval = ATH9K_ANI_POLLINTERVAL;510 511	ath9k_ani_restart(ah);512	ath9k_enable_mib_counters(ah);513}514