diff mbox series

[6/7] wifi: ath12k: fill parameters for vdev_set_tpc_power wmi command

Message ID 20230919071724.15505-7-quic_aisr@quicinc.com (mailing list archive)
State Changes Requested
Delegated to: Kalle Valo
Headers show
Series wifi: ath12k: add support for 6 GHz AP for various power modes | expand

Commit Message

Aishwarya R (QUIC) Sept. 19, 2023, 7:17 a.m. UTC
Prepare the parameters which is needed for wmi cmd
vdev_set_tpc_power.

Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1

Signed-off-by: Aishwarya R <quic_aisr@quicinc.com>
---
 drivers/net/wireless/ath/ath12k/core.h |   1 +
 drivers/net/wireless/ath/ath12k/mac.c  | 273 +++++++++++++++++++++++++
 drivers/net/wireless/ath/ath12k/mac.h  |   3 +
 drivers/net/wireless/ath/ath12k/wmi.c  |   1 +
 drivers/net/wireless/ath/ath12k/wmi.h  |   1 +
 5 files changed, 279 insertions(+)

Comments

Jeff Johnson Sept. 19, 2023, 8:09 p.m. UTC | #1
On 9/19/2023 12:17 AM, Aishwarya R wrote:
> Prepare the parameters which is needed for wmi cmd
> vdev_set_tpc_power.
> 
> Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1
> 
> Signed-off-by: Aishwarya R <quic_aisr@quicinc.com>
> ---
>   drivers/net/wireless/ath/ath12k/core.h |   1 +
>   drivers/net/wireless/ath/ath12k/mac.c  | 273 +++++++++++++++++++++++++
>   drivers/net/wireless/ath/ath12k/mac.h  |   3 +
>   drivers/net/wireless/ath/ath12k/wmi.c  |   1 +
>   drivers/net/wireless/ath/ath12k/wmi.h  |   1 +
>   5 files changed, 279 insertions(+)
> 
> diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
> index 01768fe79bd6..e9bf87f740cd 100644
> --- a/drivers/net/wireless/ath/ath12k/core.h
> +++ b/drivers/net/wireless/ath/ath12k/core.h
> @@ -606,6 +606,7 @@ struct ath12k {
>   	bool monitor_vdev_created;
>   	bool monitor_started;
>   	int monitor_vdev_id;
> +	s8 max_allowed_tx_power;
>   };
>   
>   struct ath12k_band_cap {
> diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
> index 0683b22137b0..35bd472267c1 100644
> --- a/drivers/net/wireless/ath/ath12k/mac.c
> +++ b/drivers/net/wireless/ath/ath12k/mac.c
> @@ -5883,6 +5883,279 @@ static u8 ath12k_mac_get_num_pwr_levels(struct cfg80211_chan_def *chan_def)
>   	return num_pwr_levels;
>   }
>   
> +static u16 ath12k_mac_get_6ghz_start_frequency(struct cfg80211_chan_def *chan_def)
> +{
> +	u16 diff_seq;
> +
> +	/* It is to get the lowest channel number's center frequency of the chan.

s/It is to //

also suggest any function documentation should appear before the 
function, and if the function requires documentation, that kernel-doc 
format be used

same guidance applies to the functions that follow

> +	 * For example,
> +	 * bandwidth=40 MHz, center frequency is 5965, lowest channel is 1
> +	 * with center frequency 5955, its diff is 5965 - 5955 = 10.
> +	 * bandwidth=80 MHz, center frequency is 5985, lowest channel is 1
> +	 * with center frequency 5955, its diff is 5985 - 5955 = 30.
> +	 * bandwidth=160 MHz, center frequency is 6025, lowest channel is 1
> +	 * with center frequency 5955, its diff is 6025 - 5955 = 70.
> +	 */
> +	switch (chan_def->width) {
> +	case NL80211_CHAN_WIDTH_160:
> +		diff_seq = 70;
> +		break;
> +	case NL80211_CHAN_WIDTH_80:
> +	case NL80211_CHAN_WIDTH_80P80:
> +		diff_seq = 30;
> +		break;
> +	case NL80211_CHAN_WIDTH_40:
> +		diff_seq = 10;
> +		break;
> +	default:
> +		diff_seq = 0;
> +	}
> +
> +	return chan_def->center_freq1 - diff_seq;
> +}
> +
> +static u16 ath12k_mac_get_seg_freq(struct cfg80211_chan_def *chan_def,
> +				   u16 start_seq, u8 seq)
> +{
> +	u16 seg_seq;
> +
> +	/* It is to get the center frequency of the specific bandwidth.
> +	 * start_seq means the lowest channel number's center frequency.
> +	 * seq 0/1/2/3 means 20 MHz/40 MHz/80 MHz/160 MHz & 80P80.
> +	 * For example,
> +	 * lowest channel is 1, its center frequency 5955,
> +	 * center frequency is 5955 when bandwidth=20 MHz, its diff is 5955 - 5955 = 0.
> +	 * lowest channel is 1, its center frequency 5955,
> +	 * center frequency is 5965 when bandwidth=40 MHz, its diff is 5965 - 5955 = 10.
> +	 * lowest channel is 1, its center frequency 5955,
> +	 * center frequency is 5985 when bandwidth=80 MHz, its diff is 5985 - 5955 = 30.
> +	 * lowest channel is 1, its center frequency 5955,
> +	 * center frequency is 6025 when bandwidth=160 MHz, its diff is 6025 - 5955 = 70.
> +	 */
> +	if (chan_def->width == NL80211_CHAN_WIDTH_80P80 && seq == 3)
> +		return chan_def->center_freq2;
> +
> +	seg_seq = 10 * (BIT(seq) - 1);
> +	return seg_seq + start_seq;
> +}
> +
> +static void ath12k_mac_get_psd_channel(struct ath12k *ar,
> +				       u16 step_freq,
> +				       u16 *start_freq,
> +				       u16 *center_freq,
> +				       u8 i,
> +				       struct ieee80211_channel **temp_chan,
> +				       s8 *tx_power)
> +{
> +	/* It is to get the center frequency for each 20 MHz.
> +	 * For example, if the chan is 160 MHz and center frequency is 6025,
> +	 * then it include 8 channels, they are 1/5/9/13/17/21/25/29,
> +	 * channel number 1's center frequency is 5955, it is parameter start_freq.
> +	 * parameter i is the step of the 8 channels. i is 0~7 for the 8 channels.
> +	 * the channel 1/5/9/13/17/21/25/29 maps i=0/1/2/3/4/5/6/7,
> +	 * and maps its center frequency is 5955/5975/5995/6015/6035/6055/6075/6095,
> +	 * the gap is 20 for each channel, parameter step_freq means the gap.
> +	 * after get the center frequency of each channel, it is easy to find the
> +	 * struct ieee80211_channel of it and get the max_reg_power.
> +	 */
> +	*center_freq = *start_freq + i * step_freq;
> +	*temp_chan = ieee80211_get_channel(ar->hw->wiphy, *center_freq);
> +	*tx_power = (*temp_chan)->max_reg_power;
> +}
> +
> +static void ath12k_mac_get_eirp_power(struct ath12k *ar,
> +				      u16 *start_freq,
> +				      u16 *center_freq,
> +				      u8 i,
> +				      struct ieee80211_channel **temp_chan,
> +				      struct cfg80211_chan_def *def,
> +				      s8 *tx_power)
> +{
> +	/* It is to get the center frequency for 20 MHz/40 MHz/80 MHz/
> +	 * 160 MHz & 80P80 bandwidth, and then plus 10 to the center frequency,
> +	 * it is the center frequency of a channel number.
> +	 * For example, when configured channel number is 1.
> +	 * center frequency is 5965 when bandwidth=40 MHz, after plus 10, it is 5975,
> +	 * then it is channel number 5.
> +	 * center frequency is 5985 when bandwidth=80 MHz, after plus 10, it is 5995,
> +	 * then it is channel number 9.
> +	 * center frequency is 6025 when bandwidth=160 MHz, after plus 10, it is 6035,
> +	 * then it is channel number 17.
> +	 * after get the center frequency of each channel, it is easy to find the
> +	 * struct ieee80211_channel of it and get the max_reg_power.
> +	 */
> +	*center_freq = ath12k_mac_get_seg_freq(def, *start_freq, i);
> +	*center_freq += 10;
> +	*temp_chan = ieee80211_get_channel(ar->hw->wiphy, *center_freq);
> +	*tx_power = (*temp_chan)->max_reg_power;
> +}
> +
> +void ath12k_mac_fill_reg_tpc_info(struct ath12k *ar,
> +				  struct ieee80211_vif *vif,
> +				  struct ieee80211_chanctx_conf *ctx)
> +{
> +	struct ath12k_base *ab = ar->ab;
> +	struct ath12k_vif *arvif = (void *)vif->drv_priv;
> +	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
> +	struct ath12k_reg_tpc_power_info *reg_tpc_info = &arvif->reg_tpc_info;
> +	struct ieee80211_channel *chan, *temp_chan;
> +	u8 pwr_lvl_idx, num_pwr_levels, pwr_reduction;
> +	bool is_psd_power = false, is_tpe_present = false;
> +	s8 max_tx_power[IEEE80211_MAX_NUM_PWR_LEVEL],
> +		psd_power, tx_power = 0, eirp_power = 0;
> +	u16 start_freq = 0, center_freq = 0;
> +
> +	chan = ctx->def.chan;
> +	start_freq = ath12k_mac_get_6ghz_start_frequency(&ctx->def);
> +	pwr_reduction = bss_conf->pwr_reduction;
> +
> +	if (arvif->vdev_type == WMI_VDEV_TYPE_STA &&
> +	    arvif->reg_tpc_info.num_pwr_levels) {
> +		is_tpe_present = true;
> +		num_pwr_levels = arvif->reg_tpc_info.num_pwr_levels;
> +	} else {
> +		num_pwr_levels = ath12k_mac_get_num_pwr_levels(&ctx->def);
> +	}
> +
> +	for (pwr_lvl_idx = 0; pwr_lvl_idx < num_pwr_levels; pwr_lvl_idx++) {
> +		/* STA received TPE IE*/
> +		if (is_tpe_present) {
> +			/* local power is PSD power*/
> +			if (chan->flags & IEEE80211_CHAN_PSD) {
> +				/* Connecting AP is psd power */
> +				if (reg_tpc_info->is_psd_power) {
> +					is_psd_power = true;
> +					ath12k_mac_get_psd_channel(ar, 20,
> +								   &start_freq,
> +								   &center_freq,
> +								   pwr_lvl_idx,
> +								   &temp_chan,
> +								   &tx_power);
> +					psd_power = temp_chan->psd;
> +					eirp_power = tx_power;
> +					max_tx_power[pwr_lvl_idx] =
> +						min_t(s8,
> +						      psd_power,
> +						      reg_tpc_info->tpe[pwr_lvl_idx]);
> +				/* Connecting AP is not psd power */
> +				} else {
> +					ath12k_mac_get_eirp_power(ar,
> +								  &start_freq,
> +								  &center_freq,
> +								  pwr_lvl_idx,
> +								  &temp_chan,
> +								  &ctx->def,
> +								  &tx_power);
> +					psd_power = temp_chan->psd;
> +					/* convert psd power to EIRP power based
> +					 * on channel width
> +					 */
> +					tx_power =
> +						min_t(s8, tx_power,
> +						      psd_power + 13 + pwr_lvl_idx * 3);
> +					max_tx_power[pwr_lvl_idx] =
> +						min_t(s8,
> +						      tx_power,
> +						      reg_tpc_info->tpe[pwr_lvl_idx]);
> +			}

bad brace placement?

> +			/* local power is not PSD power */
> +			} else {
> +				/* Connecting AP is psd power */
> +				if (reg_tpc_info->is_psd_power) {
> +					is_psd_power = true;
> +					ath12k_mac_get_psd_channel(ar, 20,
> +								   &start_freq,
> +								   &center_freq,
> +								   pwr_lvl_idx,
> +								   &temp_chan,
> +								   &tx_power);
> +					eirp_power = tx_power;
> +					max_tx_power[pwr_lvl_idx] =
> +						reg_tpc_info->tpe[pwr_lvl_idx];
> +				/* Connecting AP is not psd power */
> +				} else {
> +					ath12k_mac_get_eirp_power(ar,
> +								  &start_freq,
> +								  &center_freq,
> +								  pwr_lvl_idx,
> +								  &temp_chan,
> +								  &ctx->def,
> +								  &tx_power);
> +					max_tx_power[pwr_lvl_idx] =
> +						min_t(s8,
> +						      tx_power,
> +						      reg_tpc_info->tpe[pwr_lvl_idx]);
> +				}
> +		}

bad brace placement?

> +		/* STA not received TPE IE */
> +		} else {
> +			/* local power is PSD power*/
> +			if (chan->flags & IEEE80211_CHAN_PSD) {
> +				is_psd_power = true;
> +				ath12k_mac_get_psd_channel(ar, 20,
> +							   &start_freq,
> +							   &center_freq,
> +							   pwr_lvl_idx,
> +							   &temp_chan,
> +							   &tx_power);
> +				psd_power = temp_chan->psd;
> +				eirp_power = tx_power;
> +				max_tx_power[pwr_lvl_idx] = psd_power;
> +			} else {
> +				ath12k_mac_get_eirp_power(ar,
> +							  &start_freq,
> +							  &center_freq,
> +							  pwr_lvl_idx,
> +							  &temp_chan,
> +							  &ctx->def,
> +							  &tx_power);
> +				max_tx_power[pwr_lvl_idx] = tx_power;
> +			}
> +		}
> +
> +		if (is_psd_power) {
> +			/* If AP local power constraint is present */
> +			if (pwr_reduction)
> +				eirp_power = eirp_power - pwr_reduction;
> +
> +			/* If FW updated max tx power is non zero, then take the min of
> +			 * firmware updated ap tx power
> +			 * and max power derived from above mentioned parameters.
> +			 */
> +			ath12k_dbg(ab, ATH12K_DBG_MAC,
> +				   "eirp power : %d firmware report power : %d\n",
> +				   eirp_power, ar->max_allowed_tx_power);
> +			if ((ar->max_allowed_tx_power) && (ab->hw_params->idle_ps))
> +				eirp_power = min_t(s8,
> +						   eirp_power,
> +						   ar->max_allowed_tx_power);
> +		} else {
> +			/* If AP local power constraint is present */
> +			if (pwr_reduction)
> +				max_tx_power[pwr_lvl_idx] =
> +					max_tx_power[pwr_lvl_idx] - pwr_reduction;
> +			/* If FW updated max tx power is non zero, then take the min of
> +			 * firmware updated ap tx power
> +			 * and max power derived from above mentioned parameters.
> +			 */
> +			if ((ar->max_allowed_tx_power) && (ab->hw_params->idle_ps))
> +				max_tx_power[pwr_lvl_idx] =
> +					min_t(s8,
> +					      max_tx_power[pwr_lvl_idx],
> +					      ar->max_allowed_tx_power);
> +		}
> +		reg_tpc_info->chan_power_info[pwr_lvl_idx].chan_cfreq = center_freq;
> +		reg_tpc_info->chan_power_info[pwr_lvl_idx].tx_power =
> +			max_tx_power[pwr_lvl_idx];
> +	}
> +
> +	reg_tpc_info->num_pwr_levels = num_pwr_levels;
> +	reg_tpc_info->is_psd_power = is_psd_power;
> +	reg_tpc_info->eirp_power = eirp_power;
> +	reg_tpc_info->power_type_6ghz =
> +		ath12k_ieee80211_ap_pwr_type_convert(vif->bss_conf.power_type);
> +}
> +
>   static void ath12k_mac_parse_tx_pwr_env(struct ath12k *ar,
>   					struct ieee80211_vif *vif,
>   					struct ieee80211_chanctx_conf *ctx)
> diff --git a/drivers/net/wireless/ath/ath12k/mac.h b/drivers/net/wireless/ath/ath12k/mac.h
> index 82f590004d05..ce2ce7f324d7 100644
> --- a/drivers/net/wireless/ath/ath12k/mac.h
> +++ b/drivers/net/wireless/ath/ath12k/mac.h
> @@ -68,6 +68,9 @@ struct ath12k *ath12k_mac_get_ar_by_vdev_id(struct ath12k_base *ab, u32 vdev_id)
>   struct ath12k *ath12k_mac_get_ar_by_pdev_id(struct ath12k_base *ab, u32 pdev_id);
>   enum wmi_vdev_type ath12k_mac_get_ar_vdev_type(struct ath12k *ar);
>   
> +void ath12k_mac_fill_reg_tpc_info(struct ath12k *ar,
> +				  struct ieee80211_vif *vif,
> +				  struct ieee80211_chanctx_conf *ctx);
>   void ath12k_mac_drain_tx(struct ath12k *ar);
>   void ath12k_mac_peer_cleanup_all(struct ath12k *ar);
>   int ath12k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx);
> diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
> index 211bdb915173..5bfca2513730 100644
> --- a/drivers/net/wireless/ath/ath12k/wmi.c
> +++ b/drivers/net/wireless/ath/ath12k/wmi.c
> @@ -5588,6 +5588,7 @@ static void ath12k_vdev_start_resp_event(struct ath12k_base *ab, struct sk_buff
>   	}
>   
>   	ar->last_wmi_vdev_start_status = 0;
> +	ar->max_allowed_tx_power = le32_to_cpu(vdev_start_resp.max_allowed_tx_power);
>   
>   	status = le32_to_cpu(vdev_start_resp.status);
>   
> diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h
> index c3b110af1272..9f24f8ded52f 100644
> --- a/drivers/net/wireless/ath/ath12k/wmi.h
> +++ b/drivers/net/wireless/ath/ath12k/wmi.h
> @@ -3796,6 +3796,7 @@ struct wmi_vdev_start_resp_event {
>   	};
>   	__le32 cfgd_tx_streams;
>   	__le32 cfgd_rx_streams;
> +	__le32 max_allowed_tx_power;
>   } __packed;
>   
>   /* VDEV start response status codes */
diff mbox series

Patch

diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
index 01768fe79bd6..e9bf87f740cd 100644
--- a/drivers/net/wireless/ath/ath12k/core.h
+++ b/drivers/net/wireless/ath/ath12k/core.h
@@ -606,6 +606,7 @@  struct ath12k {
 	bool monitor_vdev_created;
 	bool monitor_started;
 	int monitor_vdev_id;
+	s8 max_allowed_tx_power;
 };
 
 struct ath12k_band_cap {
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 0683b22137b0..35bd472267c1 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -5883,6 +5883,279 @@  static u8 ath12k_mac_get_num_pwr_levels(struct cfg80211_chan_def *chan_def)
 	return num_pwr_levels;
 }
 
+static u16 ath12k_mac_get_6ghz_start_frequency(struct cfg80211_chan_def *chan_def)
+{
+	u16 diff_seq;
+
+	/* It is to get the lowest channel number's center frequency of the chan.
+	 * For example,
+	 * bandwidth=40 MHz, center frequency is 5965, lowest channel is 1
+	 * with center frequency 5955, its diff is 5965 - 5955 = 10.
+	 * bandwidth=80 MHz, center frequency is 5985, lowest channel is 1
+	 * with center frequency 5955, its diff is 5985 - 5955 = 30.
+	 * bandwidth=160 MHz, center frequency is 6025, lowest channel is 1
+	 * with center frequency 5955, its diff is 6025 - 5955 = 70.
+	 */
+	switch (chan_def->width) {
+	case NL80211_CHAN_WIDTH_160:
+		diff_seq = 70;
+		break;
+	case NL80211_CHAN_WIDTH_80:
+	case NL80211_CHAN_WIDTH_80P80:
+		diff_seq = 30;
+		break;
+	case NL80211_CHAN_WIDTH_40:
+		diff_seq = 10;
+		break;
+	default:
+		diff_seq = 0;
+	}
+
+	return chan_def->center_freq1 - diff_seq;
+}
+
+static u16 ath12k_mac_get_seg_freq(struct cfg80211_chan_def *chan_def,
+				   u16 start_seq, u8 seq)
+{
+	u16 seg_seq;
+
+	/* It is to get the center frequency of the specific bandwidth.
+	 * start_seq means the lowest channel number's center frequency.
+	 * seq 0/1/2/3 means 20 MHz/40 MHz/80 MHz/160 MHz & 80P80.
+	 * For example,
+	 * lowest channel is 1, its center frequency 5955,
+	 * center frequency is 5955 when bandwidth=20 MHz, its diff is 5955 - 5955 = 0.
+	 * lowest channel is 1, its center frequency 5955,
+	 * center frequency is 5965 when bandwidth=40 MHz, its diff is 5965 - 5955 = 10.
+	 * lowest channel is 1, its center frequency 5955,
+	 * center frequency is 5985 when bandwidth=80 MHz, its diff is 5985 - 5955 = 30.
+	 * lowest channel is 1, its center frequency 5955,
+	 * center frequency is 6025 when bandwidth=160 MHz, its diff is 6025 - 5955 = 70.
+	 */
+	if (chan_def->width == NL80211_CHAN_WIDTH_80P80 && seq == 3)
+		return chan_def->center_freq2;
+
+	seg_seq = 10 * (BIT(seq) - 1);
+	return seg_seq + start_seq;
+}
+
+static void ath12k_mac_get_psd_channel(struct ath12k *ar,
+				       u16 step_freq,
+				       u16 *start_freq,
+				       u16 *center_freq,
+				       u8 i,
+				       struct ieee80211_channel **temp_chan,
+				       s8 *tx_power)
+{
+	/* It is to get the center frequency for each 20 MHz.
+	 * For example, if the chan is 160 MHz and center frequency is 6025,
+	 * then it include 8 channels, they are 1/5/9/13/17/21/25/29,
+	 * channel number 1's center frequency is 5955, it is parameter start_freq.
+	 * parameter i is the step of the 8 channels. i is 0~7 for the 8 channels.
+	 * the channel 1/5/9/13/17/21/25/29 maps i=0/1/2/3/4/5/6/7,
+	 * and maps its center frequency is 5955/5975/5995/6015/6035/6055/6075/6095,
+	 * the gap is 20 for each channel, parameter step_freq means the gap.
+	 * after get the center frequency of each channel, it is easy to find the
+	 * struct ieee80211_channel of it and get the max_reg_power.
+	 */
+	*center_freq = *start_freq + i * step_freq;
+	*temp_chan = ieee80211_get_channel(ar->hw->wiphy, *center_freq);
+	*tx_power = (*temp_chan)->max_reg_power;
+}
+
+static void ath12k_mac_get_eirp_power(struct ath12k *ar,
+				      u16 *start_freq,
+				      u16 *center_freq,
+				      u8 i,
+				      struct ieee80211_channel **temp_chan,
+				      struct cfg80211_chan_def *def,
+				      s8 *tx_power)
+{
+	/* It is to get the center frequency for 20 MHz/40 MHz/80 MHz/
+	 * 160 MHz & 80P80 bandwidth, and then plus 10 to the center frequency,
+	 * it is the center frequency of a channel number.
+	 * For example, when configured channel number is 1.
+	 * center frequency is 5965 when bandwidth=40 MHz, after plus 10, it is 5975,
+	 * then it is channel number 5.
+	 * center frequency is 5985 when bandwidth=80 MHz, after plus 10, it is 5995,
+	 * then it is channel number 9.
+	 * center frequency is 6025 when bandwidth=160 MHz, after plus 10, it is 6035,
+	 * then it is channel number 17.
+	 * after get the center frequency of each channel, it is easy to find the
+	 * struct ieee80211_channel of it and get the max_reg_power.
+	 */
+	*center_freq = ath12k_mac_get_seg_freq(def, *start_freq, i);
+	*center_freq += 10;
+	*temp_chan = ieee80211_get_channel(ar->hw->wiphy, *center_freq);
+	*tx_power = (*temp_chan)->max_reg_power;
+}
+
+void ath12k_mac_fill_reg_tpc_info(struct ath12k *ar,
+				  struct ieee80211_vif *vif,
+				  struct ieee80211_chanctx_conf *ctx)
+{
+	struct ath12k_base *ab = ar->ab;
+	struct ath12k_vif *arvif = (void *)vif->drv_priv;
+	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+	struct ath12k_reg_tpc_power_info *reg_tpc_info = &arvif->reg_tpc_info;
+	struct ieee80211_channel *chan, *temp_chan;
+	u8 pwr_lvl_idx, num_pwr_levels, pwr_reduction;
+	bool is_psd_power = false, is_tpe_present = false;
+	s8 max_tx_power[IEEE80211_MAX_NUM_PWR_LEVEL],
+		psd_power, tx_power = 0, eirp_power = 0;
+	u16 start_freq = 0, center_freq = 0;
+
+	chan = ctx->def.chan;
+	start_freq = ath12k_mac_get_6ghz_start_frequency(&ctx->def);
+	pwr_reduction = bss_conf->pwr_reduction;
+
+	if (arvif->vdev_type == WMI_VDEV_TYPE_STA &&
+	    arvif->reg_tpc_info.num_pwr_levels) {
+		is_tpe_present = true;
+		num_pwr_levels = arvif->reg_tpc_info.num_pwr_levels;
+	} else {
+		num_pwr_levels = ath12k_mac_get_num_pwr_levels(&ctx->def);
+	}
+
+	for (pwr_lvl_idx = 0; pwr_lvl_idx < num_pwr_levels; pwr_lvl_idx++) {
+		/* STA received TPE IE*/
+		if (is_tpe_present) {
+			/* local power is PSD power*/
+			if (chan->flags & IEEE80211_CHAN_PSD) {
+				/* Connecting AP is psd power */
+				if (reg_tpc_info->is_psd_power) {
+					is_psd_power = true;
+					ath12k_mac_get_psd_channel(ar, 20,
+								   &start_freq,
+								   &center_freq,
+								   pwr_lvl_idx,
+								   &temp_chan,
+								   &tx_power);
+					psd_power = temp_chan->psd;
+					eirp_power = tx_power;
+					max_tx_power[pwr_lvl_idx] =
+						min_t(s8,
+						      psd_power,
+						      reg_tpc_info->tpe[pwr_lvl_idx]);
+				/* Connecting AP is not psd power */
+				} else {
+					ath12k_mac_get_eirp_power(ar,
+								  &start_freq,
+								  &center_freq,
+								  pwr_lvl_idx,
+								  &temp_chan,
+								  &ctx->def,
+								  &tx_power);
+					psd_power = temp_chan->psd;
+					/* convert psd power to EIRP power based
+					 * on channel width
+					 */
+					tx_power =
+						min_t(s8, tx_power,
+						      psd_power + 13 + pwr_lvl_idx * 3);
+					max_tx_power[pwr_lvl_idx] =
+						min_t(s8,
+						      tx_power,
+						      reg_tpc_info->tpe[pwr_lvl_idx]);
+			}
+			/* local power is not PSD power */
+			} else {
+				/* Connecting AP is psd power */
+				if (reg_tpc_info->is_psd_power) {
+					is_psd_power = true;
+					ath12k_mac_get_psd_channel(ar, 20,
+								   &start_freq,
+								   &center_freq,
+								   pwr_lvl_idx,
+								   &temp_chan,
+								   &tx_power);
+					eirp_power = tx_power;
+					max_tx_power[pwr_lvl_idx] =
+						reg_tpc_info->tpe[pwr_lvl_idx];
+				/* Connecting AP is not psd power */
+				} else {
+					ath12k_mac_get_eirp_power(ar,
+								  &start_freq,
+								  &center_freq,
+								  pwr_lvl_idx,
+								  &temp_chan,
+								  &ctx->def,
+								  &tx_power);
+					max_tx_power[pwr_lvl_idx] =
+						min_t(s8,
+						      tx_power,
+						      reg_tpc_info->tpe[pwr_lvl_idx]);
+				}
+		}
+		/* STA not received TPE IE */
+		} else {
+			/* local power is PSD power*/
+			if (chan->flags & IEEE80211_CHAN_PSD) {
+				is_psd_power = true;
+				ath12k_mac_get_psd_channel(ar, 20,
+							   &start_freq,
+							   &center_freq,
+							   pwr_lvl_idx,
+							   &temp_chan,
+							   &tx_power);
+				psd_power = temp_chan->psd;
+				eirp_power = tx_power;
+				max_tx_power[pwr_lvl_idx] = psd_power;
+			} else {
+				ath12k_mac_get_eirp_power(ar,
+							  &start_freq,
+							  &center_freq,
+							  pwr_lvl_idx,
+							  &temp_chan,
+							  &ctx->def,
+							  &tx_power);
+				max_tx_power[pwr_lvl_idx] = tx_power;
+			}
+		}
+
+		if (is_psd_power) {
+			/* If AP local power constraint is present */
+			if (pwr_reduction)
+				eirp_power = eirp_power - pwr_reduction;
+
+			/* If FW updated max tx power is non zero, then take the min of
+			 * firmware updated ap tx power
+			 * and max power derived from above mentioned parameters.
+			 */
+			ath12k_dbg(ab, ATH12K_DBG_MAC,
+				   "eirp power : %d firmware report power : %d\n",
+				   eirp_power, ar->max_allowed_tx_power);
+			if ((ar->max_allowed_tx_power) && (ab->hw_params->idle_ps))
+				eirp_power = min_t(s8,
+						   eirp_power,
+						   ar->max_allowed_tx_power);
+		} else {
+			/* If AP local power constraint is present */
+			if (pwr_reduction)
+				max_tx_power[pwr_lvl_idx] =
+					max_tx_power[pwr_lvl_idx] - pwr_reduction;
+			/* If FW updated max tx power is non zero, then take the min of
+			 * firmware updated ap tx power
+			 * and max power derived from above mentioned parameters.
+			 */
+			if ((ar->max_allowed_tx_power) && (ab->hw_params->idle_ps))
+				max_tx_power[pwr_lvl_idx] =
+					min_t(s8,
+					      max_tx_power[pwr_lvl_idx],
+					      ar->max_allowed_tx_power);
+		}
+		reg_tpc_info->chan_power_info[pwr_lvl_idx].chan_cfreq = center_freq;
+		reg_tpc_info->chan_power_info[pwr_lvl_idx].tx_power =
+			max_tx_power[pwr_lvl_idx];
+	}
+
+	reg_tpc_info->num_pwr_levels = num_pwr_levels;
+	reg_tpc_info->is_psd_power = is_psd_power;
+	reg_tpc_info->eirp_power = eirp_power;
+	reg_tpc_info->power_type_6ghz =
+		ath12k_ieee80211_ap_pwr_type_convert(vif->bss_conf.power_type);
+}
+
 static void ath12k_mac_parse_tx_pwr_env(struct ath12k *ar,
 					struct ieee80211_vif *vif,
 					struct ieee80211_chanctx_conf *ctx)
diff --git a/drivers/net/wireless/ath/ath12k/mac.h b/drivers/net/wireless/ath/ath12k/mac.h
index 82f590004d05..ce2ce7f324d7 100644
--- a/drivers/net/wireless/ath/ath12k/mac.h
+++ b/drivers/net/wireless/ath/ath12k/mac.h
@@ -68,6 +68,9 @@  struct ath12k *ath12k_mac_get_ar_by_vdev_id(struct ath12k_base *ab, u32 vdev_id)
 struct ath12k *ath12k_mac_get_ar_by_pdev_id(struct ath12k_base *ab, u32 pdev_id);
 enum wmi_vdev_type ath12k_mac_get_ar_vdev_type(struct ath12k *ar);
 
+void ath12k_mac_fill_reg_tpc_info(struct ath12k *ar,
+				  struct ieee80211_vif *vif,
+				  struct ieee80211_chanctx_conf *ctx);
 void ath12k_mac_drain_tx(struct ath12k *ar);
 void ath12k_mac_peer_cleanup_all(struct ath12k *ar);
 int ath12k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx);
diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
index 211bdb915173..5bfca2513730 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
@@ -5588,6 +5588,7 @@  static void ath12k_vdev_start_resp_event(struct ath12k_base *ab, struct sk_buff
 	}
 
 	ar->last_wmi_vdev_start_status = 0;
+	ar->max_allowed_tx_power = le32_to_cpu(vdev_start_resp.max_allowed_tx_power);
 
 	status = le32_to_cpu(vdev_start_resp.status);
 
diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h
index c3b110af1272..9f24f8ded52f 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.h
+++ b/drivers/net/wireless/ath/ath12k/wmi.h
@@ -3796,6 +3796,7 @@  struct wmi_vdev_start_resp_event {
 	};
 	__le32 cfgd_tx_streams;
 	__le32 cfgd_rx_streams;
+	__le32 max_allowed_tx_power;
 } __packed;
 
 /* VDEV start response status codes */