@@ -1466,6 +1466,7 @@ struct rtw_dm_info {
u8 thermal_avg[RTW_RF_PATH_MAX];
u8 thermal_meter_k;
s8 delta_power_index[RTW_RF_PATH_MAX];
+ s8 delta_power_index_last[RTW_RF_PATH_MAX];
u8 default_ofdm_index;
bool pwr_trk_triggered;
bool pwr_trk_init_trigger;
@@ -61,6 +61,46 @@ static int rtw8821c_read_efuse(struct rtw_dev *rtwdev, u8 *log_map)
return 0;
}
+static const u32 rtw8821c_txscale_tbl[] = {
+ 0x081, 0x088, 0x090, 0x099, 0x0a2, 0x0ac, 0x0b6, 0x0c0, 0x0cc, 0x0d8,
+ 0x0e5, 0x0f2, 0x101, 0x110, 0x120, 0x131, 0x143, 0x156, 0x16a, 0x180,
+ 0x197, 0x1af, 0x1c8, 0x1e3, 0x200, 0x21e, 0x23e, 0x261, 0x285, 0x2ab,
+ 0x2d3, 0x2fe, 0x32b, 0x35c, 0x38e, 0x3c4, 0x3fe
+};
+
+static const u8 rtw8821c_get_swing_index(struct rtw_dev *rtwdev)
+{
+ u8 i = 0;
+ u32 swing, table_value;
+
+ swing = rtw_read32_mask(rtwdev, REG_TXSCALE_A, 0xffe00000);
+ for (i = 0; i < ARRAY_SIZE(rtw8821c_txscale_tbl); i++) {
+ table_value = rtw8821c_txscale_tbl[i];
+ if (swing == table_value)
+ break;
+ }
+
+ return i;
+}
+
+static void rtw8821c_pwrtrack_init(struct rtw_dev *rtwdev)
+{
+ struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+ u8 swing_idx = rtw8821c_get_swing_index(rtwdev);
+
+ if (swing_idx >= ARRAY_SIZE(rtw8821c_txscale_tbl))
+ dm_info->default_ofdm_index = 24;
+ else
+ dm_info->default_ofdm_index = swing_idx;
+
+ ewma_thermal_init(&dm_info->avg_thermal[RF_PATH_A]);
+ dm_info->delta_power_index[RF_PATH_A] = 0;
+ dm_info->delta_power_index_last[RF_PATH_A] = 0;
+ dm_info->pwr_trk_triggered = false;
+ dm_info->pwr_trk_init_trigger = true;
+ dm_info->thermal_meter_k = rtwdev->efuse.thermal_meter_k;
+}
+
static void rtw8821c_phy_set_param(struct rtw_dev *rtwdev)
{
u8 crystal_cap, val;
@@ -103,6 +143,8 @@ static void rtw8821c_phy_set_param(struct rtw_dev *rtwdev)
rtw_phy_init(rtwdev);
rtwdev->dm_info.cck_pd_default = rtw_read8(rtwdev, REG_CSRATIO) & 0x1f;
+
+ rtw8821c_pwrtrack_init(rtwdev);
}
static int rtw8821c_mac_init(struct rtw_dev *rtwdev)
@@ -362,6 +404,7 @@ static void rtw8821c_set_channel_bb_swing(struct rtw_dev *rtwdev, u8 channel,
{
rtw_write32_mask(rtwdev, REG_TXSCALE_A, GENMASK(31, 21),
rtw8821c_get_bb_swing(rtwdev, channel));
+ rtw8821c_pwrtrack_init(rtwdev);
}
static void rtw8821c_set_channel(struct rtw_dev *rtwdev, u8 channel, u8 bw,
@@ -597,6 +640,152 @@ static void rtw8821c_phy_calibration(struct rtw_dev *rtwdev)
rtw8821c_do_iqk(rtwdev);
}
+static void
+rtw8821c_txagc_swing_offset(struct rtw_dev *rtwdev, u8 pwr_idx_offset,
+ s8 pwr_idx_offset_lower,
+ s8 *txagc_idx, u8 *swing_idx)
+{
+ struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+ s8 delta_pwr_idx = dm_info->delta_power_index[RF_PATH_A];
+ u8 swing_upper_bound = dm_info->default_ofdm_index + 10;
+ u8 swing_lower_bound = 0;
+ u8 max_pwr_idx_offset = 0xf;
+ s8 agc_index = 0;
+ u8 swing_index = dm_info->default_ofdm_index;
+
+ pwr_idx_offset = min_t(u8, pwr_idx_offset, max_pwr_idx_offset);
+ pwr_idx_offset_lower = max_t(s8, pwr_idx_offset_lower, -15);
+
+ if (delta_pwr_idx >= 0) {
+ if (delta_pwr_idx <= pwr_idx_offset) {
+ agc_index = delta_pwr_idx;
+ swing_index = dm_info->default_ofdm_index;
+ } else if (delta_pwr_idx > pwr_idx_offset) {
+ agc_index = pwr_idx_offset;
+ swing_index = dm_info->default_ofdm_index +
+ delta_pwr_idx - pwr_idx_offset;
+ swing_index = min_t(u8, swing_index, swing_upper_bound);
+ }
+ } else if (delta_pwr_idx < 0) {
+ if (delta_pwr_idx >= pwr_idx_offset_lower) {
+ agc_index = delta_pwr_idx;
+ swing_index = dm_info->default_ofdm_index;
+ } else if (delta_pwr_idx < pwr_idx_offset_lower) {
+ if (dm_info->default_ofdm_index >
+ (pwr_idx_offset_lower - delta_pwr_idx))
+ swing_index = dm_info->default_ofdm_index +
+ delta_pwr_idx - pwr_idx_offset_lower;
+ else
+ swing_index = swing_lower_bound;
+
+ agc_index = pwr_idx_offset_lower;
+ }
+ }
+
+ if (swing_index >= ARRAY_SIZE(rtw8821c_txscale_tbl)) {
+ rtw_warn(rtwdev, "swing index overflow\n");
+ swing_index = ARRAY_SIZE(rtw8821c_txscale_tbl) - 1;
+ }
+
+ *txagc_idx = agc_index;
+ *swing_idx = swing_index;
+}
+
+static void rtw8821c_pwrtrack_set_pwr(struct rtw_dev *rtwdev, u8 pwr_idx_offset,
+ s8 pwr_idx_offset_lower)
+{
+ s8 txagc_idx;
+ u8 swing_idx;
+
+ rtw8821c_txagc_swing_offset(rtwdev, pwr_idx_offset, pwr_idx_offset_lower,
+ &txagc_idx, &swing_idx);
+ rtw_write32_mask(rtwdev, REG_TXAGCIDX, GENMASK(6, 1), txagc_idx);
+ rtw_write32_mask(rtwdev, REG_TXSCALE_A, GENMASK(31, 21),
+ rtw8821c_txscale_tbl[swing_idx]);
+}
+
+static void rtw8821c_pwrtrack_set(struct rtw_dev *rtwdev)
+{
+ struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+ u8 pwr_idx_offset, tx_pwr_idx;
+ s8 pwr_idx_offset_lower;
+ u8 channel = rtwdev->hal.current_channel;
+ u8 band_width = rtwdev->hal.current_band_width;
+ u8 regd = rtwdev->regd.txpwr_regd;
+ u8 tx_rate = dm_info->tx_rate;
+ u8 max_pwr_idx = rtwdev->chip->max_power_index;
+
+ tx_pwr_idx = rtw_phy_get_tx_power_index(rtwdev, RF_PATH_A, tx_rate,
+ band_width, channel, regd);
+
+ tx_pwr_idx = min_t(u8, tx_pwr_idx, max_pwr_idx);
+
+ pwr_idx_offset = max_pwr_idx - tx_pwr_idx;
+ pwr_idx_offset_lower = 0 - tx_pwr_idx;
+
+ rtw8821c_pwrtrack_set_pwr(rtwdev, pwr_idx_offset, pwr_idx_offset_lower);
+}
+
+static void rtw8821c_phy_pwrtrack(struct rtw_dev *rtwdev)
+{
+ struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+ struct rtw_swing_table swing_table;
+ u8 thermal_value, delta;
+
+ rtw_phy_config_swing_table(rtwdev, &swing_table);
+
+ if (rtwdev->efuse.thermal_meter[0] == 0xff)
+ return;
+
+ thermal_value = rtw_read_rf(rtwdev, RF_PATH_A, RF_T_METER, 0xfc00);
+
+ rtw_phy_pwrtrack_avg(rtwdev, thermal_value, RF_PATH_A);
+
+ if (dm_info->pwr_trk_init_trigger)
+ dm_info->pwr_trk_init_trigger = false;
+ else if (!rtw_phy_pwrtrack_thermal_changed(rtwdev, thermal_value,
+ RF_PATH_A))
+ goto iqk;
+
+ delta = rtw_phy_pwrtrack_get_delta(rtwdev, RF_PATH_A);
+
+ delta = min_t(u8, delta, RTW_PWR_TRK_TBL_SZ - 1);
+
+ dm_info->delta_power_index[RF_PATH_A] =
+ rtw_phy_pwrtrack_get_pwridx(rtwdev, &swing_table, RF_PATH_A,
+ RF_PATH_A, delta);
+ if (dm_info->delta_power_index[RF_PATH_A] ==
+ dm_info->delta_power_index_last[RF_PATH_A])
+ goto iqk;
+ else
+ dm_info->delta_power_index_last[RF_PATH_A] =
+ dm_info->delta_power_index[RF_PATH_A];
+ rtw8821c_pwrtrack_set(rtwdev);
+
+iqk:
+ if (rtw_phy_pwrtrack_need_iqk(rtwdev))
+ rtw8821c_do_iqk(rtwdev);
+}
+
+static void rtw8821c_pwr_track(struct rtw_dev *rtwdev)
+{
+ struct rtw_efuse *efuse = &rtwdev->efuse;
+ struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+
+ if (efuse->power_track_type != 0)
+ return;
+
+ if (!dm_info->pwr_trk_triggered) {
+ rtw_write_rf(rtwdev, RF_PATH_A, RF_T_METER,
+ GENMASK(17, 16), 0x03);
+ dm_info->pwr_trk_triggered = true;
+ return;
+ }
+
+ rtw8821c_phy_pwrtrack(rtwdev);
+ dm_info->pwr_trk_triggered = false;
+}
+
static void rtw8821c_phy_cck_pd_set(struct rtw_dev *rtwdev, u8 new_lvl)
{
struct rtw_dm_info *dm_info = &rtwdev->dm_info;
@@ -1060,6 +1249,106 @@ static struct rtw_chip_ops rtw8821c_ops = {
.false_alarm_statistics = rtw8821c_false_alarm_statistics,
.phy_calibration = rtw8821c_phy_calibration,
.cck_pd_set = rtw8821c_phy_cck_pd_set,
+ .pwr_track = rtw8821c_pwr_track,
+};
+
+static const u8 rtw8821c_pwrtrk_5gb_n[][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 1, 1, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, 8, 8, 8, 9, 9, 9, 10, 10,
+ 11, 11, 12, 12, 12, 12, 12},
+ {0, 1, 1, 1, 2, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, 8, 8, 9, 9, 10, 10, 11,
+ 11, 12, 12, 12, 12, 12, 12, 12},
+ {0, 1, 2, 2, 3, 4, 4, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 9, 10, 10, 11,
+ 11, 12, 12, 12, 12, 12, 12},
+};
+
+static const u8 rtw8821c_pwrtrk_5gb_p[][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 11, 11,
+ 12, 12, 12, 12, 12, 12, 12},
+ {0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, 8, 8, 9, 10, 10, 11, 11,
+ 12, 12, 12, 12, 12, 12, 12, 12},
+ {0, 1, 1, 1, 2, 3, 3, 3, 4, 4, 4, 5, 6, 6, 7, 7, 8, 8, 9, 10, 10, 11,
+ 11, 12, 12, 12, 12, 12, 12, 12},
+};
+
+static const u8 rtw8821c_pwrtrk_5ga_n[][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 1, 1, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, 8, 8, 8, 9, 9, 9, 10, 10,
+ 11, 11, 12, 12, 12, 12, 12},
+ {0, 1, 1, 1, 2, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, 8, 8, 9, 9, 10, 10, 11,
+ 11, 12, 12, 12, 12, 12, 12, 12},
+ {0, 1, 2, 2, 3, 4, 4, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 9, 10, 10, 11,
+ 11, 12, 12, 12, 12, 12, 12},
+};
+
+static const u8 rtw8821c_pwrtrk_5ga_p[][RTW_PWR_TRK_TBL_SZ] = {
+ {0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 11, 11,
+ 12, 12, 12, 12, 12, 12, 12},
+ {0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, 8, 8, 9, 10, 10, 11, 11,
+ 12, 12, 12, 12, 12, 12, 12, 12},
+ {0, 1, 1, 1, 2, 3, 3, 3, 4, 4, 4, 5, 6, 6, 7, 7, 8, 8, 9, 10, 10, 11,
+ 11, 12, 12, 12, 12, 12, 12, 12},
+};
+
+static const u8 rtw8821c_pwrtrk_2gb_n[] = {
+ 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4,
+ 4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 8, 8, 9
+};
+
+static const u8 rtw8821c_pwrtrk_2gb_p[] = {
+ 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 5, 5,
+ 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 9, 9, 9, 9
+};
+
+static const u8 rtw8821c_pwrtrk_2ga_n[] = {
+ 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4,
+ 4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 8, 8, 9
+};
+
+static const u8 rtw8821c_pwrtrk_2ga_p[] = {
+ 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 5, 5,
+ 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 9, 9, 9, 9
+};
+
+static const u8 rtw8821c_pwrtrk_2g_cck_b_n[] = {
+ 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4,
+ 4, 5, 5, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 9
+};
+
+static const u8 rtw8821c_pwrtrk_2g_cck_b_p[] = {
+ 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5,
+ 5, 6, 6, 7, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9
+};
+
+static const u8 rtw8821c_pwrtrk_2g_cck_a_n[] = {
+ 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4,
+ 4, 5, 5, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 9
+};
+
+static const u8 rtw8821c_pwrtrk_2g_cck_a_p[] = {
+ 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5,
+ 5, 6, 6, 7, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9
+};
+
+const struct rtw_pwr_track_tbl rtw8821c_rtw_pwr_track_tbl = {
+ .pwrtrk_5gb_n[0] = rtw8821c_pwrtrk_5gb_n[0],
+ .pwrtrk_5gb_n[1] = rtw8821c_pwrtrk_5gb_n[1],
+ .pwrtrk_5gb_n[2] = rtw8821c_pwrtrk_5gb_n[2],
+ .pwrtrk_5gb_p[0] = rtw8821c_pwrtrk_5gb_p[0],
+ .pwrtrk_5gb_p[1] = rtw8821c_pwrtrk_5gb_p[1],
+ .pwrtrk_5gb_p[2] = rtw8821c_pwrtrk_5gb_p[2],
+ .pwrtrk_5ga_n[0] = rtw8821c_pwrtrk_5ga_n[0],
+ .pwrtrk_5ga_n[1] = rtw8821c_pwrtrk_5ga_n[1],
+ .pwrtrk_5ga_n[2] = rtw8821c_pwrtrk_5ga_n[2],
+ .pwrtrk_5ga_p[0] = rtw8821c_pwrtrk_5ga_p[0],
+ .pwrtrk_5ga_p[1] = rtw8821c_pwrtrk_5ga_p[1],
+ .pwrtrk_5ga_p[2] = rtw8821c_pwrtrk_5ga_p[2],
+ .pwrtrk_2gb_n = rtw8821c_pwrtrk_2gb_n,
+ .pwrtrk_2gb_p = rtw8821c_pwrtrk_2gb_p,
+ .pwrtrk_2ga_n = rtw8821c_pwrtrk_2ga_n,
+ .pwrtrk_2ga_p = rtw8821c_pwrtrk_2ga_p,
+ .pwrtrk_2g_cckb_n = rtw8821c_pwrtrk_2g_cck_b_n,
+ .pwrtrk_2g_cckb_p = rtw8821c_pwrtrk_2g_cck_b_p,
+ .pwrtrk_2g_ccka_n = rtw8821c_pwrtrk_2g_cck_a_n,
+ .pwrtrk_2g_ccka_p = rtw8821c_pwrtrk_2g_cck_a_p,
};
struct rtw_chip_info rtw8821c_hw_spec = {
@@ -1104,6 +1393,8 @@ struct rtw_chip_info rtw8821c_hw_spec = {
.rfe_defs = rtw8821c_rfe_defs,
.rfe_defs_size = ARRAY_SIZE(rtw8821c_rfe_defs),
.rx_ldpc = false,
+ .pwr_track_tbl = &rtw8821c_rtw_pwr_track_tbl,
+ .iqk_threshold = 8,
};
EXPORT_SYMBOL(rtw8821c_hw_spec);
@@ -200,6 +200,7 @@ _rtw_write32s_mask(struct rtw_dev *rtwdev, u32 addr, u32 mask, u32 data)
#define REG_TXSCALE_A 0xc1c
#define REG_TXDFIR 0xc20
#define REG_RXIGI_A 0xc50
+#define REG_TXAGCIDX 0xc94
#define REG_TRSW 0xca0
#define REG_RFESEL0 0xcb0
#define REG_RFESEL8 0xcb4