blob: eb2c6e8bf5b9b006e3850ac34f8edf2a24670547 [file] [log] [blame]
From 186a919f2ff018e53c1e1bc9e015b409525e7273 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 025/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.
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 a0047d7..11efe29 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 c591f67..c372e78 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 1e34e0a..0c7b693 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 4afa2a2..6ac992a 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 58179c0..b19ff06 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 6a394c3..f1d6817 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 eb213f8..46e3e5a 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 16a01b1..ffd3536 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 5953b25..5a60ccc 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 186af3c..6ab45cc 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 24b331e..74aabf0 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 c16b25a..e56ddd8 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 7f4d4e0..c30418c 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 4c20a67..8ec1dc1 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.18.0