diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index c315ef96e91e..396e6dd08b04 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -17,6 +17,7 @@ struct rtw89_pci_info;
 struct rtw89_mac_gen_def;
 struct rtw89_phy_gen_def;
 struct rtw89_efuse_block_cfg;
+struct rtw89_fw_txpwr_track_cfg;
 
 extern const struct ieee80211_ops rtw89_ops;
 
@@ -38,6 +39,8 @@ extern const struct ieee80211_ops rtw89_ops;
 #define RSSI_FACTOR 1
 #define RTW89_RSSI_RAW_TO_DBM(rssi) ((s8)((rssi) >> RSSI_FACTOR) - MAX_RSSI)
 #define RTW89_TX_DIV_RSSI_RAW_TH (2 << RSSI_FACTOR)
+#define DELTA_SWINGIDX_SIZE 30
+
 #define RTW89_RADIOTAP_ROOM_HE sizeof(struct ieee80211_radiotap_he)
 #define RTW89_RADIOTAP_ROOM_EHT \
 	(sizeof(struct ieee80211_radiotap_tlv) + \
@@ -3935,6 +3938,7 @@ struct rtw89_fw_elm_info {
 	struct rtw89_phy_table *bb_gain;
 	struct rtw89_phy_table *rf_radio[RF_PATH_MAX];
 	struct rtw89_phy_table *rf_nctl;
+	struct rtw89_fw_txpwr_track_cfg *txpwr_trk;
 };
 
 struct rtw89_fw_info {
diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index b11ed35e265d..8c8ae13751b3 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -658,6 +658,72 @@ int rtw89_fw_recognize_txpwr_from_elm(struct rtw89_dev *rtwdev,
 	return 0;
 }
 
+static
+int rtw89_build_txpwr_trk_tbl_from_elm(struct rtw89_dev *rtwdev,
+				       const struct rtw89_fw_element_hdr *elm,
+				       const union rtw89_fw_element_arg arg)
+{
+	struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info;
+	const struct rtw89_chip_info *chip = rtwdev->chip;
+	u32 needed_bitmap = 0;
+	u32 offset = 0;
+	int subband;
+	u32 bitmap;
+	int type;
+
+	if (chip->support_bands & BIT(NL80211_BAND_6GHZ))
+		needed_bitmap |= RTW89_DEFAULT_NEEDED_FW_TXPWR_TRK_6GHZ;
+	if (chip->support_bands & BIT(NL80211_BAND_5GHZ))
+		needed_bitmap |= RTW89_DEFAULT_NEEDED_FW_TXPWR_TRK_5GHZ;
+	if (chip->support_bands & BIT(NL80211_BAND_2GHZ))
+		needed_bitmap |= RTW89_DEFAULT_NEEDED_FW_TXPWR_TRK_2GHZ;
+
+	bitmap = le32_to_cpu(elm->u.txpwr_trk.bitmap);
+
+	if ((bitmap & needed_bitmap) != needed_bitmap) {
+		rtw89_warn(rtwdev, "needed txpwr trk bitmap %08x but %0x8x\n",
+			   needed_bitmap, bitmap);
+		return -ENOENT;
+	}
+
+	elm_info->txpwr_trk = kzalloc(sizeof(*elm_info->txpwr_trk), GFP_KERNEL);
+	if (!elm_info->txpwr_trk)
+		return -ENOMEM;
+
+	for (type = 0; bitmap; type++, bitmap >>= 1) {
+		if (!(bitmap & BIT(0)))
+			continue;
+
+		if (type >= __RTW89_FW_TXPWR_TRK_TYPE_6GHZ_START &&
+		    type <= __RTW89_FW_TXPWR_TRK_TYPE_6GHZ_MAX)
+			subband = 4;
+		else if (type >= __RTW89_FW_TXPWR_TRK_TYPE_5GHZ_START &&
+			 type <= __RTW89_FW_TXPWR_TRK_TYPE_5GHZ_MAX)
+			subband = 3;
+		else if (type >= __RTW89_FW_TXPWR_TRK_TYPE_2GHZ_START &&
+			 type <= __RTW89_FW_TXPWR_TRK_TYPE_2GHZ_MAX)
+			subband = 1;
+		else
+			break;
+
+		elm_info->txpwr_trk->delta[type] = &elm->u.txpwr_trk.contents[offset];
+
+		offset += subband;
+		if (offset * DELTA_SWINGIDX_SIZE > le32_to_cpu(elm->size))
+			goto err;
+	}
+
+	return 0;
+
+err:
+	rtw89_warn(rtwdev, "unexpected txpwr trk offset %d over size %d\n",
+		   offset, le32_to_cpu(elm->size));
+	kfree(elm_info->txpwr_trk);
+	elm_info->txpwr_trk = NULL;
+
+	return -EFAULT;
+}
+
 static const struct rtw89_fw_element_handler __fw_element_handlers[] = {
 	[RTW89_FW_ELEMENT_ID_BBMCU0] = {__rtw89_fw_recognize_from_elm,
 					{ .fw_type = RTW89_FW_BBMCU0 }, NULL},
@@ -710,6 +776,9 @@ static const struct rtw89_fw_element_handler __fw_element_handlers[] = {
 		rtw89_fw_recognize_txpwr_from_elm,
 		{ .offset = offsetof(struct rtw89_rfe_data, tx_shape_lmt_ru.conf) }, NULL,
 	},
+	[RTW89_FW_ELEMENT_ID_TXPWR_TRK] = {
+		rtw89_build_txpwr_trk_tbl_from_elm, {}, "PWR_TRK",
+	},
 };
 
 int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev)
@@ -1135,6 +1204,8 @@ static void rtw89_unload_firmware_elements(struct rtw89_dev *rtwdev)
 	for (i = 0; i < ARRAY_SIZE(elm_info->rf_radio); i++)
 		rtw89_free_phy_tbl_from_elm(elm_info->rf_radio[i]);
 	rtw89_free_phy_tbl_from_elm(elm_info->rf_nctl);
+
+	kfree(elm_info->txpwr_trk);
 }
 
 void rtw89_unload_firmware(struct rtw89_dev *rtwdev)
diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h
index d4db9ab0b5e8..99fb8471a223 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.h
+++ b/drivers/net/wireless/realtek/rtw89/fw.h
@@ -3426,6 +3426,7 @@ enum rtw89_fw_element_id {
 	RTW89_FW_ELEMENT_ID_TXPWR_LMT_RU_6GHZ = 15,
 	RTW89_FW_ELEMENT_ID_TX_SHAPE_LMT = 16,
 	RTW89_FW_ELEMENT_ID_TX_SHAPE_LMT_RU = 17,
+	RTW89_FW_ELEMENT_ID_TXPWR_TRK = 18,
 
 	RTW89_FW_ELEMENT_ID_NUM,
 };
@@ -3446,6 +3447,7 @@ enum rtw89_fw_element_id {
 					     BIT(RTW89_FW_ELEMENT_ID_RADIO_A) | \
 					     BIT(RTW89_FW_ELEMENT_ID_RADIO_B) | \
 					     BIT(RTW89_FW_ELEMENT_ID_RF_NCTL) | \
+					     BIT(RTW89_FW_ELEMENT_ID_TXPWR_TRK) | \
 					     BITS_OF_RTW89_TXPWR_FW_ELEMENTS)
 
 struct __rtw89_fw_txpwr_element {
@@ -3457,6 +3459,59 @@ struct __rtw89_fw_txpwr_element {
 	u8 content[];
 } __packed;
 
+enum rtw89_fw_txpwr_trk_type {
+	__RTW89_FW_TXPWR_TRK_TYPE_6GHZ_START = 0,
+	RTW89_FW_TXPWR_TRK_TYPE_6GB_N = 0,
+	RTW89_FW_TXPWR_TRK_TYPE_6GB_P = 1,
+	RTW89_FW_TXPWR_TRK_TYPE_6GA_N = 2,
+	RTW89_FW_TXPWR_TRK_TYPE_6GA_P = 3,
+	__RTW89_FW_TXPWR_TRK_TYPE_6GHZ_MAX = 3,
+
+	__RTW89_FW_TXPWR_TRK_TYPE_5GHZ_START = 4,
+	RTW89_FW_TXPWR_TRK_TYPE_5GB_N = 4,
+	RTW89_FW_TXPWR_TRK_TYPE_5GB_P = 5,
+	RTW89_FW_TXPWR_TRK_TYPE_5GA_N = 6,
+	RTW89_FW_TXPWR_TRK_TYPE_5GA_P = 7,
+	__RTW89_FW_TXPWR_TRK_TYPE_5GHZ_MAX = 7,
+
+	__RTW89_FW_TXPWR_TRK_TYPE_2GHZ_START = 8,
+	RTW89_FW_TXPWR_TRK_TYPE_2GB_N = 8,
+	RTW89_FW_TXPWR_TRK_TYPE_2GB_P = 9,
+	RTW89_FW_TXPWR_TRK_TYPE_2GA_N = 10,
+	RTW89_FW_TXPWR_TRK_TYPE_2GA_P = 11,
+	RTW89_FW_TXPWR_TRK_TYPE_2G_CCK_B_N = 12,
+	RTW89_FW_TXPWR_TRK_TYPE_2G_CCK_B_P = 13,
+	RTW89_FW_TXPWR_TRK_TYPE_2G_CCK_A_N = 14,
+	RTW89_FW_TXPWR_TRK_TYPE_2G_CCK_A_P = 15,
+	__RTW89_FW_TXPWR_TRK_TYPE_2GHZ_MAX = 15,
+
+	RTW89_FW_TXPWR_TRK_TYPE_NR,
+};
+
+struct rtw89_fw_txpwr_track_cfg {
+	const s8 (*delta[RTW89_FW_TXPWR_TRK_TYPE_NR])[DELTA_SWINGIDX_SIZE];
+};
+
+#define RTW89_DEFAULT_NEEDED_FW_TXPWR_TRK_6GHZ \
+	(BIT(RTW89_FW_TXPWR_TRK_TYPE_6GB_N) | \
+	 BIT(RTW89_FW_TXPWR_TRK_TYPE_6GB_P) | \
+	 BIT(RTW89_FW_TXPWR_TRK_TYPE_6GA_N) | \
+	 BIT(RTW89_FW_TXPWR_TRK_TYPE_6GA_P))
+#define RTW89_DEFAULT_NEEDED_FW_TXPWR_TRK_5GHZ \
+	(BIT(RTW89_FW_TXPWR_TRK_TYPE_5GB_N) | \
+	 BIT(RTW89_FW_TXPWR_TRK_TYPE_5GB_P) | \
+	 BIT(RTW89_FW_TXPWR_TRK_TYPE_5GA_N) | \
+	 BIT(RTW89_FW_TXPWR_TRK_TYPE_5GA_P))
+#define RTW89_DEFAULT_NEEDED_FW_TXPWR_TRK_2GHZ \
+	(BIT(RTW89_FW_TXPWR_TRK_TYPE_2GB_N) | \
+	 BIT(RTW89_FW_TXPWR_TRK_TYPE_2GB_P) | \
+	 BIT(RTW89_FW_TXPWR_TRK_TYPE_2GA_N) | \
+	 BIT(RTW89_FW_TXPWR_TRK_TYPE_2GA_P) | \
+	 BIT(RTW89_FW_TXPWR_TRK_TYPE_2G_CCK_B_N) | \
+	 BIT(RTW89_FW_TXPWR_TRK_TYPE_2G_CCK_B_P) | \
+	 BIT(RTW89_FW_TXPWR_TRK_TYPE_2G_CCK_A_N) | \
+	 BIT(RTW89_FW_TXPWR_TRK_TYPE_2G_CCK_A_P))
+
 struct rtw89_fw_element_hdr {
 	__le32 id; /* enum rtw89_fw_element_id */
 	__le32 size; /* exclude header size */
@@ -3477,6 +3532,11 @@ struct rtw89_fw_element_hdr {
 				__le32 data;
 			} __packed regs[];
 		} __packed reg2;
+		struct {
+			__le32 bitmap; /* bitmap of enum rtw89_fw_txpwr_trk_type */
+			__le32 rsvd;
+			s8 contents[][DELTA_SWINGIDX_SIZE];
+		} __packed txpwr_trk;
 		struct __rtw89_fw_txpwr_element txpwr;
 	} __packed u;
 } __packed;
diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h
index d6363defcde6..6792b73e9ca3 100644
--- a/drivers/net/wireless/realtek/rtw89/phy.h
+++ b/drivers/net/wireless/realtek/rtw89/phy.h
@@ -291,8 +291,6 @@ struct rtw89_txpwr_byrate_cfg {
 	u32 data;
 };
 
-#define DELTA_SWINGIDX_SIZE 30
-
 struct rtw89_txpwr_track_cfg {
 	const s8 (*delta_swingidx_6gb_n)[DELTA_SWINGIDX_SIZE];
 	const s8 (*delta_swingidx_6gb_p)[DELTA_SWINGIDX_SIZE];
