cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

antenna.c (25192B)


      1/*
      2 * Copyright (c) 2012 Qualcomm Atheros, Inc.
      3 *
      4 * Permission to use, copy, modify, and/or distribute this software for any
      5 * purpose with or without fee is hereby granted, provided that the above
      6 * copyright notice and this permission notice appear in all copies.
      7 *
      8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15 */
     16
     17#include "ath9k.h"
     18
     19/*
     20 * AR9285
     21 * ======
     22 *
     23 * EEPROM has 2 4-bit fields containing the card configuration.
     24 *
     25 * antdiv_ctl1:
     26 * ------------
     27 * bb_enable_ant_div_lnadiv : 1
     28 * bb_ant_div_alt_gaintb    : 1
     29 * bb_ant_div_main_gaintb   : 1
     30 * bb_enable_ant_fast_div   : 1
     31 *
     32 * antdiv_ctl2:
     33 * -----------
     34 * bb_ant_div_alt_lnaconf  : 2
     35 * bb_ant_div_main_lnaconf : 2
     36 *
     37 * The EEPROM bits are used as follows:
     38 * ------------------------------------
     39 *
     40 * bb_enable_ant_div_lnadiv      - Enable LNA path rx antenna diversity/combining.
     41 *                                 Set in AR_PHY_MULTICHAIN_GAIN_CTL.
     42 *
     43 * bb_ant_div_[alt/main]_gaintb  - 0 -> Antenna config Alt/Main uses gaintable 0
     44 *                                 1 -> Antenna config Alt/Main uses gaintable 1
     45 *                                 Set in AR_PHY_MULTICHAIN_GAIN_CTL.
     46 *
     47 * bb_enable_ant_fast_div        - Enable fast antenna diversity.
     48 *                                 Set in AR_PHY_CCK_DETECT.
     49 *
     50 * bb_ant_div_[alt/main]_lnaconf - Alt/Main LNA diversity/combining input config.
     51 *                                 Set in AR_PHY_MULTICHAIN_GAIN_CTL.
     52 *                                 10=LNA1
     53 *                                 01=LNA2
     54 *                                 11=LNA1+LNA2
     55 *                                 00=LNA1-LNA2
     56 *
     57 * AR9485 / AR9565 / AR9331
     58 * ========================
     59 *
     60 * The same bits are present in the EEPROM, but the location in the
     61 * EEPROM is different (ant_div_control in ar9300_BaseExtension_1).
     62 *
     63 * ant_div_alt_lnaconf      ==> bit 0~1
     64 * ant_div_main_lnaconf     ==> bit 2~3
     65 * ant_div_alt_gaintb       ==> bit 4
     66 * ant_div_main_gaintb      ==> bit 5
     67 * enable_ant_div_lnadiv    ==> bit 6
     68 * enable_ant_fast_div      ==> bit 7
     69 */
     70
     71static inline bool ath_is_alt_ant_ratio_better(struct ath_ant_comb *antcomb,
     72					       int alt_ratio, int maxdelta,
     73					       int mindelta, int main_rssi_avg,
     74					       int alt_rssi_avg, int pkt_count)
     75{
     76	if (pkt_count <= 50)
     77		return false;
     78
     79	if (alt_rssi_avg > main_rssi_avg + mindelta)
     80		return true;
     81
     82	if (alt_ratio >= antcomb->ant_ratio2 &&
     83	    alt_rssi_avg >= antcomb->low_rssi_thresh &&
     84	    (alt_rssi_avg > main_rssi_avg + maxdelta))
     85		return true;
     86
     87	return false;
     88}
     89
     90static inline bool ath_ant_div_comb_alt_check(struct ath_hw_antcomb_conf *conf,
     91					      struct ath_ant_comb *antcomb,
     92					      int alt_ratio, int alt_rssi_avg,
     93					      int main_rssi_avg)
     94{
     95	bool result, set1, set2;
     96
     97	result = set1 = set2 = false;
     98
     99	if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2 &&
    100	    conf->alt_lna_conf == ATH_ANT_DIV_COMB_LNA1)
    101		set1 = true;
    102
    103	if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA1 &&
    104	    conf->alt_lna_conf == ATH_ANT_DIV_COMB_LNA2)
    105		set2 = true;
    106
    107	switch (conf->div_group) {
    108	case 0:
    109		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
    110			result = true;
    111		break;
    112	case 1:
    113	case 2:
    114		if (alt_rssi_avg < 4 || alt_rssi_avg < antcomb->low_rssi_thresh)
    115			break;
    116
    117		if ((set1 && (alt_rssi_avg >= (main_rssi_avg - 5))) ||
    118		    (set2 && (alt_rssi_avg >= (main_rssi_avg - 2))) ||
    119		    (alt_ratio > antcomb->ant_ratio))
    120			result = true;
    121
    122		break;
    123	case 3:
    124		if (alt_rssi_avg < 4 || alt_rssi_avg < antcomb->low_rssi_thresh)
    125			break;
    126
    127		if ((set1 && (alt_rssi_avg >= (main_rssi_avg - 3))) ||
    128		    (set2 && (alt_rssi_avg >= (main_rssi_avg + 3))) ||
    129		    (alt_ratio > antcomb->ant_ratio))
    130			result = true;
    131
    132		break;
    133	}
    134
    135	return result;
    136}
    137
    138static void ath_lnaconf_alt_good_scan(struct ath_ant_comb *antcomb,
    139				      struct ath_hw_antcomb_conf ant_conf,
    140				      int main_rssi_avg)
    141{
    142	antcomb->quick_scan_cnt = 0;
    143
    144	if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
    145		antcomb->rssi_lna2 = main_rssi_avg;
    146	else if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA1)
    147		antcomb->rssi_lna1 = main_rssi_avg;
    148
    149	switch ((ant_conf.main_lna_conf << 4) | ant_conf.alt_lna_conf) {
    150	case 0x10: /* LNA2 A-B */
    151		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
    152		antcomb->first_quick_scan_conf =
    153			ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
    154		antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
    155		break;
    156	case 0x20: /* LNA1 A-B */
    157		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
    158		antcomb->first_quick_scan_conf =
    159			ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
    160		antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
    161		break;
    162	case 0x21: /* LNA1 LNA2 */
    163		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA2;
    164		antcomb->first_quick_scan_conf =
    165			ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
    166		antcomb->second_quick_scan_conf =
    167			ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
    168		break;
    169	case 0x12: /* LNA2 LNA1 */
    170		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1;
    171		antcomb->first_quick_scan_conf =
    172			ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
    173		antcomb->second_quick_scan_conf =
    174			ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
    175		break;
    176	case 0x13: /* LNA2 A+B */
    177		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
    178		antcomb->first_quick_scan_conf =
    179			ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
    180		antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
    181		break;
    182	case 0x23: /* LNA1 A+B */
    183		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
    184		antcomb->first_quick_scan_conf =
    185			ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
    186		antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
    187		break;
    188	default:
    189		break;
    190	}
    191}
    192
    193static void ath_ant_set_alt_ratio(struct ath_ant_comb *antcomb,
    194				  struct ath_hw_antcomb_conf *conf)
    195{
    196	/* set alt to the conf with maximun ratio */
    197	if (antcomb->first_ratio && antcomb->second_ratio) {
    198		if (antcomb->rssi_second > antcomb->rssi_third) {
    199			/* first alt*/
    200			if ((antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
    201			    (antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2))
    202				/* Set alt LNA1 or LNA2*/
    203				if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
    204					conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
    205				else
    206					conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
    207			else
    208				/* Set alt to A+B or A-B */
    209				conf->alt_lna_conf =
    210					antcomb->first_quick_scan_conf;
    211		} else if ((antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
    212			   (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2)) {
    213			/* Set alt LNA1 or LNA2 */
    214			if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
    215				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
    216			else
    217				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
    218		} else {
    219			/* Set alt to A+B or A-B */
    220			conf->alt_lna_conf = antcomb->second_quick_scan_conf;
    221		}
    222	} else if (antcomb->first_ratio) {
    223		/* first alt */
    224		if ((antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
    225		    (antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2))
    226			/* Set alt LNA1 or LNA2 */
    227			if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
    228				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
    229			else
    230				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
    231		else
    232			/* Set alt to A+B or A-B */
    233			conf->alt_lna_conf = antcomb->first_quick_scan_conf;
    234	} else if (antcomb->second_ratio) {
    235		/* second alt */
    236		if ((antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
    237		    (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2))
    238			/* Set alt LNA1 or LNA2 */
    239			if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
    240				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
    241			else
    242				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
    243		else
    244			/* Set alt to A+B or A-B */
    245			conf->alt_lna_conf = antcomb->second_quick_scan_conf;
    246	} else {
    247		/* main is largest */
    248		if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) ||
    249		    (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2))
    250			/* Set alt LNA1 or LNA2 */
    251			if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
    252				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
    253			else
    254				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
    255		else
    256			/* Set alt to A+B or A-B */
    257			conf->alt_lna_conf = antcomb->main_conf;
    258	}
    259}
    260
    261static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
    262				       struct ath_hw_antcomb_conf *div_ant_conf,
    263				       int main_rssi_avg, int alt_rssi_avg,
    264				       int alt_ratio)
    265{
    266	/* alt_good */
    267	switch (antcomb->quick_scan_cnt) {
    268	case 0:
    269		/* set alt to main, and alt to first conf */
    270		div_ant_conf->main_lna_conf = antcomb->main_conf;
    271		div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
    272		break;
    273	case 1:
    274		/* set alt to main, and alt to first conf */
    275		div_ant_conf->main_lna_conf = antcomb->main_conf;
    276		div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
    277		antcomb->rssi_first = main_rssi_avg;
    278		antcomb->rssi_second = alt_rssi_avg;
    279
    280		if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
    281			/* main is LNA1 */
    282			if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
    283						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
    284						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
    285						main_rssi_avg, alt_rssi_avg,
    286						antcomb->total_pkt_count))
    287				antcomb->first_ratio = true;
    288			else
    289				antcomb->first_ratio = false;
    290		} else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
    291			if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
    292						ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
    293						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
    294						main_rssi_avg, alt_rssi_avg,
    295						antcomb->total_pkt_count))
    296				antcomb->first_ratio = true;
    297			else
    298				antcomb->first_ratio = false;
    299		} else {
    300			if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
    301						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
    302						0,
    303						main_rssi_avg, alt_rssi_avg,
    304						antcomb->total_pkt_count))
    305				antcomb->first_ratio = true;
    306			else
    307				antcomb->first_ratio = false;
    308		}
    309		break;
    310	case 2:
    311		antcomb->alt_good = false;
    312		antcomb->scan_not_start = false;
    313		antcomb->scan = false;
    314		antcomb->rssi_first = main_rssi_avg;
    315		antcomb->rssi_third = alt_rssi_avg;
    316
    317		switch(antcomb->second_quick_scan_conf) {
    318		case ATH_ANT_DIV_COMB_LNA1:
    319			antcomb->rssi_lna1 = alt_rssi_avg;
    320			break;
    321		case ATH_ANT_DIV_COMB_LNA2:
    322			antcomb->rssi_lna2 = alt_rssi_avg;
    323			break;
    324		case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
    325			if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2)
    326				antcomb->rssi_lna2 = main_rssi_avg;
    327			else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1)
    328				antcomb->rssi_lna1 = main_rssi_avg;
    329			break;
    330		default:
    331			break;
    332		}
    333
    334		if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
    335		    div_ant_conf->lna1_lna2_switch_delta)
    336			div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
    337		else
    338			div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
    339
    340		if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
    341			if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
    342						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
    343						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
    344						main_rssi_avg, alt_rssi_avg,
    345						antcomb->total_pkt_count))
    346				antcomb->second_ratio = true;
    347			else
    348				antcomb->second_ratio = false;
    349		} else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
    350			if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
    351						ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
    352						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
    353						main_rssi_avg, alt_rssi_avg,
    354						antcomb->total_pkt_count))
    355				antcomb->second_ratio = true;
    356			else
    357				antcomb->second_ratio = false;
    358		} else {
    359			if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
    360						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
    361						0,
    362						main_rssi_avg, alt_rssi_avg,
    363						antcomb->total_pkt_count))
    364				antcomb->second_ratio = true;
    365			else
    366				antcomb->second_ratio = false;
    367		}
    368
    369		ath_ant_set_alt_ratio(antcomb, div_ant_conf);
    370
    371		break;
    372	default:
    373		break;
    374	}
    375}
    376
    377static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf,
    378					  struct ath_ant_comb *antcomb,
    379					  int alt_ratio)
    380{
    381	ant_conf->main_gaintb = 0;
    382	ant_conf->alt_gaintb = 0;
    383
    384	if (ant_conf->div_group == 0) {
    385		/* Adjust the fast_div_bias based on main and alt lna conf */
    386		switch ((ant_conf->main_lna_conf << 4) |
    387				ant_conf->alt_lna_conf) {
    388		case 0x01: /* A-B LNA2 */
    389			ant_conf->fast_div_bias = 0x3b;
    390			break;
    391		case 0x02: /* A-B LNA1 */
    392			ant_conf->fast_div_bias = 0x3d;
    393			break;
    394		case 0x03: /* A-B A+B */
    395			ant_conf->fast_div_bias = 0x1;
    396			break;
    397		case 0x10: /* LNA2 A-B */
    398			ant_conf->fast_div_bias = 0x7;
    399			break;
    400		case 0x12: /* LNA2 LNA1 */
    401			ant_conf->fast_div_bias = 0x2;
    402			break;
    403		case 0x13: /* LNA2 A+B */
    404			ant_conf->fast_div_bias = 0x7;
    405			break;
    406		case 0x20: /* LNA1 A-B */
    407			ant_conf->fast_div_bias = 0x6;
    408			break;
    409		case 0x21: /* LNA1 LNA2 */
    410			ant_conf->fast_div_bias = 0x0;
    411			break;
    412		case 0x23: /* LNA1 A+B */
    413			ant_conf->fast_div_bias = 0x6;
    414			break;
    415		case 0x30: /* A+B A-B */
    416			ant_conf->fast_div_bias = 0x1;
    417			break;
    418		case 0x31: /* A+B LNA2 */
    419			ant_conf->fast_div_bias = 0x3b;
    420			break;
    421		case 0x32: /* A+B LNA1 */
    422			ant_conf->fast_div_bias = 0x3d;
    423			break;
    424		default:
    425			break;
    426		}
    427	} else if (ant_conf->div_group == 1) {
    428		/* Adjust the fast_div_bias based on main and alt_lna_conf */
    429		switch ((ant_conf->main_lna_conf << 4) |
    430			ant_conf->alt_lna_conf) {
    431		case 0x01: /* A-B LNA2 */
    432			ant_conf->fast_div_bias = 0x1;
    433			break;
    434		case 0x02: /* A-B LNA1 */
    435			ant_conf->fast_div_bias = 0x1;
    436			break;
    437		case 0x03: /* A-B A+B */
    438			ant_conf->fast_div_bias = 0x1;
    439			break;
    440		case 0x10: /* LNA2 A-B */
    441			if (!(antcomb->scan) &&
    442			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
    443				ant_conf->fast_div_bias = 0x3f;
    444			else
    445				ant_conf->fast_div_bias = 0x1;
    446			break;
    447		case 0x12: /* LNA2 LNA1 */
    448			ant_conf->fast_div_bias = 0x1;
    449			break;
    450		case 0x13: /* LNA2 A+B */
    451			if (!(antcomb->scan) &&
    452			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
    453				ant_conf->fast_div_bias = 0x3f;
    454			else
    455				ant_conf->fast_div_bias = 0x1;
    456			break;
    457		case 0x20: /* LNA1 A-B */
    458			if (!(antcomb->scan) &&
    459			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
    460				ant_conf->fast_div_bias = 0x3f;
    461			else
    462				ant_conf->fast_div_bias = 0x1;
    463			break;
    464		case 0x21: /* LNA1 LNA2 */
    465			ant_conf->fast_div_bias = 0x1;
    466			break;
    467		case 0x23: /* LNA1 A+B */
    468			if (!(antcomb->scan) &&
    469			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
    470				ant_conf->fast_div_bias = 0x3f;
    471			else
    472				ant_conf->fast_div_bias = 0x1;
    473			break;
    474		case 0x30: /* A+B A-B */
    475			ant_conf->fast_div_bias = 0x1;
    476			break;
    477		case 0x31: /* A+B LNA2 */
    478			ant_conf->fast_div_bias = 0x1;
    479			break;
    480		case 0x32: /* A+B LNA1 */
    481			ant_conf->fast_div_bias = 0x1;
    482			break;
    483		default:
    484			break;
    485		}
    486	} else if (ant_conf->div_group == 2) {
    487		/* Adjust the fast_div_bias based on main and alt_lna_conf */
    488		switch ((ant_conf->main_lna_conf << 4) |
    489				ant_conf->alt_lna_conf) {
    490		case 0x01: /* A-B LNA2 */
    491			ant_conf->fast_div_bias = 0x1;
    492			break;
    493		case 0x02: /* A-B LNA1 */
    494			ant_conf->fast_div_bias = 0x1;
    495			break;
    496		case 0x03: /* A-B A+B */
    497			ant_conf->fast_div_bias = 0x1;
    498			break;
    499		case 0x10: /* LNA2 A-B */
    500			if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
    501				ant_conf->fast_div_bias = 0x1;
    502			else
    503				ant_conf->fast_div_bias = 0x2;
    504			break;
    505		case 0x12: /* LNA2 LNA1 */
    506			ant_conf->fast_div_bias = 0x1;
    507			break;
    508		case 0x13: /* LNA2 A+B */
    509			if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
    510				ant_conf->fast_div_bias = 0x1;
    511			else
    512				ant_conf->fast_div_bias = 0x2;
    513			break;
    514		case 0x20: /* LNA1 A-B */
    515			if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
    516				ant_conf->fast_div_bias = 0x1;
    517			else
    518				ant_conf->fast_div_bias = 0x2;
    519			break;
    520		case 0x21: /* LNA1 LNA2 */
    521			ant_conf->fast_div_bias = 0x1;
    522			break;
    523		case 0x23: /* LNA1 A+B */
    524			if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
    525				ant_conf->fast_div_bias = 0x1;
    526			else
    527				ant_conf->fast_div_bias = 0x2;
    528			break;
    529		case 0x30: /* A+B A-B */
    530			ant_conf->fast_div_bias = 0x1;
    531			break;
    532		case 0x31: /* A+B LNA2 */
    533			ant_conf->fast_div_bias = 0x1;
    534			break;
    535		case 0x32: /* A+B LNA1 */
    536			ant_conf->fast_div_bias = 0x1;
    537			break;
    538		default:
    539			break;
    540		}
    541
    542		if (antcomb->fast_div_bias)
    543			ant_conf->fast_div_bias = antcomb->fast_div_bias;
    544	} else if (ant_conf->div_group == 3) {
    545		switch ((ant_conf->main_lna_conf << 4) |
    546			ant_conf->alt_lna_conf) {
    547		case 0x01: /* A-B LNA2 */
    548			ant_conf->fast_div_bias = 0x1;
    549			break;
    550		case 0x02: /* A-B LNA1 */
    551			ant_conf->fast_div_bias = 0x39;
    552			break;
    553		case 0x03: /* A-B A+B */
    554			ant_conf->fast_div_bias = 0x1;
    555			break;
    556		case 0x10: /* LNA2 A-B */
    557			ant_conf->fast_div_bias = 0x2;
    558			break;
    559		case 0x12: /* LNA2 LNA1 */
    560			ant_conf->fast_div_bias = 0x3f;
    561			break;
    562		case 0x13: /* LNA2 A+B */
    563			ant_conf->fast_div_bias = 0x2;
    564			break;
    565		case 0x20: /* LNA1 A-B */
    566			ant_conf->fast_div_bias = 0x3;
    567			break;
    568		case 0x21: /* LNA1 LNA2 */
    569			ant_conf->fast_div_bias = 0x3;
    570			break;
    571		case 0x23: /* LNA1 A+B */
    572			ant_conf->fast_div_bias = 0x3;
    573			break;
    574		case 0x30: /* A+B A-B */
    575			ant_conf->fast_div_bias = 0x1;
    576			break;
    577		case 0x31: /* A+B LNA2 */
    578			ant_conf->fast_div_bias = 0x6;
    579			break;
    580		case 0x32: /* A+B LNA1 */
    581			ant_conf->fast_div_bias = 0x1;
    582			break;
    583		default:
    584			break;
    585		}
    586	}
    587}
    588
    589static void ath_ant_try_scan(struct ath_ant_comb *antcomb,
    590			     struct ath_hw_antcomb_conf *conf,
    591			     int curr_alt_set, int alt_rssi_avg,
    592			     int main_rssi_avg)
    593{
    594	switch (curr_alt_set) {
    595	case ATH_ANT_DIV_COMB_LNA2:
    596		antcomb->rssi_lna2 = alt_rssi_avg;
    597		antcomb->rssi_lna1 = main_rssi_avg;
    598		antcomb->scan = true;
    599		/* set to A+B */
    600		conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
    601		conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
    602		break;
    603	case ATH_ANT_DIV_COMB_LNA1:
    604		antcomb->rssi_lna1 = alt_rssi_avg;
    605		antcomb->rssi_lna2 = main_rssi_avg;
    606		antcomb->scan = true;
    607		/* set to A+B */
    608		conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
    609		conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
    610		break;
    611	case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
    612		antcomb->rssi_add = alt_rssi_avg;
    613		antcomb->scan = true;
    614		/* set to A-B */
    615		conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
    616		break;
    617	case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2:
    618		antcomb->rssi_sub = alt_rssi_avg;
    619		antcomb->scan = false;
    620		if (antcomb->rssi_lna2 >
    621		    (antcomb->rssi_lna1 + conf->lna1_lna2_switch_delta)) {
    622			/* use LNA2 as main LNA */
    623			if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
    624			    (antcomb->rssi_add > antcomb->rssi_sub)) {
    625				/* set to A+B */
    626				conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
    627				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
    628			} else if (antcomb->rssi_sub >
    629				   antcomb->rssi_lna1) {
    630				/* set to A-B */
    631				conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
    632				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
    633			} else {
    634				/* set to LNA1 */
    635				conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
    636				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
    637			}
    638		} else {
    639			/* use LNA1 as main LNA */
    640			if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
    641			    (antcomb->rssi_add > antcomb->rssi_sub)) {
    642				/* set to A+B */
    643				conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
    644				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
    645			} else if (antcomb->rssi_sub >
    646				   antcomb->rssi_lna1) {
    647				/* set to A-B */
    648				conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
    649				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
    650			} else {
    651				/* set to LNA2 */
    652				conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
    653				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
    654			}
    655		}
    656		break;
    657	default:
    658		break;
    659	}
    660}
    661
    662static bool ath_ant_try_switch(struct ath_hw_antcomb_conf *div_ant_conf,
    663			       struct ath_ant_comb *antcomb,
    664			       int alt_ratio, int alt_rssi_avg,
    665			       int main_rssi_avg, int curr_main_set,
    666			       int curr_alt_set)
    667{
    668	bool ret = false;
    669
    670	if (ath_ant_div_comb_alt_check(div_ant_conf, antcomb, alt_ratio,
    671				       alt_rssi_avg, main_rssi_avg)) {
    672		if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) {
    673			/*
    674			 * Switch main and alt LNA.
    675			 */
    676			div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
    677			div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
    678		} else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) {
    679			div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
    680			div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
    681		}
    682
    683		ret = true;
    684	} else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) &&
    685		   (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) {
    686		/*
    687		  Set alt to another LNA.
    688		*/
    689		if (curr_main_set == ATH_ANT_DIV_COMB_LNA2)
    690			div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
    691		else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1)
    692			div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
    693
    694		ret = true;
    695	}
    696
    697	return ret;
    698}
    699
    700static bool ath_ant_short_scan_check(struct ath_ant_comb *antcomb)
    701{
    702	int alt_ratio;
    703
    704	if (!antcomb->scan || !antcomb->alt_good)
    705		return false;
    706
    707	if (time_after(jiffies, antcomb->scan_start_time +
    708		       msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
    709		return true;
    710
    711	if (antcomb->total_pkt_count == ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
    712		alt_ratio = ((antcomb->alt_recv_cnt * 100) /
    713			     antcomb->total_pkt_count);
    714		if (alt_ratio < antcomb->ant_ratio)
    715			return true;
    716	}
    717
    718	return false;
    719}
    720
    721void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs)
    722{
    723	struct ath_hw_antcomb_conf div_ant_conf;
    724	struct ath_ant_comb *antcomb = &sc->ant_comb;
    725	int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
    726	int curr_main_set;
    727	int main_rssi = rs->rs_rssi_ctl[0];
    728	int alt_rssi = rs->rs_rssi_ctl[1];
    729	int rx_ant_conf,  main_ant_conf;
    730	bool short_scan = false, ret;
    731
    732	rx_ant_conf = (rs->rs_rssi_ctl[2] >> ATH_ANT_RX_CURRENT_SHIFT) &
    733		       ATH_ANT_RX_MASK;
    734	main_ant_conf = (rs->rs_rssi_ctl[2] >> ATH_ANT_RX_MAIN_SHIFT) &
    735			 ATH_ANT_RX_MASK;
    736
    737	if (alt_rssi >= antcomb->low_rssi_thresh) {
    738		antcomb->ant_ratio = ATH_ANT_DIV_COMB_ALT_ANT_RATIO;
    739		antcomb->ant_ratio2 = ATH_ANT_DIV_COMB_ALT_ANT_RATIO2;
    740	} else {
    741		antcomb->ant_ratio = ATH_ANT_DIV_COMB_ALT_ANT_RATIO_LOW_RSSI;
    742		antcomb->ant_ratio2 = ATH_ANT_DIV_COMB_ALT_ANT_RATIO2_LOW_RSSI;
    743	}
    744
    745	/* Record packet only when both main_rssi and  alt_rssi is positive */
    746	if (main_rssi > 0 && alt_rssi > 0) {
    747		antcomb->total_pkt_count++;
    748		antcomb->main_total_rssi += main_rssi;
    749		antcomb->alt_total_rssi  += alt_rssi;
    750
    751		if (main_ant_conf == rx_ant_conf)
    752			antcomb->main_recv_cnt++;
    753		else
    754			antcomb->alt_recv_cnt++;
    755	}
    756
    757	if (main_ant_conf == rx_ant_conf) {
    758		ANT_STAT_INC(sc, ANT_MAIN, recv_cnt);
    759		ANT_LNA_INC(sc, ANT_MAIN, rx_ant_conf);
    760	} else {
    761		ANT_STAT_INC(sc, ANT_ALT, recv_cnt);
    762		ANT_LNA_INC(sc, ANT_ALT, rx_ant_conf);
    763	}
    764
    765	/* Short scan check */
    766	short_scan = ath_ant_short_scan_check(antcomb);
    767
    768	if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
    769	     rs->rs_moreaggr) && !short_scan)
    770		return;
    771
    772	if (antcomb->total_pkt_count) {
    773		alt_ratio = ((antcomb->alt_recv_cnt * 100) /
    774			     antcomb->total_pkt_count);
    775		main_rssi_avg = (antcomb->main_total_rssi /
    776				 antcomb->total_pkt_count);
    777		alt_rssi_avg = (antcomb->alt_total_rssi /
    778				 antcomb->total_pkt_count);
    779	}
    780
    781	ath9k_hw_antdiv_comb_conf_get(sc->sc_ah, &div_ant_conf);
    782	curr_alt_set = div_ant_conf.alt_lna_conf;
    783	curr_main_set = div_ant_conf.main_lna_conf;
    784	antcomb->count++;
    785
    786	if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
    787		if (alt_ratio > antcomb->ant_ratio) {
    788			ath_lnaconf_alt_good_scan(antcomb, div_ant_conf,
    789						  main_rssi_avg);
    790			antcomb->alt_good = true;
    791		} else {
    792			antcomb->alt_good = false;
    793		}
    794
    795		antcomb->count = 0;
    796		antcomb->scan = true;
    797		antcomb->scan_not_start = true;
    798	}
    799
    800	if (!antcomb->scan) {
    801		ret = ath_ant_try_switch(&div_ant_conf, antcomb, alt_ratio,
    802					 alt_rssi_avg, main_rssi_avg,
    803					 curr_main_set, curr_alt_set);
    804		if (ret)
    805			goto div_comb_done;
    806	}
    807
    808	if (!antcomb->scan &&
    809	    (alt_rssi_avg < (main_rssi_avg + div_ant_conf.lna1_lna2_delta)))
    810		goto div_comb_done;
    811
    812	if (!antcomb->scan_not_start) {
    813		ath_ant_try_scan(antcomb, &div_ant_conf, curr_alt_set,
    814				 alt_rssi_avg, main_rssi_avg);
    815	} else {
    816		if (!antcomb->alt_good) {
    817			antcomb->scan_not_start = false;
    818			/* Set alt to another LNA */
    819			if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) {
    820				div_ant_conf.main_lna_conf =
    821					ATH_ANT_DIV_COMB_LNA2;
    822				div_ant_conf.alt_lna_conf =
    823					ATH_ANT_DIV_COMB_LNA1;
    824			} else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) {
    825				div_ant_conf.main_lna_conf =
    826					ATH_ANT_DIV_COMB_LNA1;
    827				div_ant_conf.alt_lna_conf =
    828					ATH_ANT_DIV_COMB_LNA2;
    829			}
    830			goto div_comb_done;
    831		}
    832		ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
    833						   main_rssi_avg, alt_rssi_avg,
    834						   alt_ratio);
    835		antcomb->quick_scan_cnt++;
    836	}
    837
    838div_comb_done:
    839	ath_ant_div_conf_fast_divbias(&div_ant_conf, antcomb, alt_ratio);
    840	ath9k_hw_antdiv_comb_conf_set(sc->sc_ah, &div_ant_conf);
    841	ath9k_debug_stat_ant(sc, &div_ant_conf, main_rssi_avg, alt_rssi_avg);
    842
    843	antcomb->scan_start_time = jiffies;
    844	antcomb->total_pkt_count = 0;
    845	antcomb->main_total_rssi = 0;
    846	antcomb->alt_total_rssi = 0;
    847	antcomb->main_recv_cnt = 0;
    848	antcomb->alt_recv_cnt = 0;
    849}