root/drivers/staging/rtl8723bs/hal/odm_DIG.c
// SPDX-License-Identifier: GPL-2.0
/******************************************************************************
 *
 * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
 *
 ******************************************************************************/

#include "odm_precomp.h"

void odm_NHMCounterStatisticsInit(void *pDM_VOID)
{
        struct dm_odm_t *pDM_Odm = (struct dm_odm_t *)pDM_VOID;

        /* PHY parameters initialize for n series */
        rtw_write16(pDM_Odm->Adapter, ODM_REG_NHM_TIMER_11N+2, 0x2710); /* 0x894[31:16]= 0x2710 Time duration for NHM unit: 4us, 0x2710 =40ms */
        /* rtw_write16(pDM_Odm->Adapter, ODM_REG_NHM_TIMER_11N+2, 0x4e20);      0x894[31:16]= 0x4e20    Time duration for NHM unit: 4us, 0x4e20 =80ms */
        rtw_write16(pDM_Odm->Adapter, ODM_REG_NHM_TH9_TH10_11N+2, 0xffff);      /* 0x890[31:16]= 0xffff th_9, th_10 */
        /* rtw_write32(pDM_Odm->Adapter, ODM_REG_NHM_TH3_TO_TH0_11N, 0xffffff5c);       0x898 = 0xffffff5c              th_3, th_2, th_1, th_0 */
        rtw_write32(pDM_Odm->Adapter, ODM_REG_NHM_TH3_TO_TH0_11N, 0xffffff52);  /* 0x898 = 0xffffff52           th_3, th_2, th_1, th_0 */
        rtw_write32(pDM_Odm->Adapter, ODM_REG_NHM_TH7_TO_TH4_11N, 0xffffffff);  /* 0x89c = 0xffffffff           th_7, th_6, th_5, th_4 */
        PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_FPGA0_IQK_11N, bMaskByte0, 0xff);                /* 0xe28[7:0]= 0xff             th_8 */
        PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_NHM_TH9_TH10_11N, BIT10|BIT9|BIT8, 0x7); /* 0x890[9:8]=3                 enable CCX */
        PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_OFDM_FA_RSTC_11N, BIT7, 0x1);            /* 0xc0c[7]= 1                  max power among all RX ants */
}

void odm_NHMCounterStatistics(void *pDM_VOID)
{
        struct dm_odm_t *pDM_Odm = (struct dm_odm_t *)pDM_VOID;

        /*  Get NHM report */
        odm_GetNHMCounterStatistics(pDM_Odm);

        /*  Reset NHM counter */
        odm_NHMCounterStatisticsReset(pDM_Odm);
}

void odm_GetNHMCounterStatistics(void *pDM_VOID)
{
        struct dm_odm_t *pDM_Odm = (struct dm_odm_t *)pDM_VOID;
        u32 value32 = 0;

        value32 = PHY_QueryBBReg(pDM_Odm->Adapter, ODM_REG_NHM_CNT_11N, bMaskDWord);

        pDM_Odm->NHM_cnt_0 = (u8)(value32 & bMaskByte0);
}

void odm_NHMCounterStatisticsReset(void *pDM_VOID)
{
        struct dm_odm_t *pDM_Odm = (struct dm_odm_t *)pDM_VOID;

        PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_NHM_TH9_TH10_11N, BIT1, 0);
        PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_NHM_TH9_TH10_11N, BIT1, 1);
}

void odm_NHMBBInit(void *pDM_VOID)
{
        struct dm_odm_t *pDM_Odm = (struct dm_odm_t *)pDM_VOID;

        pDM_Odm->adaptivity_flag = false;
        pDM_Odm->tolerance_cnt = 3;
        pDM_Odm->NHMLastTxOkcnt = 0;
        pDM_Odm->NHMLastRxOkcnt = 0;
        pDM_Odm->NHMCurTxOkcnt = 0;
        pDM_Odm->NHMCurRxOkcnt = 0;
}

/*  */
void odm_NHMBB(void *pDM_VOID)
{
        struct dm_odm_t *pDM_Odm = (struct dm_odm_t *)pDM_VOID;
        /* u8 test_status; */
        /* struct false_ALARM_STATISTICS *pFalseAlmCnt = &pDM_Odm->FalseAlmCnt; */

        pDM_Odm->NHMCurTxOkcnt =
                *(pDM_Odm->pNumTxBytesUnicast)-pDM_Odm->NHMLastTxOkcnt;
        pDM_Odm->NHMCurRxOkcnt =
                *(pDM_Odm->pNumRxBytesUnicast)-pDM_Odm->NHMLastRxOkcnt;
        pDM_Odm->NHMLastTxOkcnt =
                *(pDM_Odm->pNumTxBytesUnicast);
        pDM_Odm->NHMLastRxOkcnt =
                *(pDM_Odm->pNumRxBytesUnicast);


        if ((pDM_Odm->NHMCurTxOkcnt) + 1 > (u64)(pDM_Odm->NHMCurRxOkcnt<<2) + 1) { /* Tx > 4*Rx possible for adaptivity test */
                if (pDM_Odm->NHM_cnt_0 >= 190 || pDM_Odm->adaptivity_flag == true) {
                        /* Enable EDCCA since it is possible running Adaptivity testing */
                        /* test_status = 1; */
                        pDM_Odm->adaptivity_flag = true;
                        pDM_Odm->tolerance_cnt = 0;
                } else {
                        if (pDM_Odm->tolerance_cnt < 3)
                                pDM_Odm->tolerance_cnt = pDM_Odm->tolerance_cnt + 1;
                        else
                                pDM_Odm->tolerance_cnt = 4;
                        /* test_status = 5; */
                        if (pDM_Odm->tolerance_cnt > 3) {
                                /* test_status = 3; */
                                pDM_Odm->adaptivity_flag = false;
                        }
                }
        } else { /*  TX<RX */
                if (pDM_Odm->adaptivity_flag == true && pDM_Odm->NHM_cnt_0 <= 200) {
                        /* test_status = 2; */
                        pDM_Odm->tolerance_cnt = 0;
                } else {
                        if (pDM_Odm->tolerance_cnt < 3)
                                pDM_Odm->tolerance_cnt = pDM_Odm->tolerance_cnt + 1;
                        else
                                pDM_Odm->tolerance_cnt = 4;
                        /* test_status = 5; */
                        if (pDM_Odm->tolerance_cnt > 3) {
                                /* test_status = 4; */
                                pDM_Odm->adaptivity_flag = false;
                        }
                }
        }
}

void odm_SearchPwdBLowerBound(void *pDM_VOID, u8 IGI_target)
{
        struct dm_odm_t *pDM_Odm = (struct dm_odm_t *)pDM_VOID;
        u32 value32 = 0;
        u8 cnt, IGI;
        bool bAdjust = true;
        s8 TH_L2H_dmc, TH_H2L_dmc;
        s8 Diff;

        IGI = 0x50; /*  find H2L, L2H lower bound */
        ODM_Write_DIG(pDM_Odm, IGI);


        Diff = IGI_target-(s8)IGI;
        TH_L2H_dmc = pDM_Odm->TH_L2H_ini + Diff;
        if (TH_L2H_dmc > 10)
                TH_L2H_dmc = 10;
        TH_H2L_dmc = TH_L2H_dmc - pDM_Odm->TH_EDCCA_HL_diff;
        PHY_SetBBReg(pDM_Odm->Adapter, rOFDM0_ECCAThreshold, bMaskByte0, (u8)TH_L2H_dmc);
        PHY_SetBBReg(pDM_Odm->Adapter, rOFDM0_ECCAThreshold, bMaskByte2, (u8)TH_H2L_dmc);

        mdelay(5);

        while (bAdjust) {
                for (cnt = 0; cnt < 20; cnt++) {
                        value32 = PHY_QueryBBReg(pDM_Odm->Adapter, ODM_REG_RPT_11N, bMaskDWord);

                        if (value32 & BIT30)
                                pDM_Odm->txEdcca1 = pDM_Odm->txEdcca1 + 1;
                        else if (value32 & BIT29)
                                pDM_Odm->txEdcca1 = pDM_Odm->txEdcca1 + 1;
                        else
                                pDM_Odm->txEdcca0 = pDM_Odm->txEdcca0 + 1;
                }

                if (pDM_Odm->txEdcca1 > 5) {
                        IGI = IGI-1;
                        TH_L2H_dmc = TH_L2H_dmc + 1;
                        if (TH_L2H_dmc > 10)
                                TH_L2H_dmc = 10;
                        TH_H2L_dmc = TH_L2H_dmc - pDM_Odm->TH_EDCCA_HL_diff;
                        PHY_SetBBReg(pDM_Odm->Adapter, rOFDM0_ECCAThreshold, bMaskByte0, (u8)TH_L2H_dmc);
                        PHY_SetBBReg(pDM_Odm->Adapter, rOFDM0_ECCAThreshold, bMaskByte2, (u8)TH_H2L_dmc);

                        pDM_Odm->TxHangFlg = true;
                        pDM_Odm->txEdcca1 = 0;
                        pDM_Odm->txEdcca0 = 0;

                        if (TH_L2H_dmc == 10) {
                                bAdjust = false;
                                pDM_Odm->TxHangFlg = false;
                                pDM_Odm->txEdcca1 = 0;
                                pDM_Odm->txEdcca0 = 0;
                                pDM_Odm->H2L_lb = TH_H2L_dmc;
                                pDM_Odm->L2H_lb = TH_L2H_dmc;
                                pDM_Odm->Adaptivity_IGI_upper = IGI;
                        }
                } else {
                        bAdjust = false;
                        pDM_Odm->TxHangFlg = false;
                        pDM_Odm->txEdcca1 = 0;
                        pDM_Odm->txEdcca0 = 0;
                        pDM_Odm->H2L_lb = TH_H2L_dmc;
                        pDM_Odm->L2H_lb = TH_L2H_dmc;
                        pDM_Odm->Adaptivity_IGI_upper = IGI;
                }
        }
}

void odm_AdaptivityInit(void *pDM_VOID)
{
        struct dm_odm_t *pDM_Odm = (struct dm_odm_t *)pDM_VOID;

        if (pDM_Odm->Carrier_Sense_enable == false)
                pDM_Odm->TH_L2H_ini = 0xf7; /*  -7 */
        else
                pDM_Odm->TH_L2H_ini = 0xa;

        pDM_Odm->AdapEn_RSSI = 20;
        pDM_Odm->TH_EDCCA_HL_diff = 7;

        pDM_Odm->IGI_Base = 0x32;
        pDM_Odm->IGI_target = 0x1c;
        pDM_Odm->ForceEDCCA = 0;
        pDM_Odm->NHM_disable = false;
        pDM_Odm->TxHangFlg = true;
        pDM_Odm->txEdcca0 = 0;
        pDM_Odm->txEdcca1 = 0;
        pDM_Odm->H2L_lb = 0;
        pDM_Odm->L2H_lb = 0;
        pDM_Odm->Adaptivity_IGI_upper = 0;
        odm_NHMBBInit(pDM_Odm);

        PHY_SetBBReg(pDM_Odm->Adapter, REG_RD_CTRL, BIT11, 1); /*  stop counting if EDCCA is asserted */
}


void odm_Adaptivity(void *pDM_VOID, u8 IGI)
{
        struct dm_odm_t *pDM_Odm = (struct dm_odm_t *)pDM_VOID;
        s8 TH_L2H_dmc, TH_H2L_dmc;
        s8 Diff, IGI_target;
        bool EDCCA_State = false;

        if (!(pDM_Odm->SupportAbility & ODM_BB_ADAPTIVITY)) {
                return;
        }

        if (*pDM_Odm->pBandWidth == ODM_BW20M) /* CHANNEL_WIDTH_20 */
                IGI_target = pDM_Odm->IGI_Base;
        else if (*pDM_Odm->pBandWidth == ODM_BW40M)
                IGI_target = pDM_Odm->IGI_Base + 2;
        else
                IGI_target = pDM_Odm->IGI_Base;
        pDM_Odm->IGI_target = (u8) IGI_target;

        /* Search pwdB lower bound */
        if (pDM_Odm->TxHangFlg == true) {
                PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_DBG_RPT_11N, bMaskDWord, 0x208);
                odm_SearchPwdBLowerBound(pDM_Odm, pDM_Odm->IGI_target);
        }

        if ((!pDM_Odm->bLinked) || (*pDM_Odm->pChannel > 149)) { /*  Band4 doesn't need adaptivity */
                PHY_SetBBReg(pDM_Odm->Adapter, rOFDM0_ECCAThreshold, bMaskByte0, 0x7f);
                PHY_SetBBReg(pDM_Odm->Adapter, rOFDM0_ECCAThreshold, bMaskByte2, 0x7f);
                return;
        }

        if (!pDM_Odm->ForceEDCCA) {
                if (pDM_Odm->RSSI_Min > pDM_Odm->AdapEn_RSSI)
                        EDCCA_State = true;
                else if (pDM_Odm->RSSI_Min < (pDM_Odm->AdapEn_RSSI - 5))
                        EDCCA_State = false;
        } else
                EDCCA_State = true;

        if (
                pDM_Odm->bLinked &&
                pDM_Odm->Carrier_Sense_enable == false &&
                pDM_Odm->NHM_disable == false &&
                pDM_Odm->TxHangFlg == false
        )
                odm_NHMBB(pDM_Odm);

        if (EDCCA_State) {
                Diff = IGI_target-(s8)IGI;
                TH_L2H_dmc = pDM_Odm->TH_L2H_ini + Diff;
                if (TH_L2H_dmc > 10)
                        TH_L2H_dmc = 10;

                TH_H2L_dmc = TH_L2H_dmc - pDM_Odm->TH_EDCCA_HL_diff;

                /* replace lower bound to prevent EDCCA always equal  */
                if (TH_H2L_dmc < pDM_Odm->H2L_lb)
                        TH_H2L_dmc = pDM_Odm->H2L_lb;
                if (TH_L2H_dmc < pDM_Odm->L2H_lb)
                        TH_L2H_dmc = pDM_Odm->L2H_lb;
        } else {
                TH_L2H_dmc = 0x7f;
                TH_H2L_dmc = 0x7f;
        }
        PHY_SetBBReg(pDM_Odm->Adapter, rOFDM0_ECCAThreshold, bMaskByte0, (u8)TH_L2H_dmc);
        PHY_SetBBReg(pDM_Odm->Adapter, rOFDM0_ECCAThreshold, bMaskByte2, (u8)TH_H2L_dmc);
}

void ODM_Write_DIG(void *pDM_VOID, u8 CurrentIGI)
{
        struct dm_odm_t *pDM_Odm = (struct dm_odm_t *)pDM_VOID;
        struct dig_t *pDM_DigTable = &pDM_Odm->DM_DigTable;

        if (pDM_DigTable->bStopDIG) {
                return;
        }

        if (pDM_DigTable->CurIGValue != CurrentIGI) {
                /* 1 Check initial gain by upper bound */
                if (!pDM_DigTable->bPSDInProgress) {
                        if (CurrentIGI > pDM_DigTable->rx_gain_range_max) {
                                CurrentIGI = pDM_DigTable->rx_gain_range_max;
                        }

                }

                /* 1 Set IGI value */
                PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG(IGI_A, pDM_Odm), ODM_BIT(IGI, pDM_Odm), CurrentIGI);

                PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG(IGI_B, pDM_Odm), ODM_BIT(IGI, pDM_Odm), CurrentIGI);

                pDM_DigTable->CurIGValue = CurrentIGI;
        }

}

bool odm_DigAbort(void *pDM_VOID)
{
        struct dm_odm_t *pDM_Odm = (struct dm_odm_t *)pDM_VOID;

        /* SupportAbility */
        if (!(pDM_Odm->SupportAbility & ODM_BB_FA_CNT)) {
                return  true;
        }

        /* SupportAbility */
        if (!(pDM_Odm->SupportAbility & ODM_BB_DIG)) {
                return  true;
        }

        /* ScanInProcess */
        if (*(pDM_Odm->pbScanInProcess)) {
                return  true;
        }

        /* add by Neil Chen to avoid PSD is processing */
        if (pDM_Odm->bDMInitialGainEnable == false) {
                return  true;
        }

        return  false;
}

void odm_DIGInit(void *pDM_VOID)
{
        struct dm_odm_t *pDM_Odm = (struct dm_odm_t *)pDM_VOID;
        struct dig_t *pDM_DigTable = &pDM_Odm->DM_DigTable;

        pDM_DigTable->bStopDIG = false;
        pDM_DigTable->bPSDInProgress = false;
        pDM_DigTable->CurIGValue = (u8) PHY_QueryBBReg(pDM_Odm->Adapter, ODM_REG(IGI_A, pDM_Odm), ODM_BIT(IGI, pDM_Odm));
        pDM_DigTable->RssiLowThresh     = DM_DIG_THRESH_LOW;
        pDM_DigTable->RssiHighThresh    = DM_DIG_THRESH_HIGH;
        pDM_DigTable->FALowThresh       = DMfalseALARM_THRESH_LOW;
        pDM_DigTable->FAHighThresh      = DMfalseALARM_THRESH_HIGH;
        pDM_DigTable->BackoffVal = DM_DIG_BACKOFF_DEFAULT;
        pDM_DigTable->BackoffVal_range_max = DM_DIG_BACKOFF_MAX;
        pDM_DigTable->BackoffVal_range_min = DM_DIG_BACKOFF_MIN;
        pDM_DigTable->PreCCK_CCAThres = 0xFF;
        pDM_DigTable->CurCCK_CCAThres = 0x83;
        pDM_DigTable->ForbiddenIGI = DM_DIG_MIN_NIC;
        pDM_DigTable->LargeFAHit = 0;
        pDM_DigTable->Recover_cnt = 0;
        pDM_DigTable->bMediaConnect_0 = false;
        pDM_DigTable->bMediaConnect_1 = false;

        /* To Initialize pDM_Odm->bDMInitialGainEnable == false to avoid DIG error */
        pDM_Odm->bDMInitialGainEnable = true;

        pDM_DigTable->DIG_Dynamic_MIN_0 = DM_DIG_MIN_NIC;
        pDM_DigTable->DIG_Dynamic_MIN_1 = DM_DIG_MIN_NIC;

        /* To Initi BT30 IGI */
        pDM_DigTable->BT30_CurIGI = 0x32;

        pDM_DigTable->rx_gain_range_max = DM_DIG_MAX_NIC;
        pDM_DigTable->rx_gain_range_min = DM_DIG_MIN_NIC;
}


void odm_DIG(void *pDM_VOID)
{
        struct dm_odm_t *pDM_Odm = (struct dm_odm_t *)pDM_VOID;

        /*  Common parameters */
        struct dig_t *pDM_DigTable = &pDM_Odm->DM_DigTable;
        struct false_ALARM_STATISTICS *pFalseAlmCnt = &pDM_Odm->FalseAlmCnt;
        bool FirstConnect, FirstDisConnect;
        u8 DIG_MaxOfMin, DIG_Dynamic_MIN;
        u8 dm_dig_max, dm_dig_min;
        u8 CurrentIGI = pDM_DigTable->CurIGValue;
        u8 offset;
        u32 dm_FA_thres[3];
        u8 Adap_IGI_Upper = 0;
        u32 TxTp = 0, RxTp = 0;
        bool bDFSBand = false;
        bool bPerformance = true, bFirstTpTarget = false, bFirstCoverage = false;

        if (odm_DigAbort(pDM_Odm))
                return;

        if (pDM_Odm->adaptivity_flag == true)
                Adap_IGI_Upper = pDM_Odm->Adaptivity_IGI_upper;


        /* 1 Update status */
        DIG_Dynamic_MIN = pDM_DigTable->DIG_Dynamic_MIN_0;
        FirstConnect = (pDM_Odm->bLinked) && (pDM_DigTable->bMediaConnect_0 == false);
        FirstDisConnect = (!pDM_Odm->bLinked) && (pDM_DigTable->bMediaConnect_0 == true);

        /* 1 Boundary Decision */
        /* 2 For WIN\CE */
        dm_dig_max = 0x5A;
        dm_dig_min = DM_DIG_MIN_NIC;
        DIG_MaxOfMin = DM_DIG_MAX_AP;

        /* 1 Adjust boundary by RSSI */
        if (pDM_Odm->bLinked && bPerformance) {
                /* 2 Modify DIG upper bound */
                /* 4 Modify DIG upper bound for 92E, 8723A\B, 8821 & 8812 BT */
                if (pDM_Odm->bBtLimitedDig == 1) {
                        offset = 10;
                } else
                        offset = 15;

                if ((pDM_Odm->RSSI_Min + offset) > dm_dig_max)
                        pDM_DigTable->rx_gain_range_max = dm_dig_max;
                else if ((pDM_Odm->RSSI_Min + offset) < dm_dig_min)
                        pDM_DigTable->rx_gain_range_max = dm_dig_min;
                else
                        pDM_DigTable->rx_gain_range_max = pDM_Odm->RSSI_Min + offset;

                /* 2 Modify DIG lower bound */
                /* if (pDM_Odm->bOneEntryOnly) */
                {
                        if (pDM_Odm->RSSI_Min < dm_dig_min)
                                DIG_Dynamic_MIN = dm_dig_min;
                        else if (pDM_Odm->RSSI_Min > DIG_MaxOfMin)
                                DIG_Dynamic_MIN = DIG_MaxOfMin;
                        else
                                DIG_Dynamic_MIN = pDM_Odm->RSSI_Min;
                }
        } else {
                pDM_DigTable->rx_gain_range_max = dm_dig_max;
                DIG_Dynamic_MIN = dm_dig_min;
        }

        /* 1 Force Lower Bound for AntDiv */
        if (pDM_Odm->bLinked && !pDM_Odm->bOneEntryOnly) {
                if (pDM_Odm->SupportAbility & ODM_BB_ANT_DIV) {
                        if (
                                pDM_Odm->AntDivType == CG_TRX_HW_ANTDIV ||
                                pDM_Odm->AntDivType == CG_TRX_SMART_ANTDIV ||
                                pDM_Odm->AntDivType == S0S1_SW_ANTDIV
                        ) {
                                if (pDM_DigTable->AntDiv_RSSI_max > DIG_MaxOfMin)
                                        DIG_Dynamic_MIN = DIG_MaxOfMin;
                                else
                                        DIG_Dynamic_MIN = (u8) pDM_DigTable->AntDiv_RSSI_max;
                        }
                }
        }

        /* 1 Modify DIG lower bound, deal with abnormal case */
        /* 2 Abnormal false alarm case */
        if (FirstDisConnect) {
                pDM_DigTable->rx_gain_range_min = DIG_Dynamic_MIN;
                pDM_DigTable->ForbiddenIGI = DIG_Dynamic_MIN;
        } else
                pDM_DigTable->rx_gain_range_min =
                        odm_ForbiddenIGICheck(pDM_Odm, DIG_Dynamic_MIN, CurrentIGI);

        if (pDM_Odm->bLinked && !FirstConnect) {
                if (
                        (pDM_Odm->PhyDbgInfo.NumQryBeaconPkt < 5) &&
                        pDM_Odm->bsta_state
                ) {
                        pDM_DigTable->rx_gain_range_min = dm_dig_min;
                }
        }

        /* 2 Abnormal lower bound case */
        if (pDM_DigTable->rx_gain_range_min > pDM_DigTable->rx_gain_range_max) {
                pDM_DigTable->rx_gain_range_min = pDM_DigTable->rx_gain_range_max;
        }


        /* 1 False alarm threshold decision */
        odm_FAThresholdCheck(pDM_Odm, bDFSBand, bPerformance, RxTp, TxTp, dm_FA_thres);

        /* 1 Adjust initial gain by false alarm */
        if (pDM_Odm->bLinked && bPerformance) {

                if (bFirstTpTarget || FirstConnect) {
                        pDM_DigTable->LargeFAHit = 0;

                        if (pDM_Odm->RSSI_Min < DIG_MaxOfMin) {
                                if (CurrentIGI < pDM_Odm->RSSI_Min)
                                        CurrentIGI = pDM_Odm->RSSI_Min;
                        } else {
                                if (CurrentIGI < DIG_MaxOfMin)
                                        CurrentIGI = DIG_MaxOfMin;
                        }

                } else {
                        if (pFalseAlmCnt->Cnt_all > dm_FA_thres[2])
                                CurrentIGI = CurrentIGI + 4;
                        else if (pFalseAlmCnt->Cnt_all > dm_FA_thres[1])
                                CurrentIGI = CurrentIGI + 2;
                        else if (pFalseAlmCnt->Cnt_all < dm_FA_thres[0])
                                CurrentIGI = CurrentIGI - 2;

                        if (
                                (pDM_Odm->PhyDbgInfo.NumQryBeaconPkt < 5) &&
                                (pFalseAlmCnt->Cnt_all < DM_DIG_FA_TH1) &&
                                (pDM_Odm->bsta_state)
                        ) {
                                CurrentIGI = pDM_DigTable->rx_gain_range_min;
                        }
                }
        } else {

                if (FirstDisConnect || bFirstCoverage) {
                        CurrentIGI = dm_dig_min;
                } else {
                        if (pFalseAlmCnt->Cnt_all > dm_FA_thres[2])
                                CurrentIGI = CurrentIGI + 4;
                        else if (pFalseAlmCnt->Cnt_all > dm_FA_thres[1])
                                CurrentIGI = CurrentIGI + 2;
                        else if (pFalseAlmCnt->Cnt_all < dm_FA_thres[0])
                                CurrentIGI = CurrentIGI - 2;
                }
        }

        /* 1 Check initial gain by upper/lower bound */
        if (CurrentIGI < pDM_DigTable->rx_gain_range_min)
                CurrentIGI = pDM_DigTable->rx_gain_range_min;

        if (CurrentIGI > pDM_DigTable->rx_gain_range_max)
                CurrentIGI = pDM_DigTable->rx_gain_range_max;

        /* 1 Force upper bound and lower bound for adaptivity */
        if (
                pDM_Odm->SupportAbility & ODM_BB_ADAPTIVITY &&
                pDM_Odm->adaptivity_flag == true
        ) {
                if (CurrentIGI > Adap_IGI_Upper)
                        CurrentIGI = Adap_IGI_Upper;

                if (pDM_Odm->IGI_LowerBound != 0) {
                        if (CurrentIGI < pDM_Odm->IGI_LowerBound)
                                CurrentIGI = pDM_Odm->IGI_LowerBound;
                }
        }


        /* 1 Update status */
        if (pDM_Odm->bBtHsOperation) {
                if (pDM_Odm->bLinked) {
                        if (pDM_DigTable->BT30_CurIGI > (CurrentIGI))
                                ODM_Write_DIG(pDM_Odm, CurrentIGI);
                        else
                                ODM_Write_DIG(pDM_Odm, pDM_DigTable->BT30_CurIGI);

                        pDM_DigTable->bMediaConnect_0 = pDM_Odm->bLinked;
                        pDM_DigTable->DIG_Dynamic_MIN_0 = DIG_Dynamic_MIN;
                } else {
                        if (pDM_Odm->bLinkInProcess)
                                ODM_Write_DIG(pDM_Odm, 0x1c);
                        else if (pDM_Odm->bBtConnectProcess)
                                ODM_Write_DIG(pDM_Odm, 0x28);
                        else
                                ODM_Write_DIG(pDM_Odm, pDM_DigTable->BT30_CurIGI);/* ODM_Write_DIG(pDM_Odm, pDM_DigTable->CurIGValue); */
                }
        } else { /*  BT is not using */
                ODM_Write_DIG(pDM_Odm, CurrentIGI);/* ODM_Write_DIG(pDM_Odm, pDM_DigTable->CurIGValue); */
                pDM_DigTable->bMediaConnect_0 = pDM_Odm->bLinked;
                pDM_DigTable->DIG_Dynamic_MIN_0 = DIG_Dynamic_MIN;
        }
}

void odm_DIGbyRSSI_LPS(void *pDM_VOID)
{
        struct dm_odm_t *pDM_Odm = (struct dm_odm_t *)pDM_VOID;
        struct false_ALARM_STATISTICS *pFalseAlmCnt = &pDM_Odm->FalseAlmCnt;

        u8 RSSI_Lower = DM_DIG_MIN_NIC;   /* 0x1E or 0x1C */
        u8 CurrentIGI = pDM_Odm->RSSI_Min;

        CurrentIGI = CurrentIGI+RSSI_OFFSET_DIG;

        /*  Using FW PS mode to make IGI */
        /* Adjust by  FA in LPS MODE */
        if (pFalseAlmCnt->Cnt_all > DM_DIG_FA_TH2_LPS)
                CurrentIGI = CurrentIGI+4;
        else if (pFalseAlmCnt->Cnt_all > DM_DIG_FA_TH1_LPS)
                CurrentIGI = CurrentIGI+2;
        else if (pFalseAlmCnt->Cnt_all < DM_DIG_FA_TH0_LPS)
                CurrentIGI = CurrentIGI-2;


        /* Lower bound checking */

        /* RSSI Lower bound check */
        RSSI_Lower = max(pDM_Odm->RSSI_Min - 10, DM_DIG_MIN_NIC);

        /* Upper and Lower Bound checking */
        if (CurrentIGI > DM_DIG_MAX_NIC)
                CurrentIGI = DM_DIG_MAX_NIC;
        else if (CurrentIGI < RSSI_Lower)
                CurrentIGI = RSSI_Lower;

        ODM_Write_DIG(pDM_Odm, CurrentIGI);
        /* ODM_Write_DIG(pDM_Odm, pDM_DigTable->CurIGValue); */
}

/* 3 ============================================================ */
/* 3 FASLE ALARM CHECK */
/* 3 ============================================================ */

void odm_FalseAlarmCounterStatistics(void *pDM_VOID)
{
        struct dm_odm_t *pDM_Odm = (struct dm_odm_t *)pDM_VOID;
        struct false_ALARM_STATISTICS *FalseAlmCnt = &pDM_Odm->FalseAlmCnt;
        u32 ret_value;

        if (!(pDM_Odm->SupportAbility & ODM_BB_FA_CNT))
                return;

        /* hold ofdm counter */
        /* hold page C counter */
        PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_OFDM_FA_HOLDC_11N, BIT31, 1);
        /* hold page D counter */
        PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_OFDM_FA_RSTD_11N, BIT31, 1);

        ret_value = PHY_QueryBBReg(
                pDM_Odm->Adapter, ODM_REG_OFDM_FA_TYPE1_11N, bMaskDWord
        );
        FalseAlmCnt->Cnt_Fast_Fsync = (ret_value&0xffff);
        FalseAlmCnt->Cnt_SB_Search_fail = ((ret_value&0xffff0000)>>16);

        ret_value = PHY_QueryBBReg(
                pDM_Odm->Adapter, ODM_REG_OFDM_FA_TYPE2_11N, bMaskDWord
        );
        FalseAlmCnt->Cnt_OFDM_CCA = (ret_value&0xffff);
        FalseAlmCnt->Cnt_Parity_Fail = ((ret_value&0xffff0000)>>16);

        ret_value = PHY_QueryBBReg(
                pDM_Odm->Adapter, ODM_REG_OFDM_FA_TYPE3_11N, bMaskDWord
        );
        FalseAlmCnt->Cnt_Rate_Illegal = (ret_value&0xffff);
        FalseAlmCnt->Cnt_Crc8_fail = ((ret_value&0xffff0000)>>16);

        ret_value = PHY_QueryBBReg(
                pDM_Odm->Adapter, ODM_REG_OFDM_FA_TYPE4_11N, bMaskDWord
        );
        FalseAlmCnt->Cnt_Mcs_fail = (ret_value&0xffff);

        FalseAlmCnt->Cnt_Ofdm_fail =
                FalseAlmCnt->Cnt_Parity_Fail +
                FalseAlmCnt->Cnt_Rate_Illegal +
                FalseAlmCnt->Cnt_Crc8_fail +
                FalseAlmCnt->Cnt_Mcs_fail +
                FalseAlmCnt->Cnt_Fast_Fsync +
                FalseAlmCnt->Cnt_SB_Search_fail;

        {
                /* hold cck counter */
                PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_CCK_FA_RST_11N, BIT12, 1);
                PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_CCK_FA_RST_11N, BIT14, 1);

                ret_value = PHY_QueryBBReg(
                        pDM_Odm->Adapter, ODM_REG_CCK_FA_LSB_11N, bMaskByte0
                );
                FalseAlmCnt->Cnt_Cck_fail = ret_value;

                ret_value = PHY_QueryBBReg(
                        pDM_Odm->Adapter, ODM_REG_CCK_FA_MSB_11N, bMaskByte3
                );
                FalseAlmCnt->Cnt_Cck_fail += (ret_value&0xff)<<8;

                ret_value = PHY_QueryBBReg(
                        pDM_Odm->Adapter, ODM_REG_CCK_CCA_CNT_11N, bMaskDWord
                );
                FalseAlmCnt->Cnt_CCK_CCA =
                        ((ret_value&0xFF)<<8) | ((ret_value&0xFF00)>>8);
        }

        FalseAlmCnt->Cnt_all = (
                FalseAlmCnt->Cnt_Fast_Fsync +
                FalseAlmCnt->Cnt_SB_Search_fail +
                FalseAlmCnt->Cnt_Parity_Fail +
                FalseAlmCnt->Cnt_Rate_Illegal +
                FalseAlmCnt->Cnt_Crc8_fail +
                FalseAlmCnt->Cnt_Mcs_fail +
                FalseAlmCnt->Cnt_Cck_fail
        );

        FalseAlmCnt->Cnt_CCA_all =
                FalseAlmCnt->Cnt_OFDM_CCA + FalseAlmCnt->Cnt_CCK_CCA;
}


void odm_FAThresholdCheck(
        void *pDM_VOID,
        bool bDFSBand,
        bool bPerformance,
        u32 RxTp,
        u32 TxTp,
        u32 *dm_FA_thres
)
{
        struct dm_odm_t *pDM_Odm = (struct dm_odm_t *)pDM_VOID;

        if (pDM_Odm->bLinked && (bPerformance || bDFSBand)) {
                /*  For NIC */
                dm_FA_thres[0] = DM_DIG_FA_TH0;
                dm_FA_thres[1] = DM_DIG_FA_TH1;
                dm_FA_thres[2] = DM_DIG_FA_TH2;
        } else {
                dm_FA_thres[0] = 2000;
                dm_FA_thres[1] = 4000;
                dm_FA_thres[2] = 5000;
        }
}

u8 odm_ForbiddenIGICheck(void *pDM_VOID, u8 DIG_Dynamic_MIN, u8 CurrentIGI)
{
        struct dm_odm_t *pDM_Odm = (struct dm_odm_t *)pDM_VOID;
        struct dig_t *pDM_DigTable = &pDM_Odm->DM_DigTable;
        struct false_ALARM_STATISTICS *pFalseAlmCnt = &pDM_Odm->FalseAlmCnt;
        u8 rx_gain_range_min = pDM_DigTable->rx_gain_range_min;

        if (pFalseAlmCnt->Cnt_all > 10000) {
                if (pDM_DigTable->LargeFAHit != 3)
                        pDM_DigTable->LargeFAHit++;

                /* if (pDM_DigTable->ForbiddenIGI < pDM_DigTable->CurIGValue) */
                if (pDM_DigTable->ForbiddenIGI < CurrentIGI) {
                        pDM_DigTable->ForbiddenIGI = CurrentIGI;
                        /* pDM_DigTable->ForbiddenIGI = pDM_DigTable->CurIGValue; */
                        pDM_DigTable->LargeFAHit = 1;
                }

                if (pDM_DigTable->LargeFAHit >= 3) {
                        if ((pDM_DigTable->ForbiddenIGI + 2) > pDM_DigTable->rx_gain_range_max)
                                rx_gain_range_min = pDM_DigTable->rx_gain_range_max;
                        else
                                rx_gain_range_min = (pDM_DigTable->ForbiddenIGI + 2);
                        pDM_DigTable->Recover_cnt = 1800;
                }
        } else {
                if (pDM_DigTable->Recover_cnt != 0) {
                        pDM_DigTable->Recover_cnt--;
                } else {
                        if (pDM_DigTable->LargeFAHit < 3) {
                                if ((pDM_DigTable->ForbiddenIGI - 2) < DIG_Dynamic_MIN) { /* DM_DIG_MIN) */
                                        pDM_DigTable->ForbiddenIGI = DIG_Dynamic_MIN; /* DM_DIG_MIN; */
                                        rx_gain_range_min = DIG_Dynamic_MIN; /* DM_DIG_MIN; */
                                } else {
                                        pDM_DigTable->ForbiddenIGI -= 2;
                                        rx_gain_range_min = (pDM_DigTable->ForbiddenIGI + 2);
                                }
                        } else
                                pDM_DigTable->LargeFAHit = 0;
                }
        }

        return rx_gain_range_min;

}

/* 3 ============================================================ */
/* 3 CCK Packet Detect Threshold */
/* 3 ============================================================ */

void odm_CCKPacketDetectionThresh(void *pDM_VOID)
{
        struct dm_odm_t *pDM_Odm = (struct dm_odm_t *)pDM_VOID;
        struct false_ALARM_STATISTICS *FalseAlmCnt = &pDM_Odm->FalseAlmCnt;
        u8 CurCCK_CCAThres;


        if (
                !(pDM_Odm->SupportAbility & ODM_BB_CCK_PD) ||
                !(pDM_Odm->SupportAbility & ODM_BB_FA_CNT)
        ) {
                return;
        }

        if (pDM_Odm->ExtLNA)
                return;

        if (pDM_Odm->bLinked) {
                if (pDM_Odm->RSSI_Min > 25)
                        CurCCK_CCAThres = 0xcd;
                else if ((pDM_Odm->RSSI_Min <= 25) && (pDM_Odm->RSSI_Min > 10))
                        CurCCK_CCAThres = 0x83;
                else {
                        if (FalseAlmCnt->Cnt_Cck_fail > 1000)
                                CurCCK_CCAThres = 0x83;
                        else
                                CurCCK_CCAThres = 0x40;
                }
        } else {
                if (FalseAlmCnt->Cnt_Cck_fail > 1000)
                        CurCCK_CCAThres = 0x83;
                else
                        CurCCK_CCAThres = 0x40;
        }

        ODM_Write_CCK_CCA_Thres(pDM_Odm, CurCCK_CCAThres);
}

void ODM_Write_CCK_CCA_Thres(void *pDM_VOID, u8 CurCCK_CCAThres)
{
        struct dm_odm_t *pDM_Odm = (struct dm_odm_t *)pDM_VOID;
        struct dig_t *pDM_DigTable = &pDM_Odm->DM_DigTable;

        /* modify by Guo.Mingzhi 2012-01-03 */
        if (pDM_DigTable->CurCCK_CCAThres != CurCCK_CCAThres)
                rtw_write8(pDM_Odm->Adapter, ODM_REG(CCK_CCA, pDM_Odm), CurCCK_CCAThres);

        pDM_DigTable->PreCCK_CCAThres = pDM_DigTable->CurCCK_CCAThres;
        pDM_DigTable->CurCCK_CCAThres = CurCCK_CCAThres;
}