wifi: rtw89: 8851b: add TX power related functions
authorPing-Ke Shih <pkshih@realtek.com>
Fri, 19 May 2023 03:14:54 +0000 (11:14 +0800)
committerKalle Valo <kvalo@kernel.org>
Thu, 25 May 2023 16:11:29 +0000 (19:11 +0300)
Get TX power value from tables according to selected country and channel,
and set proper power to registers.

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20230519031500.21087-2-pkshih@realtek.com
drivers/net/wireless/realtek/rtw89/rtw8851b.c

index 6ecdd3d..cead009 100644 (file)
@@ -448,6 +448,34 @@ static void rtw8851b_phycap_parsing_thermal_trim(struct rtw89_dev *rtwdev,
        }
 }
 
+static void rtw8851b_thermal_trim(struct rtw89_dev *rtwdev)
+{
+#define __thm_setting(raw)                             \
+({                                                     \
+       u8 __v = (raw);                                 \
+       ((__v & 0x1) << 3) | ((__v & 0x1f) >> 1);       \
+})
+       struct rtw89_power_trim_info *info = &rtwdev->pwr_trim;
+       u8 i, val;
+
+       if (!info->pg_thermal_trim) {
+               rtw89_debug(rtwdev, RTW89_DBG_RFK,
+                           "[THERMAL][TRIM] no PG, do nothing\n");
+
+               return;
+       }
+
+       for (i = 0; i < RF_PATH_NUM_8851B; i++) {
+               val = __thm_setting(info->thermal_trim[i]);
+               rtw89_write_rf(rtwdev, i, RR_TM2, RR_TM2_OFF, val);
+
+               rtw89_debug(rtwdev, RTW89_DBG_RFK,
+                           "[THERMAL][TRIM] path=%d thermal_setting=0x%x\n",
+                           i, val);
+       }
+#undef __thm_setting
+}
+
 static void rtw8851b_phycap_parsing_pa_bias_trim(struct rtw89_dev *rtwdev,
                                                 u8 *phycap_map)
 {
@@ -468,6 +496,32 @@ static void rtw8851b_phycap_parsing_pa_bias_trim(struct rtw89_dev *rtwdev,
        }
 }
 
+static void rtw8851b_pa_bias_trim(struct rtw89_dev *rtwdev)
+{
+       struct rtw89_power_trim_info *info = &rtwdev->pwr_trim;
+       u8 pabias_2g, pabias_5g;
+       u8 i;
+
+       if (!info->pg_pa_bias_trim) {
+               rtw89_debug(rtwdev, RTW89_DBG_RFK,
+                           "[PA_BIAS][TRIM] no PG, do nothing\n");
+
+               return;
+       }
+
+       for (i = 0; i < RF_PATH_NUM_8851B; i++) {
+               pabias_2g = u8_get_bits(info->pa_bias_trim[i], GENMASK(3, 0));
+               pabias_5g = u8_get_bits(info->pa_bias_trim[i], GENMASK(7, 4));
+
+               rtw89_debug(rtwdev, RTW89_DBG_RFK,
+                           "[PA_BIAS][TRIM] path=%d 2G=0x%x 5G=0x%x\n",
+                           i, pabias_2g, pabias_5g);
+
+               rtw89_write_rf(rtwdev, i, RR_BIASA, RR_BIASA_TXG, pabias_2g);
+               rtw89_write_rf(rtwdev, i, RR_BIASA, RR_BIASA_TXA, pabias_5g);
+       }
+}
+
 static void rtw8851b_phycap_parsing_gain_comp(struct rtw89_dev *rtwdev, u8 *phycap_map)
 {
        static const u32 comp_addrs[][RTW89_SUBBAND_2GHZ_5GHZ_NR] = {
@@ -565,6 +619,12 @@ static void rtw8851b_rfe_gpio(struct rtw89_dev *rtwdev)
        }
 }
 
+static void rtw8851b_power_trim(struct rtw89_dev *rtwdev)
+{
+       rtw8851b_thermal_trim(rtwdev);
+       rtw8851b_pa_bias_trim(rtwdev);
+}
+
 static void rtw8851b_set_channel_mac(struct rtw89_dev *rtwdev,
                                     const struct rtw89_chan *chan,
                                     u8 mac_idx)
@@ -1355,6 +1415,195 @@ static void rtw8851b_set_channel_help(struct rtw89_dev *rtwdev, bool enter,
        }
 }
 
+static u32 rtw8851b_bb_cal_txpwr_ref(struct rtw89_dev *rtwdev,
+                                    enum rtw89_phy_idx phy_idx, s16 ref)
+{
+       const u16 tssi_16dbm_cw = 0x12c;
+       const u8 base_cw_0db = 0x27;
+       const s8 ofst_int = 0;
+       s16 pwr_s10_3;
+       s16 rf_pwr_cw;
+       u16 bb_pwr_cw;
+       u32 pwr_cw;
+       u32 tssi_ofst_cw;
+
+       pwr_s10_3 = (ref << 1) + (s16)(ofst_int) + (s16)(base_cw_0db << 3);
+       bb_pwr_cw = u16_get_bits(pwr_s10_3, GENMASK(2, 0));
+       rf_pwr_cw = u16_get_bits(pwr_s10_3, GENMASK(8, 3));
+       rf_pwr_cw = clamp_t(s16, rf_pwr_cw, 15, 63);
+       pwr_cw = (rf_pwr_cw << 3) | bb_pwr_cw;
+
+       tssi_ofst_cw = (u32)((s16)tssi_16dbm_cw + (ref << 1) - (16 << 3));
+       rtw89_debug(rtwdev, RTW89_DBG_TXPWR,
+                   "[TXPWR] tssi_ofst_cw=%d rf_cw=0x%x bb_cw=0x%x\n",
+                   tssi_ofst_cw, rf_pwr_cw, bb_pwr_cw);
+
+       return u32_encode_bits(tssi_ofst_cw, B_DPD_TSSI_CW) |
+              u32_encode_bits(pwr_cw, B_DPD_PWR_CW) |
+              u32_encode_bits(ref, B_DPD_REF);
+}
+
+static void rtw8851b_set_txpwr_ref(struct rtw89_dev *rtwdev,
+                                  enum rtw89_phy_idx phy_idx)
+{
+       static const u32 addr[RF_PATH_NUM_8851B] = {0x5800};
+       const u32 mask = B_DPD_TSSI_CW | B_DPD_PWR_CW | B_DPD_REF;
+       const u8 ofst_ofdm = 0x4;
+       const u8 ofst_cck = 0x8;
+       const s16 ref_ofdm = 0;
+       const s16 ref_cck = 0;
+       u32 val;
+       u8 i;
+
+       rtw89_debug(rtwdev, RTW89_DBG_TXPWR, "[TXPWR] set txpwr reference\n");
+
+       rtw89_mac_txpwr_write32_mask(rtwdev, phy_idx, R_AX_PWR_RATE_CTRL,
+                                    B_AX_PWR_REF, 0x0);
+
+       rtw89_debug(rtwdev, RTW89_DBG_TXPWR, "[TXPWR] set bb ofdm txpwr ref\n");
+       val = rtw8851b_bb_cal_txpwr_ref(rtwdev, phy_idx, ref_ofdm);
+
+       for (i = 0; i < RF_PATH_NUM_8851B; i++)
+               rtw89_phy_write32_idx(rtwdev, addr[i] + ofst_ofdm, mask, val,
+                                     phy_idx);
+
+       rtw89_debug(rtwdev, RTW89_DBG_TXPWR, "[TXPWR] set bb cck txpwr ref\n");
+       val = rtw8851b_bb_cal_txpwr_ref(rtwdev, phy_idx, ref_cck);
+
+       for (i = 0; i < RF_PATH_NUM_8851B; i++)
+               rtw89_phy_write32_idx(rtwdev, addr[i] + ofst_cck, mask, val,
+                                     phy_idx);
+}
+
+static void rtw8851b_bb_set_tx_shape_dfir(struct rtw89_dev *rtwdev,
+                                         const struct rtw89_chan *chan,
+                                         u8 tx_shape_idx,
+                                         enum rtw89_phy_idx phy_idx)
+{
+#define __DFIR_CFG_ADDR(i) (R_TXFIR0 + ((i) << 2))
+#define __DFIR_CFG_MASK 0xffffffff
+#define __DFIR_CFG_NR 8
+#define __DECL_DFIR_PARAM(_name, _val...) \
+       static const u32 param_ ## _name[] = {_val}; \
+       static_assert(ARRAY_SIZE(param_ ## _name) == __DFIR_CFG_NR)
+
+       __DECL_DFIR_PARAM(flat,
+                         0x023D23FF, 0x0029B354, 0x000FC1C8, 0x00FDB053,
+                         0x00F86F9A, 0x06FAEF92, 0x00FE5FCC, 0x00FFDFF5);
+       __DECL_DFIR_PARAM(sharp,
+                         0x023D83FF, 0x002C636A, 0x0013F204, 0x00008090,
+                         0x00F87FB0, 0x06F99F83, 0x00FDBFBA, 0x00003FF5);
+       __DECL_DFIR_PARAM(sharp_14,
+                         0x023B13FF, 0x001C42DE, 0x00FDB0AD, 0x00F60F6E,
+                         0x00FD8F92, 0x0602D011, 0x0001C02C, 0x00FFF00A);
+       u8 ch = chan->channel;
+       const u32 *param;
+       u32 addr;
+       int i;
+
+       if (ch > 14) {
+               rtw89_warn(rtwdev,
+                          "set tx shape dfir by unknown ch: %d on 2G\n", ch);
+               return;
+       }
+
+       if (ch == 14)
+               param = param_sharp_14;
+       else
+               param = tx_shape_idx == 0 ? param_flat : param_sharp;
+
+       for (i = 0; i < __DFIR_CFG_NR; i++) {
+               addr = __DFIR_CFG_ADDR(i);
+               rtw89_debug(rtwdev, RTW89_DBG_TXPWR,
+                           "set tx shape dfir: 0x%x: 0x%x\n", addr, param[i]);
+               rtw89_phy_write32_idx(rtwdev, addr, __DFIR_CFG_MASK, param[i],
+                                     phy_idx);
+       }
+
+#undef __DECL_DFIR_PARAM
+#undef __DFIR_CFG_NR
+#undef __DFIR_CFG_MASK
+#undef __DECL_CFG_ADDR
+}
+
+static void rtw8851b_set_tx_shape(struct rtw89_dev *rtwdev,
+                                 const struct rtw89_chan *chan,
+                                 enum rtw89_phy_idx phy_idx)
+{
+       u8 band = chan->band_type;
+       u8 regd = rtw89_regd_get(rtwdev, band);
+       u8 tx_shape_cck = rtw89_8851b_tx_shape[band][RTW89_RS_CCK][regd];
+       u8 tx_shape_ofdm = rtw89_8851b_tx_shape[band][RTW89_RS_OFDM][regd];
+
+       if (band == RTW89_BAND_2G)
+               rtw8851b_bb_set_tx_shape_dfir(rtwdev, chan, tx_shape_cck, phy_idx);
+
+       rtw89_phy_write32_mask(rtwdev, R_DCFO_OPT, B_TXSHAPE_TRIANGULAR_CFG,
+                              tx_shape_ofdm);
+}
+
+static void rtw8851b_set_txpwr(struct rtw89_dev *rtwdev,
+                              const struct rtw89_chan *chan,
+                              enum rtw89_phy_idx phy_idx)
+{
+       rtw89_phy_set_txpwr_byrate(rtwdev, chan, phy_idx);
+       rtw89_phy_set_txpwr_offset(rtwdev, chan, phy_idx);
+       rtw8851b_set_tx_shape(rtwdev, chan, phy_idx);
+       rtw89_phy_set_txpwr_limit(rtwdev, chan, phy_idx);
+       rtw89_phy_set_txpwr_limit_ru(rtwdev, chan, phy_idx);
+}
+
+static void rtw8851b_set_txpwr_ctrl(struct rtw89_dev *rtwdev,
+                                   enum rtw89_phy_idx phy_idx)
+{
+       rtw8851b_set_txpwr_ref(rtwdev, phy_idx);
+}
+
+static
+void rtw8851b_set_txpwr_ul_tb_offset(struct rtw89_dev *rtwdev,
+                                    s8 pw_ofst, enum rtw89_mac_idx mac_idx)
+{
+       u32 reg;
+
+       if (pw_ofst < -16 || pw_ofst > 15) {
+               rtw89_warn(rtwdev, "[ULTB] Err pwr_offset=%d\n", pw_ofst);
+               return;
+       }
+
+       reg = rtw89_mac_reg_by_idx(R_AX_PWR_UL_TB_CTRL, mac_idx);
+       rtw89_write32_set(rtwdev, reg, B_AX_PWR_UL_TB_CTRL_EN);
+
+       reg = rtw89_mac_reg_by_idx(R_AX_PWR_UL_TB_1T, mac_idx);
+       rtw89_write32_mask(rtwdev, reg, B_AX_PWR_UL_TB_1T_MASK, pw_ofst);
+
+       pw_ofst = max_t(s8, pw_ofst - 3, -16);
+       reg = rtw89_mac_reg_by_idx(R_AX_PWR_UL_TB_2T, mac_idx);
+       rtw89_write32_mask(rtwdev, reg, B_AX_PWR_UL_TB_2T_MASK, pw_ofst);
+}
+
+static int
+rtw8851b_init_txpwr_unit(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx)
+{
+       int ret;
+
+       ret = rtw89_mac_txpwr_write32(rtwdev, phy_idx, R_AX_PWR_UL_CTRL2, 0x07763333);
+       if (ret)
+               return ret;
+
+       ret = rtw89_mac_txpwr_write32(rtwdev, phy_idx, R_AX_PWR_COEXT_CTRL, 0x01ebf000);
+       if (ret)
+               return ret;
+
+       ret = rtw89_mac_txpwr_write32(rtwdev, phy_idx, R_AX_PWR_UL_CTRL0, 0x0002f8ff);
+       if (ret)
+               return ret;
+
+       rtw8851b_set_txpwr_ul_tb_offset(rtwdev, 0, phy_idx == RTW89_PHY_1 ?
+                                                  RTW89_MAC_1 : RTW89_MAC_0);
+
+       return 0;
+}
+
 static void rtw8851b_btc_set_rfe(struct rtw89_dev *rtwdev)
 {
        struct rtw89_btc *btc = &rtwdev->btc;
@@ -1711,6 +1960,11 @@ static const struct rtw89_chip_ops rtw8851b_chip_ops = {
        .read_phycap            = rtw8851b_read_phycap,
        .fem_setup              = NULL,
        .rfe_gpio               = rtw8851b_rfe_gpio,
+       .power_trim             = rtw8851b_power_trim,
+       .set_txpwr              = rtw8851b_set_txpwr,
+       .set_txpwr_ctrl         = rtw8851b_set_txpwr_ctrl,
+       .init_txpwr_unit        = rtw8851b_init_txpwr_unit,
+       .set_txpwr_ul_tb_offset = rtw8851b_set_txpwr_ul_tb_offset,
        .pwr_on_func            = rtw8851b_pwr_on_func,
        .pwr_off_func           = rtw8851b_pwr_off_func,
        .fill_txdesc            = rtw89_core_fill_txdesc,