diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h
index a660651..1a98747 100644
--- a/drivers/net/wireless/realtek/rtw88/main.h
+++ b/drivers/net/wireless/realtek/rtw88/main.h
@@ -614,8 +614,7 @@ struct rtw_chip_ops {
 		       u32 addr, u32 mask);
 	bool (*write_rf)(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path,
 			 u32 addr, u32 mask, u32 data);
-	void (*set_tx_power_index)(struct rtw_dev *rtwdev, u8 power_index,
-				   u8 rf_path, u8 rate);
+	void (*set_tx_power_index)(struct rtw_dev *rtwdev);
 	int (*rsvd_page_dump)(struct rtw_dev *rtwdev, u8 *buf, u32 offset,
 			      u32 size);
 	void (*set_antenna)(struct rtw_dev *rtwdev, u8 antenna_tx,
@@ -810,6 +809,7 @@ struct rtw_chip_info {
 	u8 dig_min;
 	u8 txgi_factor;
 	bool is_pwr_by_rate_dec;
+	u8 max_power_index;
 
 	bool ht_supported;
 	bool vht_supported;
@@ -995,6 +995,8 @@ struct rtw_hal {
 			  [RTW_CHANNEL_WIDTH_MAX]
 			  [RTW_RATE_SECTION_MAX]
 			  [RTW_MAX_CHANNEL_NUM_5G];
+	s8 tx_pwr_tbl[RTW_RF_PATH_MAX]
+		     [DESC_RATE_MAX];
 };
 
 struct rtw_dev {
diff --git a/drivers/net/wireless/realtek/rtw88/phy.c b/drivers/net/wireless/realtek/rtw88/phy.c
index 79f25e8..9b5b8bf 100644
--- a/drivers/net/wireless/realtek/rtw88/phy.c
+++ b/drivers/net/wireless/realtek/rtw88/phy.c
@@ -818,33 +818,41 @@ void rtw_phy_load_tables(struct rtw_dev *rtwdev)
 
 #define RTW_MAX_POWER_INDEX		0x3F
 
-static u8 cck_rates[] = {DESC_RATE1M, DESC_RATE2M, DESC_RATE5_5M, DESC_RATE11M};
-static u8 ofdm_rates[] = {DESC_RATE6M,  DESC_RATE9M,  DESC_RATE12M,
-			  DESC_RATE18M, DESC_RATE24M, DESC_RATE36M,
-			  DESC_RATE48M, DESC_RATE54M};
-static u8 ht_1s_rates[] = {DESC_RATEMCS0, DESC_RATEMCS1, DESC_RATEMCS2,
-			   DESC_RATEMCS3, DESC_RATEMCS4, DESC_RATEMCS5,
-			   DESC_RATEMCS6, DESC_RATEMCS7};
-static u8 ht_2s_rates[] = {DESC_RATEMCS8,  DESC_RATEMCS9,  DESC_RATEMCS10,
-			   DESC_RATEMCS11, DESC_RATEMCS12, DESC_RATEMCS13,
-			   DESC_RATEMCS14, DESC_RATEMCS15};
-static u8 vht_1s_rates[] = {DESC_RATEVHT1SS_MCS0, DESC_RATEVHT1SS_MCS1,
-			    DESC_RATEVHT1SS_MCS2, DESC_RATEVHT1SS_MCS3,
-			    DESC_RATEVHT1SS_MCS4, DESC_RATEVHT1SS_MCS5,
-			    DESC_RATEVHT1SS_MCS6, DESC_RATEVHT1SS_MCS7,
-			    DESC_RATEVHT1SS_MCS8, DESC_RATEVHT1SS_MCS9};
-static u8 vht_2s_rates[] = {DESC_RATEVHT2SS_MCS0, DESC_RATEVHT2SS_MCS1,
-			    DESC_RATEVHT2SS_MCS2, DESC_RATEVHT2SS_MCS3,
-			    DESC_RATEVHT2SS_MCS4, DESC_RATEVHT2SS_MCS5,
-			    DESC_RATEVHT2SS_MCS6, DESC_RATEVHT2SS_MCS7,
-			    DESC_RATEVHT2SS_MCS8, DESC_RATEVHT2SS_MCS9};
-static u8 cck_size = ARRAY_SIZE(cck_rates);
-static u8 ofdm_size = ARRAY_SIZE(ofdm_rates);
-static u8 ht_1s_size = ARRAY_SIZE(ht_1s_rates);
-static u8 ht_2s_size = ARRAY_SIZE(ht_2s_rates);
-static u8 vht_1s_size = ARRAY_SIZE(vht_1s_rates);
-static u8 vht_2s_size = ARRAY_SIZE(vht_2s_rates);
-
+u8 cck_rates[] = {DESC_RATE1M, DESC_RATE2M, DESC_RATE5_5M, DESC_RATE11M};
+u8 ofdm_rates[] = {DESC_RATE6M,  DESC_RATE9M,  DESC_RATE12M,
+		   DESC_RATE18M, DESC_RATE24M, DESC_RATE36M,
+		   DESC_RATE48M, DESC_RATE54M};
+u8 ht_1s_rates[] = {DESC_RATEMCS0, DESC_RATEMCS1, DESC_RATEMCS2,
+		    DESC_RATEMCS3, DESC_RATEMCS4, DESC_RATEMCS5,
+		    DESC_RATEMCS6, DESC_RATEMCS7};
+u8 ht_2s_rates[] = {DESC_RATEMCS8,  DESC_RATEMCS9,  DESC_RATEMCS10,
+		    DESC_RATEMCS11, DESC_RATEMCS12, DESC_RATEMCS13,
+		    DESC_RATEMCS14, DESC_RATEMCS15};
+u8 vht_1s_rates[] = {DESC_RATEVHT1SS_MCS0, DESC_RATEVHT1SS_MCS1,
+		     DESC_RATEVHT1SS_MCS2, DESC_RATEVHT1SS_MCS3,
+		     DESC_RATEVHT1SS_MCS4, DESC_RATEVHT1SS_MCS5,
+		     DESC_RATEVHT1SS_MCS6, DESC_RATEVHT1SS_MCS7,
+		     DESC_RATEVHT1SS_MCS8, DESC_RATEVHT1SS_MCS9};
+u8 vht_2s_rates[] = {DESC_RATEVHT2SS_MCS0, DESC_RATEVHT2SS_MCS1,
+		     DESC_RATEVHT2SS_MCS2, DESC_RATEVHT2SS_MCS3,
+		     DESC_RATEVHT2SS_MCS4, DESC_RATEVHT2SS_MCS5,
+		     DESC_RATEVHT2SS_MCS6, DESC_RATEVHT2SS_MCS7,
+		     DESC_RATEVHT2SS_MCS8, DESC_RATEVHT2SS_MCS9};
+u8 cck_size = ARRAY_SIZE(cck_rates);
+u8 ofdm_size = ARRAY_SIZE(ofdm_rates);
+u8 ht_1s_size = ARRAY_SIZE(ht_1s_rates);
+u8 ht_2s_size = ARRAY_SIZE(ht_2s_rates);
+u8 vht_1s_size = ARRAY_SIZE(vht_1s_rates);
+u8 vht_2s_size = ARRAY_SIZE(vht_2s_rates);
+u8 *rate_section[RTW_RATE_SECTION_MAX] = {cck_rates, ofdm_rates,
+					  ht_1s_rates, ht_2s_rates,
+					  vht_1s_rates, vht_2s_rates};
+u8 rate_size[RTW_RATE_SECTION_MAX] = {ARRAY_SIZE(cck_rates),
+				      ARRAY_SIZE(ofdm_rates),
+				      ARRAY_SIZE(ht_1s_rates),
+				      ARRAY_SIZE(ht_2s_rates),
+				      ARRAY_SIZE(vht_1s_rates),
+				      ARRAY_SIZE(vht_2s_rates)};
 static const u8 rtw_channel_idx_5g[RTW_MAX_CHANNEL_NUM_5G] = {
 	36,  38,  40,  42,  44,  46,  48, /* Band 1 */
 	52,  54,  56,  58,  60,  62,  64, /* Band 2 */
@@ -1087,6 +1095,7 @@ void phy_set_tx_power_level_by_path(struct rtw_dev *rtwdev, u8 ch, u8 path)
 
 void rtw_phy_set_tx_power_level(struct rtw_dev *rtwdev, u8 channel)
 {
+	struct rtw_chip_info *chip = rtwdev->chip;
 	struct rtw_hal *hal = &rtwdev->hal;
 	u8 path;
 
@@ -1095,6 +1104,7 @@ void rtw_phy_set_tx_power_level(struct rtw_dev *rtwdev, u8 channel)
 	for (path = 0; path < hal->rf_path_num; path++)
 		phy_set_tx_power_level_by_path(rtwdev, channel, path);
 
+	chip->ops->set_tx_power_index(rtwdev);
 	mutex_unlock(&hal->tx_power_mutex);
 }
 
@@ -1140,8 +1150,8 @@ u8 phy_get_tx_power_index(void *adapter, u8 rf_path, u8 rate,
 
 	tx_power += offset;
 
-	if (tx_power > RTW_MAX_POWER_INDEX)
-		tx_power = RTW_MAX_POWER_INDEX;
+	if (tx_power > rtwdev->chip->max_power_index)
+		tx_power = rtwdev->chip->max_power_index;
 
 	return tx_power;
 }
@@ -1150,7 +1160,6 @@ void phy_set_tx_power_index_by_rs(void *adapter, u8 ch, u8 path, u8 rs)
 {
 	struct rtw_dev *rtwdev = adapter;
 	struct rtw_hal *hal = &rtwdev->hal;
-	struct rtw_chip_info *chip = rtwdev->chip;
 	u8 regd = rtwdev->regd.txpwr_regd;
 	u8 *rates;
 	u8 size;
@@ -1158,26 +1167,18 @@ void phy_set_tx_power_index_by_rs(void *adapter, u8 ch, u8 path, u8 rs)
 	u8 pwr_idx;
 	u8 bw;
 	int i;
-	u8 *rate_sections[RTW_RATE_SECTION_MAX] = {
-		cck_rates, ofdm_rates, ht_1s_rates, ht_2s_rates,
-		vht_1s_rates, vht_2s_rates,
-	};
-	u8 sizes[RTW_RATE_SECTION_MAX] = {
-		cck_size, ofdm_size, ht_1s_size, ht_2s_size,
-		vht_1s_size, vht_2s_size,
-	};
 
 	if (rs >= RTW_RATE_SECTION_MAX)
 		return;
 
-	rates = rate_sections[rs];
-	size = sizes[rs];
+	rates = rate_section[rs];
+	size = rate_size[rs];
 	bw = hal->current_band_width;
 	for (i = 0; i < size; i++) {
 		rate = rates[i];
 		pwr_idx = phy_get_tx_power_index(adapter, path, rate, bw, ch,
 						 regd);
-		chip->ops->set_tx_power_index(rtwdev, pwr_idx, path, rate);
+		hal->tx_pwr_tbl[path][rate] = pwr_idx;
 	}
 }
 
diff --git a/drivers/net/wireless/realtek/rtw88/phy.h b/drivers/net/wireless/realtek/rtw88/phy.h
index f855d75..8dfbb4b 100644
--- a/drivers/net/wireless/realtek/rtw88/phy.h
+++ b/drivers/net/wireless/realtek/rtw88/phy.h
@@ -7,6 +7,15 @@
 
 #include "debug.h"
 
+extern u8 cck_rates[];
+extern u8 ofdm_rates[];
+extern u8 ht_1s_rates[];
+extern u8 ht_2s_rates[];
+extern u8 vht_1s_rates[];
+extern u8 vht_2s_rates[];
+extern u8 *rate_section[];
+extern u8 rate_size[];
+
 void rtw_phy_init(struct rtw_dev *rtwdev);
 void rtw_phy_dynamic_mechanism(struct rtw_dev *rtwdev);
 u8 rtw_phy_rf_power_2_rssi(s8 *rf_power, u8 path_num);
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822b.c b/drivers/net/wireless/realtek/rtw88/rtw8822b.c
index 376ed88..95af97f 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c
@@ -888,27 +888,37 @@ static void rtw8822b_query_rx_desc(struct rtw_dev *rtwdev, u8 *rx_desc,
 	rtw_rx_fill_rx_status(rtwdev, pkt_stat, hdr, rx_status, phy_status);
 }
 
-static void rtw8822b_set_tx_power_index(struct rtw_dev *rtwdev, u8 power_index,
-					u8 path, u8 rate)
+static void
+rtw8822b_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, u8 rs)
 {
+	struct rtw_hal *hal = &rtwdev->hal;
 	static const u32 offset_txagc[2] = {0x1d00, 0x1d80};
 	static u32 phy_pwr_idx;
-	u8 rate_idx;
-	u8 shift;
+	u8 rate, rate_idx, pwr_index, shift;
+	int j;
+
+	for (j = 0; j < rate_size[rs]; j++) {
+		rate = rate_section[rs][j];
+		pwr_index = hal->tx_pwr_tbl[path][rate];
+		shift = rate & 0x3;
+		phy_pwr_idx |= ((u32)pwr_index << (shift * 8));
+		if (shift == 0x3) {
+			rate_idx = rate & 0xfc;
+			rtw_write32(rtwdev, offset_txagc[path] + rate_idx,
+				    phy_pwr_idx);
+			phy_pwr_idx = 0;
+		}
+	}
+}
 
-	if (path > RF_PATH_B || rate > 0x53)
-		return;
+static void rtw8822b_set_tx_power_index(struct rtw_dev *rtwdev)
+{
+	struct rtw_hal *hal = &rtwdev->hal;
+	int rs, path;
 
-	/*
-	 * 8822B uses four bytes tx power index, driver needs to combine every
-	 * one-byte value for the phydm
-	 */
-	shift = rate & 0x3;
-	phy_pwr_idx |= ((u32)power_index << (shift * 8));
-	if (shift == 0x3) {
-		rate_idx = rate & 0xfc;
-		rtw_write32(rtwdev, offset_txagc[path] + rate_idx, phy_pwr_idx);
-		phy_pwr_idx = 0;
+	for (path = 0; path < hal->rf_path_num; path++) {
+		for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++)
+			rtw8822b_set_tx_power_index_by_rate(rtwdev, path, rs);
 	}
 }
 
@@ -1566,6 +1576,7 @@ struct rtw_chip_info rtw8822b_hw_spec = {
 	.rxff_size = 24576,
 	.txgi_factor = 1,
 	.is_pwr_by_rate_dec = true,
+	.max_power_index = 0x3f,
 	.csi_buf_pg_num = 0,
 	.band = RTW_BAND_2G | RTW_BAND_5G,
 	.page_size = 128,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.c b/drivers/net/wireless/realtek/rtw88/rtw8822c.c
index 19e8e7b..9c3f0f1 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c
@@ -751,10 +751,80 @@ static void rtw8822c_query_rx_desc(struct rtw_dev *rtwdev, u8 *rx_desc,
 	rtw_rx_fill_rx_status(rtwdev, pkt_stat, hdr, rx_status, phy_status);
 }
 
-static void rtw8822c_set_tx_power_index(struct rtw_dev *rtwdev, u8 power_index,
-					u8 path, u8 rate)
+static void
+rtw8822c_set_write_tx_power_ref(struct rtw_dev *rtwdev, u8 *tx_pwr_ref_cck,
+				u8 *tx_pwr_ref_ofdm)
 {
-	/* 8822C will use TSSI to track tx power */
+	struct rtw_hal *hal = &rtwdev->hal;
+	u32 txref_cck[2] = {0x18a0, 0x41a0};
+	u32 txref_ofdm[2] = {0x18e8, 0x41e8};
+	u8 path;
+
+	for (path = 0; path < hal->rf_path_num; path++) {
+		rtw_write32_mask(rtwdev, 0x1c90, BIT(15), 0);
+		rtw_write32_mask(rtwdev, txref_cck[path], 0x7f0000,
+				 tx_pwr_ref_cck[path]);
+	}
+	for (path = 0; path < hal->rf_path_num; path++) {
+		rtw_write32_mask(rtwdev, 0x1c90, BIT(15), 0);
+		rtw_write32_mask(rtwdev, txref_ofdm[path], 0x1fc00,
+				 tx_pwr_ref_ofdm[path]);
+	}
+}
+
+static void rtw8822c_set_tx_power_diff(struct rtw_dev *rtwdev, u8 rate,
+				       s8 *diff_idx)
+{
+	u32 offset_txagc = 0x3a00;
+	u8 rate_idx = rate & 0xfc;
+	u8 pwr_idx[4];
+	u32 phy_pwr_idx;
+	int i;
+
+	for (i = 0; i < 4; i++)
+		pwr_idx[i] = diff_idx[i] & 0x7f;
+
+	phy_pwr_idx = pwr_idx[0] |
+		      (pwr_idx[1] << 8) |
+		      (pwr_idx[2] << 16) |
+		      (pwr_idx[3] << 24);
+
+	rtw_write32_mask(rtwdev, 0x1c90, BIT(15), 0x0);
+	rtw_write32_mask(rtwdev, offset_txagc + rate_idx, MASKDWORD,
+			 phy_pwr_idx);
+}
+
+static void rtw8822c_set_tx_power_index(struct rtw_dev *rtwdev)
+{
+	struct rtw_hal *hal = &rtwdev->hal;
+	u8 rs, rate, j;
+	u8 pwr_ref_cck[2] = {hal->tx_pwr_tbl[RF_PATH_A][DESC_RATE11M],
+			     hal->tx_pwr_tbl[RF_PATH_B][DESC_RATE11M]};
+	u8 pwr_ref_ofdm[2] = {hal->tx_pwr_tbl[RF_PATH_A][DESC_RATEMCS7],
+			      hal->tx_pwr_tbl[RF_PATH_B][DESC_RATEMCS7]};
+	s8 diff_a, diff_b;
+	u8 pwr_a, pwr_b;
+	s8 diff_idx[4];
+
+	rtw8822c_set_write_tx_power_ref(rtwdev, pwr_ref_cck, pwr_ref_ofdm);
+	for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) {
+		for (j = 0; j < rate_size[rs]; j++) {
+			rate = rate_section[rs][j];
+			pwr_a = hal->tx_pwr_tbl[RF_PATH_A][rate];
+			pwr_b = hal->tx_pwr_tbl[RF_PATH_B][rate];
+			if (rs == 0) {
+				diff_a = (s8)pwr_a - (s8)pwr_ref_cck[0];
+				diff_b = (s8)pwr_b - (s8)pwr_ref_cck[1];
+			} else {
+				diff_a = (s8)pwr_a - (s8)pwr_ref_ofdm[0];
+				diff_b = (s8)pwr_b - (s8)pwr_ref_ofdm[1];
+			}
+			diff_idx[rate % 4] = min(diff_a, diff_b);
+			if (rate % 4 == 3)
+				rtw8822c_set_tx_power_diff(rtwdev, rate - 3,
+							   diff_idx);
+		}
+	}
 }
 
 static void rtw8822c_cfg_ldo25(struct rtw_dev *rtwdev, bool enable)
@@ -1182,6 +1252,7 @@ struct rtw_chip_info rtw8822c_hw_spec = {
 	.rxff_size = 24576,
 	.txgi_factor = 2,
 	.is_pwr_by_rate_dec = false,
+	.max_power_index = 0x7f,
 	.csi_buf_pg_num = 50,
 	.band = RTW_BAND_2G | RTW_BAND_5G,
 	.page_size = 128,
