All of lore.kernel.org
 help / color / mirror / Atom feed
From: viktor.barna@celeno.com
To: linux-wireless@vger.kernel.org
Cc: Kalle Valo <kvalo@codeaurora.org>,
	"David S . Miller" <davem@davemloft.net>,
	Jakub Kicinski <kuba@kernel.org>,
	Aviad Brikman <aviad.brikman@celeno.com>,
	Eliav Farber <eliav.farber@gmail.com>,
	Maksym Kokhan <maksym.kokhan@celeno.com>,
	Oleksandr Savchenko <oleksandr.savchenko@celeno.com>,
	Shay Bar <shay.bar@celeno.com>,
	Viktor Barna <viktor.barna@celeno.com>
Subject: [RFC v2 54/96] cl8k: add power.c
Date: Tue, 24 May 2022 14:34:20 +0300	[thread overview]
Message-ID: <20220524113502.1094459-55-viktor.barna@celeno.com> (raw)
In-Reply-To: <20220524113502.1094459-1-viktor.barna@celeno.com>

From: Viktor Barna <viktor.barna@celeno.com>

(Part of the split. Please, take a look at the cover letter for more
details).

Signed-off-by: Viktor Barna <viktor.barna@celeno.com>
---
 drivers/net/wireless/celeno/cl8k/power.c | 1123 ++++++++++++++++++++++
 1 file changed, 1123 insertions(+)
 create mode 100644 drivers/net/wireless/celeno/cl8k/power.c

diff --git a/drivers/net/wireless/celeno/cl8k/power.c b/drivers/net/wireless/celeno/cl8k/power.c
new file mode 100644
index 000000000000..ef62c4b7a332
--- /dev/null
+++ b/drivers/net/wireless/celeno/cl8k/power.c
@@ -0,0 +1,1123 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/* Copyright(c) 2019-2022, Celeno Communications Ltd. */
+
+#include <linux/string.h>
+
+#include "reg/reg_access.h"
+#include "channel.h"
+#include "debug.h"
+#include "utils.h"
+#include "e2p.h"
+#include "power.h"
+
+static u8 cl_power_table_read(struct cl_hw *cl_hw)
+{
+	u8 pwr_table_id = 0;
+
+	if (cl_e2p_read(cl_hw->chip, &pwr_table_id, 1, ADDR_GEN_PWR_TABLE_ID + cl_hw->tcv_idx))
+		return U8_MAX;
+
+	return pwr_table_id;
+}
+
+static int cl_power_table_fill(struct cl_hw *cl_hw)
+{
+	u8 pwr_table_id = cl_power_table_read(cl_hw);
+	u8 platform_idx = cl_hw->chip->platform.idx;
+	struct cl_platform_table *table = NULL;
+
+	table = cl_platform_get_active_table(cl_hw->chip, platform_idx);
+	if (!table)
+		return cl_hw->chip->conf->ce_production_mode ? 0 : -1;
+
+	switch (pwr_table_id) {
+	case 0:
+		if (cl_band_is_5g(cl_hw)) {
+			memcpy(cl_hw->power_table_info.data->conv_table,
+			       table->power_conv_table_5,
+			       NUM_POWER_WORDS);
+			cl_hw->tx_power_version = 5;
+		} else if (IS_REAL_PHY(cl_hw->chip)) {
+			CL_DBG_ERROR(cl_hw, "Power table ID (%u) is valid for 5g only\n",
+				     pwr_table_id);
+
+			if (!cl_hw_is_prod_or_listener(cl_hw))
+				return -EINVAL;
+		}
+		break;
+	case 1:
+		if (cl_band_is_24g(cl_hw)) {
+			memcpy(cl_hw->power_table_info.data->conv_table,
+			       table->power_conv_table_2,
+			       NUM_POWER_WORDS);
+			cl_hw->tx_power_version = 25;
+		} else if (IS_REAL_PHY(cl_hw->chip)) {
+			CL_DBG_ERROR(cl_hw, "Power table ID (%u) is valid for 2.4g only\n",
+				     pwr_table_id);
+
+			if (!cl_hw_is_prod_or_listener(cl_hw))
+				return -1;
+		}
+		break;
+	case 2:
+		if (cl_band_is_6g(cl_hw)) {
+			memcpy(cl_hw->power_table_info.data->conv_table,
+			       table->power_conv_table_6,
+			       NUM_POWER_WORDS);
+			cl_hw->tx_power_version = 1;
+		} else if (IS_REAL_PHY(cl_hw->chip)) {
+			CL_DBG_ERROR(cl_hw, "Power table ID (%u) is valid for 6g only\n",
+				     pwr_table_id);
+
+			if (!cl_hw_is_prod_or_listener(cl_hw))
+				return -1;
+		}
+		break;
+	default:
+		if (IS_REAL_PHY(cl_hw->chip)) {
+			CL_DBG_ERROR(cl_hw, "Power table ID is not configured in EEPROM\n");
+
+			if (!cl_hw_is_prod_or_listener(cl_hw))
+				return -1;
+		}
+	}
+
+	cl_dbg_verbose(cl_hw, "Power table ID %u (V%u)\n", pwr_table_id, cl_hw->tx_power_version);
+
+	return 0;
+}
+
+int cl_power_table_alloc(struct cl_hw *cl_hw)
+{
+	struct cl_power_table_data *buf = NULL;
+	u32 len = sizeof(struct cl_power_table_data);
+	dma_addr_t phys_dma_addr;
+
+	buf = dma_alloc_coherent(cl_hw->chip->dev, len, &phys_dma_addr, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	cl_hw->power_table_info.data = buf;
+	cl_hw->power_table_info.dma_addr = phys_dma_addr;
+
+	return cl_power_table_fill(cl_hw);
+}
+
+void cl_power_table_free(struct cl_hw *cl_hw)
+{
+	struct cl_power_table_info *power_table_info = &cl_hw->power_table_info;
+	u32 len = sizeof(struct cl_power_table_data);
+	dma_addr_t phys_dma_addr = power_table_info->dma_addr;
+
+	if (!power_table_info->data)
+		return;
+
+	dma_free_coherent(cl_hw->chip->dev, len, (void *)power_table_info->data, phys_dma_addr);
+	power_table_info->data = NULL;
+}
+
+static s32 convert_str_int_q8(s8 *str)
+{
+	s32 x, y;
+
+	if (!str)
+		return 0;
+	if (sscanf(str, "%d.%2d", &x, &y) == 0)
+		return 0;
+	if (!strstr(str, "."))
+		return x << 8;
+	if (y < 10 && (*(strstr(str, ".") + 1) != '0'))
+		y *= 10;
+	return ((x * 100 + y) << 8) / 100;
+}
+
+u8 cl_power_tx_ant(struct cl_hw *cl_hw, enum cl_wrs_mode mode)
+{
+	if (mode == WRS_MODE_CCK)
+		return hweight8(cl_hw->conf->ce_cck_tx_ant_mask);
+
+	if (mode <= WRS_MODE_VHT)
+		return min_t(u8, cl_hw->num_antennas, MAX_ANTENNAS_OFDM_HT_VHT);
+
+	return cl_hw->num_antennas;
+}
+
+s32 cl_power_antenna_gain_q8(struct cl_hw *cl_hw)
+{
+	u8 channel = cl_hw->channel;
+
+	if (channel >= 36 && channel <= 64)
+		return convert_str_int_q8(cl_hw->conf->ce_ant_gain_36_64);
+	else if (channel >= 100 && channel <= 140)
+		return convert_str_int_q8(cl_hw->conf->ce_ant_gain_100_140);
+	else if (channel >= 149 && channel < 165)
+		return convert_str_int_q8(cl_hw->conf->ce_ant_gain_149_165);
+	else
+		return convert_str_int_q8(cl_hw->conf->ce_ant_gain); /* 2.4g and 6g */
+}
+
+s32 cl_power_antenna_gain_q1(struct cl_hw *cl_hw)
+{
+	return cl_power_antenna_gain_q8(cl_hw) >> 7;
+}
+
+s32 cl_power_array_gain_q8(struct cl_hw *cl_hw, u8 tx_ant)
+{
+	/*
+	 * Format in NVRAM of ce_arr_gain=A,B,C,D,E,F
+	 * A is the array gain with 1 tx_ant, B is with 2 tx_ant and so on...
+	 */
+	int arr_gain_val = 0;
+	int arr_gain_len = 0;
+	int idx = 0;
+	char *arr_gain_cpy = NULL;
+	char *arr_gain_cpy_p = NULL;
+	char *arr_gain_str = NULL;
+
+	arr_gain_len = strlen(cl_hw->conf->ce_arr_gain) + 1;
+	arr_gain_cpy_p = kzalloc(arr_gain_len, GFP_ATOMIC);
+	arr_gain_cpy = arr_gain_cpy_p;
+
+	if (!arr_gain_cpy)
+		return 0;
+
+	/* Copy cl_hw->conf->ce_arr_gain so its value won't be changed by strsep() */
+	memcpy(arr_gain_cpy, cl_hw->conf->ce_arr_gain, arr_gain_len);
+
+	/* Arr_gain_str points to the array gain of 1 tx_ant */
+	arr_gain_str = strsep(&arr_gain_cpy, ",");
+
+	/* Only a single value in ce_arr_gain - same value will be applied for all tx_ant */
+	if (!arr_gain_cpy) {
+		arr_gain_val = convert_str_int_q8(arr_gain_str);
+	} else {
+		/* Keep iterating until getting to the correct ant idx */
+		for (idx = 1; arr_gain_str && (idx < tx_ant); idx++)
+			arr_gain_str = strsep(&arr_gain_cpy, ",");
+
+		arr_gain_val = arr_gain_str ? convert_str_int_q8(arr_gain_str) : 0;
+	}
+
+	kfree(arr_gain_cpy_p);
+
+	return arr_gain_val;
+}
+
+s8 cl_power_array_gain_q2(struct cl_hw *cl_hw, u8 tx_ant)
+{
+	return (s8)(cl_power_array_gain_q8(cl_hw, tx_ant) >> 6);
+}
+
+s32 cl_power_array_gain_q1(struct cl_hw *cl_hw, u8 tx_ant)
+{
+	return cl_power_array_gain_q8(cl_hw, tx_ant) >> 7;
+}
+
+static s32 cl_power_bf_gain_q8(struct cl_hw *cl_hw, u8 tx_ant, u8 nss)
+{
+	/*
+	 * Format in NVRAM of ce_bf_gain=A,B,C,D
+	 * A is the bf gain with 1 NSS, B is with 2 NSS and so on...
+	 */
+	int bf_gain_val = 0;
+	int bf_gain_len = 0;
+	int idx = 0;
+	char *bf_gain_cpy = NULL;
+	char *bf_gain_cpy_p = NULL;
+	char *bf_gain_str = NULL;
+	s8 *bf_gain_ptr = NULL;
+
+	if (tx_ant == 6) {
+		bf_gain_ptr = cl_hw->conf->ce_bf_gain_6_ant;
+	} else if (tx_ant == 5) {
+		bf_gain_ptr = cl_hw->conf->ce_bf_gain_5_ant;
+	} else if (tx_ant == 4) {
+		bf_gain_ptr = cl_hw->conf->ce_bf_gain_4_ant;
+	} else if (tx_ant == 3) {
+		bf_gain_ptr = cl_hw->conf->ce_bf_gain_3_ant;
+	} else if (tx_ant == 2) {
+		bf_gain_ptr = cl_hw->conf->ce_bf_gain_2_ant;
+	} else if (tx_ant == 1) {
+		goto out;
+	} else {
+		pr_err("[%s]: invalid tx_ant %u\n", __func__, tx_ant);
+		goto out;
+	}
+
+	bf_gain_len = strlen(bf_gain_ptr) + 1;
+	bf_gain_cpy_p = kzalloc(bf_gain_len, GFP_ATOMIC);
+	bf_gain_cpy = bf_gain_cpy_p;
+
+	if (!bf_gain_cpy)
+		return 0;
+
+	/* Copy cl_hw->conf->ce_bf_gain_*_ant so its value won't be changed by strsep() */
+	memcpy(bf_gain_cpy, bf_gain_ptr, bf_gain_len);
+
+	/* Bf_gain_str points to the bf gain of 1 SS */
+	bf_gain_str = strsep(&bf_gain_cpy, ",");
+
+	/* Keep iterating until getting to the correct ss index */
+	for (idx = 0; bf_gain_str && (idx < nss); idx++)
+		bf_gain_str = strsep(&bf_gain_cpy, ",");
+
+	bf_gain_val = bf_gain_str ? convert_str_int_q8(bf_gain_str) : 0;
+
+	kfree(bf_gain_cpy_p);
+ out:
+	return bf_gain_val;
+}
+
+s32 cl_power_bf_gain_q1(struct cl_hw *cl_hw, u8 tx_ant, u8 nss)
+{
+	return cl_power_bf_gain_q8(cl_hw, tx_ant, nss) >> 7;
+}
+
+static s32 cl_power_min_ant_q8(struct cl_hw *cl_hw)
+{
+	return convert_str_int_q8(cl_hw->conf->ci_min_ant_pwr);
+}
+
+s32 cl_power_min_ant_q1(struct cl_hw *cl_hw)
+{
+	return cl_power_min_ant_q8(cl_hw) >> 7;
+};
+
+s8 cl_power_bw_factor_q2(struct cl_hw *cl_hw, u8 bw)
+{
+	/*
+	 * Format in NVRAM of ci_bw_factor=A,B,C,D
+	 * A is the bw factor for bw 20MHz, B is for 40MHz and so on..
+	 */
+	int bw_factor_val = 0;
+	int bw_factor_len = 0;
+	int idx = 0;
+	char *bw_factor_cpy = NULL;
+	char *bw_factor_cpy_p = NULL;
+	char *bw_factor_str = NULL;
+
+	bw_factor_len = strlen(cl_hw->conf->ci_bw_factor) + 1;
+	bw_factor_cpy = kzalloc(bw_factor_len, GFP_ATOMIC);
+	bw_factor_cpy = bw_factor_cpy_p;
+
+	if (!bw_factor_cpy)
+		return 0;
+
+	/* Copy cl_hw->conf->ci_bw_factor so its value won't be changed by strsep() */
+	memcpy(bw_factor_cpy, cl_hw->conf->ci_bw_factor, bw_factor_len);
+
+	/* Bw_factor_str points to the bw factor of 20MHz */
+	bw_factor_str = strsep(&bw_factor_cpy, ",");
+
+	/* Only a single value in ci_bw_factor - same value will be applied for all bandwidths */
+	if (!bw_factor_cpy) {
+		bw_factor_val = convert_str_int_q8(bw_factor_str);
+	} else {
+		/* Keep iterating until getting to the correct bw index */
+		for (idx = 0; bw_factor_str && (idx < bw); idx++)
+			bw_factor_str = strsep(&bw_factor_cpy, ",");
+
+		bw_factor_val = bw_factor_str ? convert_str_int_q8(bw_factor_str) : 0;
+	}
+
+	kfree(bw_factor_cpy_p);
+
+	return (s8)(bw_factor_val >> 6);
+}
+
+static s32 cl_power_average_calib_q8(struct cl_hw *cl_hw, u8 ant_num)
+{
+	u8 ant = 0, ant_cnt = 0;
+	u8 chan_idx = cl_channel_to_index(cl_hw, cl_hw->channel);
+	s32 total_calib_pow = 0;
+
+	if (chan_idx == INVALID_CHAN_IDX)
+		return 0;
+
+	for (ant = 0; ant < MAX_ANTENNAS && ant_cnt < ant_num; ant++) {
+		if (!(cl_hw->mask_num_antennas & BIT(ant)))
+			continue;
+
+		total_calib_pow += cl_hw->tx_pow_info[chan_idx][ant].power;
+		ant_cnt++;
+	}
+
+	return ((total_calib_pow << 8) / ant_num);
+}
+
+s32 cl_power_average_calib_q1(struct cl_hw *cl_hw, u8 ant_num)
+{
+	return cl_power_average_calib_q8(cl_hw, ant_num) >> 7;
+}
+
+static s32 cl_power_total_q8(struct cl_hw *cl_hw, s8 pwr_offset_q1, u8 tx_ant, u8 nss,
+			     enum cl_wrs_mode mode, bool is_auto_resp)
+{
+	s32 bf_gain_q8 =  0;
+	s32 antenna_gain_q8 = cl_power_antenna_gain_q8(cl_hw);
+	s32 array_gain_q8 = cl_power_array_gain_q8(cl_hw, tx_ant);
+	s32 pwr_offset_q8 = (s32)pwr_offset_q1 << 7;
+	s32 calib_power_q8 = cl_power_average_calib_q8(cl_hw, tx_ant);
+	s32 total_power_q8 = 0;
+
+	if (!is_auto_resp)
+		bf_gain_q8 = (mode > WRS_MODE_OFDM) ? cl_power_bf_gain_q8(cl_hw, tx_ant, nss) : 0;
+
+	total_power_q8 = calib_power_q8 + bf_gain_q8 + array_gain_q8 +
+		antenna_gain_q8 + pwr_offset_q8;
+
+	/* FCC calculation */
+	if (cl_hw->channel_info.standard == NL80211_DFS_FCC)
+		total_power_q8 -= min(bf_gain_q8 + antenna_gain_q8, 6 << 8);
+
+	return total_power_q8;
+}
+
+static s32 cl_power_eirp_delta_q1(struct cl_hw *cl_hw, u8 bw, s8 pwr_offset_q1, u8 tx_ant,
+				  u8 nss, enum cl_wrs_mode mode, bool is_auto_resp)
+{
+	/* Calculate total TX power */
+	s32 total_power_q8 = cl_power_total_q8(cl_hw, pwr_offset_q1, tx_ant, nss,
+					       mode, is_auto_resp);
+
+	/* EIRP power limit */
+	s32 eirp_power_limit_q8 = cl_chan_info_get_eirp_limit_q8(cl_hw, bw);
+
+	/* Delta between total TX power and EIRP limit */
+	return (total_power_q8 - eirp_power_limit_q8) >> 7;
+}
+
+static s8 cl_power_calc_q1(struct cl_hw *cl_hw, s8 mcs_offset_q1, u8 bw, u8 nss,
+			   enum cl_wrs_mode mode, bool is_auto_resp, u8 *trunc_pwr_q1)
+{
+	/* Result is in 0.5dBm resolution */
+	u8 tx_ant = cl_power_tx_ant(cl_hw, mode);
+	s32 calib_power_q1 = cl_power_average_calib_q1(cl_hw, tx_ant);
+	s32 res_q1 = calib_power_q1 + mcs_offset_q1;
+	s32 min_pwr_q1 = POWER_MIN_DB_Q1;
+	u32 trunc_pwr_val_q1 = 0;
+	bool eirp_regulatory_en = cl_hw->chip->conf->ce_production_mode ?
+		cl_hw->conf->ce_eirp_regulatory_prod_en : cl_hw->conf->ce_eirp_regulatory_op_en;
+
+	if (cl_hw->channel_info.use_channel_info && eirp_regulatory_en) {
+		s32 delta_power_q1 = cl_power_eirp_delta_q1(cl_hw, bw, mcs_offset_q1,
+							    tx_ant, nss, mode, is_auto_resp);
+
+		if (delta_power_q1 > 0) {
+			/*
+			 * If tx power is greater than the limitation
+			 * subtract delta power from the result
+			 */
+			res_q1 -= delta_power_q1;
+			trunc_pwr_val_q1 = delta_power_q1;
+		}
+	}
+
+	if (is_auto_resp)
+		min_pwr_q1 += cl_power_min_ant_q1(cl_hw);
+
+	if (res_q1 < min_pwr_q1) {
+		trunc_pwr_val_q1 = max((s32)trunc_pwr_val_q1 - min_pwr_q1 - res_q1, 0);
+		res_q1 = min_pwr_q1;
+	}
+
+	if (is_auto_resp)
+		res_q1 += cl_power_array_gain_q1(cl_hw, tx_ant);
+
+	if (trunc_pwr_q1)
+		*trunc_pwr_q1 = (u8)trunc_pwr_val_q1;
+
+	return (s8)res_q1;
+}
+
+static s8 cl_power_offset_he(struct cl_hw *cl_hw, u8 bw, u8 mcs)
+{
+	u8 channel = cl_hw->channel;
+	s8 *ppmcs = NULL;
+
+	switch (cl_hw->conf->ci_band_num) {
+	case BAND_5G:
+		if (channel >= 36 && channel <= 64)
+			ppmcs = cl_hw->conf->ce_ppmcs_offset_he_36_64;
+		else if (channel >= 100 && channel <= 140)
+			ppmcs = cl_hw->conf->ce_ppmcs_offset_he_100_140;
+		else
+			ppmcs = cl_hw->conf->ce_ppmcs_offset_he_149_165;
+		break;
+	case BAND_24G:
+		ppmcs = cl_hw->conf->ce_ppmcs_offset_he;
+		break;
+	case BAND_6G:
+		ppmcs = cl_hw->conf->ce_ppmcs_offset_he_6g;
+		break;
+	default:
+		return 0;
+	}
+
+	return ppmcs[mcs] + cl_hw->conf->ce_ppbw_offset[bw];
+}
+
+static s8 cl_power_offset_ht_vht(struct cl_hw *cl_hw, u8 bw, u8 mcs)
+{
+	u8 channel = cl_hw->channel;
+	s8 *ppmcs = NULL;
+
+	switch (cl_hw->conf->ci_band_num) {
+	case BAND_5G:
+		if (channel >= 36 && channel <= 64)
+			ppmcs = cl_hw->conf->ce_ppmcs_offset_ht_vht_36_64;
+		else if (channel >= 100 && channel <= 140)
+			ppmcs = cl_hw->conf->ce_ppmcs_offset_ht_vht_100_140;
+		else
+			ppmcs = cl_hw->conf->ce_ppmcs_offset_ht_vht_149_165;
+		break;
+	case BAND_24G:
+		ppmcs = cl_hw->conf->ce_ppmcs_offset_ht;
+		break;
+	case BAND_6G:
+	default:
+		return 0;
+	}
+
+	return ppmcs[mcs] + cl_hw->conf->ce_ppbw_offset[bw];
+}
+
+static s8 cl_power_offset_ofdm(struct cl_hw *cl_hw, u8 mcs)
+{
+	u8 channel = cl_hw->channel;
+	s8 *ppmcs = NULL;
+
+	switch (cl_hw->conf->ci_band_num) {
+	case BAND_5G:
+		if (channel >= 36 && channel <= 64)
+			ppmcs = cl_hw->conf->ce_ppmcs_offset_ofdm_36_64;
+		else if (channel >= 100 && channel <= 140)
+			ppmcs = cl_hw->conf->ce_ppmcs_offset_ofdm_100_140;
+		else
+			ppmcs = cl_hw->conf->ce_ppmcs_offset_ofdm_149_165;
+		break;
+	case BAND_24G:
+		ppmcs = cl_hw->conf->ce_ppmcs_offset_ofdm;
+		break;
+	case BAND_6G:
+	default:
+		return 0;
+	}
+
+	return ppmcs[mcs] + cl_hw->conf->ce_ppbw_offset[CHNL_BW_20];
+}
+
+static s8 cl_power_offset_cck(struct cl_hw *cl_hw, u8 mcs)
+{
+	s8 *ppmcs = cl_hw->conf->ce_ppmcs_offset_cck;
+
+	if (cl_band_is_24g(cl_hw))
+		return ppmcs[mcs] + cl_hw->conf->ce_ppbw_offset[CHNL_BW_20];
+
+	return 0;
+}
+
+s8 cl_power_offset_q1(struct cl_hw *cl_hw, u8 mode, u8 bw, u8 mcs)
+{
+	if (mode == WRS_MODE_HE)
+		return cl_power_offset_he(cl_hw, bw, mcs);
+	else if (mode == WRS_MODE_HT || mode == WRS_MODE_VHT)
+		return cl_power_offset_ht_vht(cl_hw, bw, mcs);
+	else if (mode == WRS_MODE_OFDM)
+		return cl_power_offset_ofdm(cl_hw, mcs);
+	else if (mode == WRS_MODE_CCK)
+		return cl_power_offset_cck(cl_hw, mcs);
+
+	return 0;
+}
+
+#define UPPER_POWER_MARGIN_Q2 (38 << 2)
+#define LOWER_POWER_MARGIN_Q2 (50 << 2)
+
+s8 cl_power_offset_check_margin(struct cl_hw *cl_hw, u8 bw, u8 ant_idx, s8 offset_q2)
+{
+	s8 new_offset_q2 = 0;
+	s8 bw_factor_q2 = cl_hw->power_db.bw_factor_q2[bw];
+	s8 ant_factor_q2 = cl_hw->power_db.ant_factor_q2[ant_idx];
+	s8 total_offset_upper_q2 = bw_factor_q2 + offset_q2;
+	s8 total_offset_lower_q2 = bw_factor_q2 + ant_factor_q2 + offset_q2;
+	bool upper_limit_valid = (total_offset_upper_q2 <= UPPER_POWER_MARGIN_Q2);
+	bool lower_limit_valid = (total_offset_lower_q2 <= LOWER_POWER_MARGIN_Q2);
+
+	if (upper_limit_valid && lower_limit_valid) {
+		return offset_q2;
+	} else if (!upper_limit_valid && lower_limit_valid) {
+		new_offset_q2 = UPPER_POWER_MARGIN_Q2 - bw_factor_q2;
+
+		return new_offset_q2;
+	} else if (upper_limit_valid && !lower_limit_valid) {
+		new_offset_q2 = LOWER_POWER_MARGIN_Q2 - bw_factor_q2 - ant_factor_q2;
+
+		return new_offset_q2;
+	}
+
+	new_offset_q2 = min(UPPER_POWER_MARGIN_Q2 - bw_factor_q2,
+			    LOWER_POWER_MARGIN_Q2 - bw_factor_q2 - ant_factor_q2);
+
+	return new_offset_q2;
+}
+
+static s32 cl_power_calc_total_from_eirp_q1(struct cl_hw *cl_hw, s32 tx_power, u8 nss,
+					    enum cl_wrs_mode mode, u8 *trunc_pwr_q1)
+{
+	s32 pwr_q1, total_pwr_q1, delta_pwr_q1 = 0;
+	u8 tx_ant;
+	s32 antenna_gain_q1;
+	s32 array_gain_q1;
+	s32 bf_gain_q1;
+	bool eirp_regulatory_en = cl_hw->chip->conf->ce_production_mode ?
+		cl_hw->conf->ce_eirp_regulatory_prod_en : cl_hw->conf->ce_eirp_regulatory_op_en;
+
+	pwr_q1 = tx_power << 1;
+
+	tx_ant = cl_power_tx_ant(cl_hw, mode);
+	array_gain_q1 = cl_power_array_gain_q1(cl_hw, tx_ant);
+	antenna_gain_q1 = cl_power_antenna_gain_q1(cl_hw);
+	/* bf gain is not used for CCK or OFDM */
+	bf_gain_q1 = (mode > WRS_MODE_OFDM) ? cl_power_bf_gain_q1(cl_hw, tx_ant, nss) : 0;
+
+	/* FCC calculation */
+	if (cl_hw->channel_info.standard == NL80211_DFS_FCC)
+		pwr_q1 -= min(bf_gain_q1 + antenna_gain_q1, 6 << 1);
+
+	if (cl_hw->channel_info.use_channel_info && eirp_regulatory_en) {
+		s32 eirp_pwr_limit_q1;
+
+		eirp_pwr_limit_q1 = cl_chan_info_get_eirp_limit_q8(cl_hw, 0) >> 7;
+		if (pwr_q1 > eirp_pwr_limit_q1) {
+			delta_pwr_q1 = pwr_q1 - eirp_pwr_limit_q1;
+			pwr_q1 = eirp_pwr_limit_q1;
+		}
+	}
+
+	total_pwr_q1 = pwr_q1 - antenna_gain_q1 - array_gain_q1 - bf_gain_q1;
+	if (total_pwr_q1 < POWER_MIN_DB_Q1) {
+		delta_pwr_q1 = max(delta_pwr_q1 - (POWER_MIN_DB_Q1 - total_pwr_q1), 0);
+		total_pwr_q1 = POWER_MIN_DB_Q1;
+	}
+
+	if (trunc_pwr_q1)
+		*trunc_pwr_q1 = (u8)delta_pwr_q1;
+
+	return total_pwr_q1;
+}
+
+static s32 cl_power_calc_auto_resp_from_eirp_q1(struct cl_hw *cl_hw, s32 tx_power, u8 nss,
+						enum cl_wrs_mode mode)
+{
+	s32 auto_resp_total_pwr_q1, auto_resp_min_pwr_q1;
+	u8 tx_ant;
+	s32 array_gain_q1;
+	s32 total_pwr_q1;
+
+	auto_resp_min_pwr_q1 = POWER_MIN_DB_Q1 + cl_power_min_ant_q1(cl_hw);
+	tx_ant = cl_power_tx_ant(cl_hw, mode);
+	array_gain_q1 = cl_power_array_gain_q1(cl_hw, tx_ant);
+	total_pwr_q1 = cl_power_calc_total_from_eirp_q1(cl_hw, tx_power, nss, mode, NULL);
+
+	auto_resp_total_pwr_q1 = array_gain_q1 + total_pwr_q1;
+	if (auto_resp_total_pwr_q1 < auto_resp_min_pwr_q1)
+		auto_resp_total_pwr_q1 = auto_resp_min_pwr_q1;
+
+	return auto_resp_total_pwr_q1;
+}
+
+static s8 cl_calc_ant_pwr_q1(struct cl_hw *cl_hw, u8 bw, u8 nss, u8 mcs,
+			     enum cl_wrs_mode mode, u8 *trunc_val)
+{
+	s32 eirp_pwr = 0;
+	s8 ant_pwr_q1;
+
+	eirp_pwr = cl_hw->new_tx_power;
+	if (eirp_pwr) {
+		ant_pwr_q1 = cl_power_calc_total_from_eirp_q1(cl_hw, eirp_pwr, nss,
+							      mode, trunc_val);
+	} else {
+		s8 pwr_offset_q1;
+
+		pwr_offset_q1 = cl_power_offset_q1(cl_hw, mode, bw, mcs);
+		ant_pwr_q1 = cl_power_calc_q1(cl_hw, pwr_offset_q1, bw, nss,
+					      mode, false, trunc_val);
+	}
+	return ant_pwr_q1;
+}
+
+static s8 cl_calc_auto_resp_pwr_q1(struct cl_hw *cl_hw, u8 bw, u8 nss, u8 mcs,
+				   enum cl_wrs_mode mode)
+{
+	s32 eirp_pwr = 0;
+	s8 auto_resp_pwr_q1;
+
+	eirp_pwr = cl_hw->new_tx_power;
+	if (eirp_pwr) {
+		auto_resp_pwr_q1 = cl_power_calc_auto_resp_from_eirp_q1(cl_hw, eirp_pwr,
+									nss, mode);
+	} else {
+		s8 pwr_offset_q1;
+
+		pwr_offset_q1 = cl_power_offset_q1(cl_hw, mode, bw, mcs);
+		auto_resp_pwr_q1 = cl_power_calc_q1(cl_hw, pwr_offset_q1, bw, nss,
+						    mode, true, NULL);
+	}
+	return auto_resp_pwr_q1;
+}
+
+static void cl_power_tables_update_cck(struct cl_hw *cl_hw,
+				       struct cl_pwr_tables *pwr_tables)
+{
+	u8 mcs;
+	u8 trunc_value = 0;
+
+	/* CCK - Enforce EIRP limitations */
+	for (mcs = 0; mcs < WRS_MCS_MAX_CCK; mcs++) {
+		pwr_tables->ant_pwr_cck[mcs] = cl_calc_ant_pwr_q1(cl_hw, 0, 0, mcs, WRS_MODE_CCK,
+								  &trunc_value);
+
+		cl_hw->pwr_trunc.cck[mcs] = trunc_value;
+
+		/* Auto response */
+		pwr_tables->pwr_auto_resp_cck[mcs] = cl_calc_auto_resp_pwr_q1(cl_hw, 0, 0, mcs,
+									      WRS_MODE_CCK);
+	}
+}
+
+static void cl_power_tables_update_ofdm(struct cl_hw *cl_hw,
+					struct cl_pwr_tables *pwr_tables)
+{
+	u8 mcs;
+	u8 trunc_value = 0;
+
+	/* OFDM - Enforce EIRP limitations */
+	for (mcs = 0; mcs < WRS_MCS_MAX_OFDM; mcs++) {
+		pwr_tables->ant_pwr_ofdm[mcs] = cl_calc_ant_pwr_q1(cl_hw, 0, 0, mcs, WRS_MODE_OFDM,
+								   &trunc_value);
+		cl_hw->pwr_trunc.ofdm[mcs] = trunc_value;
+
+		/* Auto response */
+		pwr_tables->pwr_auto_resp_ofdm[mcs] = cl_calc_auto_resp_pwr_q1(cl_hw, 0, 0, mcs,
+									       WRS_MODE_OFDM);
+	}
+}
+
+static u8 cl_power_tables_update_ht_vht(struct cl_hw *cl_hw,
+					struct cl_pwr_tables *pwr_tables)
+{
+	bool is_24g = cl_band_is_24g(cl_hw);
+	bool is_5g = cl_band_is_5g(cl_hw);
+	u8 bw;
+	u8 nss;
+	u8 mcs;
+	u8 trunc_value = 0;
+	u8 min_bw_idx_limit_vht = 0;
+	u8 max_mcs_ht_vht = (is_5g || (is_24g && cl_hw->conf->ci_vht_cap_24g)) ?
+		WRS_MCS_MAX_VHT : WRS_MCS_MAX_HT;
+	s16 min_bw_limit = 0;
+	s32 eirp_power_limit_q8;
+
+	for (bw = 0, min_bw_limit = 0xFFFF; bw < cl_max_bw_idx(WRS_MODE_VHT, is_24g); bw++) {
+		if (!cl_hw_is_prod_or_listener(cl_hw) &&
+		    !cl_chan_info_get(cl_hw, cl_hw->channel, bw))
+			continue;
+
+		/* Find lowest EIRP power limitation among all bw for auto resp calculations */
+		eirp_power_limit_q8 = cl_chan_info_get_eirp_limit_q8(cl_hw, bw);
+		if (eirp_power_limit_q8 < min_bw_limit) {
+			min_bw_limit = eirp_power_limit_q8;
+			min_bw_idx_limit_vht = bw;
+		}
+
+		/* HT/VHT - Enforce EIRP limitations */
+		for (mcs = 0; mcs < max_mcs_ht_vht; mcs++) {
+			for (nss = 0; nss < PWR_TBL_VHT_BF_SIZE; nss++) {
+				pwr_tables->ant_pwr_ht_vht[bw][mcs][nss] =
+					cl_calc_ant_pwr_q1(cl_hw, bw, nss, mcs, WRS_MODE_VHT,
+							   &trunc_value);
+				cl_hw->pwr_trunc.ht_vht[bw][mcs][nss] = trunc_value;
+			}
+		}
+	}
+
+	/* Auto resp HT/VHT - Enforce EIRP limitations */
+	for (mcs = 0; mcs < max_mcs_ht_vht; mcs++)
+		pwr_tables->pwr_auto_resp_ht_vht[mcs] =
+			cl_calc_auto_resp_pwr_q1(cl_hw, min_bw_idx_limit_vht, 0, mcs,
+						 WRS_MODE_VHT);
+
+	return min_bw_idx_limit_vht;
+}
+
+static u8 cl_power_tables_update_he(struct cl_hw *cl_hw,
+				    struct cl_pwr_tables *pwr_tables)
+{
+	bool is_24g = cl_band_is_24g(cl_hw);
+	u8 bw;
+	u8 nss;
+	u8 mcs;
+	u8 trunc_value = 0;
+	u8 min_bw_idx_limit_he = 0;
+	s16 min_bw_limit = 0;
+	s32 eirp_power_limit_q8;
+
+	for (bw = 0, min_bw_limit = 0xFFFF; bw < cl_max_bw_idx(WRS_MODE_HE, is_24g); bw++) {
+		if (!cl_hw_is_prod_or_listener(cl_hw) &&
+		    !cl_chan_info_get(cl_hw, cl_hw->channel, bw))
+			continue;
+
+		/* Find lowest EIRP power limitation among all bw for auto resp calculations */
+		eirp_power_limit_q8 = cl_chan_info_get_eirp_limit_q8(cl_hw, bw);
+		if (eirp_power_limit_q8 < min_bw_limit) {
+			min_bw_limit = eirp_power_limit_q8;
+			min_bw_idx_limit_he = bw;
+		}
+
+		/* HE - Enforce EIRP limitations */
+		for (mcs = 0; mcs < WRS_MCS_MAX_HE; mcs++) {
+			for (nss = 0; nss < PWR_TBL_HE_BF_SIZE; nss++) {
+				pwr_tables->ant_pwr_he[bw][mcs][nss] =
+					cl_calc_ant_pwr_q1(cl_hw, bw, nss, mcs, WRS_MODE_HE,
+							   &trunc_value);
+				cl_hw->pwr_trunc.he[bw][mcs][nss] = trunc_value;
+			}
+		}
+	}
+
+	/* Auto resp HE - Enforce EIRP limitations */
+	for (mcs = 0; mcs < WRS_MCS_MAX_HE; mcs++)
+		pwr_tables->pwr_auto_resp_he[mcs] =
+			cl_calc_auto_resp_pwr_q1(cl_hw, min_bw_idx_limit_he, 0, mcs, WRS_MODE_HE);
+
+	return min_bw_idx_limit_he;
+}
+
+static u8 cl_power_calc_max(struct cl_hw *cl_hw, u8 bw, enum cl_wrs_mode mode)
+{
+	u8 tx_ant = cl_power_tx_ant(cl_hw, mode);
+	/* Total TX power - pass is_auto_resp = true in order to ignore bf gain */
+	s32 total_power_q8 = cl_power_total_q8(cl_hw, 0, tx_ant, 0, mode, true);
+	/* EIRP power limit */
+	s32 eirp_power_limit_q8 = cl_chan_info_get_eirp_limit_q8(cl_hw, bw);
+
+	return (min(total_power_q8, eirp_power_limit_q8) >> 8);
+}
+
+static s8 cl_power_vns_calc_q1(struct cl_hw *cl_hw, u8 bw,
+			       enum cl_wrs_mode mode, bool is_auto_resp)
+{
+	u8 max_tx_pwr = cl_power_calc_max(cl_hw, bw, mode);
+	u8 tx_ant = cl_power_tx_ant(cl_hw, mode);
+	s32 vns_pwr_limit_q8 = min_t(u8, cl_hw->conf->ci_vns_pwr_limit, max_tx_pwr) << 8;
+	s32 antenna_gain_q8 = cl_power_antenna_gain_q8(cl_hw);
+	s32 array_gain_q8 = (is_auto_resp ? 0 : cl_power_array_gain_q8(cl_hw, tx_ant));
+	s32 min_ant_pwr_q8 = cl_power_min_ant_q8(cl_hw);
+	s32 min_pwr_q8 = is_auto_resp ? (POWER_MIN_DB_Q8 + min_ant_pwr_q8) : POWER_MIN_DB_Q8;
+	s32 res_q8 = vns_pwr_limit_q8 - antenna_gain_q8 - array_gain_q8;
+
+	if (res_q8 < min_pwr_q8)
+		res_q8 = min_pwr_q8;
+
+	/* Result should be in 0.5dBm resolution */
+	return (s8)(res_q8 >> 7);
+}
+
+static void cl_power_tables_update_vns(struct cl_hw *cl_hw,
+				       struct cl_pwr_tables *pwr_tables,
+				       u8 min_bw_idx_limit_vht,
+				       u8 min_bw_idx_limit_he)
+{
+	/* VNS */
+	pwr_tables->ant_pwr_vns_he =
+		cl_power_vns_calc_q1(cl_hw, min_bw_idx_limit_he, WRS_MODE_HE, false);
+	pwr_tables->ant_pwr_vns_ht_vht =
+		cl_power_vns_calc_q1(cl_hw, min_bw_idx_limit_vht, WRS_MODE_VHT, false);
+	pwr_tables->ant_pwr_vns_ofdm =
+		cl_power_vns_calc_q1(cl_hw, 0, WRS_MODE_OFDM, false);
+	pwr_tables->ant_pwr_vns_cck =
+		cl_power_vns_calc_q1(cl_hw, 0, WRS_MODE_CCK, false);
+
+	/* Auto response VNS */
+	pwr_tables->pwr_auto_resp_vns_he =
+		cl_power_vns_calc_q1(cl_hw, min_bw_idx_limit_he, WRS_MODE_HE, true);
+	pwr_tables->pwr_auto_resp_vns_ht_vht =
+		cl_power_vns_calc_q1(cl_hw, min_bw_idx_limit_vht, WRS_MODE_VHT, true);
+	pwr_tables->pwr_auto_resp_vns_ofdm =
+		cl_power_vns_calc_q1(cl_hw, 0, WRS_MODE_OFDM, true);
+	pwr_tables->pwr_auto_resp_vns_cck =
+		cl_power_vns_calc_q1(cl_hw, 0, WRS_MODE_CCK, true);
+}
+
+static void cl_power_tables_update_by_offset(struct cl_hw *cl_hw,
+					     struct cl_pwr_tables *pwr_tables,
+					     s8 offset)
+{
+	u8 mcs = 0;
+	u8 bw = 0;
+	u8 nss = 0;
+
+	/* CCK - Enforce EIRP limitations */
+	for (mcs = 0; mcs < WRS_MCS_MAX_CCK; mcs++) {
+		pwr_tables->ant_pwr_cck[mcs] += offset;
+
+		/* Auto response */
+		pwr_tables->pwr_auto_resp_cck[mcs] += offset;
+	}
+
+	/* OFDM - Enforce EIRP limitations */
+	for (mcs = 0; mcs < WRS_MCS_MAX_OFDM; mcs++) {
+		pwr_tables->ant_pwr_ofdm[mcs] += offset;
+
+		/* Auto response */
+		pwr_tables->pwr_auto_resp_ofdm[mcs] += offset;
+	}
+
+	for (bw = 0; bw < CHNL_BW_MAX; bw++) {
+		/* HT/VHT - Enforce EIRP limitations */
+		for (mcs = 0; mcs < WRS_MCS_MAX_VHT; mcs++) {
+			for (nss = 0; nss < PWR_TBL_VHT_BF_SIZE; nss++)
+				pwr_tables->ant_pwr_ht_vht[bw][mcs][nss] += offset;
+
+			/*
+			 * Auto response:
+			 * always with disabled BF so the offset of the last nss is used
+			 */
+			pwr_tables->pwr_auto_resp_ht_vht[mcs] += offset;
+		}
+
+		/* HE - Enforce EIRP limitations */
+		for (mcs = 0; mcs < WRS_MCS_MAX_HE; mcs++) {
+			for (nss = 0; nss < PWR_TBL_HE_BF_SIZE; nss++)
+				pwr_tables->ant_pwr_he[bw][mcs][nss] += offset;
+
+			/*
+			 * Auto response:
+			 * always with disabled BF so the offset of the last nss is used
+			 */
+			pwr_tables->pwr_auto_resp_he[mcs] += offset;
+		}
+	}
+}
+
+static s8 cl_power_get_offset(u16 percentage)
+{
+	if (percentage >= 94)
+		return 0;
+	else if (percentage >= 84)
+		return -1; /* -0.5dBm */
+	else if (percentage >= 75)
+		return -2; /* -1dBm */
+	else if (percentage >= 67)
+		return -3; /* -1.5dBm */
+	else if (percentage >= 59)
+		return -4; /* -2dBm */
+	else if (percentage >= 54)
+		return -5; /* -2.5dBm */
+	else if (percentage >= 48)
+		return -6; /* -3dBm */
+	else if (percentage >= 43)
+		return -7; /* -3.5dBm */
+	else if (percentage >= 38)
+		return -8; /* -4dBm */
+	else if (percentage >= 34)
+		return -9; /* -4.5dBm */
+	else if (percentage >= 30)
+		return -10; /* -5dBm */
+	else if (percentage >= 27)
+		return -11; /* -5.5dBm */
+	else if (percentage >= 24)
+		return -12; /* -6dBm */
+	else if (percentage >= 22)
+		return -13; /* -6.5dBm */
+	else if (percentage >= 19)
+		return -14; /* -7dBm */
+	else if (percentage >= 17)
+		return -15; /* -7.5dBm */
+	else if (percentage >= 15)
+		return -16; /* -8dBm */
+	else if (percentage >= 14)
+		return -17; /* -8.5dBm */
+	else if (percentage >= 12)
+		return -18; /* -9dBm */
+	else if (percentage >= 11)
+		return -19; /* -9.5dBm */
+	else if (percentage >= 10)
+		return -20; /* -10dBm */
+	else if (percentage >= 9)
+		return -21; /* -10.5dBm */
+	else if (percentage >= 8)
+		return -22; /* -11dBm */
+	else if (percentage >= 7)
+		return -23; /* -11.5dBm */
+	else if (percentage >= 6)
+		return -24; /* -12dBm */
+	else if (percentage >= 5)
+		return -26; /* -13dBm */
+	else if (percentage >= 4)
+		return -28; /* -14dBm */
+	else if (percentage >= 3)
+		return -30; /* -15dBm */
+	else if (percentage >= 2)
+		return -34; /* -17dBm */
+	else if (percentage >= 1)
+		return -40; /* -20dBm */
+
+	/* Should not get here */
+	return 0;
+}
+
+static void cl_power_control_apply_percentage(struct cl_hw *cl_hw)
+{
+	struct cl_power_db *power_db = &cl_hw->power_db;
+	u8 percentage = cl_hw->conf->ce_tx_power_control;
+
+	power_db->curr_percentage = percentage;
+
+	if (percentage != 100) {
+		power_db->curr_offset = cl_power_get_offset(percentage);
+		cl_power_tables_update_by_offset(cl_hw,
+						 &cl_hw->phy_data_info.data->pwr_tables,
+						 power_db->curr_offset);
+	}
+}
+
+void cl_power_tables_update(struct cl_hw *cl_hw, struct cl_pwr_tables *pwr_tables)
+{
+	bool is_24g = cl_band_is_24g(cl_hw);
+	bool is_6g = cl_band_is_6g(cl_hw);
+	u8 min_bw_idx_limit_he = 0;
+	u8 min_bw_idx_limit_vht = 0;
+
+	memset(pwr_tables, 0, sizeof(struct cl_pwr_tables));
+
+	if (is_24g)
+		cl_power_tables_update_cck(cl_hw, pwr_tables);
+
+	cl_power_tables_update_ofdm(cl_hw, pwr_tables);
+
+	if (!is_6g)
+		min_bw_idx_limit_vht = cl_power_tables_update_ht_vht(cl_hw, pwr_tables);
+
+	min_bw_idx_limit_he = cl_power_tables_update_he(cl_hw, pwr_tables);
+
+	cl_hw->new_tx_power = 0;
+
+	cl_power_tables_update_vns(cl_hw, pwr_tables, min_bw_idx_limit_vht, min_bw_idx_limit_he);
+
+	cl_power_control_apply_percentage(cl_hw);
+}
+
+static s32 cl_power_get_max_cck(struct cl_hw *cl_hw)
+{
+	struct cl_pwr_tables *pwr_tables = &cl_hw->phy_data_info.data->pwr_tables;
+	u8 mcs = 0;
+	u8 tx_ant = cl_power_tx_ant(cl_hw, WRS_MODE_CCK);
+	s32 ant_gain_q1 = cl_power_antenna_gain_q1(cl_hw);
+	s32 arr_gain_q1 = cl_power_array_gain_q1(cl_hw, tx_ant);
+	s32 total_pwr_q1 = 0;
+	s32 max_pwr_q1 = 0;
+
+	for (mcs = 0; mcs < WRS_MCS_MAX_CCK; mcs++) {
+		total_pwr_q1 = pwr_tables->ant_pwr_cck[mcs] + ant_gain_q1 + arr_gain_q1;
+
+		if (total_pwr_q1 > max_pwr_q1)
+			max_pwr_q1 = total_pwr_q1;
+	}
+
+	return max_pwr_q1;
+}
+
+static s32 cl_power_get_max_ofdm(struct cl_hw *cl_hw)
+{
+	struct cl_pwr_tables *pwr_tables = &cl_hw->phy_data_info.data->pwr_tables;
+	u8 mcs = 0;
+	u8 tx_ant = cl_power_tx_ant(cl_hw, WRS_MODE_OFDM);
+	s32 ant_gain_q1 = cl_power_antenna_gain_q1(cl_hw);
+	s32 arr_gain_q1 = cl_power_array_gain_q1(cl_hw, tx_ant);
+	s32 total_pwr_q1 = 0;
+	s32 max_pwr_q1 = 0;
+
+	for (mcs = 0; mcs < WRS_MCS_MAX_OFDM; mcs++) {
+		total_pwr_q1 = pwr_tables->ant_pwr_ofdm[mcs] + ant_gain_q1 + arr_gain_q1;
+
+		if (total_pwr_q1 > max_pwr_q1)
+			max_pwr_q1 = total_pwr_q1;
+	}
+
+	return max_pwr_q1;
+}
+
+static s32 cl_power_get_max_ht_vht(struct cl_hw *cl_hw)
+{
+	struct cl_pwr_tables *pwr_tables = &cl_hw->phy_data_info.data->pwr_tables;
+	u8 tx_ant = cl_power_tx_ant(cl_hw, WRS_MODE_VHT);
+	u8 mcs = 0;
+	u8 bw = 0;
+	u8 bf = 0;
+	s32 ant_gain_q1 = cl_power_antenna_gain_q1(cl_hw);
+	s32 arr_gain_q1 = cl_power_array_gain_q1(cl_hw, tx_ant);
+	s32 total_pwr_q1 = 0;
+	s32 max_pwr_q1 = 0;
+
+	for (bw = 0; bw < CHNL_BW_MAX; bw++) {
+		for (mcs = 0; mcs < WRS_MCS_MAX_VHT; mcs++) {
+			for (bf = 0; bf < PWR_TBL_VHT_BF_SIZE; bf++) {
+				total_pwr_q1 = pwr_tables->ant_pwr_ht_vht[bw][mcs][bf] +
+					ant_gain_q1 + arr_gain_q1;
+
+				if (total_pwr_q1 > max_pwr_q1)
+					max_pwr_q1 = total_pwr_q1;
+			}
+		}
+	}
+
+	return max_pwr_q1;
+}
+
+static s32 cl_power_get_max_he(struct cl_hw *cl_hw)
+{
+	struct cl_pwr_tables *pwr_tables = &cl_hw->phy_data_info.data->pwr_tables;
+	u8 tx_ant = cl_power_tx_ant(cl_hw, WRS_MODE_HE);
+	u8 mcs = 0;
+	u8 bw = 0;
+	u8 bf = 0;
+	s32 ant_gain_q1 = cl_power_antenna_gain_q1(cl_hw);
+	s32 arr_gain_q1 = cl_power_array_gain_q1(cl_hw, tx_ant);
+	s32 total_pwr_q1 = 0;
+	s32 max_pwr_q1 = 0;
+
+	for (bw = 0; bw < CHNL_BW_MAX; bw++) {
+		for (mcs = 0; mcs < WRS_MCS_MAX_HE; mcs++) {
+			for (bf = 0; bf < PWR_TBL_HE_BF_SIZE; bf++) {
+				total_pwr_q1 = pwr_tables->ant_pwr_he[bw][mcs][bf] +
+					ant_gain_q1 + arr_gain_q1;
+
+				if (total_pwr_q1 > max_pwr_q1)
+					max_pwr_q1 = total_pwr_q1;
+			}
+		}
+	}
+
+	return max_pwr_q1;
+}
+
+s32 cl_power_get_max(struct cl_hw *cl_hw)
+{
+	bool is_24g = cl_band_is_24g(cl_hw);
+	bool is_6g = cl_band_is_6g(cl_hw);
+	s32 max_pwr_cck_q1 = is_24g ? cl_power_get_max_cck(cl_hw) : S32_MIN;
+	s32 max_pwr_ofdm_q1 = cl_power_get_max_ofdm(cl_hw);
+	s32 max_pwr_ht_vht_q1 = !is_6g ? cl_power_get_max_ht_vht(cl_hw) : S32_MIN;
+	s32 max_pwr_he_q1 = cl_power_get_max_he(cl_hw);
+	s32 max_pwr_q1 = 0;
+
+	max_pwr_q1 = max(max_pwr_q1, max_pwr_cck_q1);
+	max_pwr_q1 = max(max_pwr_q1, max_pwr_ofdm_q1);
+	max_pwr_q1 = max(max_pwr_q1, max_pwr_ht_vht_q1);
+	max_pwr_q1 = max(max_pwr_q1, max_pwr_he_q1);
+
+	return (max_pwr_q1 >> 1);
+}
+
-- 
2.36.1


  parent reply	other threads:[~2022-05-24 11:40 UTC|newest]

Thread overview: 125+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-05-24 11:33 [RFC v2 00/96] wireless: cl8k driver for Celeno IEEE 802.11ax devices viktor.barna
2022-05-24 11:33 ` [RFC v2 01/96] celeno: add Kconfig viktor.barna
2022-05-24 11:33 ` [RFC v2 02/96] celeno: add Makefile viktor.barna
2022-05-24 11:33 ` [RFC v2 03/96] cl8k: add Kconfig viktor.barna
2022-05-26 18:18   ` Johannes Berg
2022-05-27  6:09     ` Kalle Valo
2022-07-11 23:04       ` Viktor Barna
2022-07-13  7:32   ` Kalle Valo
2022-05-24 11:33 ` [RFC v2 04/96] cl8k: add Makefile viktor.barna
2022-05-26 18:24   ` Johannes Berg
2022-07-13  7:39   ` Kalle Valo
2022-05-24 11:33 ` [RFC v2 05/96] cl8k: add ampdu.c viktor.barna
2022-05-26 18:19   ` Johannes Berg
2022-05-26 18:22   ` Johannes Berg
2022-05-24 11:33 ` [RFC v2 06/96] cl8k: add ampdu.h viktor.barna
2022-05-24 11:33 ` [RFC v2 07/96] cl8k: add bf.c viktor.barna
2022-05-24 17:24   ` Jeff Johnson
2022-05-24 11:33 ` [RFC v2 08/96] cl8k: add bf.h viktor.barna
2022-05-24 11:33 ` [RFC v2 09/96] cl8k: add calib.c viktor.barna
2022-05-24 11:33 ` [RFC v2 10/96] cl8k: add calib.h viktor.barna
2022-05-24 11:33 ` [RFC v2 11/96] cl8k: add channel.c viktor.barna
2022-05-24 11:33 ` [RFC v2 12/96] cl8k: add channel.h viktor.barna
2022-05-24 11:33 ` [RFC v2 13/96] cl8k: add chip.c viktor.barna
2022-05-24 11:33 ` [RFC v2 14/96] cl8k: add chip.h viktor.barna
2022-05-24 11:33 ` [RFC v2 15/96] cl8k: add config.c viktor.barna
2022-05-24 11:33 ` [RFC v2 16/96] cl8k: add config.h viktor.barna
2022-05-25 18:31   ` Jeff Johnson
2022-05-24 11:33 ` [RFC v2 17/96] cl8k: add debug.c viktor.barna
2022-05-24 11:33 ` [RFC v2 18/96] cl8k: add debug.h viktor.barna
2022-05-24 11:33 ` [RFC v2 19/96] cl8k: add def.h viktor.barna
2022-05-25 18:39   ` Jeff Johnson
2022-05-24 11:33 ` [RFC v2 20/96] cl8k: add dfs.c viktor.barna
2022-05-24 11:33 ` [RFC v2 21/96] cl8k: add dfs.h viktor.barna
2022-05-24 11:33 ` [RFC v2 22/96] cl8k: add dsp.c viktor.barna
2022-05-24 11:33 ` [RFC v2 23/96] cl8k: add dsp.h viktor.barna
2022-05-24 11:33 ` [RFC v2 24/96] cl8k: add e2p.c viktor.barna
2022-05-24 11:33 ` [RFC v2 25/96] cl8k: add e2p.h viktor.barna
2022-05-24 11:33 ` [RFC v2 26/96] cl8k: add eeprom.h viktor.barna
2022-05-24 11:33 ` [RFC v2 27/96] cl8k: add ela.c viktor.barna
2022-05-24 11:33 ` [RFC v2 28/96] cl8k: add ela.h viktor.barna
2022-05-24 11:33 ` [RFC v2 29/96] cl8k: add enhanced_tim.c viktor.barna
2022-05-24 11:33 ` [RFC v2 30/96] cl8k: add enhanced_tim.h viktor.barna
2022-05-24 11:33 ` [RFC v2 31/96] cl8k: add fw.c viktor.barna
2022-05-24 11:33 ` [RFC v2 32/96] cl8k: add fw.h viktor.barna
2022-05-25 18:58   ` Jeff Johnson
2022-05-24 11:33 ` [RFC v2 33/96] cl8k: add hw.c viktor.barna
2022-05-24 11:34 ` [RFC v2 34/96] cl8k: add hw.h viktor.barna
2022-05-24 11:34 ` [RFC v2 35/96] cl8k: add ipc_shared.h viktor.barna
2022-05-24 11:34 ` [RFC v2 36/96] cl8k: add key.c viktor.barna
2022-05-26 19:38   ` Johannes Berg
2022-07-11 23:10     ` Viktor Barna
2022-05-24 11:34 ` [RFC v2 37/96] cl8k: add key.h viktor.barna
2022-05-24 11:34 ` [RFC v2 38/96] cl8k: add mac80211.c viktor.barna
2022-05-26 19:49   ` Johannes Berg
2022-07-11 23:13     ` Viktor Barna
2022-05-24 11:34 ` [RFC v2 39/96] cl8k: add mac80211.h viktor.barna
2022-05-26 19:52   ` Johannes Berg
2022-05-24 11:34 ` [RFC v2 40/96] cl8k: add mac_addr.c viktor.barna
2022-05-26 22:31   ` Jeff Johnson
2022-05-24 11:34 ` [RFC v2 41/96] cl8k: add mac_addr.h viktor.barna
2022-05-24 11:34 ` [RFC v2 42/96] cl8k: add main.c viktor.barna
2022-05-26 23:01   ` Jeff Johnson
2022-05-24 11:34 ` [RFC v2 43/96] cl8k: add main.h viktor.barna
2022-05-24 11:34 ` [RFC v2 44/96] cl8k: add maintenance.c viktor.barna
2022-05-24 11:34 ` [RFC v2 45/96] cl8k: add maintenance.h viktor.barna
2022-05-24 11:34 ` [RFC v2 46/96] cl8k: add motion_sense.c viktor.barna
2022-05-24 11:34 ` [RFC v2 47/96] cl8k: add motion_sense.h viktor.barna
2022-05-24 11:34 ` [RFC v2 48/96] cl8k: add pci.c viktor.barna
2022-05-24 11:34 ` [RFC v2 49/96] cl8k: add pci.h viktor.barna
2022-05-24 11:34 ` [RFC v2 50/96] cl8k: add phy.c viktor.barna
2022-06-01  0:27   ` Jeff Johnson
2022-07-11 23:16     ` Viktor Barna
2022-05-24 11:34 ` [RFC v2 51/96] cl8k: add phy.h viktor.barna
2022-05-24 11:34 ` [RFC v2 52/96] cl8k: add platform.c viktor.barna
2022-05-24 11:34 ` [RFC v2 53/96] cl8k: add platform.h viktor.barna
2022-05-24 11:34 ` viktor.barna [this message]
2022-05-24 11:34 ` [RFC v2 55/96] cl8k: add power.h viktor.barna
2022-05-24 11:34 ` [RFC v2 56/96] cl8k: add radio.c viktor.barna
2022-05-24 11:34 ` [RFC v2 57/96] cl8k: add radio.h viktor.barna
2022-05-24 11:34 ` [RFC v2 58/96] cl8k: add rates.c viktor.barna
2022-05-24 11:34 ` [RFC v2 59/96] cl8k: add rates.h viktor.barna
2022-05-26 19:54   ` Johannes Berg
2022-07-11 23:17     ` Viktor Barna
2022-07-12  7:17       ` Johannes Berg
2022-05-24 11:34 ` [RFC v2 60/96] cl8k: add recovery.c viktor.barna
2022-05-24 11:34 ` [RFC v2 61/96] cl8k: add recovery.h viktor.barna
2022-05-24 11:34 ` [RFC v2 62/96] cl8k: add regdom.c viktor.barna
2022-05-24 11:34 ` [RFC v2 63/96] cl8k: add regdom.h viktor.barna
2022-05-24 11:34 ` [RFC v2 64/96] cl8k: add reg/reg_access.h viktor.barna
2022-05-24 11:34 ` [RFC v2 65/96] cl8k: add reg/reg_defs.h viktor.barna
2022-05-24 11:34 ` [RFC v2 66/96] cl8k: add rfic.c viktor.barna
2022-05-24 11:34 ` [RFC v2 67/96] cl8k: add rfic.h viktor.barna
2022-06-02 20:40   ` Jeff Johnson
2022-07-11 23:18     ` Viktor Barna
2022-05-24 11:34 ` [RFC v2 68/96] cl8k: add rx.c viktor.barna
2022-05-24 11:34 ` [RFC v2 69/96] cl8k: add rx.h viktor.barna
2022-05-24 11:34 ` [RFC v2 70/96] cl8k: add scan.c viktor.barna
2022-05-24 11:34 ` [RFC v2 71/96] cl8k: add scan.h viktor.barna
2022-05-24 11:34 ` [RFC v2 72/96] cl8k: add sounding.c viktor.barna
2022-05-24 11:34 ` [RFC v2 73/96] cl8k: add sounding.h viktor.barna
2022-05-24 11:34 ` [RFC v2 74/96] cl8k: add sta.c viktor.barna
2022-05-24 11:34 ` [RFC v2 75/96] cl8k: add sta.h viktor.barna
2022-05-24 11:34 ` [RFC v2 76/96] cl8k: add stats.c viktor.barna
2022-06-02 20:59   ` Jeff Johnson
2022-07-11 23:20     ` Viktor Barna
2022-05-24 11:34 ` [RFC v2 77/96] cl8k: add stats.h viktor.barna
2022-05-24 11:34 ` [RFC v2 78/96] cl8k: add tcv.c viktor.barna
2022-05-24 11:34 ` [RFC v2 79/96] cl8k: add tcv.h viktor.barna
2022-05-24 11:34 ` [RFC v2 80/96] cl8k: add temperature.c viktor.barna
2022-05-24 11:34 ` [RFC v2 81/96] cl8k: add temperature.h viktor.barna
2022-05-24 11:34 ` [RFC v2 82/96] cl8k: add traffic.c viktor.barna
2022-05-24 11:34 ` [RFC v2 83/96] cl8k: add traffic.h viktor.barna
2022-05-24 11:34 ` [RFC v2 84/96] cl8k: add tx.c viktor.barna
2022-05-24 11:34 ` [RFC v2 85/96] cl8k: add tx.h viktor.barna
2022-05-24 11:34 ` [RFC v2 86/96] cl8k: add utils.c viktor.barna
2022-05-24 11:34 ` [RFC v2 87/96] cl8k: add utils.h viktor.barna
2022-05-24 11:34 ` [RFC v2 88/96] cl8k: add version.c viktor.barna
2022-05-24 11:34 ` [RFC v2 89/96] cl8k: add version.h viktor.barna
2022-05-24 11:34 ` [RFC v2 90/96] cl8k: add vif.c viktor.barna
2022-05-24 11:34 ` [RFC v2 91/96] cl8k: add vif.h viktor.barna
2022-05-24 11:34 ` [RFC v2 92/96] cl8k: add vns.c viktor.barna
2022-05-24 11:34 ` [RFC v2 93/96] cl8k: add vns.h viktor.barna
2022-05-24 11:35 ` [RFC v2 94/96] cl8k: add wrs.c viktor.barna
2022-05-24 11:35 ` [RFC v2 95/96] cl8k: add wrs.h viktor.barna
2022-05-24 11:35 ` [RFC v2 96/96] wireless: add Celeno vendor viktor.barna

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220524113502.1094459-55-viktor.barna@celeno.com \
    --to=viktor.barna@celeno.com \
    --cc=aviad.brikman@celeno.com \
    --cc=davem@davemloft.net \
    --cc=eliav.farber@gmail.com \
    --cc=kuba@kernel.org \
    --cc=kvalo@codeaurora.org \
    --cc=linux-wireless@vger.kernel.org \
    --cc=maksym.kokhan@celeno.com \
    --cc=oleksandr.savchenko@celeno.com \
    --cc=shay.bar@celeno.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.