return 0;
 }
 
+int ath12k_mac_get_fw_stats(struct ath12k *ar,
+                           struct ath12k_fw_stats_req_params *param)
+{
+       struct ath12k_base *ab = ar->ab;
+       struct ath12k_hw *ah = ath12k_ar_to_ah(ar);
+       unsigned long timeout, time_left;
+       int ret;
+
+       guard(mutex)(&ah->hw_mutex);
+
+       if (ah->state != ATH12K_HW_STATE_ON)
+               return -ENETDOWN;
+
+       /* FW stats can get split when exceeding the stats data buffer limit.
+        * In that case, since there is no end marking for the back-to-back
+        * received 'update stats' event, we keep a 3 seconds timeout in case,
+        * fw_stats_done is not marked yet
+        */
+       timeout = jiffies + msecs_to_jiffies(3 * 1000);
+       ath12k_fw_stats_reset(ar);
+
+       reinit_completion(&ar->fw_stats_complete);
+
+       ret = ath12k_wmi_send_stats_request_cmd(ar, param->stats_id,
+                                               param->vdev_id, param->pdev_id);
+
+       if (ret) {
+               ath12k_warn(ab, "failed to request fw stats: %d\n", ret);
+               return ret;
+       }
+
+       ath12k_dbg(ab, ATH12K_DBG_WMI,
+                  "get fw stat pdev id %d vdev id %d stats id 0x%x\n",
+                  param->pdev_id, param->vdev_id, param->stats_id);
+
+       time_left = wait_for_completion_timeout(&ar->fw_stats_complete, 1 * HZ);
+
+       if (!time_left) {
+               ath12k_warn(ab, "time out while waiting for get fw stats\n");
+               return -ETIMEDOUT;
+       }
+
+       /* Firmware sends WMI_UPDATE_STATS_EVENTID back-to-back
+        * when stats data buffer limit is reached. fw_stats_complete
+        * is completed once host receives first event from firmware, but
+        * still end might not be marked in the TLV.
+        * Below loop is to confirm that firmware completed sending all the event
+        * and fw_stats_done is marked true when end is marked in the TLV.
+        */
+       for (;;) {
+               if (time_after(jiffies, timeout))
+                       break;
+               spin_lock_bh(&ar->data_lock);
+               if (ar->fw_stats.fw_stats_done) {
+                       spin_unlock_bh(&ar->data_lock);
+                       break;
+               }
+               spin_unlock_bh(&ar->data_lock);
+       }
+       return 0;
+}
+
+static int ath12k_mac_op_get_txpower(struct ieee80211_hw *hw,
+                                    struct ieee80211_vif *vif,
+                                    unsigned int link_id,
+                                    int *dbm)
+{
+       struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif);
+       struct ath12k_fw_stats_req_params params = {};
+       struct ath12k_fw_stats_pdev *pdev;
+       struct ath12k_hw *ah = hw->priv;
+       struct ath12k_link_vif *arvif;
+       struct ath12k_base *ab;
+       struct ath12k *ar;
+       int ret;
+
+       /* Final Tx power is minimum of Target Power, CTL power, Regulatory
+        * Power, PSD EIRP Power. We just know the Regulatory power from the
+        * regulatory rules obtained. FW knows all these power and sets the min
+        * of these. Hence, we request the FW pdev stats in which FW reports
+        * the minimum of all vdev's channel Tx power.
+        */
+       lockdep_assert_wiphy(hw->wiphy);
+
+       arvif = wiphy_dereference(ah->hw->wiphy, ahvif->link[link_id]);
+       if (!arvif || !arvif->ar)
+               return -EINVAL;
+
+       ar = arvif->ar;
+       ab = ar->ab;
+       if (ah->state != ATH12K_HW_STATE_ON)
+               goto err_fallback;
+
+       if (test_bit(ATH12K_FLAG_CAC_RUNNING, &ar->dev_flags))
+               return -EAGAIN;
+
+       /* Limit the requests to Firmware for fetching the tx power */
+       if (ar->chan_tx_pwr != ATH12K_PDEV_TX_POWER_INVALID &&
+           time_before(jiffies,
+                       msecs_to_jiffies(ATH12K_PDEV_TX_POWER_REFRESH_TIME_MSECS) +
+                                        ar->last_tx_power_update))
+               goto send_tx_power;
+
+       params.pdev_id = ar->pdev->pdev_id;
+       params.vdev_id = arvif->vdev_id;
+       params.stats_id = WMI_REQUEST_PDEV_STAT;
+       ret = ath12k_mac_get_fw_stats(ar, ¶ms);
+       if (ret) {
+               ath12k_warn(ab, "failed to request fw pdev stats: %d\n", ret);
+               goto err_fallback;
+       }
+
+       spin_lock_bh(&ar->data_lock);
+       pdev = list_first_entry_or_null(&ar->fw_stats.pdevs,
+                                       struct ath12k_fw_stats_pdev, list);
+       if (!pdev) {
+               spin_unlock_bh(&ar->data_lock);
+               goto err_fallback;
+       }
+
+       /* tx power reported by firmware is in units of 0.5 dBm */
+       ar->chan_tx_pwr = pdev->chan_tx_power / 2;
+       spin_unlock_bh(&ar->data_lock);
+       ar->last_tx_power_update = jiffies;
+
+send_tx_power:
+       *dbm = ar->chan_tx_pwr;
+       ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "txpower fetched from firmware %d dBm\n",
+                  *dbm);
+       return 0;
+
+err_fallback:
+       /* We didn't get txpower from FW. Hence, relying on vif->bss_conf.txpower */
+       *dbm = vif->bss_conf.txpower;
+       ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "txpower from firmware NaN, reported %d dBm\n",
+                  *dbm);
+       return 0;
+}
+
 static u8
 ath12k_mac_find_link_id_by_ar(struct ath12k_vif *ahvif, struct ath12k *ar)
 {
        ar->num_created_vdevs = 0;
        ar->num_peers = 0;
        ar->allocated_vdev_map = 0;
+       ar->chan_tx_pwr = ATH12K_PDEV_TX_POWER_INVALID;
 
        /* Configure monitor status ring with default rx_filter to get rx status
         * such as rssi, rx_duration.
         */
        ar->rx_channel = ctx->def.chan;
        spin_unlock_bh(&ar->data_lock);
+       ar->chan_tx_pwr = ATH12K_PDEV_TX_POWER_INVALID;
 
        return 0;
 }
         */
        ar->rx_channel = NULL;
        spin_unlock_bh(&ar->data_lock);
+       ar->chan_tx_pwr = ATH12K_PDEV_TX_POWER_INVALID;
 }
 
 static enum wmi_phy_mode
        return 0;
 }
 
-int ath12k_mac_get_fw_stats(struct ath12k *ar,
-                           struct ath12k_fw_stats_req_params *param)
-{
-       struct ath12k_base *ab = ar->ab;
-       struct ath12k_hw *ah = ath12k_ar_to_ah(ar);
-       unsigned long timeout, time_left;
-       int ret;
-
-       guard(mutex)(&ah->hw_mutex);
-
-       if (ah->state != ATH12K_HW_STATE_ON)
-               return -ENETDOWN;
-
-       /* FW stats can get split when exceeding the stats data buffer limit.
-        * In that case, since there is no end marking for the back-to-back
-        * received 'update stats' event, we keep a 3 seconds timeout in case,
-        * fw_stats_done is not marked yet
-        */
-       timeout = jiffies + msecs_to_jiffies(3 * 1000);
-       ath12k_fw_stats_reset(ar);
-
-       reinit_completion(&ar->fw_stats_complete);
-
-       ret = ath12k_wmi_send_stats_request_cmd(ar, param->stats_id,
-                                               param->vdev_id, param->pdev_id);
-
-       if (ret) {
-               ath12k_warn(ab, "failed to request fw stats: %d\n", ret);
-               return ret;
-       }
-
-       ath12k_dbg(ab, ATH12K_DBG_WMI,
-                  "get fw stat pdev id %d vdev id %d stats id 0x%x\n",
-                  param->pdev_id, param->vdev_id, param->stats_id);
-
-       time_left = wait_for_completion_timeout(&ar->fw_stats_complete, 1 * HZ);
-
-       if (!time_left) {
-               ath12k_warn(ab, "time out while waiting for get fw stats\n");
-               return -ETIMEDOUT;
-       }
-
-       /* Firmware sends WMI_UPDATE_STATS_EVENTID back-to-back
-        * when stats data buffer limit is reached. fw_stats_complete
-        * is completed once host receives first event from firmware, but
-        * still end might not be marked in the TLV.
-        * Below loop is to confirm that firmware completed sending all the event
-        * and fw_stats_done is marked true when end is marked in the TLV.
-        */
-       for (;;) {
-               if (time_after(jiffies, timeout))
-                       break;
-               spin_lock_bh(&ar->data_lock);
-               if (ar->fw_stats.fw_stats_done) {
-                       spin_unlock_bh(&ar->data_lock);
-                       break;
-               }
-               spin_unlock_bh(&ar->data_lock);
-       }
-       return 0;
-}
-
 static void ath12k_mac_op_sta_statistics(struct ieee80211_hw *hw,
                                         struct ieee80211_vif *vif,
                                         struct ieee80211_sta *sta,
        .assign_vif_chanctx             = ath12k_mac_op_assign_vif_chanctx,
        .unassign_vif_chanctx           = ath12k_mac_op_unassign_vif_chanctx,
        .switch_vif_chanctx             = ath12k_mac_op_switch_vif_chanctx,
+       .get_txpower                    = ath12k_mac_op_get_txpower,
        .set_rts_threshold              = ath12k_mac_op_set_rts_threshold,
        .set_frag_threshold             = ath12k_mac_op_set_frag_threshold,
        .set_bitrate_mask               = ath12k_mac_op_set_bitrate_mask,
                        goto err_unregister_hw;
                }
 
+               ath12k_fw_stats_init(ar);
                ath12k_debugfs_register(ar);
        }
 
-       init_completion(&ar->fw_stats_complete);
-
        return 0;
 
 err_unregister_hw: