@@ -3000,6 +3000,41 @@ struct rtw89_hw_scan_info {
u8 op_band;
};
+enum rtw89_phy_bb_gain_band {
+ RTW89_BB_GAIN_BAND_2G = 0,
+ RTW89_BB_GAIN_BAND_5G_L = 1,
+ RTW89_BB_GAIN_BAND_5G_M = 2,
+ RTW89_BB_GAIN_BAND_5G_H = 3,
+ RTW89_BB_GAIN_BAND_6G_L = 4,
+ RTW89_BB_GAIN_BAND_6G_M = 5,
+ RTW89_BB_GAIN_BAND_6G_H = 6,
+ RTW89_BB_GAIN_BAND_6G_UH = 7,
+
+ RTW89_BB_GAIN_BAND_NR,
+};
+
+enum rtw89_phy_bb_rxsc_num {
+ RTW89_BB_RXSC_NUM_40 = 9, /* SC: 0, 1~8 */
+ RTW89_BB_RXSC_NUM_80 = 13, /* SC: 0, 1~8, 9~12 */
+ RTW89_BB_RXSC_NUM_160 = 15, /* SC: 0, 1~8, 9~12, 13~14 */
+};
+
+struct rtw89_phy_bb_gain_info {
+ s8 lna_gain[RTW89_BB_GAIN_BAND_NR][RF_PATH_MAX][LNA_GAIN_NUM];
+ s8 tia_gain[RTW89_BB_GAIN_BAND_NR][RF_PATH_MAX][TIA_GAIN_NUM];
+ s8 lna_gain_bypass[RTW89_BB_GAIN_BAND_NR][RF_PATH_MAX][LNA_GAIN_NUM];
+ s8 lna_op1db[RTW89_BB_GAIN_BAND_NR][RF_PATH_MAX][LNA_GAIN_NUM];
+ s8 tia_lna_op1db[RTW89_BB_GAIN_BAND_NR][RF_PATH_MAX]
+ [LNA_GAIN_NUM + 1]; /* TIA0_LNA0~6 + TIA1_LNA6 */
+ s8 rpl_ofst_20[RTW89_BB_GAIN_BAND_NR][RF_PATH_MAX];
+ s8 rpl_ofst_40[RTW89_BB_GAIN_BAND_NR][RF_PATH_MAX]
+ [RTW89_BB_RXSC_NUM_40];
+ s8 rpl_ofst_80[RTW89_BB_GAIN_BAND_NR][RF_PATH_MAX]
+ [RTW89_BB_RXSC_NUM_80];
+ s8 rpl_ofst_160[RTW89_BB_GAIN_BAND_NR][RF_PATH_MAX]
+ [RTW89_BB_RXSC_NUM_160];
+};
+
struct rtw89_dev {
struct ieee80211_hw *hw;
struct device *dev;
@@ -3062,6 +3097,8 @@ struct rtw89_dev {
struct rtw89_env_monitor_info env_monitor;
struct rtw89_dig_info dig;
struct rtw89_phy_ch_info ch_info;
+ struct rtw89_phy_bb_gain_info bb_gain;
+
struct delayed_work track_work;
struct delayed_work coex_act1_work;
struct delayed_work coex_bt_devinfo_work;
@@ -790,6 +790,245 @@ static void rtw89_phy_config_bb_reg(struct rtw89_dev *rtwdev,
rtw89_phy_write32(rtwdev, reg->addr, reg->data);
}
+union rtw89_phy_bb_gain_arg {
+ u32 addr;
+ struct {
+ union {
+ u8 type;
+ struct {
+ u8 rxsc_start:4;
+ u8 bw:4;
+ };
+ };
+ u8 path;
+ u8 gain_band;
+ u8 cfg_type;
+ };
+} __packed;
+
+static void
+rtw89_phy_cfg_bb_gain_error(struct rtw89_dev *rtwdev,
+ union rtw89_phy_bb_gain_arg arg, u32 data)
+{
+ struct rtw89_phy_bb_gain_info *gain = &rtwdev->bb_gain;
+ u8 type = arg.type;
+ u8 path = arg.path;
+ u8 gband = arg.gain_band;
+ int i;
+
+ switch (type) {
+ case 0:
+ for (i = 0; i < 4; i++, data >>= 8)
+ gain->lna_gain[gband][path][i] = data & 0xff;
+ break;
+ case 1:
+ for (i = 4; i < 7; i++, data >>= 8)
+ gain->lna_gain[gband][path][i] = data & 0xff;
+ break;
+ case 2:
+ for (i = 0; i < 2; i++, data >>= 8)
+ gain->tia_gain[gband][path][i] = data & 0xff;
+ break;
+ default:
+ rtw89_warn(rtwdev,
+ "bb gain error {0x%x:0x%x} with unknown type: %d\n",
+ arg.addr, data, type);
+ break;
+ }
+}
+
+enum rtw89_phy_bb_rxsc_start_idx {
+ RTW89_BB_RXSC_START_IDX_FULL = 0,
+ RTW89_BB_RXSC_START_IDX_20 = 1,
+ RTW89_BB_RXSC_START_IDX_20_1 = 5,
+ RTW89_BB_RXSC_START_IDX_40 = 9,
+ RTW89_BB_RXSC_START_IDX_80 = 13,
+};
+
+static void
+rtw89_phy_cfg_bb_rpl_ofst(struct rtw89_dev *rtwdev,
+ union rtw89_phy_bb_gain_arg arg, u32 data)
+{
+ struct rtw89_phy_bb_gain_info *gain = &rtwdev->bb_gain;
+ u8 rxsc_start = arg.rxsc_start;
+ u8 bw = arg.bw;
+ u8 path = arg.path;
+ u8 gband = arg.gain_band;
+ u8 rxsc;
+ s8 ofst;
+ int i;
+
+ switch (bw) {
+ case RTW89_CHANNEL_WIDTH_20:
+ gain->rpl_ofst_20[gband][path] = (s8)data;
+ break;
+ case RTW89_CHANNEL_WIDTH_40:
+ if (rxsc_start == RTW89_BB_RXSC_START_IDX_FULL) {
+ gain->rpl_ofst_40[gband][path][0] = (s8)data;
+ } else if (rxsc_start == RTW89_BB_RXSC_START_IDX_20) {
+ for (i = 0; i < 2; i++, data >>= 8) {
+ rxsc = RTW89_BB_RXSC_START_IDX_20 + i;
+ ofst = (s8)(data & 0xff);
+ gain->rpl_ofst_40[gband][path][rxsc] = ofst;
+ }
+ }
+ break;
+ case RTW89_CHANNEL_WIDTH_80:
+ if (rxsc_start == RTW89_BB_RXSC_START_IDX_FULL) {
+ gain->rpl_ofst_80[gband][path][0] = (s8)data;
+ } else if (rxsc_start == RTW89_BB_RXSC_START_IDX_20) {
+ for (i = 0; i < 4; i++, data >>= 8) {
+ rxsc = RTW89_BB_RXSC_START_IDX_20 + i;
+ ofst = (s8)(data & 0xff);
+ gain->rpl_ofst_80[gband][path][rxsc] = ofst;
+ }
+ } else if (rxsc_start == RTW89_BB_RXSC_START_IDX_40) {
+ for (i = 0; i < 2; i++, data >>= 8) {
+ rxsc = RTW89_BB_RXSC_START_IDX_40 + i;
+ ofst = (s8)(data & 0xff);
+ gain->rpl_ofst_80[gband][path][rxsc] = ofst;
+ }
+ }
+ break;
+ case RTW89_CHANNEL_WIDTH_160:
+ if (rxsc_start == RTW89_BB_RXSC_START_IDX_FULL) {
+ gain->rpl_ofst_160[gband][path][0] = (s8)data;
+ } else if (rxsc_start == RTW89_BB_RXSC_START_IDX_20) {
+ for (i = 0; i < 4; i++, data >>= 8) {
+ rxsc = RTW89_BB_RXSC_START_IDX_20 + i;
+ ofst = (s8)(data & 0xff);
+ gain->rpl_ofst_160[gband][path][rxsc] = ofst;
+ }
+ } else if (rxsc_start == RTW89_BB_RXSC_START_IDX_20_1) {
+ for (i = 0; i < 4; i++, data >>= 8) {
+ rxsc = RTW89_BB_RXSC_START_IDX_20_1 + i;
+ ofst = (s8)(data & 0xff);
+ gain->rpl_ofst_160[gband][path][rxsc] = ofst;
+ }
+ } else if (rxsc_start == RTW89_BB_RXSC_START_IDX_40) {
+ for (i = 0; i < 4; i++, data >>= 8) {
+ rxsc = RTW89_BB_RXSC_START_IDX_40 + i;
+ ofst = (s8)(data & 0xff);
+ gain->rpl_ofst_160[gband][path][rxsc] = ofst;
+ }
+ } else if (rxsc_start == RTW89_BB_RXSC_START_IDX_80) {
+ for (i = 0; i < 2; i++, data >>= 8) {
+ rxsc = RTW89_BB_RXSC_START_IDX_80 + i;
+ ofst = (s8)(data & 0xff);
+ gain->rpl_ofst_160[gband][path][rxsc] = ofst;
+ }
+ }
+ break;
+ default:
+ rtw89_warn(rtwdev,
+ "bb rpl ofst {0x%x:0x%x} with unknown bw: %d\n",
+ arg.addr, data, bw);
+ break;
+ }
+}
+
+static void
+rtw89_phy_cfg_bb_gain_bypass(struct rtw89_dev *rtwdev,
+ union rtw89_phy_bb_gain_arg arg, u32 data)
+{
+ struct rtw89_phy_bb_gain_info *gain = &rtwdev->bb_gain;
+ u8 type = arg.type;
+ u8 path = arg.path;
+ u8 gband = arg.gain_band;
+ int i;
+
+ switch (type) {
+ case 0:
+ for (i = 0; i < 4; i++, data >>= 8)
+ gain->lna_gain_bypass[gband][path][i] = data & 0xff;
+ break;
+ case 1:
+ for (i = 4; i < 7; i++, data >>= 8)
+ gain->lna_gain_bypass[gband][path][i] = data & 0xff;
+ break;
+ default:
+ rtw89_warn(rtwdev,
+ "bb gain bypass {0x%x:0x%x} with unknown type: %d\n",
+ arg.addr, data, type);
+ break;
+ }
+}
+
+static void
+rtw89_phy_cfg_bb_gain_op1db(struct rtw89_dev *rtwdev,
+ union rtw89_phy_bb_gain_arg arg, u32 data)
+{
+ struct rtw89_phy_bb_gain_info *gain = &rtwdev->bb_gain;
+ u8 type = arg.type;
+ u8 path = arg.path;
+ u8 gband = arg.gain_band;
+ int i;
+
+ switch (type) {
+ case 0:
+ for (i = 0; i < 4; i++, data >>= 8)
+ gain->lna_op1db[gband][path][i] = data & 0xff;
+ break;
+ case 1:
+ for (i = 4; i < 7; i++, data >>= 8)
+ gain->lna_op1db[gband][path][i] = data & 0xff;
+ break;
+ case 2:
+ for (i = 0; i < 4; i++, data >>= 8)
+ gain->tia_lna_op1db[gband][path][i] = data & 0xff;
+ break;
+ case 3:
+ for (i = 4; i < 8; i++, data >>= 8)
+ gain->tia_lna_op1db[gband][path][i] = data & 0xff;
+ break;
+ default:
+ rtw89_warn(rtwdev,
+ "bb gain op1db {0x%x:0x%x} with unknown type: %d\n",
+ arg.addr, data, type);
+ break;
+ }
+}
+
+static void rtw89_phy_config_bb_gain(struct rtw89_dev *rtwdev,
+ const struct rtw89_reg2_def *reg,
+ enum rtw89_rf_path rf_path,
+ void *extra_data)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ union rtw89_phy_bb_gain_arg arg = { .addr = reg->addr };
+
+ if (arg.gain_band >= RTW89_BB_GAIN_BAND_NR)
+ return;
+
+ if (arg.path >= chip->rf_path_num)
+ return;
+
+ if (arg.addr >= 0xf9 && arg.addr <= 0xfe) {
+ rtw89_warn(rtwdev, "bb gain table with flow ctrl\n");
+ return;
+ }
+
+ switch (arg.cfg_type) {
+ case 0:
+ rtw89_phy_cfg_bb_gain_error(rtwdev, arg, reg->data);
+ break;
+ case 1:
+ rtw89_phy_cfg_bb_rpl_ofst(rtwdev, arg, reg->data);
+ break;
+ case 2:
+ rtw89_phy_cfg_bb_gain_bypass(rtwdev, arg, reg->data);
+ break;
+ case 3:
+ rtw89_phy_cfg_bb_gain_op1db(rtwdev, arg, reg->data);
+ break;
+ default:
+ rtw89_warn(rtwdev,
+ "bb gain {0x%x:0x%x} with unknown cfg type: %d\n",
+ arg.addr, reg->data, arg.cfg_type);
+ break;
+ }
+}
+
static void
rtw89_phy_cofig_rf_reg_store(struct rtw89_dev *rtwdev,
const struct rtw89_reg2_def *reg,
@@ -1033,9 +1272,13 @@ void rtw89_phy_init_bb_reg(struct rtw89_dev *rtwdev)
{
const struct rtw89_chip_info *chip = rtwdev->chip;
const struct rtw89_phy_table *bb_table = chip->bb_table;
+ const struct rtw89_phy_table *bb_gain_table = chip->bb_gain_table;
rtw89_phy_init_reg(rtwdev, bb_table, rtw89_phy_config_bb_reg, NULL);
rtw89_chip_init_txpwr_unit(rtwdev, RTW89_PHY_0);
+ if (bb_gain_table)
+ rtw89_phy_init_reg(rtwdev, bb_gain_table,
+ rtw89_phy_config_bb_gain, NULL);
rtw89_phy_bb_reset(rtwdev, RTW89_PHY_0);
}
@@ -495,6 +495,184 @@ static void rtw8852c_power_trim(struct rtw89_dev *rtwdev)
rtw8852c_pa_bias_trim(rtwdev);
}
+struct rtw8852c_bb_gain {
+ u32 gain_g[BB_PATH_NUM_8852C];
+ u32 gain_a[BB_PATH_NUM_8852C];
+ u32 gain_mask;
+};
+
+static const struct rtw8852c_bb_gain bb_gain_lna[LNA_GAIN_NUM] = {
+ { .gain_g = {0x4678, 0x475C}, .gain_a = {0x45DC, 0x4740},
+ .gain_mask = 0x00ff0000 },
+ { .gain_g = {0x4678, 0x475C}, .gain_a = {0x45DC, 0x4740},
+ .gain_mask = 0xff000000 },
+ { .gain_g = {0x467C, 0x4760}, .gain_a = {0x4660, 0x4744},
+ .gain_mask = 0x000000ff },
+ { .gain_g = {0x467C, 0x4760}, .gain_a = {0x4660, 0x4744},
+ .gain_mask = 0x0000ff00 },
+ { .gain_g = {0x467C, 0x4760}, .gain_a = {0x4660, 0x4744},
+ .gain_mask = 0x00ff0000 },
+ { .gain_g = {0x467C, 0x4760}, .gain_a = {0x4660, 0x4744},
+ .gain_mask = 0xff000000 },
+ { .gain_g = {0x4680, 0x4764}, .gain_a = {0x4664, 0x4748},
+ .gain_mask = 0x000000ff },
+};
+
+static const struct rtw8852c_bb_gain bb_gain_tia[TIA_GAIN_NUM] = {
+ { .gain_g = {0x4680, 0x4764}, .gain_a = {0x4664, 0x4748},
+ .gain_mask = 0x00ff0000 },
+ { .gain_g = {0x4680, 0x4764}, .gain_a = {0x4664, 0x4748},
+ .gain_mask = 0xff000000 },
+};
+
+struct rtw8852c_bb_gain_bypass {
+ u32 gain_g[BB_PATH_NUM_8852C];
+ u32 gain_a[BB_PATH_NUM_8852C];
+ u32 gain_mask_g;
+ u32 gain_mask_a;
+};
+
+static
+const struct rtw8852c_bb_gain_bypass bb_gain_bypass_lna[LNA_GAIN_NUM] = {
+ { .gain_g = {0x4BB8, 0x4C7C}, .gain_a = {0x4BB4, 0x4C78},
+ .gain_mask_g = 0xff000000, .gain_mask_a = 0xff},
+ { .gain_g = {0x4BBC, 0x4C80}, .gain_a = {0x4BB4, 0x4C78},
+ .gain_mask_g = 0xff, .gain_mask_a = 0xff00},
+ { .gain_g = {0x4BBC, 0x4C80}, .gain_a = {0x4BB4, 0x4C78},
+ .gain_mask_g = 0xff00, .gain_mask_a = 0xff0000},
+ { .gain_g = {0x4BBC, 0x4C80}, .gain_a = {0x4BB4, 0x4C78},
+ .gain_mask_g = 0xff0000, .gain_mask_a = 0xff000000},
+ { .gain_g = {0x4BBC, 0x4C80}, .gain_a = {0x4BB8, 0x4C7C},
+ .gain_mask_g = 0xff000000, .gain_mask_a = 0xff},
+ { .gain_g = {0x4BC0, 0x4C84}, .gain_a = {0x4BB8, 0x4C7C},
+ .gain_mask_g = 0xff, .gain_mask_a = 0xff00},
+ { .gain_g = {0x4BC0, 0x4C84}, .gain_a = {0x4BB8, 0x4C7C},
+ .gain_mask_g = 0xff00, .gain_mask_a = 0xff0000},
+};
+
+struct rtw8852c_bb_gain_op1db {
+ struct {
+ u32 lna[BB_PATH_NUM_8852C];
+ u32 tia_lna[BB_PATH_NUM_8852C];
+ u32 mask;
+ } reg[LNA_GAIN_NUM];
+ u32 reg_tia0_lna6[BB_PATH_NUM_8852C];
+ u32 mask_tia0_lna6;
+};
+
+static const struct rtw8852c_bb_gain_op1db bb_gain_op1db_a = {
+ .reg = {
+ { .lna = {0x4668, 0x474c}, .tia_lna = {0x4670, 0x4754},
+ .mask = 0xff},
+ { .lna = {0x4668, 0x474c}, .tia_lna = {0x4670, 0x4754},
+ .mask = 0xff00},
+ { .lna = {0x4668, 0x474c}, .tia_lna = {0x4670, 0x4754},
+ .mask = 0xff0000},
+ { .lna = {0x4668, 0x474c}, .tia_lna = {0x4670, 0x4754},
+ .mask = 0xff000000},
+ { .lna = {0x466c, 0x4750}, .tia_lna = {0x4674, 0x4758},
+ .mask = 0xff},
+ { .lna = {0x466c, 0x4750}, .tia_lna = {0x4674, 0x4758},
+ .mask = 0xff00},
+ { .lna = {0x466c, 0x4750}, .tia_lna = {0x4674, 0x4758},
+ .mask = 0xff0000},
+ },
+ .reg_tia0_lna6 = {0x4674, 0x4758},
+ .mask_tia0_lna6 = 0xff000000,
+};
+
+static enum rtw89_phy_bb_gain_band
+rtw8852c_mapping_gain_band(enum rtw89_subband subband)
+{
+ switch (subband) {
+ default:
+ case RTW89_CH_2G:
+ return RTW89_BB_GAIN_BAND_2G;
+ case RTW89_CH_5G_BAND_1:
+ return RTW89_BB_GAIN_BAND_5G_L;
+ case RTW89_CH_5G_BAND_3:
+ return RTW89_BB_GAIN_BAND_5G_M;
+ case RTW89_CH_5G_BAND_4:
+ return RTW89_BB_GAIN_BAND_5G_H;
+ case RTW89_CH_6G_BAND_IDX0:
+ case RTW89_CH_6G_BAND_IDX1:
+ return RTW89_BB_GAIN_BAND_6G_L;
+ case RTW89_CH_6G_BAND_IDX2:
+ case RTW89_CH_6G_BAND_IDX3:
+ return RTW89_BB_GAIN_BAND_6G_M;
+ case RTW89_CH_6G_BAND_IDX4:
+ case RTW89_CH_6G_BAND_IDX5:
+ return RTW89_BB_GAIN_BAND_6G_H;
+ case RTW89_CH_6G_BAND_IDX6:
+ case RTW89_CH_6G_BAND_IDX7:
+ return RTW89_BB_GAIN_BAND_6G_UH;
+ }
+}
+
+static void rtw8852c_set_gain_error(struct rtw89_dev *rtwdev,
+ enum rtw89_subband subband,
+ enum rtw89_rf_path path)
+{
+ const struct rtw89_phy_bb_gain_info *gain = &rtwdev->bb_gain;
+ u8 gain_band = rtw8852c_mapping_gain_band(subband);
+ s32 val;
+ u32 reg;
+ u32 mask;
+ int i;
+
+ for (i = 0; i < LNA_GAIN_NUM; i++) {
+ if (subband == RTW89_CH_2G)
+ reg = bb_gain_lna[i].gain_g[path];
+ else
+ reg = bb_gain_lna[i].gain_a[path];
+
+ mask = bb_gain_lna[i].gain_mask;
+ val = gain->lna_gain[gain_band][path][i];
+ rtw89_phy_write32_mask(rtwdev, reg, mask, val);
+
+ if (subband == RTW89_CH_2G) {
+ reg = bb_gain_bypass_lna[i].gain_g[path];
+ mask = bb_gain_bypass_lna[i].gain_mask_g;
+ } else {
+ reg = bb_gain_bypass_lna[i].gain_a[path];
+ mask = bb_gain_bypass_lna[i].gain_mask_a;
+ }
+
+ val = gain->lna_gain_bypass[gain_band][path][i];
+ rtw89_phy_write32_mask(rtwdev, reg, mask, val);
+
+ if (subband != RTW89_CH_2G) {
+ reg = bb_gain_op1db_a.reg[i].lna[path];
+ mask = bb_gain_op1db_a.reg[i].mask;
+ val = gain->lna_op1db[gain_band][path][i];
+ rtw89_phy_write32_mask(rtwdev, reg, mask, val);
+
+ reg = bb_gain_op1db_a.reg[i].tia_lna[path];
+ mask = bb_gain_op1db_a.reg[i].mask;
+ val = gain->tia_lna_op1db[gain_band][path][i];
+ rtw89_phy_write32_mask(rtwdev, reg, mask, val);
+ }
+ }
+
+ if (subband != RTW89_CH_2G) {
+ reg = bb_gain_op1db_a.reg_tia0_lna6[path];
+ mask = bb_gain_op1db_a.mask_tia0_lna6;
+ val = gain->tia_lna_op1db[gain_band][path][7];
+ rtw89_phy_write32_mask(rtwdev, reg, mask, val);
+ }
+
+ for (i = 0; i < TIA_GAIN_NUM; i++) {
+ if (subband == RTW89_CH_2G)
+ reg = bb_gain_tia[i].gain_g[path];
+ else
+ reg = bb_gain_tia[i].gain_a[path];
+
+ mask = bb_gain_tia[i].gain_mask;
+ val = gain->tia_gain[gain_band][path][i];
+ rtw89_phy_write32_mask(rtwdev, reg, mask, val);
+ }
+}
+
static void rtw8852c_bb_reset_all(struct rtw89_dev *rtwdev,
enum rtw89_phy_idx phy_idx)
{
@@ -8,6 +8,7 @@
#include "core.h"
#define RF_PATH_NUM_8852C 2
+#define BB_PATH_NUM_8852C 2
struct rtw8852c_u_efuse {
u8 rsvd[0x38];