wifi: mt76: mt7915: enable .sta_set_txpwr support
authorRyder Lee <ryder.lee@mediatek.com>
Tue, 22 Nov 2022 07:53:11 +0000 (15:53 +0800)
committerFelix Fietkau <nbd@nbd.name>
Thu, 1 Dec 2022 16:29:14 +0000 (17:29 +0100)
This adds support for adjusting the Txpower level while pushing
traffic to an associated station. The allowed range is from 0 to
the maximum power of channel.

Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
drivers/net/wireless/mediatek/mt76/mt7915/init.c
drivers/net/wireless/mediatek/mt76/mt7915/main.c
drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h

index b48c2ba9273dd69bc33989b0476449baa493be19..79bd7bf93f331090c5d11d5a9e5c2714ccfbb7e1 100644 (file)
@@ -355,6 +355,9 @@ mt7915_init_wiphy(struct ieee80211_hw *hw)
        wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_DISCOVERY);
        wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT);
 
+       if (!is_mt7915(&dev->mt76))
+               wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_STA_TX_PWR);
+
        if (!mdev->dev->of_node ||
            !of_property_read_bool(mdev->dev->of_node,
                                   "mediatek,disable-radar-background"))
index 6ef4634fac6b20f3484c27140e35321b86696068..98ed56a5ed6961e554ed8facde66da00ba89bac7 100644 (file)
@@ -1134,6 +1134,39 @@ static void mt7915_sta_set_decap_offload(struct ieee80211_hw *hw,
        mt76_connac_mcu_wtbl_update_hdr_trans(&dev->mt76, vif, sta);
 }
 
+static int mt7915_sta_set_txpwr(struct ieee80211_hw *hw,
+                               struct ieee80211_vif *vif,
+                               struct ieee80211_sta *sta)
+{
+       struct mt7915_phy *phy = mt7915_hw_phy(hw);
+       struct mt7915_dev *dev = mt7915_hw_dev(hw);
+       s16 txpower = sta->deflink.txpwr.power;
+       int ret;
+
+       if (sta->deflink.txpwr.type == NL80211_TX_POWER_AUTOMATIC)
+               txpower = 0;
+
+       mutex_lock(&dev->mt76.mutex);
+
+       /* NOTE: temporarily use 0 as minimum limit, which is a
+        * global setting and will be applied to all stations.
+        */
+       ret = mt7915_mcu_set_txpower_frame_min(phy, 0);
+       if (ret)
+               goto out;
+
+       /* This only applies to data frames while pushing traffic,
+        * whereas the management frames or other packets that are
+        * using fixed rate can be configured via TxD.
+        */
+       ret = mt7915_mcu_set_txpower_frame(phy, vif, sta, txpower);
+
+out:
+       mutex_unlock(&dev->mt76.mutex);
+
+       return ret;
+}
+
 static const char mt7915_gstrings_stats[][ETH_GSTRING_LEN] = {
        "tx_ampdu_cnt",
        "tx_stop_q_empty_cnt",
@@ -1499,6 +1532,7 @@ const struct ieee80211_ops mt7915_ops = {
        .set_bitrate_mask = mt7915_set_bitrate_mask,
        .set_coverage_class = mt7915_set_coverage_class,
        .sta_statistics = mt7915_sta_statistics,
+       .sta_set_txpwr = mt7915_sta_set_txpwr,
        .sta_set_4addr = mt7915_sta_set_4addr,
        .sta_set_decap_offload = mt7915_sta_set_decap_offload,
        .add_twt_setup = mt7915_mac_add_twt_setup,
index f27071f1b777baea43e36e7797e245d0636c4750..ed20ffc588779122723cec6e17591241891a8002 100644 (file)
@@ -3105,6 +3105,88 @@ out:
                                 &req, sizeof(req), false);
 }
 
+int mt7915_mcu_set_txpower_frame_min(struct mt7915_phy *phy, s8 txpower)
+{
+       struct mt7915_dev *dev = phy->dev;
+       struct {
+               u8 format_id;
+               u8 rsv;
+               u8 band_idx;
+               s8 txpower_min;
+       } __packed req = {
+               .format_id = TX_POWER_LIMIT_FRAME_MIN,
+               .band_idx = phy->band_idx,
+               .txpower_min = txpower * 2, /* 0.5db */
+       };
+
+       return mt76_mcu_send_msg(&dev->mt76,
+                                MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req,
+                                sizeof(req), true);
+}
+
+int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy,
+                                struct ieee80211_vif *vif,
+                                struct ieee80211_sta *sta, s8 txpower)
+{
+       struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
+       struct mt7915_dev *dev = phy->dev;
+       struct mt76_phy *mphy = phy->mt76;
+       struct {
+               u8 format_id;
+               u8 rsv[3];
+               u8 band_idx;
+               s8 txpower_max;
+               __le16 wcid;
+               s8 txpower_offs[48];
+       } __packed req = {
+               .format_id = TX_POWER_LIMIT_FRAME,
+               .band_idx = phy->band_idx,
+               .txpower_max = DIV_ROUND_UP(mphy->txpower_cur, 2),
+               .wcid = cpu_to_le16(msta->wcid.idx),
+       };
+       int ret, n_chains = hweight8(mphy->antenna_mask);
+       s8 txpower_sku[MT7915_SKU_RATE_NUM];
+
+       ret = mt7915_mcu_get_txpower_sku(phy, txpower_sku, sizeof(txpower_sku));
+       if (ret)
+               return ret;
+
+       txpower = txpower * 2 - mt76_tx_power_nss_delta(n_chains);
+       if (txpower > mphy->txpower_cur || txpower < 0)
+               return -EINVAL;
+
+       if (txpower) {
+               u32 offs, len, i;
+
+               if (sta->deflink.ht_cap.ht_supported) {
+                       const u8 *sku_len = mt7915_sku_group_len;
+
+                       offs = sku_len[SKU_CCK] + sku_len[SKU_OFDM];
+                       len = sku_len[SKU_HT_BW20] + sku_len[SKU_HT_BW40];
+
+                       if (sta->deflink.vht_cap.vht_supported) {
+                               offs += len;
+                               len = sku_len[SKU_VHT_BW20] * 4;
+
+                               if (sta->deflink.he_cap.has_he) {
+                                       offs += len + sku_len[SKU_HE_RU26] * 3;
+                                       len = sku_len[SKU_HE_RU242] * 4;
+                               }
+                       }
+               } else {
+                       return -EINVAL;
+               }
+
+               for (i = 0; i < len; i++, offs++)
+                       req.txpower_offs[i] =
+                               DIV_ROUND_UP(txpower - txpower_sku[offs], 2);
+       }
+
+       return mt76_mcu_send_msg(&dev->mt76,
+                                MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req,
+                                sizeof(req), true);
+}
+
 int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy)
 {
        struct mt7915_dev *dev = phy->dev;
@@ -3116,7 +3198,7 @@ int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy)
                u8 dbdc_idx;
                s8 val[MT7915_SKU_RATE_NUM];
        } __packed req = {
-               .format_id = 4,
+               .format_id = TX_POWER_LIMIT_TABLE,
                .dbdc_idx = phy != &dev->phy,
        };
        struct mt76_power_limits limits_array;
@@ -3166,11 +3248,11 @@ int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len)
                u8 band;
                u8 _rsv;
        } __packed req = {
-               .format_id = 7,
+               .format_id = TX_POWER_LIMIT_INFO,
                .category = RATE_POWER_INFO,
                .band = phy != &dev->phy,
        };
-       s8 res[MT7915_SKU_RATE_NUM][2];
+       s8 txpower_sku[MT7915_SKU_RATE_NUM][2];
        struct sk_buff *skb;
        int ret, i;
 
@@ -3180,9 +3262,9 @@ int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len)
        if (ret)
                return ret;
 
-       memcpy(res, skb->data + 4, sizeof(res));
+       memcpy(txpower_sku, skb->data + 4, sizeof(txpower_sku));
        for (i = 0; i < len; i++)
-               txpower[i] = res[i][req.band];
+               txpower[i] = txpower_sku[i][req.band_idx];
 
        dev_kfree_skb(skb);
 
@@ -3220,7 +3302,7 @@ int mt7915_mcu_set_sku_en(struct mt7915_phy *phy, bool enable)
                u8 dbdc_idx;
                u8 rsv;
        } __packed req = {
-               .format_id = 0,
+               .format_id = TX_POWER_LIMIT_ENABLE,
                .dbdc_idx = phy != &dev->phy,
                .sku_enable = enable,
        };
index 2fc09fd53777a02c517597d657b5b6d47ddd98a2..46c517e50ae4f12ff4a06f21989abef465f96947 100644 (file)
@@ -419,6 +419,14 @@ enum {
 #define RATE_CFG_PHY_TYPE              GENMASK(27, 24)
 #define RATE_CFG_HE_LTF                        GENMASK(31, 28)
 
+enum {
+       TX_POWER_LIMIT_ENABLE,
+       TX_POWER_LIMIT_TABLE = 0x4,
+       TX_POWER_LIMIT_INFO = 0x7,
+       TX_POWER_LIMIT_FRAME = 0x11,
+       TX_POWER_LIMIT_FRAME_MIN = 0x12,
+};
+
 enum {
        SPR_ENABLE = 0x1,
        SPR_ENABLE_SD = 0x3,
index b5fa6c0b47752a2423c26cdd88a3831b710b612f..42f213430758d157aae8789aaf05995f63ef366a 100644 (file)
@@ -528,6 +528,10 @@ int mt7915_mcu_set_ser(struct mt7915_dev *dev, u8 action, u8 set, u8 band);
 int mt7915_mcu_set_sku_en(struct mt7915_phy *phy, bool enable);
 int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy);
 int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len);
+int mt7915_mcu_set_txpower_frame_min(struct mt7915_phy *phy, s8 txpower);
+int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy,
+                                struct ieee80211_vif *vif,
+                                struct ieee80211_sta *sta, s8 txpower);
 int mt7915_mcu_set_txbf(struct mt7915_dev *dev, u8 action);
 int mt7915_mcu_set_fcc5_lpn(struct mt7915_dev *dev, int val);
 int mt7915_mcu_set_pulse_th(struct mt7915_dev *dev,