[PATCH v4 2/2] wifi: ath12k: Add Support to Calculate and Display TPC Values

Aditya Kumar Singh aditya.kumar.singh at oss.qualcomm.com
Mon Jan 27 21:08:41 PST 2025


On 1/27/25 13:02, Roopni Devanathan wrote:
> From: Sowmiya Sree Elavalagan <quic_ssreeela at quicinc.com>
> 
> Transmit Power Control(TPC) stats should display per chain TPC value per
> radio. Add debugfs support to read and display TPC stats type and TPC
> stats. Take power values for each preamble type, rate and NSS combination
> from a particular index from each power arrays based on number of chains,
> NSS, modes, MCS and tx beamforming enabled/disabled parameters. Minimum
> of the values taken from reg power table, rates and Conformance Test
> Limit(CTL) array table should give the TPC which is in 0.25 dBm steps.
> 
> Sample Output:
> -------------
> echo 1 > /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/tpc_stats_type
> cat /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/tpc_stats
> 
> *************** TPC config **************
> * powers are in 0.25 dBm steps
> reg domain-22           chan freq-5955
> power limit-126         max reg-domain Power-252
> No.of tx chain-4        No.of rates-1164
> **************** SU WITH TXBF ****************
>                                  TPC values for Active chains
> Rate idx Preamble Rate code     1-Chain 2-Chain 3-Chain 4-Chain
> 4        OFDM    0x000          39      15      1       -9
> 5        OFDM    0x001          39      15      1       -9
> .....
> 12       HT20    0x200          40      16      2       -8
> 13       HT20    0x201          40      16      2       -8
> .....
> 44       HT40    0x200          88      88      88      88
> 45       HT40    0x201          88      88      88      88
> .....
> 76       VHT20   0x300          40      16      2       -8
> 77       VHT20   0x301          40      16      2       -8
> .....
> 172      VHT40   0x300          88      88      88      88
> 173      VHT40   0x301          88      88      88      88
> .....
> 412      HE20    0x400          88      88      88      88
> 413      HE20    0x401          88      88      88      88
> .....
> 508      HE40    0x400          76      76      76      76
> 509      HE40    0x401          76      76      76      76
> .....
> 748      EHT20   0x50e          88      88      88      88
> 749      EHT20   0x50f          88      88      88      88
> .....
> 812      EHT40   0x50e          88      88      88      88
> 813      EHT40   0x50f          88      88      88      88
> .....
> 
> Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.1.1-00214-QCAHKSWPL_SILICONZ-1
> 
> Signed-off-by: Sowmiya Sree Elavalagan <quic_ssreeela at quicinc.com>
> Co-developed-by: Ramya Gnanasekar <quic_rgnanase at quicinc.com>
> Signed-off-by: Ramya Gnanasekar <quic_rgnanase at quicinc.com>
> Co-developed-by: Roopni Devanathan <quic_rdevanat at quicinc.com>
> Signed-off-by: Roopni Devanathan <quic_rdevanat at quicinc.com>
> ---
>   drivers/net/wireless/ath/ath12k/debugfs.c | 619 ++++++++++++++++++++++
>   drivers/net/wireless/ath/ath12k/debugfs.h |  86 ++-
>   drivers/net/wireless/ath/ath12k/wmi.h     |   1 +
>   3 files changed, 703 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c b/drivers/net/wireless/ath/ath12k/debugfs.c
> index 8ddcae01a2a1..51c479e0a26f 100644
> --- a/drivers/net/wireless/ath/ath12k/debugfs.c
> +++ b/drivers/net/wireless/ath/ath12k/debugfs.c

[..]

> +
> +static s16 ath12k_tpc_get_rate(struct ath12k *ar,
> +			       struct wmi_tpc_stats_info *tpc_stats,
> +			       u32 rate_idx, u32 num_chains, u32 rate_code,
> +			       enum wmi_tpc_pream_bw pream_bw,
> +			       enum wmi_halphy_ctrl_path_stats_id type,
> +			       u32 eht_rate_idx)
> +{
> +	u32 tot_nss, tot_modes, txbf_on_off, index_offset1, index_offset2, index_offset3;
> +	u8 chain_idx, stm_idx, num_streams;
> +	bool is_mu, txbf_enabled = 0;
> +	s8 rates_ctl_min, tpc_ctl;
> +	s16 rates, tpc, reg_pwr;
> +	u16 rate1, rate2;
> +	int mode, ret;
> +
> +	num_streams = 1 + ATH12K_HW_NSS(rate_code);
> +	chain_idx = num_chains - 1;
> +	stm_idx = num_streams - 1;
> +	mode = -1;
> +
> +	ret = ath12k_get_tpc_ctl_mode_idx(tpc_stats, pream_bw, &mode);
> +	if (ret) {
> +		ath12k_warn(ar->ab, "Invalid mode index received\n");
> +		tpc = TPC_INVAL;
> +		goto out;
> +	}
> +
> +	if (num_chains < num_streams) {
> +		tpc = TPC_INVAL;
> +		goto out;
> +	}
> +
> +	if (le32_to_cpu(tpc_stats->tpc_config.num_tx_chain) <= 1) {
> +		tpc = TPC_INVAL;
> +		goto out;
> +	}
> +
> +	if (type == WMI_HALPHY_PDEV_TX_SUTXBF_STATS ||
> +	    type == WMI_HALPHY_PDEV_TX_MUTXBF_STATS)
> +		txbf_enabled = 1;
> +
> +	if (type == WMI_HALPHY_PDEV_TX_MU_STATS ||
> +	    type == WMI_HALPHY_PDEV_TX_MUTXBF_STATS) {
> +		is_mu = true;
> +	} else {
> +		is_mu = false;
> +	}
> +
> +	/* Below is the min calculation of ctl array, rates array and
> +	 * regulator power table. tpc is minimum of all 3
> +	 */
> +	if (pream_bw >= WMI_TPC_PREAM_EHT20 && pream_bw <= WMI_TPC_PREAM_EHT320) {
> +		rate2 = tpc_stats->rates_array2.rate_array[eht_rate_idx];
> +		if (is_mu)
> +			rates = u32_get_bits(rate2, ATH12K_TPC_RATE_ARRAY_MU);
> +		else
> +			rates = u32_get_bits(rate2, ATH12K_TPC_RATE_ARRAY_SU);
> +	} else {
> +		rate1 = tpc_stats->rates_array1.rate_array[rate_idx];
> +		if (is_mu)
> +			rates = u32_get_bits(rate1, ATH12K_TPC_RATE_ARRAY_MU);
> +		else
> +			rates = u32_get_bits(rate1, ATH12K_TPC_RATE_ARRAY_SU);
> +	}
> +
> +	if (tpc_stats->tlvs_rcvd & WMI_TPC_CTL_PWR_ARRAY) {
> +		tot_nss = le32_to_cpu(tpc_stats->ctl_array.tpc_ctl_pwr.d1);
> +		tot_modes = le32_to_cpu(tpc_stats->ctl_array.tpc_ctl_pwr.d2);
> +		txbf_on_off = le32_to_cpu(tpc_stats->ctl_array.tpc_ctl_pwr.d3);
> +		index_offset1 = txbf_on_off * tot_modes * tot_nss;
> +		index_offset2 = tot_modes * tot_nss;
> +		index_offset3 = tot_nss;
> +
> +		tpc_ctl = *(tpc_stats->ctl_array.ctl_pwr_table +
> +			    chain_idx * index_offset1 + txbf_enabled * index_offset2
> +			    + mode * index_offset3 + stm_idx);
> +	} else {
> +		tpc_ctl = TPC_MAX;
> +		ath12k_info(ar->ab,
> +			    "ctl array for tpc stats not received from fw\n");

Extra noise? Can this be under some debug mask? Is this fatal enough to 
show to user always?

> +	}
> +
> +	rates_ctl_min = min_t(s16, rates, tpc_ctl);
> +
> +	reg_pwr = tpc_stats->max_reg_allowed_power.reg_pwr_array[chain_idx];
> +
> +	if (reg_pwr < 0)
> +		reg_pwr = TPC_INVAL;
> +
> +	tpc = min_t(s16, rates_ctl_min, reg_pwr);
> +
> +	/* MODULATION_LIMIT is the maximum power limit,tpc should not exceed
> +	 * modulation limit even if min tpc of all three array is greater
> +	 * modulation limit
> +	 */
> +	tpc = min_t(s16, tpc, MODULATION_LIMIT);
> +
> +out:
> +	return tpc;
> +}
> +
> +static u16 ath12k_get_ratecode(u16 pream_idx, u16 nss, u16 mcs_rate)
> +{
> +	u16 mode_type = ~0;
> +
> +	/* Below assignments are just for printing purpose only */
> +	switch (pream_idx) {
> +	case WMI_TPC_PREAM_CCK:
> +		mode_type = WMI_RATE_PREAMBLE_CCK;
> +		break;
> +	case WMI_TPC_PREAM_OFDM:
> +		mode_type = WMI_RATE_PREAMBLE_OFDM;
> +		break;
> +	case WMI_TPC_PREAM_HT20:
> +	case WMI_TPC_PREAM_HT40:
> +		mode_type = WMI_RATE_PREAMBLE_HT;
> +		break;
> +	case WMI_TPC_PREAM_VHT20:
> +	case WMI_TPC_PREAM_VHT40:
> +	case WMI_TPC_PREAM_VHT80:
> +	case WMI_TPC_PREAM_VHT160:
> +		mode_type = WMI_RATE_PREAMBLE_VHT;
> +		break;
> +	case WMI_TPC_PREAM_HE20:
> +	case WMI_TPC_PREAM_HE40:
> +	case WMI_TPC_PREAM_HE80:
> +	case WMI_TPC_PREAM_HE160:
> +		mode_type = WMI_RATE_PREAMBLE_HE;
> +		break;
> +	case WMI_TPC_PREAM_EHT20:
> +	case WMI_TPC_PREAM_EHT40:
> +	case WMI_TPC_PREAM_EHT60:
> +	case WMI_TPC_PREAM_EHT80:
> +	case WMI_TPC_PREAM_EHT120:
> +	case WMI_TPC_PREAM_EHT140:
> +	case WMI_TPC_PREAM_EHT160:
> +	case WMI_TPC_PREAM_EHT200:
> +	case WMI_TPC_PREAM_EHT240:
> +	case WMI_TPC_PREAM_EHT280:
> +	case WMI_TPC_PREAM_EHT320:
> +		mode_type = WMI_RATE_PREAMBLE_EHT;
> +		if (mcs_rate == 0 || mcs_rate == 1)
> +			mcs_rate += 14;
> +		else
> +			mcs_rate -= 2;
> +		break;
> +	default:
> +		return mode_type;
> +	}
> +	return ((mode_type << 8) | ((nss & 0x7) << 5) | (mcs_rate & 0x1F));
> +}
> +
> +static bool ath12k_he_supports_extra_mcs(struct ath12k *ar, int freq)
> +{
> +	struct ath12k_pdev_cap *cap = &ar->pdev->cap;
> +	struct ath12k_band_cap *cap_band;
> +	bool extra_mcs_supported;
> +
> +	if (freq <= ATH12K_2GHZ_MAX_FREQUENCY)
> +		cap_band = &cap->band[NL80211_BAND_2GHZ];
> +	else if (freq <= ATH12K_5GHZ_MAX_FREQUENCY)
> +		cap_band = &cap->band[NL80211_BAND_5GHZ];
> +	else
> +		cap_band = &cap->band[NL80211_BAND_6GHZ];
> +
> +	extra_mcs_supported = u32_get_bits(cap_band->he_cap_info[1],
> +					   HE_EXTRA_MCS_SUPPORT);
> +	return extra_mcs_supported;
> +}
> +
> +static int ath12k_tpc_fill_pream(struct ath12k *ar, char *buf, int buf_len, int len,
> +				 enum wmi_tpc_pream_bw pream_bw, u32 max_rix,
> +				 int max_nss, int max_rates, int pream_type,
> +				 enum wmi_halphy_ctrl_path_stats_id tpc_type,
> +				 int rate_idx, int eht_rate_idx)
> +{
> +	struct wmi_tpc_stats_info *tpc_stats = ar->debug.tpc_stats;
> +	int nss, rates, chains;
> +	u8 active_tx_chains;
> +	u16 rate_code;
> +	s16 tpc;
> +
> +	static const char *const pream_str[] = {
> +		[WMI_TPC_PREAM_CCK]     = "CCK",
> +		[WMI_TPC_PREAM_OFDM]    = "OFDM",
> +		[WMI_TPC_PREAM_HT20]    = "HT20",
> +		[WMI_TPC_PREAM_HT40]    = "HT40",
> +		[WMI_TPC_PREAM_VHT20]   = "VHT20",
> +		[WMI_TPC_PREAM_VHT40]   = "VHT40",
> +		[WMI_TPC_PREAM_VHT80]   = "VHT80",
> +		[WMI_TPC_PREAM_VHT160]  = "VHT160",
> +		[WMI_TPC_PREAM_HE20]    = "HE20",
> +		[WMI_TPC_PREAM_HE40]    = "HE40",
> +		[WMI_TPC_PREAM_HE80]    = "HE80",
> +		[WMI_TPC_PREAM_HE160]   = "HE160",
> +		[WMI_TPC_PREAM_EHT20]   = "EHT20",
> +

why this extra line break?

> +		[WMI_TPC_PREAM_EHT40]   = "EHT40",
> +		[WMI_TPC_PREAM_EHT60]   = "EHT60",
> +		[WMI_TPC_PREAM_EHT80]   = "EHT80",
> +		[WMI_TPC_PREAM_EHT120]   = "EHT120",
> +		[WMI_TPC_PREAM_EHT140]   = "EHT140",
> +		[WMI_TPC_PREAM_EHT160]   = "EHT160",
> +		[WMI_TPC_PREAM_EHT200]   = "EHT200",
> +		[WMI_TPC_PREAM_EHT240]   = "EHT240",
> +		[WMI_TPC_PREAM_EHT280]   = "EHT280",
> +		[WMI_TPC_PREAM_EHT320]   = "EHT320"};
> +
> +	active_tx_chains = ar->num_tx_chains;
> +
> +	for (nss = 0; nss < max_nss; nss++) {
> +		for (rates = 0; rates < max_rates; rates++, rate_idx++, max_rix++) {
> +			/* FW send extra MCS(10&11) for VHT and HE rates,
> +			 *  this is not used. Hence skipping it here
> +			 */
> +			if (pream_type == WMI_RATE_PREAMBLE_VHT &&
> +			    rates > ATH12K_VHT_MCS_MAX)
> +				continue;
> +
> +			if (pream_type == WMI_RATE_PREAMBLE_HE &&
> +			    rates > ATH12K_HE_MCS_MAX)
> +				continue;
> +
> +			if (pream_type == WMI_RATE_PREAMBLE_EHT &&
> +			    rates > ATH12K_EHT_MCS_MAX)
> +				continue;
> +
> +			rate_code = ath12k_get_ratecode(pream_bw, nss, rates);
> +			len += scnprintf(buf + len, buf_len - len,
> +				 "%d\t %s\t 0x%03x\t", max_rix,
> +				 pream_str[pream_bw], rate_code);

Align with previous ( ?

> +
> +			for (chains = 0; chains < active_tx_chains; chains++) {
> +				if (nss > chains) {
> +					len += scnprintf(buf + len,
> +							 buf_len - len,
> +							 "\t%s", "NA");
> +				} else {
> +					tpc = ath12k_tpc_get_rate(ar, tpc_stats, rate_idx,
> +								  chains + 1, rate_code,
> +								  pream_bw, tpc_type,
> +								  eht_rate_idx);
> +
> +					if (tpc == TPC_INVAL) {
> +						len += scnprintf(buf + len,
> +						       buf_len - len, "\tNA");

Align with previous ( ? It will fit 90 chars width.

> +					} else {
> +						len += scnprintf(buf + len,
> +						       buf_len - len, "\t%d",
> +						       tpc);


Align with previous ( ? It will fit 90 chars width.

> +					}
> +				}
> +			}
> +			len += scnprintf(buf + len, buf_len - len, "\n");
> +
> +			if (pream_type == WMI_RATE_PREAMBLE_EHT)
> +				/*For fetching the next eht rates pwr from rates array2*/
> +				++eht_rate_idx;
> +		}
> +	}
> +
> +	return len;
> +}
> +
> +static int ath12k_tpc_stats_print(struct ath12k *ar,
> +				  struct wmi_tpc_stats_info *tpc_stats,
> +				  char *buf, size_t len,
> +				  enum wmi_halphy_ctrl_path_stats_id type)
> +{
> +	u32 eht_idx = 0, pream_idx = 0, rate_pream_idx = 0, total_rates = 0, max_rix = 0;
> +	u32 chan_freq, num_tx_chain, caps, i, j = 1;
> +	size_t buf_len = ATH12K_TPC_STATS_BUF_SIZE;
> +	u8 nss, active_tx_chains;
> +	bool he_ext_mcs;
> +	static const char *const type_str[WMI_HALPHY_PDEV_TX_STATS_MAX] = {
> +		[WMI_HALPHY_PDEV_TX_SU_STATS]		= "SU",
> +		[WMI_HALPHY_PDEV_TX_SUTXBF_STATS]	= "SU WITH TXBF",
> +		[WMI_HALPHY_PDEV_TX_MU_STATS]		= "MU",
> +		[WMI_HALPHY_PDEV_TX_MUTXBF_STATS]	= "MU WITH TXBF"};
> +
> +	u8 max_rates[WMI_TPC_PREAM_MAX] = {

Others are const declared. Why not this?

> +		[WMI_TPC_PREAM_CCK]     = ATH12K_CCK_RATES,
> +		[WMI_TPC_PREAM_OFDM]    = ATH12K_OFDM_RATES,
> +		[WMI_TPC_PREAM_HT20]    = ATH12K_HT_RATES,
> +		[WMI_TPC_PREAM_HT40]    = ATH12K_HT_RATES,
> +		[WMI_TPC_PREAM_VHT20]   = ATH12K_VHT_RATES,
> +		[WMI_TPC_PREAM_VHT40]   = ATH12K_VHT_RATES,
> +		[WMI_TPC_PREAM_VHT80]   = ATH12K_VHT_RATES,
> +		[WMI_TPC_PREAM_VHT160]  = ATH12K_VHT_RATES,
> +		[WMI_TPC_PREAM_HE20]    = ATH12K_HE_RATES,
> +		[WMI_TPC_PREAM_HE40]    = ATH12K_HE_RATES,
> +		[WMI_TPC_PREAM_HE80]    = ATH12K_HE_RATES,
> +		[WMI_TPC_PREAM_HE160]   = ATH12K_HE_RATES,
> +		[WMI_TPC_PREAM_EHT20]   = ATH12K_EHT_RATES,
> +		[WMI_TPC_PREAM_EHT40]   = ATH12K_EHT_RATES,
> +		[WMI_TPC_PREAM_EHT60]   = ATH12K_EHT_RATES,
> +		[WMI_TPC_PREAM_EHT80]   = ATH12K_EHT_RATES,
> +		[WMI_TPC_PREAM_EHT120]  = ATH12K_EHT_RATES,
> +		[WMI_TPC_PREAM_EHT140]  = ATH12K_EHT_RATES,
> +		[WMI_TPC_PREAM_EHT160]  = ATH12K_EHT_RATES,
> +		[WMI_TPC_PREAM_EHT200]  = ATH12K_EHT_RATES,
> +		[WMI_TPC_PREAM_EHT240]  = ATH12K_EHT_RATES,
> +		[WMI_TPC_PREAM_EHT280]  = ATH12K_EHT_RATES,
> +		[WMI_TPC_PREAM_EHT320]  = ATH12K_EHT_RATES};
> +	static const u8 max_nss[WMI_TPC_PREAM_MAX] = {
> +		[WMI_TPC_PREAM_CCK]     = ATH12K_NSS_1,
> +		[WMI_TPC_PREAM_OFDM]    = ATH12K_NSS_1,
> +		[WMI_TPC_PREAM_HT20]    = ATH12K_NSS_4,
> +		[WMI_TPC_PREAM_HT40]    = ATH12K_NSS_4,
> +		[WMI_TPC_PREAM_VHT20]   = ATH12K_NSS_8,
> +		[WMI_TPC_PREAM_VHT40]   = ATH12K_NSS_8,
> +		[WMI_TPC_PREAM_VHT80]   = ATH12K_NSS_8,
> +		[WMI_TPC_PREAM_VHT160]  = ATH12K_NSS_4,
> +		[WMI_TPC_PREAM_HE20]    = ATH12K_NSS_8,
> +		[WMI_TPC_PREAM_HE40]    = ATH12K_NSS_8,
> +		[WMI_TPC_PREAM_HE80]    = ATH12K_NSS_8,
> +		[WMI_TPC_PREAM_HE160]   = ATH12K_NSS_4,
> +		[WMI_TPC_PREAM_EHT20]   = ATH12K_NSS_4,
> +		[WMI_TPC_PREAM_EHT40]   = ATH12K_NSS_4,
> +		[WMI_TPC_PREAM_EHT60]   = ATH12K_NSS_4,
> +		[WMI_TPC_PREAM_EHT80]   = ATH12K_NSS_4,
> +		[WMI_TPC_PREAM_EHT120]  = ATH12K_NSS_4,
> +		[WMI_TPC_PREAM_EHT140]  = ATH12K_NSS_4,
> +		[WMI_TPC_PREAM_EHT160]  = ATH12K_NSS_4,
> +		[WMI_TPC_PREAM_EHT200]  = ATH12K_NSS_4,
> +		[WMI_TPC_PREAM_EHT240]  = ATH12K_NSS_4,
> +		[WMI_TPC_PREAM_EHT280]  = ATH12K_NSS_4,
> +		[WMI_TPC_PREAM_EHT320]  = ATH12K_NSS_4};
> +
> +	u16 rate_idx[WMI_TPC_PREAM_MAX] = {0},
> +	    eht_rate_idx[WMI_TPC_PREAM_MAX] = {0};

nit: prefer {} over {0} ?

> +	static const u8 pream_type[WMI_TPC_PREAM_MAX] = {
> +		[WMI_TPC_PREAM_CCK]     = WMI_RATE_PREAMBLE_CCK,
> +		[WMI_TPC_PREAM_OFDM]    = WMI_RATE_PREAMBLE_OFDM,
> +		[WMI_TPC_PREAM_HT20]    = WMI_RATE_PREAMBLE_HT,
> +		[WMI_TPC_PREAM_HT40]    = WMI_RATE_PREAMBLE_HT,
> +		[WMI_TPC_PREAM_VHT20]   = WMI_RATE_PREAMBLE_VHT,
> +		[WMI_TPC_PREAM_VHT40]   = WMI_RATE_PREAMBLE_VHT,
> +		[WMI_TPC_PREAM_VHT80]   = WMI_RATE_PREAMBLE_VHT,
> +		[WMI_TPC_PREAM_VHT160]  = WMI_RATE_PREAMBLE_VHT,
> +		[WMI_TPC_PREAM_HE20]    = WMI_RATE_PREAMBLE_HE,
> +		[WMI_TPC_PREAM_HE40]    = WMI_RATE_PREAMBLE_HE,
> +		[WMI_TPC_PREAM_HE80]    = WMI_RATE_PREAMBLE_HE,
> +		[WMI_TPC_PREAM_HE160]   = WMI_RATE_PREAMBLE_HE,
> +		[WMI_TPC_PREAM_EHT20]   = WMI_RATE_PREAMBLE_EHT,
> +		[WMI_TPC_PREAM_EHT40]   = WMI_RATE_PREAMBLE_EHT,
> +		[WMI_TPC_PREAM_EHT60]   = WMI_RATE_PREAMBLE_EHT,
> +		[WMI_TPC_PREAM_EHT80]   = WMI_RATE_PREAMBLE_EHT,
> +		[WMI_TPC_PREAM_EHT120]  = WMI_RATE_PREAMBLE_EHT,
> +		[WMI_TPC_PREAM_EHT140]  = WMI_RATE_PREAMBLE_EHT,
> +		[WMI_TPC_PREAM_EHT160]  = WMI_RATE_PREAMBLE_EHT,
> +		[WMI_TPC_PREAM_EHT200]  = WMI_RATE_PREAMBLE_EHT,
> +		[WMI_TPC_PREAM_EHT240]  = WMI_RATE_PREAMBLE_EHT,
> +		[WMI_TPC_PREAM_EHT280]  = WMI_RATE_PREAMBLE_EHT,
> +		[WMI_TPC_PREAM_EHT320]  = WMI_RATE_PREAMBLE_EHT};
> +
> +	chan_freq = le32_to_cpu(tpc_stats->tpc_config.chan_freq);
> +	num_tx_chain = le32_to_cpu(tpc_stats->tpc_config.num_tx_chain);
> +	caps = le32_to_cpu(tpc_stats->tpc_config.caps);
> +
> +	active_tx_chains = ar->num_tx_chains;
> +	he_ext_mcs = ath12k_he_supports_extra_mcs(ar, chan_freq);
> +
> +	/* mcs 12&13 is sent by FW for certain HWs in rate array, skipping it as
> +	 * it is not supported
> +	 */
> +	if (he_ext_mcs) {
> +		for (i = WMI_TPC_PREAM_HE20; i <= WMI_TPC_PREAM_HE160;  ++i)

nit: extra space before ++i?

> +			max_rates[i] = ATH12K_HE_RATES;
> +	}
> +
> +	if (type == WMI_HALPHY_PDEV_TX_MU_STATS ||
> +	    type == WMI_HALPHY_PDEV_TX_MUTXBF_STATS) {
> +		pream_idx = WMI_TPC_PREAM_VHT20;
> +
> +		for (i = WMI_TPC_PREAM_CCK; i <= WMI_TPC_PREAM_HT40; ++i)
> +			max_rix += max_nss[i] * max_rates[i];
> +	}
> +	/* Enumerate all the rate indices */
> +	for (i = rate_pream_idx + 1 ; i < WMI_TPC_PREAM_MAX; i++) {

nit: extra space before ; ?

> +		nss = (max_nss[i - 1] < num_tx_chain ?
> +		       max_nss[i - 1] : num_tx_chain);
> +
> +		rate_idx[i] = rate_idx[i - 1] + max_rates[i - 1] * nss;
> +
> +		if (pream_type[i] == WMI_RATE_PREAMBLE_EHT) {
> +			eht_rate_idx[j] = eht_rate_idx[j - 1] + max_rates[i] * nss;
> +			++j;
> +		}
> +	}
> +
> +	for (i = 0 ; i < WMI_TPC_PREAM_MAX; i++) {

nit: extra space before ; ?

> +		nss = (max_nss[i] < num_tx_chain ?
> +		       max_nss[i] : num_tx_chain);
> +		total_rates += max_rates[i] * nss;
> +	}
> +
> +	len += scnprintf(buf + len, buf_len - len,
> +			 "No.of rates-%d\n", total_rates);
> +
> +	len += scnprintf(buf + len, buf_len - len,
> +			 "**************** %s ****************\n",
> +			 type_str[type]);
> +	len += scnprintf(buf + len, buf_len - len,
> +			 "\t\t\t\tTPC values for Active chains\n");
> +	len += scnprintf(buf + len, buf_len - len,
> +			 "Rate idx Preamble Rate code");
> +
> +	for (i = 1; i <= active_tx_chains; ++i) {
> +		len += scnprintf(buf + len, buf_len - len,
> +				 "\t%d-Chain", i);
> +	}
> +
> +	len += scnprintf(buf + len, buf_len - len, "\n");
> +	for (i = pream_idx; i < WMI_TPC_PREAM_MAX; i++) {
> +		if (chan_freq <= 2483) {
> +			if (i == WMI_TPC_PREAM_VHT80 ||
> +			    i == WMI_TPC_PREAM_VHT160 ||
> +			    i == WMI_TPC_PREAM_HE80 ||
> +			    i == WMI_TPC_PREAM_HE160 ||
> +			    (i >= WMI_TPC_PREAM_EHT60 &&
> +			     i <= WMI_TPC_PREAM_EHT320)) {
> +				max_rix += max_nss[i] * max_rates[i];
> +				continue;
> +			}
> +		} else {
> +			if (i == WMI_TPC_PREAM_CCK) {
> +				max_rix += max_rates[i];
> +				continue;
> +			}
> +		}
> +
> +		nss = (max_nss[i] < ar->num_tx_chains ? max_nss[i] : ar->num_tx_chains);
> +
> +		if (!(caps &
> +		    (1 << ATH12K_TPC_STATS_SUPPORT_BE_PUNC))) {
> +			if (i == WMI_TPC_PREAM_EHT60 || i == WMI_TPC_PREAM_EHT120 ||
> +			    i == WMI_TPC_PREAM_EHT140 || i == WMI_TPC_PREAM_EHT200 ||
> +			    i == WMI_TPC_PREAM_EHT240 || i == WMI_TPC_PREAM_EHT280) {
> +				max_rix += max_nss[i] * max_rates[i];
> +				continue;
> +			}
> +		}
> +
> +		len = ath12k_tpc_fill_pream(ar, buf, buf_len, len, i, max_rix, nss,
> +					    max_rates[i], pream_type[i],
> +					    type, rate_idx[i], eht_rate_idx[eht_idx]);
> +
> +		if (pream_type[i] == WMI_RATE_PREAMBLE_EHT)
> +			/*For fetch the next index eht rates from rates array2*/
> +			++eht_idx;
> +
> +		max_rix += max_nss[i] * max_rates[i];
> +	}
> +	return len;
> +}
> +
> +static void ath12k_tpc_stats_fill(struct ath12k *ar,
> +				  struct wmi_tpc_stats_info *tpc_stats,
> +				  char *buf)
> +{
> +	size_t buf_len = ATH12K_TPC_STATS_BUF_SIZE;
> +	struct wmi_tpc_config_params *tpc;
> +	size_t len = 0;
> +
> +	spin_lock_bh(&ar->data_lock);
> +	if (!tpc_stats) {

This check can be outside lock?

> +		ath12k_warn(ar->ab, "failed to find tpc stats\n");
> +		goto unlock;
> +	}
> +
> +	tpc = &tpc_stats->tpc_config;
> +	len += scnprintf(buf + len, buf_len - len, "\n");
> +	len += scnprintf(buf + len, buf_len - len,
> +			 "*************** TPC config **************\n");
> +	len += scnprintf(buf + len, buf_len - len,
> +			 "* powers are in 0.25 dBm steps\n");
> +	len += scnprintf(buf + len, buf_len - len,
> +			 "reg domain-%d\t\tchan freq-%d\n",
> +			 tpc->reg_domain, tpc->chan_freq);
> +	len += scnprintf(buf + len, buf_len - len,
> +			 "power limit-%d\t\tmax reg-domain Power-%d\n",
> +			 le32_to_cpu(tpc->twice_max_reg_power) / 2, tpc->power_limit);
> +	len += scnprintf(buf + len, buf_len - len,
> +			 "No.of tx chain-%d\t",
> +			 ar->num_tx_chains);
> +
> +	ath12k_tpc_stats_print(ar, tpc_stats, buf, len,
> +			       ar->debug.tpc_stats_type);
> +
> +unlock:
> +	spin_unlock_bh(&ar->data_lock);
> +}
> +
>   static int ath12k_open_tpc_stats(struct inode *inode, struct file *file)
>   {
>   	struct ath12k *ar = inode->i_private;
> @@ -91,6 +700,7 @@ static int ath12k_open_tpc_stats(struct inode *inode, struct file *file)
>   		return -ETIMEDOUT;
>   	}
>   
> +	ath12k_tpc_stats_fill(ar, ar->debug.tpc_stats, buf);
>   	file->private_data = no_free_ptr(buf);
>   
>   	spin_lock_bh(&ar->data_lock);
> @@ -125,6 +735,12 @@ static const struct file_operations fops_tpc_stats = {
>   	.llseek = default_llseek,
>   };
>   
> +static const struct file_operations fops_tpc_stats_type = {
> +	.write = ath12k_write_tpc_stats_type,
> +	.open = simple_open,
> +	.llseek = default_llseek,
> +};
> +
>   void ath12k_debugfs_soc_create(struct ath12k_base *ab)
>   {
>   	bool dput_needed;
> @@ -563,6 +1179,9 @@ void ath12k_debugfs_register(struct ath12k *ar)
>   
>   	debugfs_create_file("tpc_stats", 0400, ar->debug.debugfs_pdev, ar,
>   			    &fops_tpc_stats);
> +	debugfs_create_file("tpc_stats_type", 0200, ar->debug.debugfs_pdev,
> +			    ar, &fops_tpc_stats_type);
> +	init_completion(&ar->debug.tpc_complete);
>   
>   	ath12k_debugfs_htt_stats_register(ar);
>   	ath12k_debugfs_fw_stats_register(ar);
> diff --git a/drivers/net/wireless/ath/ath12k/debugfs.h b/drivers/net/wireless/ath/ath12k/debugfs.h
> index 25b5219ffe49..60c08964d883 100644
> --- a/drivers/net/wireless/ath/ath12k/debugfs.h
> +++ b/drivers/net/wireless/ath/ath12k/debugfs.h
> @@ -16,11 +16,91 @@ void ath12k_debugfs_fw_stats_process(struct ath12k *ar,
>   				     struct ath12k_fw_stats *stats);
>   void ath12k_debugfs_fw_stats_reset(struct ath12k *ar);
>   
> -#define TPC_STATS_WAIT_TIME		(1 * HZ)
> -#define TPC_STATS_TOT_ROW		700
> -#define TPC_STATS_TOT_COLUMN		100
> +#define ATH12K_CCK_RATES			4
> +#define ATH12K_OFDM_RATES			8
> +#define ATH12K_HT_RATES				8
> +#define ATH12K_VHT_RATES			12
> +#define ATH12K_HE_RATES				12
> +#define ATH12K_HE_RATES_WITH_EXTRA_MCS		14
> +#define ATH12K_EHT_RATES			16
> +#define HE_EXTRA_MCS_SUPPORT			GENMASK(31, 16)
> +#define ATH12K_NSS_1				1
> +#define ATH12K_NSS_4				4
> +#define ATH12K_NSS_8				8
> +#define ATH12K_HW_NSS(_rcode)			(((_rcode) >> 5) & 0x7)
> +#define TPC_STATS_WAIT_TIME			(1 * HZ)
> +#define MAX_TPC_PREAM_STR_LEN			7
> +#define TPC_INVAL				-128
> +#define TPC_MAX					127
> +#define TPC_STATS_WAIT_TIME			(1 * HZ)
> +#define TPC_STATS_TOT_ROW			700
> +#define TPC_STATS_TOT_COLUMN			100
> +#define MODULATION_LIMIT			126
> +
> +#define ATH12K_2GHZ_MAX_FREQUENCY		2495
> +#define ATH12K_5GHZ_MAX_FREQUENCY		5920

Not the correct place to have max freq macro. May be reg.h ?

> +
>   #define ATH12K_TPC_STATS_BUF_SIZE	(TPC_STATS_TOT_ROW * TPC_STATS_TOT_COLUMN)
>   
> +enum wmi_tpc_pream_bw {
> +	WMI_TPC_PREAM_CCK,
> +	WMI_TPC_PREAM_OFDM,
> +	WMI_TPC_PREAM_HT20,
> +	WMI_TPC_PREAM_HT40,
> +	WMI_TPC_PREAM_VHT20,
> +	WMI_TPC_PREAM_VHT40,
> +	WMI_TPC_PREAM_VHT80,
> +	WMI_TPC_PREAM_VHT160,
> +	WMI_TPC_PREAM_HE20,
> +	WMI_TPC_PREAM_HE40,
> +	WMI_TPC_PREAM_HE80,
> +	WMI_TPC_PREAM_HE160,
> +	WMI_TPC_PREAM_EHT20,
> +	WMI_TPC_PREAM_EHT40,
> +	WMI_TPC_PREAM_EHT60,
> +	WMI_TPC_PREAM_EHT80,
> +	WMI_TPC_PREAM_EHT120,
> +	WMI_TPC_PREAM_EHT140,
> +	WMI_TPC_PREAM_EHT160,
> +	WMI_TPC_PREAM_EHT200,
> +	WMI_TPC_PREAM_EHT240,
> +	WMI_TPC_PREAM_EHT280,
> +	WMI_TPC_PREAM_EHT320,
> +	WMI_TPC_PREAM_MAX
> +};
> +
> +enum ath12k_debug_tpc_stats_ctl_mode {
> +	ATH12K_TPC_STATS_CTL_MODE_LEGACY_5GHZ_6GHZ,
> +	ATH12K_TPC_STATS_CTL_MODE_HT_VHT20_5GHZ_6GHZ,
> +	ATH12K_TPC_STATS_CTL_MODE_HE_EHT20_5GHZ_6GHZ,
> +	ATH12K_TPC_STATS_CTL_MODE_HT_VHT40_5GHZ_6GHZ,
> +	ATH12K_TPC_STATS_CTL_MODE_HE_EHT40_5GHZ_6GHZ,
> +	ATH12K_TPC_STATS_CTL_MODE_VHT80_5GHZ_6GHZ,
> +	ATH12K_TPC_STATS_CTL_MODE_HE_EHT80_5GHZ_6GHZ,
> +	ATH12K_TPC_STATS_CTL_MODE_VHT160_5GHZ_6GHZ,
> +	ATH12K_TPC_STATS_CTL_MODE_HE_EHT160_5GHZ_6GHZ,
> +	ATH12K_TPC_STATS_CTL_MODE_HE_EHT320_5GHZ_6GHZ,
> +	ATH12K_TPC_STATS_CTL_MODE_CCK_2GHZ,
> +	ATH12K_TPC_STATS_CTL_MODE_LEGACY_2GHZ,
> +	ATH12K_TPC_STATS_CTL_MODE_HT20_2GHZ,
> +	ATH12K_TPC_STATS_CTL_MODE_HT40_2GHZ,
> +
> +	ATH12K_TPC_STATS_CTL_MODE_EHT80_SU_PUNC20 = 23,
> +	ATH12K_TPC_STATS_CTL_MODE_EHT160_SU_PUNC20,
> +	ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC40,
> +	ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC80,
> +	ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC120
> +};
> +
> +enum ath12k_debug_tpc_stats_support_modes {
> +	ATH12K_TPC_STATS_SUPPORT_160 = 0,
> +	ATH12K_TPC_STATS_SUPPORT_320,
> +	ATH12K_TPC_STATS_SUPPORT_AX,
> +	ATH12K_TPC_STATS_SUPPORT_AX_EXTRA_MCS,
> +	ATH12K_TPC_STATS_SUPPORT_BE,
> +	ATH12K_TPC_STATS_SUPPORT_BE_PUNC,
> +};
> +
>   #else
>   static inline void ath12k_debugfs_soc_create(struct ath12k_base *ab)
>   {
> diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h
> index f88f6a6bb15e..c474c46130b9 100644
> --- a/drivers/net/wireless/ath/ath12k/wmi.h
> +++ b/drivers/net/wireless/ath/ath12k/wmi.h
> @@ -4640,6 +4640,7 @@ enum wmi_rate_preamble {
>   	WMI_RATE_PREAMBLE_HT,
>   	WMI_RATE_PREAMBLE_VHT,
>   	WMI_RATE_PREAMBLE_HE,
> +	WMI_RATE_PREAMBLE_EHT,
>   };
>   
>   /**


-- 
Aditya



More information about the ath12k mailing list