root/drivers/net/wireless/realtek/rtlwifi/rtl8192se/rf.c
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2009-2012  Realtek Corporation.*/

#include "../wifi.h"
#include "reg.h"
#include "def.h"
#include "phy.h"
#include "rf.h"
#include "dm.h"


static void _rtl92s_get_powerbase(struct ieee80211_hw *hw, u8 *p_pwrlevel,
                                  u8 chnl, u32 *ofdmbase, u32 *mcsbase,
                                  u8 *p_final_pwridx)
{
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_phy *rtlphy = &(rtlpriv->phy);
        struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
        u32 pwrbase0, pwrbase1;
        u8 legacy_pwrdiff = 0, ht20_pwrdiff = 0;
        u8 i, pwrlevel[4];

        for (i = 0; i < 2; i++)
                pwrlevel[i] = p_pwrlevel[i];

        /* We only care about the path A for legacy. */
        if (rtlefuse->eeprom_version < 2) {
                pwrbase0 = pwrlevel[0] + (rtlefuse->legacy_ht_txpowerdiff & 0xf);
        } else {
                legacy_pwrdiff = rtlefuse->txpwr_legacyhtdiff
                                                [RF90_PATH_A][chnl - 1];

                /* For legacy OFDM, tx pwr always > HT OFDM pwr.
                 * We do not care Path B
                 * legacy OFDM pwr diff. NO BB register
                 * to notify HW. */
                pwrbase0 = pwrlevel[0] + legacy_pwrdiff;
        }

        pwrbase0 = (pwrbase0 << 24) | (pwrbase0 << 16) | (pwrbase0 << 8) |
                    pwrbase0;
        *ofdmbase = pwrbase0;

        /* MCS rates */
        if (rtlefuse->eeprom_version >= 2) {
                /* Check HT20 to HT40 diff      */
                if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20) {
                        for (i = 0; i < 2; i++) {
                                /* rf-A, rf-B */
                                /* HT 20<->40 pwr diff */
                                ht20_pwrdiff = rtlefuse->txpwr_ht20diff
                                                        [i][chnl - 1];

                                if (ht20_pwrdiff < 8) /* 0~+7 */
                                        pwrlevel[i] += ht20_pwrdiff;
                                else /* index8-15=-8~-1 */
                                        pwrlevel[i] -= (16 - ht20_pwrdiff);
                        }
                }
        }

        /* use index of rf-A */
        pwrbase1 = pwrlevel[0];
        pwrbase1 = (pwrbase1 << 24) | (pwrbase1 << 16) | (pwrbase1 << 8) |
                                pwrbase1;
        *mcsbase = pwrbase1;

        /* The following is for Antenna
         * diff from Ant-B to Ant-A */
        p_final_pwridx[0] = pwrlevel[0];
        p_final_pwridx[1] = pwrlevel[1];

        switch (rtlefuse->eeprom_regulatory) {
        case 3:
                /* The following is for calculation
                 * of the power diff for Ant-B to Ant-A. */
                if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) {
                        p_final_pwridx[0] += rtlefuse->pwrgroup_ht40
                                                [RF90_PATH_A][
                                                chnl - 1];
                        p_final_pwridx[1] += rtlefuse->pwrgroup_ht40
                                                [RF90_PATH_B][
                                                chnl - 1];
                } else {
                        p_final_pwridx[0] += rtlefuse->pwrgroup_ht20
                                                [RF90_PATH_A][
                                                chnl - 1];
                        p_final_pwridx[1] += rtlefuse->pwrgroup_ht20
                                                [RF90_PATH_B][
                                                chnl - 1];
                }
                break;
        default:
                break;
        }

        if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) {
                rtl_dbg(rtlpriv, COMP_POWER, DBG_LOUD,
                        "40MHz finalpwr_idx (A / B) = 0x%x / 0x%x\n",
                        p_final_pwridx[0], p_final_pwridx[1]);
        } else {
                rtl_dbg(rtlpriv, COMP_POWER, DBG_LOUD,
                        "20MHz finalpwr_idx (A / B) = 0x%x / 0x%x\n",
                        p_final_pwridx[0], p_final_pwridx[1]);
        }
}

static void _rtl92s_set_antennadiff(struct ieee80211_hw *hw,
                                    u8 *p_final_pwridx)
{
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
        struct rtl_phy *rtlphy = &(rtlpriv->phy);
        s8 ant_pwr_diff = 0;
        u32     u4reg_val = 0;

        if (rtlphy->rf_type == RF_2T2R) {
                ant_pwr_diff = p_final_pwridx[1] - p_final_pwridx[0];

                /* range is from 7~-8,
                 * index = 0x0~0xf */
                if (ant_pwr_diff > 7)
                        ant_pwr_diff = 7;
                if (ant_pwr_diff < -8)
                        ant_pwr_diff = -8;

                rtl_dbg(rtlpriv, COMP_POWER, DBG_LOUD,
                        "Antenna Diff from RF-B to RF-A = %d (0x%x)\n",
                        ant_pwr_diff, ant_pwr_diff & 0xf);

                ant_pwr_diff &= 0xf;
        }

        /* Antenna TX power difference */
        rtlefuse->antenna_txpwdiff[2] = 0;/* RF-D, don't care */
        rtlefuse->antenna_txpwdiff[1] = 0;/* RF-C, don't care */
        rtlefuse->antenna_txpwdiff[0] = (u8)(ant_pwr_diff);     /* RF-B */

        u4reg_val = rtlefuse->antenna_txpwdiff[2] << 8 |
                                rtlefuse->antenna_txpwdiff[1] << 4 |
                                rtlefuse->antenna_txpwdiff[0];

        rtl_set_bbreg(hw, RFPGA0_TXGAINSTAGE, (BXBTXAGC | BXCTXAGC | BXDTXAGC),
                      u4reg_val);

        rtl_dbg(rtlpriv, COMP_POWER, DBG_LOUD, "Write BCD-Diff(0x%x) = 0x%x\n",
                RFPGA0_TXGAINSTAGE, u4reg_val);
}

static void _rtl92s_get_txpower_writeval_byregulatory(struct ieee80211_hw *hw,
                                                      u8 chnl, u8 index,
                                                      u32 pwrbase0,
                                                      u32 pwrbase1,
                                                      u32 *p_outwrite_val)
{
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_phy *rtlphy = &(rtlpriv->phy);
        struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
        u8 i, chnlgroup, pwrdiff_limit[4];
        u32 writeval, customer_limit;

        /* Index 0 & 1= legacy OFDM, 2-5=HT_MCS rate */
        switch (rtlefuse->eeprom_regulatory) {
        case 0:
                /* Realtek better performance increase power diff
                 * defined by Realtek for large power */
                chnlgroup = 0;

                writeval = rtlphy->mcs_offset[chnlgroup][index] +
                                ((index < 2) ? pwrbase0 : pwrbase1);

                rtl_dbg(rtlpriv, COMP_POWER, DBG_LOUD,
                        "RTK better performance, writeval = 0x%x\n", writeval);
                break;
        case 1:
                /* Realtek regulatory increase power diff defined
                 * by Realtek for regulatory */
                if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) {
                        writeval = ((index < 2) ? pwrbase0 : pwrbase1);

                        rtl_dbg(rtlpriv, COMP_POWER, DBG_LOUD,
                                "Realtek regulatory, 40MHz, writeval = 0x%x\n",
                                writeval);
                } else {
                        chnlgroup = 0;

                        if (rtlphy->pwrgroup_cnt >= 3) {
                                if (chnl <= 3)
                                        chnlgroup = 0;
                                else if (chnl >= 4 && chnl <= 8)
                                        chnlgroup = 1;
                                else if (chnl > 8)
                                        chnlgroup = 2;
                                if (rtlphy->pwrgroup_cnt == 4)
                                        chnlgroup++;
                        }

                        writeval = rtlphy->mcs_offset[chnlgroup][index]
                                        + ((index < 2) ?
                                        pwrbase0 : pwrbase1);

                        rtl_dbg(rtlpriv, COMP_POWER, DBG_LOUD,
                                "Realtek regulatory, 20MHz, writeval = 0x%x\n",
                                writeval);
                }
                break;
        case 2:
                /* Better regulatory don't increase any power diff */
                writeval = ((index < 2) ? pwrbase0 : pwrbase1);
                rtl_dbg(rtlpriv, COMP_POWER, DBG_LOUD,
                        "Better regulatory, writeval = 0x%x\n", writeval);
                break;
        case 3:
                /* Customer defined power diff. increase power diff
                  defined by customer. */
                chnlgroup = 0;

                if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) {
                        rtl_dbg(rtlpriv, COMP_POWER, DBG_LOUD,
                                "customer's limit, 40MHz = 0x%x\n",
                                rtlefuse->pwrgroup_ht40
                                [RF90_PATH_A][chnl - 1]);
                } else {
                        rtl_dbg(rtlpriv, COMP_POWER, DBG_LOUD,
                                "customer's limit, 20MHz = 0x%x\n",
                                rtlefuse->pwrgroup_ht20
                                [RF90_PATH_A][chnl - 1]);
                }

                for (i = 0; i < 4; i++) {
                        pwrdiff_limit[i] = (u8)((rtlphy->mcs_offset
                                [chnlgroup][index] & (0x7f << (i * 8)))
                                >> (i * 8));

                        if (rtlphy->current_chan_bw ==
                            HT_CHANNEL_WIDTH_20_40) {
                                if (pwrdiff_limit[i] >
                                    rtlefuse->pwrgroup_ht40
                                    [RF90_PATH_A][chnl - 1]) {
                                        pwrdiff_limit[i] =
                                          rtlefuse->pwrgroup_ht40
                                          [RF90_PATH_A][chnl - 1];
                                }
                        } else {
                                if (pwrdiff_limit[i] >
                                    rtlefuse->pwrgroup_ht20
                                    [RF90_PATH_A][chnl - 1]) {
                                        pwrdiff_limit[i] =
                                            rtlefuse->pwrgroup_ht20
                                            [RF90_PATH_A][chnl - 1];
                                }
                        }
                }

                customer_limit = (pwrdiff_limit[3] << 24) |
                                (pwrdiff_limit[2] << 16) |
                                (pwrdiff_limit[1] << 8) |
                                (pwrdiff_limit[0]);
                rtl_dbg(rtlpriv, COMP_POWER, DBG_LOUD,
                        "Customer's limit = 0x%x\n", customer_limit);

                writeval = customer_limit + ((index < 2) ?
                                             pwrbase0 : pwrbase1);
                rtl_dbg(rtlpriv, COMP_POWER, DBG_LOUD,
                        "Customer, writeval = 0x%x\n", writeval);
                break;
        default:
                chnlgroup = 0;
                writeval = rtlphy->mcs_offset[chnlgroup][index] +
                                ((index < 2) ? pwrbase0 : pwrbase1);
                rtl_dbg(rtlpriv, COMP_POWER, DBG_LOUD,
                        "RTK better performance, writeval = 0x%x\n", writeval);
                break;
        }

        if (rtlpriv->dm.dynamic_txhighpower_lvl == TX_HIGH_PWR_LEVEL_LEVEL1)
                writeval = 0x10101010;
        else if (rtlpriv->dm.dynamic_txhighpower_lvl ==
                 TX_HIGH_PWR_LEVEL_LEVEL2)
                writeval = 0x0;

        *p_outwrite_val = writeval;

}

static void _rtl92s_write_ofdm_powerreg(struct ieee80211_hw *hw,
                                        u8 index, u32 val)
{
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_phy *rtlphy = &(rtlpriv->phy);
        struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
        u16 regoffset[6] = {0xe00, 0xe04, 0xe10, 0xe14, 0xe18, 0xe1c};
        u8 i, rfa_pwr[4];
        u8 rfa_lower_bound = 0, rfa_upper_bound = 0, rf_pwr_diff = 0;
        u32 writeval = val;

        /* If path A and Path B coexist, we must limit Path A tx power.
         * Protect Path B pwr over or under flow. We need to calculate
         * upper and lower bound of path A tx power. */
        if (rtlphy->rf_type == RF_2T2R) {
                rf_pwr_diff = rtlefuse->antenna_txpwdiff[0];

                /* Diff=-8~-1 */
                if (rf_pwr_diff >= 8) {
                        /* Prevent underflow!! */
                        rfa_lower_bound = 0x10 - rf_pwr_diff;
                /* if (rf_pwr_diff >= 0) Diff = 0-7 */
                } else {
                        rfa_upper_bound = RF6052_MAX_TX_PWR - rf_pwr_diff;
                }
        }

        for (i = 0; i < 4; i++) {
                rfa_pwr[i] = (u8)((writeval & (0x7f << (i * 8))) >> (i * 8));
                if (rfa_pwr[i]  > RF6052_MAX_TX_PWR)
                        rfa_pwr[i]  = RF6052_MAX_TX_PWR;

                /* If path A and Path B coexist, we must limit Path A tx power.
                 * Protect Path B pwr over or under flow. We need to calculate
                 * upper and lower bound of path A tx power. */
                if (rtlphy->rf_type == RF_2T2R) {
                        /* Diff=-8~-1 */
                        if (rf_pwr_diff >= 8) {
                                /* Prevent underflow!! */
                                if (rfa_pwr[i] < rfa_lower_bound)
                                        rfa_pwr[i] = rfa_lower_bound;
                        /* Diff = 0-7 */
                        } else if (rf_pwr_diff >= 1) {
                                /* Prevent overflow */
                                if (rfa_pwr[i] > rfa_upper_bound)
                                        rfa_pwr[i] = rfa_upper_bound;
                        }
                }

        }

        writeval = (rfa_pwr[3] << 24) | (rfa_pwr[2] << 16) | (rfa_pwr[1] << 8) |
                                rfa_pwr[0];

        rtl_set_bbreg(hw, regoffset[index], 0x7f7f7f7f, writeval);
}

void rtl92s_phy_rf6052_set_ofdmtxpower(struct ieee80211_hw *hw,
                                       u8 *p_pwrlevel, u8 chnl)
{
        u32 writeval, pwrbase0, pwrbase1;
        u8 index = 0;
        u8 finalpwr_idx[4];

        _rtl92s_get_powerbase(hw, p_pwrlevel, chnl, &pwrbase0, &pwrbase1,
                        &finalpwr_idx[0]);
        _rtl92s_set_antennadiff(hw, &finalpwr_idx[0]);

        for (index = 0; index < 6; index++) {
                _rtl92s_get_txpower_writeval_byregulatory(hw, chnl, index,
                                pwrbase0, pwrbase1, &writeval);

                _rtl92s_write_ofdm_powerreg(hw, index, writeval);
        }
}

void rtl92s_phy_rf6052_set_ccktxpower(struct ieee80211_hw *hw, u8 pwrlevel)
{
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
        struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
        u32 txagc = 0;
        bool dont_inc_cck_or_turboscanoff = false;

        if (((rtlefuse->eeprom_version >= 2) &&
              (rtlefuse->txpwr_safetyflag == 1)) ||
              ((rtlefuse->eeprom_version >= 2) &&
              (rtlefuse->eeprom_regulatory != 0)))
                dont_inc_cck_or_turboscanoff = true;

        if (mac->act_scanning) {
                txagc = 0x3f;
                if (dont_inc_cck_or_turboscanoff)
                        txagc = pwrlevel;
        } else {
                txagc = pwrlevel;

                if (rtlpriv->dm.dynamic_txhighpower_lvl ==
                    TX_HIGH_PWR_LEVEL_LEVEL1)
                        txagc = 0x10;
                else if (rtlpriv->dm.dynamic_txhighpower_lvl ==
                        TX_HIGH_PWR_LEVEL_LEVEL2)
                        txagc = 0x0;
        }

        if (txagc > RF6052_MAX_TX_PWR)
                txagc = RF6052_MAX_TX_PWR;

        rtl_set_bbreg(hw, RTXAGC_CCK_MCS32, BTX_AGCRATECCK, txagc);

}

bool rtl92s_phy_rf6052_config(struct ieee80211_hw *hw)
{
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_phy *rtlphy = &(rtlpriv->phy);
        u32 u4reg_val = 0;
        u8 rfpath;
        bool rtstatus = true;
        struct bb_reg_def *pphyreg;

        /* Initialize RF */
        for (rfpath = 0; rfpath < rtlphy->num_total_rfpath; rfpath++) {

                pphyreg = &rtlphy->phyreg_def[rfpath];

                /* Store original RFENV control type */
                switch (rfpath) {
                case RF90_PATH_A:
                case RF90_PATH_C:
                        u4reg_val = rtl92s_phy_query_bb_reg(hw,
                                                            pphyreg->rfintfs,
                                                            BRFSI_RFENV);
                        break;
                case RF90_PATH_B:
                case RF90_PATH_D:
                        u4reg_val = rtl92s_phy_query_bb_reg(hw,
                                                            pphyreg->rfintfs,
                                                            BRFSI_RFENV << 16);
                        break;
                }

                /* Set RF_ENV enable */
                rtl92s_phy_set_bb_reg(hw, pphyreg->rfintfe,
                                      BRFSI_RFENV << 16, 0x1);

                /* Set RF_ENV output high */
                rtl92s_phy_set_bb_reg(hw, pphyreg->rfintfo, BRFSI_RFENV, 0x1);

                /* Set bit number of Address and Data for RF register */
                rtl92s_phy_set_bb_reg(hw, pphyreg->rfhssi_para2,
                                B3WIRE_ADDRESSLENGTH, 0x0);
                rtl92s_phy_set_bb_reg(hw, pphyreg->rfhssi_para2,
                                B3WIRE_DATALENGTH, 0x0);

                /* Initialize RF from configuration file */
                switch (rfpath) {
                case RF90_PATH_A:
                        rtstatus = rtl92s_phy_config_rf(hw,
                                                (enum radio_path)rfpath);
                        break;
                case RF90_PATH_B:
                        rtstatus = rtl92s_phy_config_rf(hw,
                                                (enum radio_path)rfpath);
                        break;
                case RF90_PATH_C:
                        break;
                case RF90_PATH_D:
                        break;
                }

                /* Restore RFENV control type */
                switch (rfpath) {
                case RF90_PATH_A:
                case RF90_PATH_C:
                        rtl92s_phy_set_bb_reg(hw, pphyreg->rfintfs, BRFSI_RFENV,
                                              u4reg_val);
                        break;
                case RF90_PATH_B:
                case RF90_PATH_D:
                        rtl92s_phy_set_bb_reg(hw, pphyreg->rfintfs,
                                              BRFSI_RFENV << 16,
                                              u4reg_val);
                        break;
                }

                if (!rtstatus) {
                        pr_err("Radio[%d] Fail!!\n", rfpath);
                        goto fail;
                }

        }

        return rtstatus;

fail:
        return rtstatus;
}

void rtl92s_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth)
{
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_phy *rtlphy = &(rtlpriv->phy);

        switch (bandwidth) {
        case HT_CHANNEL_WIDTH_20:
                rtlphy->rfreg_chnlval[0] = ((rtlphy->rfreg_chnlval[0] &
                                           0xfffff3ff) | 0x0400);
                rtl_set_rfreg(hw, RF90_PATH_A, RF_CHNLBW, RFREG_OFFSET_MASK,
                                        rtlphy->rfreg_chnlval[0]);
                break;
        case HT_CHANNEL_WIDTH_20_40:
                rtlphy->rfreg_chnlval[0] = ((rtlphy->rfreg_chnlval[0] &
                                            0xfffff3ff));
                rtl_set_rfreg(hw, RF90_PATH_A, RF_CHNLBW, RFREG_OFFSET_MASK,
                                        rtlphy->rfreg_chnlval[0]);
                break;
        default:
                pr_err("unknown bandwidth: %#X\n", bandwidth);
                break;
        }
}