diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c index b85c3e54..cd5d07e6 100644 --- a/mt7996/debugfs.c +++ b/mt7996/debugfs.c @@ -870,6 +870,41 @@ mt7996_rf_regval_set(void *data, u64 val) DEFINE_DEBUGFS_ATTRIBUTE(fops_rf_regval, mt7996_rf_regval_get, mt7996_rf_regval_set, "0x%08llx\n"); +static int mt7996_vow_atf_set(void *data, u64 val) +{ + struct mt7996_dev *dev = data; + struct mt7996_phy *phy; + int ret; + + dev->vow_atf_en = !!val; + + mt7996_for_each_phy(dev, phy) { + ret = mt7996_mcu_set_vow_drr_ctrl(dev, phy->mt76->band_idx, NULL, + NULL, + VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND, 0); + if (ret) + return ret; + + ret = mt7996_mcu_set_vow_feature_ctrl(phy); + if (ret) + return ret; + } + + return 0; +} + +static int mt7996_vow_atf_get(void *data, u64 *val) +{ + struct mt7996_dev *dev = data; + + *val = dev->vow_atf_en; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_vow_atf, mt7996_vow_atf_get, mt7996_vow_atf_set, + "%lld\n"); + int mt7996_init_debugfs(struct mt7996_dev *dev) { struct dentry *dir; @@ -894,6 +929,7 @@ int mt7996_init_debugfs(struct mt7996_dev *dev) debugfs_create_devm_seqfile(dev->mt76.dev, "twt_stats", dir, mt7996_twt_stats); debugfs_create_file("rf_regval", 0600, dir, dev, &fops_rf_regval); + debugfs_create_file("vow_atf", 0600, dir, dev, &fops_vow_atf); debugfs_create_u32("dfs_hw_pattern", 0400, dir, &dev->hw_pattern); debugfs_create_file("radar_trigger", 0200, dir, dev, diff --git a/mt7996/init.c b/mt7996/init.c index 2bf3ddd8..5b68e1a7 100644 --- a/mt7996/init.c +++ b/mt7996/init.c @@ -611,6 +611,7 @@ static void mt7996_mac_init_basic_rates(struct mt7996_dev *dev) void mt7996_mac_init(struct mt7996_dev *dev) { #define HIF_TXD_V2_1 0x21 + struct mt7996_phy *phy; int i, rx_path_type; mt76_clear(dev, MT_MDP_DCR2, MT_MDP_DCR2_RX_TRANS_SHORT); @@ -657,6 +658,9 @@ void mt7996_mac_init(struct mt7996_dev *dev) for (i = MT_BAND0; i <= MT_BAND2; i++) mt7996_mac_init_band(dev, i); + mt7996_for_each_phy(dev, phy) + mt7996_vow_init(phy); + mt7996_mac_init_basic_rates(dev); } @@ -680,6 +684,17 @@ int mt7996_txbf_init(struct mt7996_dev *dev) return mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE); } +void mt7996_vow_init(struct mt7996_phy *phy) +{ + phy->dev->vow_atf_en = true; + + mt7996_mcu_set_vow_drr_ctrl(phy->dev, phy->mt76->band_idx, NULL, NULL, + VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND, 0); + mt7996_mcu_set_vow_drr_ctrl(phy->dev, phy->mt76->band_idx, NULL, NULL, + VOW_DRR_CTRL_AIRTIME_QUANTUM_ALL, 0); + mt7996_mcu_set_vow_feature_ctrl(phy); +} + static int mt7996_register_phy(struct mt7996_dev *dev, enum mt76_band_id band) { struct mt7996_phy *phy; diff --git a/mt7996/main.c b/mt7996/main.c index 560fe30d..93ff2914 100644 --- a/mt7996/main.c +++ b/mt7996/main.c @@ -1995,6 +1995,37 @@ mt7996_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif, return 0; } +static void mt7996_sta_set_airtime_weight(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 weight) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct ieee80211_link_sta *link_sta; + unsigned int link_id; + + mutex_lock(&dev->mt76.mutex); + + for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct mt7996_sta_link *msta_link; + struct mt7996_vif_link *link; + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; + + msta_link = mt7996_sta_link_protected(dev, msta, link_id); + if (!msta_link || !msta_link->wcid.sta) + continue; + + mt7996_mcu_set_vow_drr_ctrl(dev, link->mt76.band_idx, + &msta_link->wcid, &link->mt76, + VOW_DRR_CTRL_STA_ALL, weight); + } + + mutex_unlock(&dev->mt76.mutex); +} + static void mt7996_sta_set_4addr(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -2553,6 +2584,7 @@ const struct ieee80211_ops mt7996_ops = { .sta_statistics = mt7996_sta_statistics, .sta_set_4addr = mt7996_sta_set_4addr, .sta_set_decap_offload = mt7996_sta_set_decap_offload, + .sta_set_airtime_weight = mt7996_sta_set_airtime_weight, .add_twt_setup = mt7996_mac_add_twt_setup, .twt_teardown_request = mt7996_twt_teardown_request, #ifdef CONFIG_MAC80211_DEBUGFS diff --git a/mt7996/mcu.c b/mt7996/mcu.c index c0b9b1bf..1bd32c00 100644 --- a/mt7996/mcu.c +++ b/mt7996/mcu.c @@ -2639,33 +2639,191 @@ error_unlock: return ret; } -static int -mt7996_mcu_add_group(struct mt7996_dev *dev, struct mt7996_vif_link *link, - struct mt76_wcid *wcid) +/* Assignment of the STA BSS group index aligns FW. Each band has its own BSS + * group bitmap space. + * 0..3: BSS 0..3 + * 4..18: BSS 0x11..0x1f + */ +static u8 mt7996_vow_sta_bss_grp(struct mt76_vif_link *mvif) { -#define MT_STA_BSS_GROUP 1 + u8 omac_idx = mvif->omac_idx; + + if (omac_idx <= HW_BSSID_MAX) + return omac_idx; + + /* Extended BSS */ + return HW_BSSID_MAX + omac_idx - EXT_BSSID_START; +} + +int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_dev *dev, u8 band_idx, + struct mt76_wcid *wcid, struct mt76_vif_link *mvif, + enum vow_drr_ctrl_id id, u16 weight) +{ + u32 val = 0; struct { u8 __rsv1[4]; __le16 tag; __le16 len; __le16 wlan_idx; - u8 __rsv2[2]; - __le32 action; - __le32 val; - u8 __rsv3[8]; + u8 band_idx; + u8 wmm_idx; + __le32 ctrl_id; + union { + __le32 val; + u8 drr_quantum[VOW_DRR_QUANTUM_NUM]; + }; + u8 __rsv2[3]; + u8 omac_idx; } __packed req = { .tag = cpu_to_le16(UNI_VOW_DRR_CTRL), .len = cpu_to_le16(sizeof(req) - 4), - .action = cpu_to_le32(MT_STA_BSS_GROUP), - .val = cpu_to_le32(link->mt76.idx % 16), - .wlan_idx = cpu_to_le16(wcid->idx), + .wlan_idx = cpu_to_le16(wcid ? wcid->idx : 0), + .band_idx = band_idx, + .wmm_idx = mvif ? mvif->wmm_idx : 0, + .ctrl_id = cpu_to_le32(id), + .omac_idx = mvif ? mvif->omac_idx : 0, }; + switch (id) { + case VOW_DRR_CTRL_STA_ALL: + val |= FIELD_PREP(VOW_DRR_STA_BSS_GRP_MASK, + mt7996_vow_sta_bss_grp(mvif)); + val |= FIELD_PREP(VOW_DRR_STA_AC0_QNTM_MASK, + mt76_connac_vow_dwrr_quantum(weight, IEEE80211_AC_BK)); + val |= FIELD_PREP(VOW_DRR_STA_AC1_QNTM_MASK, + mt76_connac_vow_dwrr_quantum(weight, IEEE80211_AC_BE)); + val |= FIELD_PREP(VOW_DRR_STA_AC2_QNTM_MASK, + mt76_connac_vow_dwrr_quantum(weight, IEEE80211_AC_VI)); + val |= FIELD_PREP(VOW_DRR_STA_AC3_QNTM_MASK, + mt76_connac_vow_dwrr_quantum(weight, IEEE80211_AC_VO)); + req.val = cpu_to_le32(val); + break; + case VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND: + req.val = cpu_to_le32(dev->vow_atf_en ? VOW_MAX_DEFICIT_ON : + VOW_MAX_DEFICIT_OFF); + break; + case VOW_DRR_CTRL_AIRTIME_QUANTUM_ALL: { + static const u8 quantum[VOW_DRR_QUANTUM_NUM] = VOW_DRR_QUANTUM_TABLE; + + memcpy(req.drr_quantum, quantum, sizeof(quantum)); + break; + } + case VOW_DRR_CTRL_STA_PAUSE: + /* req.val 0 keeps the station unpaused */ + break; + default: + return -EINVAL; + } + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(VOW), &req, sizeof(req), true); } +int mt7996_mcu_set_vow_feature_ctrl(struct mt7996_phy *phy) +{ + bool atf_en = phy->dev->vow_atf_en; + u16 apply, ctrl; + u32 atf_ctrl, sch_ctrl; + struct { + u8 __rsv1[4]; + + __le16 tag; + __le16 len; + + /* DW0 */ + __le16 apply_bwc_enable_per_grp; + __le16 apply; + + /* DW1 */ + __le16 apply_bwc_check_time_token_per_grp; + __le16 __rsv5; + + /* DW2 */ + __le16 apply_bwc_check_len_token_per_grp; + __le16 __rsv6; + + /* DW3 */ + u8 band_idx; + u8 __rsv7[3]; + + /* DW4 */ + __le32 __rsv8; + + /* DW5 */ + __le16 bwc_enable_per_grp; + __le16 ctrl; + + /* DW6 */ + __le16 bwc_check_time_token_per_grp; + __le16 __rsv12; + + /* DW7 */ + __le16 bwc_check_len_token_per_grp; + __le16 __rsv13; + + /* DW8 */ + __le32 atf_ctrl; + + /* DW9 */ + __le32 sch_ctrl; + } __packed req = { + .tag = cpu_to_le16(UNI_VOW_FEATURE_CTRL), + .len = cpu_to_le16(sizeof(req) - 4), + .apply_bwc_enable_per_grp = cpu_to_le16(0xffff), + .apply_bwc_check_time_token_per_grp = cpu_to_le16(0xffff), + .apply_bwc_check_len_token_per_grp = cpu_to_le16(0xffff), + .band_idx = phy->mt76->band_idx, + .bwc_enable_per_grp = cpu_to_le16(0xffff), + }; + + /* apply every field this command configures */ + apply = VOW_FEATURE_APPLY_REFILL_PERIOD | VOW_FEATURE_BAND1_SEARCH_RULE | + VOW_FEATURE_BAND0_SEARCH_RULE | VOW_FEATURE_WATF_EN | + VOW_FEATURE_GRP_NO_CHANGE_IN_TXOP | VOW_FEATURE_ATF_EN | + VOW_FEATURE_BWC_TOKEN_REFILL_EN | VOW_FEATURE_BWC_EN; + + ctrl = FIELD_PREP(VOW_FEATURE_REFILL_PERIOD, VOW_REFILL_PERIOD_32US) | + FIELD_PREP(VOW_FEATURE_BAND1_SEARCH_RULE, VOW_SEARCH_WMM_FIRST) | + FIELD_PREP(VOW_FEATURE_BAND0_SEARCH_RULE, VOW_SEARCH_WMM_FIRST) | + VOW_FEATURE_GRP_NO_CHANGE_IN_TXOP | VOW_FEATURE_BWC_TOKEN_REFILL_EN; + if (atf_en) + ctrl |= VOW_FEATURE_WATF_EN | VOW_FEATURE_ATF_EN; + + atf_ctrl = VOW_ATF_APPLY_KEEP_QUANTUM | VOW_ATF_KEEP_QUANTUM | + VOW_ATF_APPLY_VOW_CTRL | VOW_ATF_VOW_CTRL_VAL | + /* reset the DRR table when SER occurs */ + FIELD_PREP(VOW_ATF_VOW_CTRL_BIT, 26); + + sch_ctrl = VOW_SCH_APPLY_CTRL | + FIELD_PREP(VOW_SCH_TYPE, VOW_SCH_TYPE_FOLLOW_POLICY) | + FIELD_PREP(VOW_SCH_POLICY, VOW_SCH_POLICY_SRR); + + req.apply = cpu_to_le16(apply); + req.ctrl = cpu_to_le16(ctrl); + req.atf_ctrl = cpu_to_le32(atf_ctrl); + req.sch_ctrl = cpu_to_le32(sch_ctrl); + + return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(VOW), &req, + sizeof(req), true); +} + +static int +mt7996_mcu_sta_init_vow(struct mt7996_dev *dev, struct mt76_vif_link *mvif, + struct mt76_wcid *wcid) +{ + int ret; + + ret = mt7996_mcu_set_vow_drr_ctrl(dev, mvif->band_idx, wcid, mvif, + VOW_DRR_CTRL_STA_PAUSE, 0); + if (ret) + return ret; + + return mt7996_mcu_set_vow_drr_ctrl(dev, mvif->band_idx, wcid, mvif, + VOW_DRR_CTRL_STA_ALL, + IEEE80211_DEFAULT_AIRTIME_WEIGHT); +} + int mt7996_mcu_mld_reconf_stop_link(struct mt7996_dev *dev, struct ieee80211_vif *vif, u16 removed_links) @@ -2867,7 +3025,7 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, } } - ret = mt7996_mcu_add_group(dev, link, wcid); + ret = mt7996_mcu_sta_init_vow(dev, &link->mt76, wcid); if (ret) { dev_kfree_skb(skb); return ret; diff --git a/mt7996/mcu.h b/mt7996/mcu.h index 55d42c9e..39a209f2 100644 --- a/mt7996/mcu.h +++ b/mt7996/mcu.h @@ -985,6 +985,7 @@ enum { enum { UNI_VOW_DRR_CTRL, + UNI_VOW_FEATURE_CTRL, UNI_VOW_RX_AT_AIRTIME_EN = 0x0b, UNI_VOW_RX_AT_AIRTIME_CLR_EN = 0x0e, }; diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h index 0d648852..91d94e66 100644 --- a/mt7996/mt7996.h +++ b/mt7996/mt7996.h @@ -240,6 +240,53 @@ struct mt7996_twt_flow { DECLARE_EWMA(avg_signal, 10, 8) +enum { + VOW_SEARCH_AC_FIRST, + VOW_SEARCH_WMM_FIRST, +}; + +enum { + VOW_SCH_TYPE_FOLLOW_POLICY, + VOW_SCH_TYPE_FOLLOW_HW, +}; + +enum { + VOW_SCH_POLICY_SRR, /* Shared Round-Robin */ + VOW_SCH_POLICY_WRR, /* Weighted Round-Robin */ +}; + +/* VOW feature control (UNI_VOW_FEATURE_CTRL) word fields. The apply mask (DW0) + * and the control word (DW5) share these bit positions. + */ +#define VOW_FEATURE_APPLY_REFILL_PERIOD BIT(0) +#define VOW_FEATURE_REFILL_PERIOD GENMASK(2, 0) +#define VOW_FEATURE_BAND1_SEARCH_RULE BIT(4) +#define VOW_FEATURE_BAND0_SEARCH_RULE BIT(5) +#define VOW_FEATURE_WATF_EN BIT(9) +#define VOW_FEATURE_GRP_NO_CHANGE_IN_TXOP BIT(12) +#define VOW_FEATURE_ATF_EN BIT(13) +#define VOW_FEATURE_BWC_TOKEN_REFILL_EN BIT(14) +#define VOW_FEATURE_BWC_EN BIT(15) + +/* ATF control word (DW8) */ +#define VOW_ATF_APPLY_KEEP_QUANTUM BIT(2) +#define VOW_ATF_KEEP_QUANTUM BIT(3) +#define VOW_ATF_APPLY_VOW_CTRL BIT(24) +#define VOW_ATF_VOW_CTRL_VAL BIT(25) +#define VOW_ATF_VOW_CTRL_BIT GENMASK(30, 26) + +/* Scheduler control word (DW9) */ +#define VOW_SCH_APPLY_CTRL BIT(6) +#define VOW_SCH_TYPE GENMASK(8, 7) +#define VOW_SCH_POLICY GENMASK(10, 9) + +enum vow_drr_ctrl_id { + VOW_DRR_CTRL_STA_ALL = 0x00, + VOW_DRR_CTRL_AIRTIME_DEFICIT_BOUND = 0x10, + VOW_DRR_CTRL_AIRTIME_QUANTUM_ALL = 0x28, + VOW_DRR_CTRL_STA_PAUSE = 0x30, +}; + struct mt7996_sta_link { struct mt76_wcid wcid; /* must be first */ @@ -519,6 +566,8 @@ struct mt7996_dev { u8 type:4; u8 fem:4; } var; + + bool vow_atf_en; }; enum { @@ -760,6 +809,11 @@ int mt7996_mcu_get_efuse_free_block(struct mt7996_dev *dev, u8 *block_num); int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap); int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 set, u8 band); int mt7996_mcu_set_txbf(struct mt7996_dev *dev, u8 action); +int mt7996_mcu_set_vow_drr_ctrl(struct mt7996_dev *dev, u8 band_idx, + struct mt76_wcid *wcid, struct mt76_vif_link *mvif, + enum vow_drr_ctrl_id id, u16 weight); +int mt7996_mcu_set_vow_feature_ctrl(struct mt7996_phy *phy); +void mt7996_vow_init(struct mt7996_phy *phy); int mt7996_mcu_set_fcc5_lpn(struct mt7996_dev *dev, int val); int mt7996_mcu_set_pulse_th(struct mt7996_dev *dev, const struct mt7996_dfs_pulse *pulse);