blob: b056189ccd406a0dcb798ac05c3048fe5233e910 [file] [log] [blame]
From c3e9680c5a1387067019b361390df5a150eb232b Mon Sep 17 00:00:00 2001
From: Allen Ye <allen.ye@mediatek.com>
Date: Fri, 24 Mar 2023 23:35:30 +0800
Subject: [PATCH 026/116] mtk: wifi: mt76: mt7996: add txpower support
Add single sku and default enable sku.
mtk: wifi: mt76: mt7996: Porting wifi6 txpower fix to eagle
Refactor txpower flow.
1. Fix wrong bbp CR address
2. Ignore RegDB power limit when we have single sku table. And dump more informaiton in debugfs.
3. Refactor get_txpower ops flow, we only consider CCK and OFDM power value as maximum.
4. Remove sku_disable due to SQC is over and default enable both sku tables.
Fix wrong power value when user set limit close to path table limit.
CR-Id: WCNCR00259302
Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
Signed-off-by: Allen Ye <allen.ye@mediatek.com>
---
eeprom.c | 54 ++++++-
mt76.h | 9 ++
mt76_connac_mcu.c | 2 +-
mt7996/eeprom.c | 34 ++++
mt7996/eeprom.h | 42 +++++
mt7996/init.c | 16 +-
mt7996/main.c | 15 ++
mt7996/mcu.c | 69 ++++++++-
mt7996/mcu.h | 2 +
mt7996/mt7996.h | 4 +
mt7996/mtk_debugfs.c | 362 +++++++++++++++++++++++++++++++++++++++++++
mt7996/mtk_mcu.c | 23 +++
mt7996/mtk_mcu.h | 92 +++++++++++
mt7996/regs.h | 27 ++--
14 files changed, 724 insertions(+), 27 deletions(-)
diff --git a/eeprom.c b/eeprom.c
index a0047d791..11efe2937 100644
--- a/eeprom.c
+++ b/eeprom.c
@@ -305,9 +305,10 @@ mt76_apply_array_limit(s8 *pwr, size_t pwr_len, const __be32 *data,
static void
mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num,
const __be32 *data, size_t len, s8 target_power,
- s8 nss_delta, s8 *max_power)
+ s8 nss_delta)
{
int i, cur;
+ s8 max_power = -128;
if (!data)
return;
@@ -319,7 +320,7 @@ mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num,
break;
mt76_apply_array_limit(pwr + pwr_len * i, pwr_len, data + 1,
- target_power, nss_delta, max_power);
+ target_power, nss_delta, &max_power);
if (--cur > 0)
continue;
@@ -335,6 +336,7 @@ mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num,
s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
struct ieee80211_channel *chan,
struct mt76_power_limits *dest,
+ struct mt76_power_path_limits *dest_path,
s8 target_power)
{
struct mt76_dev *dev = phy->dev;
@@ -342,16 +344,20 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
const __be32 *val;
char name[16];
u32 mcs_rates = dev->drv->mcs_rates;
- u32 ru_rates = ARRAY_SIZE(dest->ru[0]);
char band;
size_t len;
- s8 max_power = 0;
+ s8 max_power = -127;
+ s8 max_power_backoff = -127;
s8 txs_delta;
+ int n_chains = hweight16(phy->chainmask);
+ s8 target_power_combine = target_power + mt76_tx_power_nss_delta(n_chains);
if (!mcs_rates)
- mcs_rates = 10;
+ mcs_rates = 12;
memset(dest, target_power, sizeof(*dest));
+ if (dest_path != NULL)
+ memset(dest_path, 0, sizeof(*dest_path));
if (!IS_ENABLED(CONFIG_OF))
return target_power;
@@ -397,12 +403,44 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
val = mt76_get_of_array(np, "rates-mcs", &len, mcs_rates + 1);
mt76_apply_multi_array_limit(dest->mcs[0], ARRAY_SIZE(dest->mcs[0]),
ARRAY_SIZE(dest->mcs), val, len,
- target_power, txs_delta, &max_power);
+ target_power, txs_delta);
- val = mt76_get_of_array(np, "rates-ru", &len, ru_rates + 1);
+ val = mt76_get_of_array(np, "rates-ru", &len, ARRAY_SIZE(dest->ru[0]) + 1);
mt76_apply_multi_array_limit(dest->ru[0], ARRAY_SIZE(dest->ru[0]),
ARRAY_SIZE(dest->ru), val, len,
- target_power, txs_delta, &max_power);
+ target_power, txs_delta);
+
+ val = mt76_get_of_array(np, "rates-eht", &len, ARRAY_SIZE(dest->eht[0]) + 1);
+ mt76_apply_multi_array_limit(dest->eht[0], ARRAY_SIZE(dest->eht[0]),
+ ARRAY_SIZE(dest->eht), val, len,
+ target_power, txs_delta);
+
+ if (dest_path == NULL)
+ return max_power;
+
+ max_power_backoff = max_power;
+
+ val = mt76_get_of_array(np, "paths-cck", &len, ARRAY_SIZE(dest_path->cck));
+ mt76_apply_array_limit(dest_path->cck, ARRAY_SIZE(dest_path->cck), val,
+ target_power_combine, txs_delta, &max_power_backoff);
+
+ val = mt76_get_of_array(np, "paths-ofdm", &len, ARRAY_SIZE(dest_path->ofdm));
+ mt76_apply_array_limit(dest_path->ofdm, ARRAY_SIZE(dest_path->ofdm), val,
+ target_power_combine, txs_delta, &max_power_backoff);
+
+ val = mt76_get_of_array(np, "paths-ofdm-bf", &len, ARRAY_SIZE(dest_path->ofdm_bf));
+ mt76_apply_array_limit(dest_path->ofdm_bf, ARRAY_SIZE(dest_path->ofdm_bf), val,
+ target_power_combine, txs_delta, &max_power_backoff);
+
+ val = mt76_get_of_array(np, "paths-ru", &len, ARRAY_SIZE(dest_path->ru[0]) + 1);
+ mt76_apply_multi_array_limit(dest_path->ru[0], ARRAY_SIZE(dest_path->ru[0]),
+ ARRAY_SIZE(dest_path->ru), val, len,
+ target_power_combine, txs_delta);
+
+ val = mt76_get_of_array(np, "paths-ru-bf", &len, ARRAY_SIZE(dest_path->ru_bf[0]) + 1);
+ mt76_apply_multi_array_limit(dest_path->ru_bf[0], ARRAY_SIZE(dest_path->ru_bf[0]),
+ ARRAY_SIZE(dest_path->ru_bf), val, len,
+ target_power_combine, txs_delta);
return max_power;
}
diff --git a/mt76.h b/mt76.h
index 97a691991..20fdd050a 100644
--- a/mt76.h
+++ b/mt76.h
@@ -1059,6 +1059,14 @@ struct mt76_power_limits {
s8 eht[16][16];
};
+struct mt76_power_path_limits {
+ s8 cck[5];
+ s8 ofdm[5];
+ s8 ofdm_bf[4];
+ s8 ru[16][15];
+ s8 ru_bf[16][15];
+};
+
struct mt76_ethtool_worker_info {
u64 *data;
int idx;
@@ -1669,6 +1677,7 @@ mt76_find_channel_node(struct device_node *np, struct ieee80211_channel *chan);
s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
struct ieee80211_channel *chan,
struct mt76_power_limits *dest,
+ struct mt76_power_path_limits *dest_path,
s8 target_power);
static inline bool mt76_queue_is_wed_tx_free(struct mt76_queue *q)
diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
index 1e34e0ae3..0c7b69352 100644
--- a/mt76_connac_mcu.c
+++ b/mt76_connac_mcu.c
@@ -2150,7 +2150,7 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
sar_power = mt76_get_sar_power(phy, &chan, reg_power);
mt76_get_rate_power_limits(phy, &chan, limits,
- sar_power);
+ NULL, sar_power);
tx_power_tlv.last_msg = ch_list[idx] == last_ch;
sku_tlbv.channel = ch_list[idx];
diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
index 4afa2a295..6ac992a81 100644
--- a/mt7996/eeprom.c
+++ b/mt7996/eeprom.c
@@ -408,3 +408,37 @@ s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band)
return val & MT_EE_RATE_DELTA_SIGN ? delta : -delta;
}
+
+const u8 mt7996_sku_group_len[] = {
+ [SKU_CCK] = 4,
+ [SKU_OFDM] = 8,
+ [SKU_HT20] = 8,
+ [SKU_HT40] = 9,
+ [SKU_VHT20] = 12,
+ [SKU_VHT40] = 12,
+ [SKU_VHT80] = 12,
+ [SKU_VHT160] = 12,
+ [SKU_HE26] = 12,
+ [SKU_HE52] = 12,
+ [SKU_HE106] = 12,
+ [SKU_HE242] = 12,
+ [SKU_HE484] = 12,
+ [SKU_HE996] = 12,
+ [SKU_HE2x996] = 12,
+ [SKU_EHT26] = 16,
+ [SKU_EHT52] = 16,
+ [SKU_EHT106] = 16,
+ [SKU_EHT242] = 16,
+ [SKU_EHT484] = 16,
+ [SKU_EHT996] = 16,
+ [SKU_EHT2x996] = 16,
+ [SKU_EHT4x996] = 16,
+ [SKU_EHT26_52] = 16,
+ [SKU_EHT26_106] = 16,
+ [SKU_EHT484_242] = 16,
+ [SKU_EHT996_484] = 16,
+ [SKU_EHT996_484_242] = 16,
+ [SKU_EHT2x996_484] = 16,
+ [SKU_EHT3x996] = 16,
+ [SKU_EHT3x996_484] = 16,
+};
diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
index 58179c0c5..b19ff068e 100644
--- a/mt7996/eeprom.h
+++ b/mt7996/eeprom.h
@@ -125,4 +125,46 @@ mt7996_get_channel_group_6g(int channel)
return DIV_ROUND_UP(channel - 29, 32);
}
+enum mt7996_sku_rate_group {
+ SKU_CCK,
+ SKU_OFDM,
+
+ SKU_HT20,
+ SKU_HT40,
+
+ SKU_VHT20,
+ SKU_VHT40,
+ SKU_VHT80,
+ SKU_VHT160,
+
+ SKU_HE26,
+ SKU_HE52,
+ SKU_HE106,
+ SKU_HE242,
+ SKU_HE484,
+ SKU_HE996,
+ SKU_HE2x996,
+
+ SKU_EHT26,
+ SKU_EHT52,
+ SKU_EHT106,
+ SKU_EHT242,
+ SKU_EHT484,
+ SKU_EHT996,
+ SKU_EHT2x996,
+ SKU_EHT4x996,
+ SKU_EHT26_52,
+ SKU_EHT26_106,
+ SKU_EHT484_242,
+ SKU_EHT996_484,
+ SKU_EHT996_484_242,
+ SKU_EHT2x996_484,
+ SKU_EHT3x996,
+ SKU_EHT3x996_484,
+
+ MAX_SKU_RATE_GROUP_NUM,
+};
+
+extern const u8 mt7996_sku_group_len[MAX_SKU_RATE_GROUP_NUM];
+
#endif
diff --git a/mt7996/init.c b/mt7996/init.c
index 6a394c364..f1d681787 100644
--- a/mt7996/init.c
+++ b/mt7996/init.c
@@ -295,7 +295,12 @@ static void __mt7996_init_txpower(struct mt7996_phy *phy,
int nss_delta = mt76_tx_power_nss_delta(nss);
int pwr_delta = mt7996_eeprom_get_power_delta(dev, sband->band);
struct mt76_power_limits limits;
+ struct mt76_power_path_limits limits_path;
+ struct device_node *np;
+ phy->sku_limit_en = true;
+ phy->sku_path_en = true;
+ np = mt76_find_power_limits_node(&dev->mt76);
for (i = 0; i < sband->n_channels; i++) {
struct ieee80211_channel *chan = &sband->channels[i];
int target_power = mt7996_eeprom_get_target_power(dev, chan);
@@ -303,11 +308,18 @@ static void __mt7996_init_txpower(struct mt7996_phy *phy,
target_power += pwr_delta;
target_power = mt76_get_rate_power_limits(phy->mt76, chan,
&limits,
+ &limits_path,
target_power);
+ if (!limits_path.ofdm[0])
+ phy->sku_path_en = false;
+
target_power += nss_delta;
target_power = DIV_ROUND_UP(target_power, 2);
- chan->max_power = min_t(int, chan->max_reg_power,
- target_power);
+ if (!np)
+ chan->max_power = min_t(int, chan->max_reg_power,
+ target_power);
+ else
+ chan->max_power = target_power;
chan->orig_mpwr = target_power;
}
}
diff --git a/mt7996/main.c b/mt7996/main.c
index 57865f1de..52870fa72 100644
--- a/mt7996/main.c
+++ b/mt7996/main.c
@@ -85,6 +85,21 @@ int mt7996_run(struct ieee80211_hw *hw)
if (ret)
goto out;
+#ifdef CONFIG_MTK_DEBUG
+ ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
+ dev->dbg.sku_disable ? 0 : phy->sku_limit_en);
+
+ ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL,
+ dev->dbg.sku_disable ? 0 : phy->sku_path_en);
+#else
+ ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
+ phy->sku_limit_en);
+ ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL,
+ phy->sku_path_en);
+#endif
+ if (ret)
+ goto out;
+
set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
diff --git a/mt7996/mcu.c b/mt7996/mcu.c
index 2650dc6ff..1330ff397 100644
--- a/mt7996/mcu.c
+++ b/mt7996/mcu.c
@@ -4665,9 +4665,31 @@ int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id)
sizeof(req), true);
}
+static void
+mt7996_update_max_txpower_cur(struct mt7996_phy *phy, int tx_power)
+{
+ struct mt76_phy *mphy = phy->mt76;
+ struct ieee80211_channel *chan = mphy->main_chan;
+ int e2p_power_limit = 0;
+
+ if (chan == NULL) {
+ mphy->txpower_cur = tx_power;
+ return;
+ }
+
+ e2p_power_limit = mt7996_eeprom_get_target_power(phy->dev, chan);
+ e2p_power_limit += mt7996_eeprom_get_power_delta(phy->dev, chan->band);
+
+ if (phy->sku_limit_en)
+ mphy->txpower_cur = min_t(int, e2p_power_limit, tx_power);
+ else
+ mphy->txpower_cur = e2p_power_limit;
+}
+
int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
{
#define TX_POWER_LIMIT_TABLE_RATE 0
+#define TX_POWER_LIMIT_TABLE_PATH 1
struct mt7996_dev *dev = phy->dev;
struct mt76_phy *mphy = phy->mt76;
struct ieee80211_hw *hw = mphy->hw;
@@ -4687,13 +4709,22 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
.band_idx = phy->mt76->band_idx,
};
struct mt76_power_limits la = {};
+ struct mt76_power_path_limits la_path = {};
struct sk_buff *skb;
- int i, tx_power;
+ int i, ret, txpower_limit;
+
+ if (hw->conf.power_level == INT_MIN)
+ hw->conf.power_level = 127;
+ txpower_limit = mt7996_get_power_bound(phy, hw->conf.power_level);
- tx_power = mt7996_get_power_bound(phy, hw->conf.power_level);
- tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
- &la, tx_power);
- mphy->txpower_cur = tx_power;
+ if (phy->sku_limit_en) {
+ txpower_limit = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
+ &la, &la_path, txpower_limit);
+ mt7996_update_max_txpower_cur(phy, txpower_limit);
+ } else {
+ mt7996_update_max_txpower_cur(phy, txpower_limit);
+ return 0;
+ }
skb = mt76_mcu_msg_alloc(&dev->mt76, NULL,
sizeof(req) + MT7996_SKU_PATH_NUM);
@@ -4723,6 +4754,34 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
/* padding */
skb_put_zero(skb, MT7996_SKU_PATH_NUM - MT7996_SKU_RATE_NUM);
+ ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_WM_UNI_CMD(TXPOWER), true);
+ if (ret)
+ return ret;
+
+ /* only set per-path power table when it's configured */
+ if (!phy->sku_path_en)
+ return 0;
+
+ skb = mt76_mcu_msg_alloc(&dev->mt76, NULL,
+ sizeof(req) + MT7996_SKU_PATH_NUM);
+ if (!skb)
+ return -ENOMEM;
+ req.power_limit_type = TX_POWER_LIMIT_TABLE_PATH;
+
+ skb_put_data(skb, &req, sizeof(req));
+ skb_put_data(skb, &la_path.cck, sizeof(la_path.cck));
+ skb_put_data(skb, &la_path.ofdm, sizeof(la_path.ofdm));
+ skb_put_data(skb, &la_path.ofdm_bf, sizeof(la_path.ofdm_bf));
+
+ for (i = 0; i < 32; i++) {
+ bool bf = i % 2;
+ u8 idx = i / 2;
+ s8 *buf = bf ? la_path.ru_bf[idx] : la_path.ru[idx];
+
+ skb_put_data(skb, buf, sizeof(la_path.ru[0]));
+ }
+
return mt76_mcu_skb_send_msg(&dev->mt76, skb,
MCU_WM_UNI_CMD(TXPOWER), true);
}
diff --git a/mt7996/mcu.h b/mt7996/mcu.h
index 5953b25e8..5a60ccc55 100644
--- a/mt7996/mcu.h
+++ b/mt7996/mcu.h
@@ -911,6 +911,7 @@ struct tx_power_ctrl {
bool ate_mode_enable;
bool percentage_ctrl_enable;
bool bf_backoff_enable;
+ u8 show_info_category;
u8 power_drop_level;
};
u8 band_idx;
@@ -924,6 +925,7 @@ enum {
UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL = 3,
UNI_TXPOWER_POWER_LIMIT_TABLE_CTRL = 4,
UNI_TXPOWER_ATE_MODE_CTRL = 6,
+ UNI_TXPOWER_SHOW_INFO = 7,
};
enum {
diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
index 186af3c1d..6ab45cc7e 100644
--- a/mt7996/mt7996.h
+++ b/mt7996/mt7996.h
@@ -296,6 +296,9 @@ struct mt7996_phy {
struct mt7996_scs_ctrl scs_ctrl;
+ bool sku_limit_en;
+ bool sku_path_en;
+
#ifdef CONFIG_NL80211_TESTMODE
struct {
u32 *reg_backup;
@@ -617,6 +620,7 @@ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy);
#ifdef CONFIG_NL80211_TESTMODE
void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
#endif
+int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event);
int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable);
void mt7996_mcu_scs_sta_poll(struct work_struct *work);
diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
index 9b4d5f68c..48209668c 100644
--- a/mt7996/mtk_debugfs.c
+++ b/mt7996/mtk_debugfs.c
@@ -2440,6 +2440,364 @@ mt7996_scs_enable_set(void *data, u64 val)
DEFINE_DEBUGFS_ATTRIBUTE(fops_scs_enable, NULL,
mt7996_scs_enable_set, "%lld\n");
+static int
+mt7996_txpower_level_set(void *data, u64 val)
+{
+ struct mt7996_phy *phy = data;
+ int ret;
+
+ if (val > 100)
+ return -EINVAL;
+
+ ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_PERCENTAGE_CTRL, !!val);
+ if (ret)
+ return ret;
+
+ return mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_PERCENTAGE_DROP_CTRL, val);
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_txpower_level, NULL,
+ mt7996_txpower_level_set, "%lld\n");
+
+static ssize_t
+mt7996_get_txpower_info(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct mt7996_phy *phy = file->private_data;
+ struct mt7996_mcu_txpower_event *event;
+ struct txpower_basic_info *basic_info;
+ struct device_node *np;
+ static const size_t size = 2048;
+ int len = 0;
+ ssize_t ret;
+ char *buf;
+
+ buf = kzalloc(size, GFP_KERNEL);
+ event = kzalloc(sizeof(*event), GFP_KERNEL);
+ if (!buf || !event) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = mt7996_mcu_get_tx_power_info(phy, BASIC_INFO, event);
+ if (ret ||
+ le32_to_cpu(event->basic_info.category) != UNI_TXPOWER_BASIC_INFO)
+ goto out;
+
+ basic_info = &event->basic_info;
+
+ len += scnprintf(buf + len, size - len,
+ "======================== BASIC INFO ========================\n");
+ len += scnprintf(buf + len, size - len, " Band Index: %d, Channel Band: %d\n",
+ basic_info->band_idx, basic_info->band);
+ len += scnprintf(buf + len, size - len, " PA Type: %s\n",
+ basic_info->is_epa ? "ePA" : "iPA");
+ len += scnprintf(buf + len, size - len, " LNA Type: %s\n",
+ basic_info->is_elna ? "eLNA" : "iLNA");
+
+ len += scnprintf(buf + len, size - len,
+ "------------------------------------------------------------\n");
+ len += scnprintf(buf + len, size - len, " SKU: %s\n",
+ basic_info->sku_enable ? "enable" : "disable");
+ len += scnprintf(buf + len, size - len, " Percentage Control: %s\n",
+ basic_info->percentage_ctrl_enable ? "enable" : "disable");
+ len += scnprintf(buf + len, size - len, " Power Drop: %d [dBm]\n",
+ basic_info->power_drop_level >> 1);
+ len += scnprintf(buf + len, size - len, " Backoff: %s\n",
+ basic_info->bf_backoff_enable ? "enable" : "disable");
+ len += scnprintf(buf + len, size - len, " TX Front-end Loss: %d, %d, %d, %d\n",
+ basic_info->front_end_loss_tx[0], basic_info->front_end_loss_tx[1],
+ basic_info->front_end_loss_tx[2], basic_info->front_end_loss_tx[3]);
+ len += scnprintf(buf + len, size - len, " RX Front-end Loss: %d, %d, %d, %d\n",
+ basic_info->front_end_loss_rx[0], basic_info->front_end_loss_rx[1],
+ basic_info->front_end_loss_rx[2], basic_info->front_end_loss_rx[3]);
+ len += scnprintf(buf + len, size - len,
+ " MU TX Power Mode: %s\n",
+ basic_info->mu_tx_power_manual_enable ? "manual" : "auto");
+ len += scnprintf(buf + len, size - len,
+ " MU TX Power (Auto / Manual): %d / %d [0.5 dBm]\n",
+ basic_info->mu_tx_power_auto, basic_info->mu_tx_power_manual);
+ len += scnprintf(buf + len, size - len,
+ " Thermal Compensation: %s\n",
+ basic_info->thermal_compensate_enable ? "enable" : "disable");
+ len += scnprintf(buf + len, size - len,
+ " Theraml Compensation Value: %d\n",
+ basic_info->thermal_compensate_value);
+ np = mt76_find_power_limits_node(phy->mt76->dev);
+ len += scnprintf(buf + len, size - len,
+ " RegDB: %s\n",
+ !np ? "enable" : "disable");
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+out:
+ kfree(buf);
+ kfree(event);
+ return ret;
+}
+
+static const struct file_operations mt7996_txpower_info_fops = {
+ .read = mt7996_get_txpower_info,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+#define mt7996_txpower_puts(rate, _len) \
+({ \
+ len += scnprintf(buf + len, size - len, "%-*s:", _len, #rate " (TMAC)"); \
+ for (i = 0; i < mt7996_sku_group_len[SKU_##rate]; i++, offs++) \
+ len += scnprintf(buf + len, size - len, " %6d", \
+ event->phy_rate_info.frame_power[offs][band_idx]); \
+ len += scnprintf(buf + len, size - len, "\n"); \
+})
+
+static ssize_t
+mt7996_get_txpower_sku(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct mt7996_phy *phy = file->private_data;
+ struct mt7996_dev *dev = phy->dev;
+ struct mt7996_mcu_txpower_event *event;
+ struct ieee80211_channel *chan = phy->mt76->chandef.chan;
+ struct ieee80211_supported_band sband;
+ u8 band_idx = phy->mt76->band_idx;
+ static const size_t size = 5120;
+ int i, offs = 0, len = 0;
+ u32 target_power = 0;
+ int n_chains = hweight16(phy->mt76->chainmask);
+ int nss_delta = mt76_tx_power_nss_delta(n_chains);
+ int pwr_delta;
+ ssize_t ret;
+ char *buf;
+ u32 reg;
+
+ buf = kzalloc(size, GFP_KERNEL);
+ event = kzalloc(sizeof(*event), GFP_KERNEL);
+ if (!buf || !event) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = mt7996_mcu_get_tx_power_info(phy, PHY_RATE_INFO, event);
+ if (ret ||
+ le32_to_cpu(event->phy_rate_info.category) != UNI_TXPOWER_PHY_RATE_INFO)
+ goto out;
+
+ len += scnprintf(buf + len, size - len,
+ "\nPhy %d TX Power Table (Channel %d)\n",
+ band_idx, phy->mt76->chandef.chan->hw_value);
+ len += scnprintf(buf + len, size - len, "%-21s %6s %6s %6s %6s\n",
+ " ", "1m", "2m", "5m", "11m");
+ mt7996_txpower_puts(CCK, 21);
+
+ len += scnprintf(buf + len, size - len,
+ "%-21s %6s %6s %6s %6s %6s %6s %6s %6s\n",
+ " ", "6m", "9m", "12m", "18m", "24m", "36m", "48m",
+ "54m");
+ mt7996_txpower_puts(OFDM, 21);
+
+ len += scnprintf(buf + len, size - len,
+ "%-21s %6s %6s %6s %6s %6s %6s %6s %6s\n",
+ " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4",
+ "mcs5", "mcs6", "mcs7");
+ mt7996_txpower_puts(HT20, 21);
+
+ len += scnprintf(buf + len, size - len,
+ "%-21s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n",
+ " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5",
+ "mcs6", "mcs7", "mcs32");
+ mt7996_txpower_puts(HT40, 21);
+
+ len += scnprintf(buf + len, size - len,
+ "%-21s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n",
+ " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5",
+ "mcs6", "mcs7", "mcs8", "mcs9", "mcs10", "mcs11");
+ mt7996_txpower_puts(VHT20, 21);
+ mt7996_txpower_puts(VHT40, 21);
+ mt7996_txpower_puts(VHT80, 21);
+ mt7996_txpower_puts(VHT160, 21);
+ mt7996_txpower_puts(HE26, 21);
+ mt7996_txpower_puts(HE52, 21);
+ mt7996_txpower_puts(HE106, 21);
+ len += scnprintf(buf + len, size - len, "BW20/");
+ mt7996_txpower_puts(HE242, 16);
+ len += scnprintf(buf + len, size - len, "BW40/");
+ mt7996_txpower_puts(HE484, 16);
+ len += scnprintf(buf + len, size - len, "BW80/");
+ mt7996_txpower_puts(HE996, 16);
+ len += scnprintf(buf + len, size - len, "BW160/");
+ mt7996_txpower_puts(HE2x996, 15);
+
+ len += scnprintf(buf + len, size - len,
+ "%-21s %6s %6s %6s %6s %6s %6s %6s %6s ",
+ " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5", "mcs6", "mcs7");
+ len += scnprintf(buf + len, size - len,
+ "%6s %6s %6s %6s %6s %6s %6s %6s\n",
+ "mcs8", "mcs9", "mcs10", "mcs11", "mcs12", "mcs13", "mcs14", "mcs15");
+ mt7996_txpower_puts(EHT26, 21);
+ mt7996_txpower_puts(EHT52, 21);
+ mt7996_txpower_puts(EHT106, 21);
+ len += scnprintf(buf + len, size - len, "BW20/");
+ mt7996_txpower_puts(EHT242, 16);
+ len += scnprintf(buf + len, size - len, "BW40/");
+ mt7996_txpower_puts(EHT484, 16);
+ len += scnprintf(buf + len, size - len, "BW80/");
+ mt7996_txpower_puts(EHT996, 16);
+ len += scnprintf(buf + len, size - len, "BW160/");
+ mt7996_txpower_puts(EHT2x996, 15);
+ len += scnprintf(buf + len, size - len, "BW320/");
+ mt7996_txpower_puts(EHT4x996, 15);
+ mt7996_txpower_puts(EHT26_52, 21);
+ mt7996_txpower_puts(EHT26_106, 21);
+ mt7996_txpower_puts(EHT484_242, 21);
+ mt7996_txpower_puts(EHT996_484, 21);
+ mt7996_txpower_puts(EHT996_484_242, 21);
+ mt7996_txpower_puts(EHT2x996_484, 21);
+ mt7996_txpower_puts(EHT3x996, 21);
+ mt7996_txpower_puts(EHT3x996_484, 21);
+
+ len += scnprintf(buf + len, size - len, "\nePA Gain: %d\n",
+ event->phy_rate_info.epa_gain);
+ len += scnprintf(buf + len, size - len, "Max Power Bound: %d\n",
+ event->phy_rate_info.max_power_bound);
+ len += scnprintf(buf + len, size - len, "Min Power Bound: %d\n",
+ event->phy_rate_info.min_power_bound);
+
+ reg = MT_WF_PHYDFE_TSSI_TXCTRL01(band_idx);
+ len += scnprintf(buf + len, size - len,
+ "\nBBP TX Power (target power from TMAC) : %6ld [0.5 dBm]\n",
+ mt76_get_field(dev, reg, MT_WF_PHYDFE_TSSI_TXCTRL_POWER_TMAC));
+ len += scnprintf(buf + len, size - len,
+ "RegDB maximum power:\t%d [dBm]\n",
+ chan->max_reg_power);
+
+ if (chan->band == NL80211_BAND_2GHZ)
+ sband = phy->mt76->sband_2g.sband;
+ else if (chan->band == NL80211_BAND_5GHZ)
+ sband = phy->mt76->sband_5g.sband;
+ else if (chan->band == NL80211_BAND_6GHZ)
+ sband = phy->mt76->sband_6g.sband;
+
+ pwr_delta = mt7996_eeprom_get_power_delta(dev, sband.band);
+
+ target_power = max_t(u32, target_power, mt7996_eeprom_get_target_power(dev, chan));
+ target_power += pwr_delta + nss_delta;
+ target_power = DIV_ROUND_UP(target_power, 2);
+ len += scnprintf(buf + len, size - len,
+ "eeprom maximum power:\t%d [dBm]\n",
+ target_power);
+
+ len += scnprintf(buf + len, size - len,
+ "nss_delta:\t%d [0.5 dBm]\n",
+ nss_delta);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+out:
+ kfree(buf);
+ kfree(event);
+ return ret;
+}
+
+static const struct file_operations mt7996_txpower_sku_fops = {
+ .read = mt7996_get_txpower_sku,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+#define mt7996_txpower_path_puts(rate, arr_length) \
+({ \
+ len += scnprintf(buf + len, size - len, "%23s:", #rate " (TMAC)"); \
+ for (i = 0; i < arr_length; i++, offs++) \
+ len += scnprintf(buf + len, size - len, " %4d", \
+ event->backoff_table_info.frame_power[offs]); \
+ len += scnprintf(buf + len, size - len, "\n"); \
+})
+
+static ssize_t
+mt7996_get_txpower_path(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct mt7996_phy *phy = file->private_data;
+ struct mt7996_mcu_txpower_event *event;
+ static const size_t size = 5120;
+ int i, offs = 0, len = 0;
+ ssize_t ret;
+ char *buf;
+
+ buf = kzalloc(size, GFP_KERNEL);
+ event = kzalloc(sizeof(*event), GFP_KERNEL);
+ if (!buf || !event) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = mt7996_mcu_get_tx_power_info(phy, BACKOFF_TABLE_INFO, event);
+ if (ret ||
+ le32_to_cpu(event->phy_rate_info.category) != UNI_TXPOWER_BACKOFF_TABLE_SHOW_INFO)
+ goto out;
+
+ len += scnprintf(buf + len, size - len, "\n%*c", 25, ' ');
+ len += scnprintf(buf + len, size - len, "1T1S/2T1S/3T1S/4T1S/5T1S/2T2S/3T2S/4T2S/5T2S/"
+ "3T3S/4T3S/5T3S/4T4S/5T4S/5T5S\n");
+
+ mt7996_txpower_path_puts(CCK, 5);
+ mt7996_txpower_path_puts(OFDM, 5);
+ mt7996_txpower_path_puts(BF-OFDM, 4);
+
+ mt7996_txpower_path_puts(RU26, 15);
+ mt7996_txpower_path_puts(BF-RU26, 15);
+ mt7996_txpower_path_puts(RU52, 15);
+ mt7996_txpower_path_puts(BF-RU52, 15);
+ mt7996_txpower_path_puts(RU26_52, 15);
+ mt7996_txpower_path_puts(BF-RU26_52, 15);
+ mt7996_txpower_path_puts(RU106, 15);
+ mt7996_txpower_path_puts(BF-RU106, 15);
+ mt7996_txpower_path_puts(RU106_52, 15);
+ mt7996_txpower_path_puts(BF-RU106_52, 15);
+
+ mt7996_txpower_path_puts(BW20/RU242, 15);
+ mt7996_txpower_path_puts(BF-BW20/RU242, 15);
+ mt7996_txpower_path_puts(BW40/RU484, 15);
+ mt7996_txpower_path_puts(BF-BW40/RU484, 15);
+ mt7996_txpower_path_puts(RU242_484, 15);
+ mt7996_txpower_path_puts(BF-RU242_484, 15);
+ mt7996_txpower_path_puts(BW80/RU996, 15);
+ mt7996_txpower_path_puts(BF-BW80/RU996, 15);
+ mt7996_txpower_path_puts(RU484_996, 15);
+ mt7996_txpower_path_puts(BF-RU484_996, 15);
+ mt7996_txpower_path_puts(RU242_484_996, 15);
+ mt7996_txpower_path_puts(BF-RU242_484_996, 15);
+ mt7996_txpower_path_puts(BW160/RU996x2, 15);
+ mt7996_txpower_path_puts(BF-BW160/RU996x2, 15);
+ mt7996_txpower_path_puts(RU484_996x2, 15);
+ mt7996_txpower_path_puts(BF-RU484_996x2, 15);
+ mt7996_txpower_path_puts(RU996x3, 15);
+ mt7996_txpower_path_puts(BF-RU996x3, 15);
+ mt7996_txpower_path_puts(RU484_996x3, 15);
+ mt7996_txpower_path_puts(BF-RU484_996x3, 15);
+ mt7996_txpower_path_puts(BW320/RU996x4, 15);
+ mt7996_txpower_path_puts(BF-BW320/RU996x4, 15);
+
+ len += scnprintf(buf + len, size - len, "\nBackoff table: %s\n",
+ event->backoff_table_info.backoff_en ? "enable" : "disable");
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+out:
+ kfree(buf);
+ kfree(event);
+ return ret;
+}
+
+static const struct file_operations mt7996_txpower_path_fops = {
+ .read = mt7996_get_txpower_path,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
{
struct mt7996_dev *dev = phy->dev;
@@ -2503,6 +2861,10 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
debugfs_create_devm_seqfile(dev->mt76.dev, "tr_info", dir,
mt7996_trinfo_read);
+ debugfs_create_file("txpower_level", 0600, dir, phy, &fops_txpower_level);
+ debugfs_create_file("txpower_info", 0600, dir, phy, &mt7996_txpower_info_fops);
+ debugfs_create_file("txpower_sku", 0600, dir, phy, &mt7996_txpower_sku_fops);
+ debugfs_create_file("txpower_path", 0600, dir, phy, &mt7996_txpower_path_fops);
debugfs_create_devm_seqfile(dev->mt76.dev, "wtbl_info", dir,
mt7996_wtbl_read);
diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
index c16b25ab5..e56ddd8ff 100644
--- a/mt7996/mtk_mcu.c
+++ b/mt7996/mtk_mcu.c
@@ -12,8 +12,31 @@
#ifdef CONFIG_MTK_DEBUG
+int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event)
+{
+ struct mt7996_dev *dev = phy->dev;
+ struct tx_power_ctrl req = {
+ .tag = cpu_to_le16(UNI_TXPOWER_SHOW_INFO),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ .power_ctrl_id = UNI_TXPOWER_SHOW_INFO,
+ .show_info_category = category,
+ .band_idx = phy->mt76->band_idx,
+ };
+ struct sk_buff *skb;
+ int ret;
+ ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+ MCU_WM_UNI_CMD_QUERY(TXPOWER),
+ &req, sizeof(req), true, &skb);
+ if (ret)
+ return ret;
+ memcpy(event, skb->data, sizeof(struct mt7996_mcu_txpower_event));
+
+ dev_kfree_skb(skb);
+
+ return 0;
+}
int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val)
{
diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
index 7f4d4e029..c30418cae 100644
--- a/mt7996/mtk_mcu.h
+++ b/mt7996/mtk_mcu.h
@@ -14,6 +14,98 @@ enum {
UNI_CMD_MURU_DBG_INFO = 0x18,
};
+struct txpower_basic_info {
+ u8 category;
+ u8 rsv1;
+
+ /* basic info */
+ u8 band_idx;
+ u8 band;
+
+ /* board type info */
+ bool is_epa;
+ bool is_elna;
+
+ /* power percentage info */
+ bool percentage_ctrl_enable;
+ s8 power_drop_level;
+
+ /* frond-end loss TX info */
+ s8 front_end_loss_tx[4];
+
+ /* frond-end loss RX info */
+ s8 front_end_loss_rx[4];
+
+ /* thermal info */
+ bool thermal_compensate_enable;
+ s8 thermal_compensate_value;
+ u8 rsv2;
+
+ /* TX power max/min limit info */
+ s8 max_power_bound;
+ s8 min_power_bound;
+
+ /* power limit info */
+ bool sku_enable;
+ bool bf_backoff_enable;
+
+ /* MU TX power info */
+ bool mu_tx_power_manual_enable;
+ s8 mu_tx_power_auto;
+ s8 mu_tx_power_manual;
+ u8 rsv3;
+};
+
+struct txpower_phy_rate_info {
+ u8 category;
+ u8 band_idx;
+ u8 band;
+ u8 epa_gain;
+
+ /* rate power info [dBm] */
+ s8 frame_power[MT7996_SKU_RATE_NUM][__MT_MAX_BAND];
+
+ /* TX power max/min limit info */
+ s8 max_power_bound;
+ s8 min_power_bound;
+ u8 rsv1;
+};
+
+struct txpower_backoff_table_info {
+ u8 category;
+ u8 band_idx;
+ u8 band;
+ u8 backoff_en;
+
+ s8 frame_power[MT7996_SKU_PATH_NUM];
+ u8 rsv[3];
+};
+
+struct mt7996_mcu_txpower_event {
+ u8 _rsv[4];
+
+ __le16 tag;
+ __le16 len;
+
+ union {
+ struct txpower_basic_info basic_info;
+ struct txpower_phy_rate_info phy_rate_info;
+ struct txpower_backoff_table_info backoff_table_info;
+ };
+};
+
+enum txpower_category {
+ BASIC_INFO,
+ BACKOFF_TABLE_INFO,
+ PHY_RATE_INFO,
+};
+
+enum txpower_event {
+ UNI_TXPOWER_BASIC_INFO = 0,
+ UNI_TXPOWER_BACKOFF_TABLE_SHOW_INFO = 3,
+ UNI_TXPOWER_PHY_RATE_INFO = 5,
+};
+
#endif
#endif
diff --git a/mt7996/regs.h b/mt7996/regs.h
index 4c20a67d7..8ec1dc1c6 100644
--- a/mt7996/regs.h
+++ b/mt7996/regs.h
@@ -693,24 +693,29 @@ enum offs_rev {
((_wf) << 16) + (ofs))
#define MT_WF_PHYRX_CSD_IRPI(_band, _wf) MT_WF_PHYRX_CSD(_band, _wf, 0x1000)
-/* PHYRX CTRL */
-#define MT_WF_PHYRX_BAND_BASE 0x83080000
-#define MT_WF_PHYRX_BAND(_band, ofs) (MT_WF_PHYRX_BAND_BASE + \
+/* PHYDFE CTRL */
+#define MT_WF_PHYDFE_TSSI_TXCTRL01(_band) MT_WF_PHYRX_CSD(_band, 0, 0xc718)
+#define MT_WF_PHYDFE_TSSI_TXCTRL_POWER_TMAC GENMASK(31, 24)
+
+/* PHY CTRL */
+#define MT_WF_PHY_BAND_BASE 0x83080000
+#define MT_WF_PHY_BAND(_band, ofs) (MT_WF_PHY_BAND_BASE + \
((_band) << 20) + (ofs))
-#define MT_WF_PHYRX_BAND_GID_TAB_VLD0(_band) MT_WF_PHYRX_BAND(_band, 0x1054)
-#define MT_WF_PHYRX_BAND_GID_TAB_VLD1(_band) MT_WF_PHYRX_BAND(_band, 0x1058)
-#define MT_WF_PHYRX_BAND_GID_TAB_POS0(_band) MT_WF_PHYRX_BAND(_band, 0x105c)
-#define MT_WF_PHYRX_BAND_GID_TAB_POS1(_band) MT_WF_PHYRX_BAND(_band, 0x1060)
-#define MT_WF_PHYRX_BAND_GID_TAB_POS2(_band) MT_WF_PHYRX_BAND(_band, 0x1064)
-#define MT_WF_PHYRX_BAND_GID_TAB_POS3(_band) MT_WF_PHYRX_BAND(_band, 0x1068)
+#define MT_WF_PHYRX_BAND_GID_TAB_VLD0(_band) MT_WF_PHY_BAND(_band, 0x1054)
+#define MT_WF_PHYRX_BAND_GID_TAB_VLD1(_band) MT_WF_PHY_BAND(_band, 0x1058)
+#define MT_WF_PHYRX_BAND_GID_TAB_POS0(_band) MT_WF_PHY_BAND(_band, 0x105c)
+#define MT_WF_PHYRX_BAND_GID_TAB_POS1(_band) MT_WF_PHY_BAND(_band, 0x1060)
+#define MT_WF_PHYRX_BAND_GID_TAB_POS2(_band) MT_WF_PHY_BAND(_band, 0x1064)
+#define MT_WF_PHYRX_BAND_GID_TAB_POS3(_band) MT_WF_PHY_BAND(_band, 0x1068)
-#define MT_WF_PHYRX_BAND_RX_CTRL1(_band) MT_WF_PHYRX_BAND(_band, 0x2004)
+/* PHYRX CTRL */
+#define MT_WF_PHYRX_BAND_RX_CTRL1(_band) MT_WF_PHY_BAND(_band, 0x2004)
#define MT_WF_PHYRX_BAND_RX_CTRL1_IPI_EN GENMASK(2, 0)
#define MT_WF_PHYRX_BAND_RX_CTRL1_STSCNT_EN GENMASK(11, 9)
/* PHYRX CSD BAND */
-#define MT_WF_PHYRX_CSD_BAND_RXTD12(_band) MT_WF_PHYRX_BAND(_band, 0x8230)
+#define MT_WF_PHYRX_CSD_BAND_RXTD12(_band) MT_WF_PHY_BAND(_band, 0x8230)
#define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR_ONLY BIT(18)
#define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR BIT(29)
--
2.39.2