162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2021-2022 Intel Corporation
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <net/mac80211.h>
762306a36Sopenharmony_ci#include "fw/api/rs.h"
862306a36Sopenharmony_ci#include "iwl-drv.h"
962306a36Sopenharmony_ci#include "iwl-config.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define IWL_DECLARE_RATE_INFO(r) \
1262306a36Sopenharmony_ci	[IWL_RATE_##r##M_INDEX] = IWL_RATE_##r##M_PLCP
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/*
1562306a36Sopenharmony_ci * Translate from fw_rate_index (IWL_RATE_XXM_INDEX) to PLCP
1662306a36Sopenharmony_ci * */
1762306a36Sopenharmony_cistatic const u8 fw_rate_idx_to_plcp[IWL_RATE_COUNT] = {
1862306a36Sopenharmony_ci	IWL_DECLARE_RATE_INFO(1),
1962306a36Sopenharmony_ci	IWL_DECLARE_RATE_INFO(2),
2062306a36Sopenharmony_ci	IWL_DECLARE_RATE_INFO(5),
2162306a36Sopenharmony_ci	IWL_DECLARE_RATE_INFO(11),
2262306a36Sopenharmony_ci	IWL_DECLARE_RATE_INFO(6),
2362306a36Sopenharmony_ci	IWL_DECLARE_RATE_INFO(9),
2462306a36Sopenharmony_ci	IWL_DECLARE_RATE_INFO(12),
2562306a36Sopenharmony_ci	IWL_DECLARE_RATE_INFO(18),
2662306a36Sopenharmony_ci	IWL_DECLARE_RATE_INFO(24),
2762306a36Sopenharmony_ci	IWL_DECLARE_RATE_INFO(36),
2862306a36Sopenharmony_ci	IWL_DECLARE_RATE_INFO(48),
2962306a36Sopenharmony_ci	IWL_DECLARE_RATE_INFO(54),
3062306a36Sopenharmony_ci};
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/* mbps, mcs */
3362306a36Sopenharmony_cistatic const struct iwl_rate_mcs_info rate_mcs[IWL_RATE_COUNT] = {
3462306a36Sopenharmony_ci	{  "1", "BPSK DSSS"},
3562306a36Sopenharmony_ci	{  "2", "QPSK DSSS"},
3662306a36Sopenharmony_ci	{"5.5", "BPSK CCK"},
3762306a36Sopenharmony_ci	{ "11", "QPSK CCK"},
3862306a36Sopenharmony_ci	{  "6", "BPSK 1/2"},
3962306a36Sopenharmony_ci	{  "9", "BPSK 1/2"},
4062306a36Sopenharmony_ci	{ "12", "QPSK 1/2"},
4162306a36Sopenharmony_ci	{ "18", "QPSK 3/4"},
4262306a36Sopenharmony_ci	{ "24", "16QAM 1/2"},
4362306a36Sopenharmony_ci	{ "36", "16QAM 3/4"},
4462306a36Sopenharmony_ci	{ "48", "64QAM 2/3"},
4562306a36Sopenharmony_ci	{ "54", "64QAM 3/4"},
4662306a36Sopenharmony_ci	{ "60", "64QAM 5/6"},
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic const char * const ant_name[] = {
5062306a36Sopenharmony_ci	[ANT_NONE] = "None",
5162306a36Sopenharmony_ci	[ANT_A]    = "A",
5262306a36Sopenharmony_ci	[ANT_B]    = "B",
5362306a36Sopenharmony_ci	[ANT_AB]   = "AB",
5462306a36Sopenharmony_ci};
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic const char * const pretty_bw[] = {
5762306a36Sopenharmony_ci	"20Mhz",
5862306a36Sopenharmony_ci	"40Mhz",
5962306a36Sopenharmony_ci	"80Mhz",
6062306a36Sopenharmony_ci	"160 Mhz",
6162306a36Sopenharmony_ci	"320Mhz",
6262306a36Sopenharmony_ci};
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ciu8 iwl_fw_rate_idx_to_plcp(int idx)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	return fw_rate_idx_to_plcp[idx];
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_fw_rate_idx_to_plcp);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ciconst struct iwl_rate_mcs_info *iwl_rate_mcs(int idx)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	return &rate_mcs[idx];
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_rate_mcs);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ciconst char *iwl_rs_pretty_ant(u8 ant)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	if (ant >= ARRAY_SIZE(ant_name))
7962306a36Sopenharmony_ci		return "UNKNOWN";
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	return ant_name[ant];
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_rs_pretty_ant);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ciconst char *iwl_rs_pretty_bw(int bw)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	if (bw >= ARRAY_SIZE(pretty_bw))
8862306a36Sopenharmony_ci		return "unknown bw";
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	return pretty_bw[bw];
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_rs_pretty_bw);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic u32 iwl_legacy_rate_to_fw_idx(u32 rate_n_flags)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	int rate = rate_n_flags & RATE_LEGACY_RATE_MSK_V1;
9762306a36Sopenharmony_ci	int idx;
9862306a36Sopenharmony_ci	bool ofdm = !(rate_n_flags & RATE_MCS_CCK_MSK_V1);
9962306a36Sopenharmony_ci	int offset = ofdm ? IWL_FIRST_OFDM_RATE : 0;
10062306a36Sopenharmony_ci	int last = ofdm ? IWL_RATE_COUNT_LEGACY : IWL_FIRST_OFDM_RATE;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	for (idx = offset; idx < last; idx++)
10362306a36Sopenharmony_ci		if (iwl_fw_rate_idx_to_plcp(idx) == rate)
10462306a36Sopenharmony_ci			return idx - offset;
10562306a36Sopenharmony_ci	return IWL_RATE_INVALID;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ciu32 iwl_new_rate_from_v1(u32 rate_v1)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	u32 rate_v2 = 0;
11162306a36Sopenharmony_ci	u32 dup = 0;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	if (rate_v1 == 0)
11462306a36Sopenharmony_ci		return rate_v1;
11562306a36Sopenharmony_ci	/* convert rate */
11662306a36Sopenharmony_ci	if (rate_v1 & RATE_MCS_HT_MSK_V1) {
11762306a36Sopenharmony_ci		u32 nss = 0;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci		rate_v2 |= RATE_MCS_HT_MSK;
12062306a36Sopenharmony_ci		rate_v2 |=
12162306a36Sopenharmony_ci			rate_v1 & RATE_HT_MCS_RATE_CODE_MSK_V1;
12262306a36Sopenharmony_ci		nss = (rate_v1 & RATE_HT_MCS_MIMO2_MSK) >>
12362306a36Sopenharmony_ci			RATE_HT_MCS_NSS_POS_V1;
12462306a36Sopenharmony_ci		rate_v2 |= nss << RATE_MCS_NSS_POS;
12562306a36Sopenharmony_ci	} else if (rate_v1 & RATE_MCS_VHT_MSK_V1 ||
12662306a36Sopenharmony_ci		   rate_v1 & RATE_MCS_HE_MSK_V1) {
12762306a36Sopenharmony_ci		rate_v2 |= rate_v1 & RATE_VHT_MCS_RATE_CODE_MSK;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci		rate_v2 |= rate_v1 & RATE_MCS_NSS_MSK;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci		if (rate_v1 & RATE_MCS_HE_MSK_V1) {
13262306a36Sopenharmony_ci			u32 he_type_bits = rate_v1 & RATE_MCS_HE_TYPE_MSK_V1;
13362306a36Sopenharmony_ci			u32 he_type = he_type_bits >> RATE_MCS_HE_TYPE_POS_V1;
13462306a36Sopenharmony_ci			u32 he_106t = (rate_v1 & RATE_MCS_HE_106T_MSK_V1) >>
13562306a36Sopenharmony_ci				RATE_MCS_HE_106T_POS_V1;
13662306a36Sopenharmony_ci			u32 he_gi_ltf = (rate_v1 & RATE_MCS_HE_GI_LTF_MSK_V1) >>
13762306a36Sopenharmony_ci				RATE_MCS_HE_GI_LTF_POS;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci			if ((he_type_bits == RATE_MCS_HE_TYPE_SU ||
14062306a36Sopenharmony_ci			     he_type_bits == RATE_MCS_HE_TYPE_EXT_SU) &&
14162306a36Sopenharmony_ci			    he_gi_ltf == RATE_MCS_HE_SU_4_LTF)
14262306a36Sopenharmony_ci				/* the new rate have an additional bit to
14362306a36Sopenharmony_ci				 * represent the value 4 rather then using SGI
14462306a36Sopenharmony_ci				 * bit for this purpose - as it was done in the old
14562306a36Sopenharmony_ci				 * rate */
14662306a36Sopenharmony_ci				he_gi_ltf += (rate_v1 & RATE_MCS_SGI_MSK_V1) >>
14762306a36Sopenharmony_ci					RATE_MCS_SGI_POS_V1;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci			rate_v2 |= he_gi_ltf << RATE_MCS_HE_GI_LTF_POS;
15062306a36Sopenharmony_ci			rate_v2 |= he_type << RATE_MCS_HE_TYPE_POS;
15162306a36Sopenharmony_ci			rate_v2 |= he_106t << RATE_MCS_HE_106T_POS;
15262306a36Sopenharmony_ci			rate_v2 |= rate_v1 & RATE_HE_DUAL_CARRIER_MODE_MSK;
15362306a36Sopenharmony_ci			rate_v2 |= RATE_MCS_HE_MSK;
15462306a36Sopenharmony_ci		} else {
15562306a36Sopenharmony_ci			rate_v2 |= RATE_MCS_VHT_MSK;
15662306a36Sopenharmony_ci		}
15762306a36Sopenharmony_ci	/* if legacy format */
15862306a36Sopenharmony_ci	} else {
15962306a36Sopenharmony_ci		u32 legacy_rate = iwl_legacy_rate_to_fw_idx(rate_v1);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci		if (WARN_ON_ONCE(legacy_rate == IWL_RATE_INVALID))
16262306a36Sopenharmony_ci			legacy_rate = (rate_v1 & RATE_MCS_CCK_MSK_V1) ?
16362306a36Sopenharmony_ci				IWL_FIRST_CCK_RATE : IWL_FIRST_OFDM_RATE;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci		rate_v2 |= legacy_rate;
16662306a36Sopenharmony_ci		if (!(rate_v1 & RATE_MCS_CCK_MSK_V1))
16762306a36Sopenharmony_ci			rate_v2 |= RATE_MCS_LEGACY_OFDM_MSK;
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	/* convert flags */
17162306a36Sopenharmony_ci	if (rate_v1 & RATE_MCS_LDPC_MSK_V1)
17262306a36Sopenharmony_ci		rate_v2 |= RATE_MCS_LDPC_MSK;
17362306a36Sopenharmony_ci	rate_v2 |= (rate_v1 & RATE_MCS_CHAN_WIDTH_MSK_V1) |
17462306a36Sopenharmony_ci		(rate_v1 & RATE_MCS_ANT_AB_MSK) |
17562306a36Sopenharmony_ci		(rate_v1 & RATE_MCS_STBC_MSK) |
17662306a36Sopenharmony_ci		(rate_v1 & RATE_MCS_BF_MSK);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	dup = (rate_v1 & RATE_MCS_DUP_MSK_V1) >> RATE_MCS_DUP_POS_V1;
17962306a36Sopenharmony_ci	if (dup) {
18062306a36Sopenharmony_ci		rate_v2 |= RATE_MCS_DUP_MSK;
18162306a36Sopenharmony_ci		rate_v2 |= dup << RATE_MCS_CHAN_WIDTH_POS;
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if ((!(rate_v1 & RATE_MCS_HE_MSK_V1)) &&
18562306a36Sopenharmony_ci	    (rate_v1 & RATE_MCS_SGI_MSK_V1))
18662306a36Sopenharmony_ci		rate_v2 |= RATE_MCS_SGI_MSK;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	return rate_v2;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_new_rate_from_v1);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ciint rs_pretty_print_rate(char *buf, int bufsz, const u32 rate)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	char *type;
19562306a36Sopenharmony_ci	u8 mcs = 0, nss = 0;
19662306a36Sopenharmony_ci	u8 ant = (rate & RATE_MCS_ANT_AB_MSK) >> RATE_MCS_ANT_POS;
19762306a36Sopenharmony_ci	u32 bw = (rate & RATE_MCS_CHAN_WIDTH_MSK) >>
19862306a36Sopenharmony_ci		RATE_MCS_CHAN_WIDTH_POS;
19962306a36Sopenharmony_ci	u32 format = rate & RATE_MCS_MOD_TYPE_MSK;
20062306a36Sopenharmony_ci	bool sgi;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if (format == RATE_MCS_CCK_MSK ||
20362306a36Sopenharmony_ci	    format == RATE_MCS_LEGACY_OFDM_MSK) {
20462306a36Sopenharmony_ci		int legacy_rate = rate & RATE_LEGACY_RATE_MSK;
20562306a36Sopenharmony_ci		int index = format == RATE_MCS_CCK_MSK ?
20662306a36Sopenharmony_ci			legacy_rate :
20762306a36Sopenharmony_ci			legacy_rate + IWL_FIRST_OFDM_RATE;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci		return scnprintf(buf, bufsz, "Legacy | ANT: %s Rate: %s Mbps",
21062306a36Sopenharmony_ci				 iwl_rs_pretty_ant(ant),
21162306a36Sopenharmony_ci				 index == IWL_RATE_INVALID ? "BAD" :
21262306a36Sopenharmony_ci				 iwl_rate_mcs(index)->mbps);
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (format ==  RATE_MCS_VHT_MSK)
21662306a36Sopenharmony_ci		type = "VHT";
21762306a36Sopenharmony_ci	else if (format ==  RATE_MCS_HT_MSK)
21862306a36Sopenharmony_ci		type = "HT";
21962306a36Sopenharmony_ci	else if (format == RATE_MCS_HE_MSK)
22062306a36Sopenharmony_ci		type = "HE";
22162306a36Sopenharmony_ci	else if (format == RATE_MCS_EHT_MSK)
22262306a36Sopenharmony_ci		type = "EHT";
22362306a36Sopenharmony_ci	else
22462306a36Sopenharmony_ci		type = "Unknown"; /* shouldn't happen */
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	mcs = format == RATE_MCS_HT_MSK ?
22762306a36Sopenharmony_ci		RATE_HT_MCS_INDEX(rate) :
22862306a36Sopenharmony_ci		rate & RATE_MCS_CODE_MSK;
22962306a36Sopenharmony_ci	nss = ((rate & RATE_MCS_NSS_MSK)
23062306a36Sopenharmony_ci	       >> RATE_MCS_NSS_POS) + 1;
23162306a36Sopenharmony_ci	sgi = format == RATE_MCS_HE_MSK ?
23262306a36Sopenharmony_ci		iwl_he_is_sgi(rate) :
23362306a36Sopenharmony_ci		rate & RATE_MCS_SGI_MSK;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	return scnprintf(buf, bufsz,
23662306a36Sopenharmony_ci			 "0x%x: %s | ANT: %s BW: %s MCS: %d NSS: %d %s%s%s%s%s",
23762306a36Sopenharmony_ci			 rate, type, iwl_rs_pretty_ant(ant), iwl_rs_pretty_bw(bw), mcs, nss,
23862306a36Sopenharmony_ci			 (sgi) ? "SGI " : "NGI ",
23962306a36Sopenharmony_ci			 (rate & RATE_MCS_STBC_MSK) ? "STBC " : "",
24062306a36Sopenharmony_ci			 (rate & RATE_MCS_LDPC_MSK) ? "LDPC " : "",
24162306a36Sopenharmony_ci			 (rate & RATE_HE_DUAL_CARRIER_MODE_MSK) ? "DCM " : "",
24262306a36Sopenharmony_ci			 (rate & RATE_MCS_BF_MSK) ? "BF " : "");
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(rs_pretty_print_rate);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cibool iwl_he_is_sgi(u32 rate_n_flags)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	u32 type = rate_n_flags & RATE_MCS_HE_TYPE_MSK;
24962306a36Sopenharmony_ci	u32 ltf_gi = rate_n_flags & RATE_MCS_HE_GI_LTF_MSK;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (type == RATE_MCS_HE_TYPE_SU ||
25262306a36Sopenharmony_ci	    type == RATE_MCS_HE_TYPE_EXT_SU)
25362306a36Sopenharmony_ci		return ltf_gi == RATE_MCS_HE_SU_4_LTF_08_GI;
25462306a36Sopenharmony_ci	return false;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_he_is_sgi);
25762306a36Sopenharmony_ci
258