| From 5ebdb72f481cab029434e63a70b5c16d9fd2ec64 Mon Sep 17 00:00:00 2001 |
| From: StanleyYP Wang <StanleyYP.Wang@mediatek.com> |
| Date: Thu, 6 Apr 2023 16:40:28 +0800 |
| Subject: [PATCH 1033/1041] mtk: wifi: mt76: testmode: add testmode bf support |
| |
| Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com> |
| |
| Add iTest additional bf command |
| |
| Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com> |
| |
| fw return Gx (5g band) ibf cal struct for both 2G and 5G ibf. |
| Therefore, memcpy cannot be used for 2G ibf cal. |
| https://gerrit.mediatek.inc/c/neptune/wlan_driver/logan/+/8206056 |
| |
| Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com> |
| --- |
| mt76.h | 5 + |
| mt76_connac_mcu.h | 4 +- |
| mt7996/mcu.c | 8 +- |
| mt7996/mcu.h | 45 ++- |
| mt7996/mt7996.h | 12 +- |
| mt7996/mtk_debugfs.c | 6 +- |
| mt7996/mtk_mcu.c | 79 ++++- |
| mt7996/mtk_mcu.h | 338 +++++++++++++++++++- |
| mt7996/regs.h | 3 + |
| mt7996/testmode.c | 744 +++++++++++++++++++++++++++++++++++++++++-- |
| mt7996/testmode.h | 19 ++ |
| testmode.c | 60 ++++ |
| testmode.h | 53 +++ |
| tools/fields.c | 37 +++ |
| 14 files changed, 1354 insertions(+), 59 deletions(-) |
| |
| diff --git a/mt76.h b/mt76.h |
| index 9e8848f7..2a7b0ed9 100644 |
| --- a/mt76.h |
| +++ b/mt76.h |
| @@ -751,6 +751,11 @@ struct mt76_testmode_data { |
| u32 tx_time; |
| u32 tx_ipg; |
| |
| + u8 txbf_act; |
| + u16 txbf_param[8]; |
| + bool is_txbf_dut; |
| + bool bf_en; |
| + bool bf_ever_en; |
| bool ibf; |
| bool ebf; |
| |
| diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h |
| index 615c42b1..306e7bee 100644 |
| --- a/mt76_connac_mcu.h |
| +++ b/mt76_connac_mcu.h |
| @@ -488,7 +488,8 @@ struct sta_rec_bf { |
| bool codebook75_mu; |
| |
| u8 he_ltf; |
| - u8 rsv[3]; |
| + u8 pp_fd_val; |
| + u8 rsv[2]; |
| } __packed; |
| |
| struct sta_rec_bfee { |
| @@ -1267,6 +1268,7 @@ enum { |
| MCU_UNI_CMD_VOW = 0x37, |
| MCU_UNI_CMD_PP = 0x38, |
| MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40, |
| + MCU_UNI_CMD_TESTMODE_TRX_PARAM = 0x42, |
| MCU_UNI_CMD_TESTMODE_CTRL = 0x46, |
| MCU_UNI_CMD_PRECAL_RESULT = 0x47, |
| MCU_UNI_CMD_RRO = 0x57, |
| diff --git a/mt7996/mcu.c b/mt7996/mcu.c |
| index 7f235f91..6ba9c9d5 100644 |
| --- a/mt7996/mcu.c |
| +++ b/mt7996/mcu.c |
| @@ -1067,7 +1067,12 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb, |
| bss->hw_bss_idx = idx; |
| |
| if (vif->type == NL80211_IFTYPE_MONITOR) { |
| - memcpy(bss->bssid, phy->macaddr, ETH_ALEN); |
| + struct mt76_testmode_data *td = &phy->test; |
| + |
| + if (!td->bf_en) |
| + memcpy(bss->bssid, phy->macaddr, ETH_ALEN); |
| + else |
| + memcpy(bss->bssid, td->addr[2], ETH_ALEN); |
| return 0; |
| } |
| |
| @@ -4103,7 +4108,6 @@ int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 val, u8 band) |
| int mt7996_mcu_set_txbf(struct mt7996_dev *dev, u8 action) |
| { |
| #define MT7996_BF_MAX_SIZE sizeof(union bf_tag_tlv) |
| -#define BF_PROCESSING 4 |
| struct uni_header hdr; |
| struct sk_buff *skb; |
| struct tlv *tlv; |
| diff --git a/mt7996/mcu.h b/mt7996/mcu.h |
| index af078edd..054a616b 100644 |
| --- a/mt7996/mcu.h |
| +++ b/mt7996/mcu.h |
| @@ -685,6 +685,22 @@ struct bf_sounding_on { |
| __le32 snd_period; |
| } __packed; |
| |
| +enum sounding_mode { |
| + SU_SOUNDING, |
| + MU_SOUNDING, |
| + SU_PERIODIC_SOUNDING, |
| + MU_PERIODIC_SOUNDING, |
| + BF_PROCESSING, |
| + TXCMD_NONTB_SU_SOUNDING, |
| + TXCMD_VHT_MU_SOUNDING, |
| + TXCMD_TB_PER_BRP_SOUNDING, |
| + TXCMD_TB_SOUNDING, |
| + |
| + /* keep last */ |
| + NUM_SOUNDING_MODE, |
| + SOUNDING_MODE_MAX = NUM_SOUNDING_MODE - 1, |
| +}; |
| + |
| struct bf_hw_en_status_update { |
| __le16 tag; |
| __le16 len; |
| @@ -710,6 +726,24 @@ union bf_tag_tlv { |
| struct bf_mod_en_ctrl bf_mod_en; |
| }; |
| |
| +enum { |
| + BF_SOUNDING_OFF = 0, |
| + BF_SOUNDING_ON = 1, |
| + BF_DATA_PACKET_APPLY = 2, |
| + BF_PFMU_TAG_READ = 5, |
| + BF_PFMU_TAG_WRITE = 6, |
| + BF_STA_REC_READ = 11, |
| + BF_PHASE_CALIBRATION = 12, |
| + BF_IBF_PHASE_COMP = 13, |
| + BF_PROFILE_WRITE_20M_ALL = 15, |
| + BF_HW_EN_UPDATE = 17, |
| + BF_MOD_EN_CTRL = 20, |
| + BF_FBRPT_DBG_INFO_READ = 23, |
| + BF_TXSND_INFO = 24, |
| + BF_CMD_TXCMD = 27, |
| + BF_CFG_PHY = 28, |
| +}; |
| + |
| struct ra_rate { |
| __le16 wlan_idx; |
| u8 mode; |
| @@ -771,17 +805,6 @@ enum { |
| #define MUMIMO_UL BIT(3) |
| #define MUMIMO_DL_CERT BIT(4) |
| |
| -enum { |
| - BF_SOUNDING_ON = 1, |
| - BF_PFMU_TAG_READ = 5, |
| - BF_STA_REC_READ = 11, |
| - BF_HW_EN_UPDATE = 17, |
| - BF_MOD_EN_CTRL = 20, |
| - BF_FBRPT_DBG_INFO_READ = 23, |
| - BF_TXSND_INFO = 24, |
| - BF_CFG_PHY = 28, |
| -}; |
| - |
| enum { |
| CMD_BAND_NONE, |
| CMD_BAND_24G, |
| diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h |
| index e1a191ec..b5ce4a55 100644 |
| --- a/mt7996/mt7996.h |
| +++ b/mt7996/mt7996.h |
| @@ -479,6 +479,14 @@ struct mt7996_dev { |
| #ifdef CONFIG_MTK_VENDOR |
| bool cert_mode; |
| #endif |
| + |
| +#if defined CONFIG_NL80211_TESTMODE || defined CONFIG_MTK_DEBUG |
| + struct { |
| + void *txbf_phase_cal; |
| + void *txbf_pfmu_data; |
| + void *txbf_pfmu_tag; |
| + } test; |
| +#endif |
| }; |
| |
| enum { |
| @@ -818,7 +826,7 @@ int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val); |
| int mt7996_mcu_set_sr_enable(struct mt7996_phy *phy, u8 action, u64 val, bool set); |
| void mt7996_mcu_rx_sr_event(struct mt7996_dev *dev, struct sk_buff *skb); |
| int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev); |
| -int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx); |
| +int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx, bool bfer); |
| void mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb); |
| int mt7996_mcu_set_muru_fixed_rate_enable(struct mt7996_dev *dev, u8 action, int val); |
| int mt7996_mcu_set_muru_fixed_rate_parameter(struct mt7996_dev *dev, u8 action, void *para); |
| @@ -830,10 +838,12 @@ int mt7996_mcu_set_rfeature_trig_type(struct mt7996_phy *phy, u8 enable, u8 trig |
| void mt7996_mcu_set_ppdu_tx_type(struct mt7996_phy *phy, u8 ppdu_type); |
| void mt7996_mcu_set_nusers_ofdma(struct mt7996_phy *phy, u8 type, u8 ofdma_user_cnt); |
| void mt7996_mcu_set_cert(struct mt7996_phy *phy, u8 type); |
| +void mt7996_tm_update_channel(struct mt7996_phy *phy); |
| #endif |
| |
| #ifdef CONFIG_NET_MEDIATEK_SOC_WED |
| int mt7996_dma_rro_init(struct mt7996_dev *dev); |
| #endif /* CONFIG_NET_MEDIATEK_SOC_WED */ |
| |
| + |
| #endif |
| diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c |
| index 1530ad79..0fdb911d 100644 |
| --- a/mt7996/mtk_debugfs.c |
| +++ b/mt7996/mtk_debugfs.c |
| @@ -2803,7 +2803,7 @@ mt7996_starec_bf_read_set(void *data, u64 wlan_idx) |
| { |
| struct mt7996_phy *phy = data; |
| |
| - return mt7996_mcu_set_txbf_internal(phy, BF_STA_REC_READ, wlan_idx); |
| + return mt7996_mcu_set_txbf_internal(phy, BF_STA_REC_READ, wlan_idx, 0); |
| } |
| DEFINE_DEBUGFS_ATTRIBUTE(fops_starec_bf_read, NULL, |
| mt7996_starec_bf_read_set, "%lld\n"); |
| @@ -2847,7 +2847,7 @@ mt7996_bf_fbk_rpt_set(void *data, u64 wlan_idx) |
| { |
| struct mt7996_phy *phy = data; |
| |
| - return mt7996_mcu_set_txbf_internal(phy, BF_FBRPT_DBG_INFO_READ, wlan_idx); |
| + return mt7996_mcu_set_txbf_internal(phy, BF_FBRPT_DBG_INFO_READ, wlan_idx, 0); |
| } |
| DEFINE_DEBUGFS_ATTRIBUTE(fops_bf_fbk_rpt, NULL, |
| mt7996_bf_fbk_rpt_set, "%lld\n"); |
| @@ -2857,7 +2857,7 @@ mt7996_bf_pfmu_tag_read_set(void *data, u64 wlan_idx) |
| { |
| struct mt7996_phy *phy = data; |
| |
| - return mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, wlan_idx); |
| + return mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, wlan_idx, 1); |
| } |
| DEFINE_DEBUGFS_ATTRIBUTE(fops_bf_pfmu_tag_read, NULL, |
| mt7996_bf_pfmu_tag_read_set, "%lld\n"); |
| diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c |
| index 3b8fbe42..2d8b08cd 100644 |
| --- a/mt7996/mtk_mcu.c |
| +++ b/mt7996/mtk_mcu.c |
| @@ -296,7 +296,7 @@ __mt7996_mcu_add_uni_tlv(struct sk_buff *skb, u16 tag, u16 len) |
| return ptlv; |
| } |
| |
| -int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx) |
| +int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx, bool bfer) |
| { |
| struct mt7996_dev *dev = phy->dev; |
| #define MT7996_MTK_BF_MAX_SIZE sizeof(struct bf_starec_read) |
| @@ -319,9 +319,8 @@ int mt7996_mcu_set_txbf_internal(struct mt7996_phy *phy, u8 action, int idx) |
| |
| tlv = __mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req)); |
| req = (struct bf_pfmu_tag *)tlv; |
| -#define BFER 1 |
| req->pfmu_id = idx; |
| - req->bfer = BFER; |
| + req->bfer = bfer; |
| req->band_idx = phy->mt76->band_idx; |
| break; |
| } |
| @@ -433,10 +432,36 @@ int mt7996_mcu_set_txbf_snd_info(struct mt7996_phy *phy, void *para) |
| return mt76_mcu_skb_send_msg(&phy->dev->mt76, skb, MCU_WM_UNI_CMD(BF), false); |
| } |
| |
| +static inline void |
| +mt7996_ibf_phase_assign(struct mt7996_dev *dev, |
| + struct mt7996_ibf_cal_info *cal, |
| + struct mt7996_txbf_phase *phase) |
| +{ |
| + /* fw return ibf calibrated data with |
| + * the mt7996_txbf_phase_info_5g struct for both 2G and 5G. |
| + * Therefore, memcpy cannot be used here. |
| + */ |
| + phase_assign(cal->group, m_t0_h, true); |
| + phase_assign(cal->group, m_t1_h, true); |
| + phase_assign(cal->group, m_t2_h, true); |
| + phase_assign(cal->group, m_t2_h_sx2, false); |
| + phase_assign_rx(cal->group, r0); |
| + phase_assign_rx(cal->group, r1); |
| + phase_assign_rx(cal->group, r2); |
| + phase_assign_rx(cal->group, r3); |
| + phase_assign_rx_g0(cal->group, r2_sx2); |
| + phase_assign_rx_g0(cal->group, r3_sx2); |
| + phase_assign(cal->group, r0_reserved, false); |
| + phase_assign(cal->group, r1_reserved, false); |
| + phase_assign(cal->group, r2_reserved, false); |
| + phase_assign(cal->group, r3_reserved, false); |
| + phase_assign(cal->group, r2_sx2_reserved, false); |
| + phase_assign(cal->group, r3_sx2_reserved, false); |
| +} |
| + |
| void |
| mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb) |
| { |
| -#define HE_MODE 3 |
| struct mt7996_mcu_bf_basic_event *event; |
| |
| event = (struct mt7996_mcu_bf_basic_event *)skb->data; |
| @@ -471,13 +496,12 @@ mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb) |
| tag->t1.nr, tag->t1.nc, tag->t1.ngroup, tag->t1.lm, tag->t1.codebook, |
| tag->t1.mob_cal_en); |
| |
| - if (tag->t1.lm <= HE_MODE) { |
| + if (tag->t1.lm <= BF_LM_HE) |
| dev_info(dev->mt76.dev, "RU start = %d, RU end = %d\n", |
| tag->t1.field.ru_start_id, tag->t1.field.ru_end_id); |
| - } else { |
| + else |
| dev_info(dev->mt76.dev, "PartialBW = %d\n", |
| tag->t1.bw_info.partial_bw_info); |
| - } |
| |
| dev_info(dev->mt76.dev, "Mem Col1 = %d, Mem Row1 = %d, Mem Col2 = %d, Mem Row2 = %d\n", |
| tag->t1.col_id1, tag->t1.row_id1, tag->t1.col_id2, tag->t1.row_id2); |
| @@ -731,6 +755,47 @@ mt7996_mcu_rx_bf_event(struct mt7996_dev *dev, struct sk_buff *skb) |
| |
| break; |
| } |
| + case UNI_EVENT_BF_CAL_PHASE: { |
| + struct mt7996_ibf_cal_info *cal; |
| + struct mt7996_txbf_phase_out phase_out; |
| + struct mt7996_txbf_phase *phase; |
| + |
| + cal = (struct mt7996_ibf_cal_info *)skb->data; |
| + phase = (struct mt7996_txbf_phase *)dev->test.txbf_phase_cal; |
| + memcpy(&phase_out, &cal->phase_out, sizeof(phase_out)); |
| + switch (cal->cal_type) { |
| + case IBF_PHASE_CAL_NORMAL: |
| + case IBF_PHASE_CAL_NORMAL_INSTRUMENT: |
| + /* Only calibrate group M */ |
| + if (cal->group_l_m_n != GROUP_M) |
| + break; |
| + phase = &phase[cal->group]; |
| + phase->status = cal->status; |
| + dev_info(dev->mt76.dev, "Calibrated result = %d\n", phase->status); |
| + dev_info(dev->mt76.dev, "Group %d and Group M\n", cal->group); |
| + mt7996_ibf_phase_assign(dev, cal, phase); |
| + break; |
| + case IBF_PHASE_CAL_VERIFY: |
| + case IBF_PHASE_CAL_VERIFY_INSTRUMENT: |
| + dev_info(dev->mt76.dev, "Verification result = %d\n", cal->status); |
| + break; |
| + default: |
| + break; |
| + } |
| + |
| + dev_info(dev->mt76.dev, "c0_uh = %d, c1_uh = %d, c2_uh = %d, c3_uh = %d\n", |
| + phase_out.c0_uh, phase_out.c1_uh, phase_out.c2_uh, phase_out.c3_uh); |
| + dev_info(dev->mt76.dev, "c0_h = %d, c1_h = %d, c2_h = %d, c3_h = %d\n", |
| + phase_out.c0_h, phase_out.c1_h, phase_out.c2_h, phase_out.c3_h); |
| + dev_info(dev->mt76.dev, "c0_mh = %d, c1_mh = %d, c2_mh = %d, c3_mh = %d\n", |
| + phase_out.c0_mh, phase_out.c1_mh, phase_out.c2_mh, phase_out.c3_mh); |
| + dev_info(dev->mt76.dev, "c0_m = %d, c1_m = %d, c2_m = %d, c3_m = %d\n", |
| + phase_out.c0_m, phase_out.c1_m, phase_out.c2_m, phase_out.c3_m); |
| + dev_info(dev->mt76.dev, "c0_l = %d, c1_l = %d, c2_l = %d, c3_l = %d\n", |
| + phase_out.c0_l, phase_out.c1_l, phase_out.c2_l, phase_out.c3_l); |
| + |
| + break; |
| + } |
| default: |
| dev_info(dev->mt76.dev, "%s: unknown bf event tag %d\n", |
| __func__, event->tag); |
| diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h |
| index 44cb0327..881afef1 100644 |
| --- a/mt7996/mtk_mcu.h |
| +++ b/mt7996/mtk_mcu.h |
| @@ -176,6 +176,164 @@ struct bf_txsnd_info { |
| u8 __rsv[2]; |
| } __packed; |
| |
| +#define MAX_PHASE_GROUP_NUM 9 |
| + |
| +struct bf_phase_comp { |
| + __le16 tag; |
| + __le16 len; |
| + |
| + u8 bw; |
| + u8 jp_band; |
| + u8 band_idx; |
| + bool read_from_e2p; |
| + bool disable; |
| + u8 group; |
| + u8 rsv[2]; |
| + u8 buf[44]; |
| +} __packed; |
| + |
| +struct bf_tx_apply { |
| + __le16 tag; |
| + __le16 len; |
| + |
| + __le16 wlan_idx; |
| + bool ebf; |
| + bool ibf; |
| + bool mu_txbf; |
| + bool phase_cal; |
| + u8 rsv[2]; |
| +} __packed; |
| + |
| +struct bf_phase_cal { |
| + __le16 tag; |
| + __le16 len; |
| + |
| + u8 group_l_m_n; |
| + u8 group; |
| + u8 sx2; |
| + u8 cal_type; |
| + u8 lna_gain_level; |
| + u8 band_idx; |
| + u8 rsv[2]; |
| +} __packed; |
| + |
| +struct bf_txcmd { |
| + __le16 tag; |
| + __le16 len; |
| + |
| + u8 action; |
| + u8 bf_manual; |
| + u8 bf_bit; |
| + u8 rsv[5]; |
| +} __packed; |
| + |
| +struct bf_pfmu_data_all { |
| + __le16 tag; |
| + __le16 len; |
| + |
| + u8 pfmu_id; |
| + u8 band_idx; |
| + u8 rsv[2]; |
| + |
| + u8 buf[512]; |
| +} __packed; |
| + |
| +#define TXBF_DUT_MAC_SUBADDR 0x22 |
| +#define TXBF_GOLDEN_MAC_SUBADDR 0x11 |
| + |
| +struct mt7996_tm_bf_req { |
| + u8 _rsv[4]; |
| + |
| + union { |
| + struct bf_sounding_on sounding; |
| + struct bf_tx_apply tx_apply; |
| + struct bf_pfmu_tag pfmu_tag; |
| + struct bf_pfmu_data_all pfmu_data_all; |
| + struct bf_phase_cal phase_cal; |
| + struct bf_phase_comp phase_comp; |
| + struct bf_txcmd txcmd; |
| + }; |
| +} __packed; |
| + |
| +enum tm_trx_mac_type { |
| + TM_TRX_MAC_TX = 1, |
| + TM_TRX_MAC_RX, |
| + TM_TRX_MAC_TXRX, |
| + TM_TRX_MAC_TXRX_RXV, |
| + TM_TRX_MAC_RXV, |
| + TM_TRX_MAC_RX_RXV, |
| +}; |
| + |
| +enum tm_trx_param_idx { |
| + TM_TRX_PARAM_RSV, |
| + /* MAC */ |
| + TM_TRX_PARAM_SET_TRX, |
| + TM_TRX_PARAM_RX_FILTER, |
| + TM_TRX_PARAM_RX_FILTER_PKT_LEN, |
| + TM_TRX_PARAM_SLOT_TIME, |
| + TM_TRX_PARAM_CLEAN_PERSTA_TXQUEUE, |
| + TM_TRX_PARAM_AMPDU_WTBL, |
| + TM_TRX_PARAM_MU_RX_AID, |
| + TM_TRX_PARAM_PHY_MANUAL_TX, |
| + |
| + /* PHY */ |
| + TM_TRX_PARAM_RX_PATH, |
| + TM_TRX_PARAM_TX_STREAM, |
| + TM_TRX_PARAM_TSSI_STATUS, |
| + TM_TRX_PARAM_DPD_STATUS, |
| + TM_TRX_PARAM_RATE_POWER_OFFSET_ON_OFF, |
| + TM_TRX_PARAM_THERMO_COMP_STATUS, |
| + TM_TRX_PARAM_FREQ_OFFSET, |
| + TM_TRX_PARAM_FAGC_RSSI_PATH, |
| + TM_TRX_PARAM_PHY_STATUS_COUNT, |
| + TM_TRX_PARAM_RXV_INDEX, |
| + |
| + TM_TRX_PARAM_ANTENNA_PORT, |
| + TM_TRX_PARAM_THERMAL_ONOFF, |
| + TM_TRX_PARAM_TX_POWER_CONTROL_ALL_RF, |
| + TM_TRX_PARAM_RATE_POWER_OFFSET, |
| + TM_TRX_PARAM_SLT_CMD_TEST, |
| + TM_TRX_PARAM_SKU, |
| + TM_TRX_PARAM_POWER_PERCENTAGE_ON_OFF, |
| + TM_TRX_PARAM_BF_BACKOFF_ON_OFF, |
| + TM_TRX_PARAM_POWER_PERCENTAGE_LEVEL, |
| + TM_TRX_PARAM_FRTBL_CFG, |
| + TM_TRX_PARAM_PREAMBLE_PUNC_ON_OFF, |
| + |
| + TM_TRX_PARAM_MAX_NUM, |
| +}; |
| + |
| +enum trx_action { |
| + TM_TRX_ACTION_SET, |
| + TM_TRX_ACTION_GET, |
| +}; |
| + |
| +struct tm_trx_set { |
| + u8 type; |
| + u8 enable; |
| + u8 band_idx; |
| + u8 rsv; |
| +} __packed; |
| + |
| +struct mt7996_tm_trx_req { |
| + u8 param_num; |
| + u8 _rsv[3]; |
| + |
| + __le16 tag; |
| + __le16 len; |
| + |
| + __le16 param_idx; |
| + u8 band_idx; |
| + u8 testmode_en; |
| + u8 action; |
| + u8 rsv[3]; |
| + |
| + u32 data; |
| + struct tm_trx_set set_trx; |
| + |
| + u8 buf[220]; |
| +} __packed; |
| + |
| struct mt7996_mcu_bf_basic_event { |
| struct mt7996_mcu_rxd rxd; |
| |
| @@ -381,6 +539,181 @@ struct mt7996_pfmu_tag_event { |
| struct mt7996_pfmu_tag2 t2; |
| }; |
| |
| +struct mt7996_pfmu_tag { |
| + struct mt7996_pfmu_tag1 t1; |
| + struct mt7996_pfmu_tag2 t2; |
| +}; |
| + |
| +enum bf_lm_type { |
| + BF_LM_LEGACY, |
| + BF_LM_HT, |
| + BF_LM_VHT, |
| + BF_LM_HE, |
| + BF_LM_EHT, |
| +}; |
| + |
| +struct mt7996_txbf_phase_out { |
| + u8 c0_l; |
| + u8 c1_l; |
| + u8 c2_l; |
| + u8 c3_l; |
| + u8 c0_m; |
| + u8 c1_m; |
| + u8 c2_m; |
| + u8 c3_m; |
| + u8 c0_mh; |
| + u8 c1_mh; |
| + u8 c2_mh; |
| + u8 c3_mh; |
| + u8 c0_h; |
| + u8 c1_h; |
| + u8 c2_h; |
| + u8 c3_h; |
| + u8 c0_uh; |
| + u8 c1_uh; |
| + u8 c2_uh; |
| + u8 c3_uh; |
| +}; |
| + |
| +struct mt7996_txbf_rx_phase_2g { |
| + u8 rx_uh; |
| + u8 rx_h; |
| + u8 rx_m; |
| + u8 rx_l; |
| + u8 rx_ul; |
| +}; |
| + |
| +struct mt7996_txbf_rx_phase_5g { |
| + u8 rx_uh; |
| + u8 rx_h; |
| + u8 rx_mh; |
| + u8 rx_m; |
| + u8 rx_l; |
| + u8 rx_ul; |
| +}; |
| + |
| +struct mt7996_txbf_phase_info_2g { |
| + struct mt7996_txbf_rx_phase_2g r0; |
| + struct mt7996_txbf_rx_phase_2g r1; |
| + struct mt7996_txbf_rx_phase_2g r2; |
| + struct mt7996_txbf_rx_phase_2g r3; |
| + struct mt7996_txbf_rx_phase_2g r2_sx2; |
| + struct mt7996_txbf_rx_phase_2g r3_sx2; |
| + u8 m_t0_h; |
| + u8 m_t1_h; |
| + u8 m_t2_h; |
| + u8 m_t2_h_sx2; |
| + u8 r0_reserved; |
| + u8 r1_reserved; |
| + u8 r2_reserved; |
| + u8 r3_reserved; |
| + u8 r2_sx2_reserved; |
| + u8 r3_sx2_reserved; |
| +}; |
| + |
| +struct mt7996_txbf_phase_info_5g { |
| + struct mt7996_txbf_rx_phase_5g r0; |
| + struct mt7996_txbf_rx_phase_5g r1; |
| + struct mt7996_txbf_rx_phase_5g r2; |
| + struct mt7996_txbf_rx_phase_5g r3; |
| + struct mt7996_txbf_rx_phase_2g r2_sx2; /* no middle-high in r2_sx2 */ |
| + struct mt7996_txbf_rx_phase_2g r3_sx2; /* no middle-high in r3_sx2 */ |
| + u8 m_t0_h; |
| + u8 m_t1_h; |
| + u8 m_t2_h; |
| + u8 m_t2_h_sx2; |
| + u8 r0_reserved; |
| + u8 r1_reserved; |
| + u8 r2_reserved; |
| + u8 r3_reserved; |
| + u8 r2_sx2_reserved; |
| + u8 r3_sx2_reserved; |
| +}; |
| + |
| +struct mt7996_txbf_phase { |
| + u8 status; |
| + union { |
| + struct mt7996_txbf_phase_info_2g phase_2g; |
| + struct mt7996_txbf_phase_info_5g phase_5g; |
| + }; |
| +}; |
| + |
| +#define phase_assign(group, field, dump, ...) ({ \ |
| + if (group) { \ |
| + phase->phase_5g.field = cal->phase_5g.field; \ |
| + if (dump) \ |
| + dev_info(dev->mt76.dev, "%s = %d\n", #field, phase->phase_5g.field); \ |
| + } else { \ |
| + phase->phase_2g.field = cal->phase_5g.field; \ |
| + if (dump) \ |
| + dev_info(dev->mt76.dev, "%s = %d\n", #field, phase->phase_2g.field); \ |
| + } \ |
| +}) |
| + |
| +#define phase_assign_rx_g0(group, rx, ...) ({ \ |
| + phase_assign(group, rx.rx_uh, false); \ |
| + phase_assign(group, rx.rx_h, false); \ |
| + phase_assign(group, rx.rx_m, false); \ |
| + phase_assign(group, rx.rx_l, false); \ |
| + phase_assign(group, rx.rx_ul, false); \ |
| +}) |
| + |
| +#define phase_assign_rx(group, rx, ...) ({ \ |
| + if (group) { \ |
| + phase_assign(group, rx.rx_uh, true); \ |
| + phase_assign(group, rx.rx_h, true); \ |
| + phase->phase_5g.rx.rx_mh = cal->phase_5g.rx.rx_mh; \ |
| + dev_info(dev->mt76.dev, "%s.rx_mh = %d\n", #rx, phase->phase_5g.rx.rx_mh); \ |
| + phase_assign(group, rx.rx_m, true); \ |
| + phase_assign(group, rx.rx_l, true); \ |
| + phase_assign(group, rx.rx_ul, true); \ |
| + } else { \ |
| + phase_assign(group, rx.rx_uh, true); \ |
| + phase_assign(group, rx.rx_h, true); \ |
| + phase_assign(group, rx.rx_m, true); \ |
| + phase_assign(group, rx.rx_l, true); \ |
| + phase_assign(group, rx.rx_ul, true); \ |
| + } \ |
| +}) |
| + |
| +#define GROUP_L 0 |
| +#define GROUP_M 1 |
| +#define GROUP_H 2 |
| + |
| +struct mt7996_pfmu_data { |
| + __le16 subc_idx; |
| + __le16 phi11; |
| + __le16 phi21; |
| + __le16 phi31; |
| +}; |
| + |
| +struct mt7996_ibf_cal_info { |
| + struct mt7996_mcu_bf_basic_event event; |
| + |
| + u8 category_id; |
| + u8 group_l_m_n; |
| + u8 group; |
| + bool sx2; |
| + u8 status; |
| + u8 cal_type; |
| + u8 _rsv[2]; |
| + struct mt7996_txbf_phase_out phase_out; |
| + union { |
| + struct mt7996_txbf_phase_info_2g phase_2g; |
| + struct mt7996_txbf_phase_info_5g phase_5g; |
| + }; |
| +} __packed; |
| + |
| +enum { |
| + IBF_PHASE_CAL_UNSPEC, |
| + IBF_PHASE_CAL_NORMAL, |
| + IBF_PHASE_CAL_VERIFY, |
| + IBF_PHASE_CAL_NORMAL_INSTRUMENT, |
| + IBF_PHASE_CAL_VERIFY_INSTRUMENT, |
| +}; |
| + |
| +#define MT7996_TXBF_SUBCAR_NUM 64 |
| + |
| enum { |
| UNI_EVENT_BF_PFMU_TAG = 0x5, |
| UNI_EVENT_BF_PFMU_DATA = 0x7, |
| @@ -395,11 +728,6 @@ enum { |
| UNI_EVENT_BF_MAX_NUM |
| }; |
| |
| -enum { |
| - UNI_CMD_MURU_FIXED_RATE_CTRL = 0x11, |
| - UNI_CMD_MURU_FIXED_GROUP_RATE_CTRL, |
| -}; |
| - |
| struct uni_muru_mum_set_group_tbl_entry { |
| __le16 wlan_idx0; |
| __le16 wlan_idx1; |
| diff --git a/mt7996/regs.h b/mt7996/regs.h |
| index e94f9a90..aa04d8d2 100644 |
| --- a/mt7996/regs.h |
| +++ b/mt7996/regs.h |
| @@ -326,6 +326,9 @@ enum offs_rev { |
| #define MT_ARB_SCR_TX_DISABLE BIT(8) |
| #define MT_ARB_SCR_RX_DISABLE BIT(9) |
| |
| +#define MT_ARB_TQSAXM0(_band) MT_WF_ARB(_band, 0x180) |
| +#define MT_ARB_TQSAXM_ALTX_START_MASK GENMASK(12, 8) |
| + |
| /* RMAC: band 0(0x820e5000), band 1(0x820f5000), band 2(0x830e5000), */ |
| #define MT_WF_RMAC_BASE(_band) __BASE(WF_RMAC_BASE, (_band)) |
| #define MT_WF_RMAC(_band, ofs) (MT_WF_RMAC_BASE(_band) + (ofs)) |
| diff --git a/mt7996/testmode.c b/mt7996/testmode.c |
| index 26ae5827..2fb36a97 100644 |
| --- a/mt7996/testmode.c |
| +++ b/mt7996/testmode.c |
| @@ -23,6 +23,7 @@ enum { |
| TM_CHANGED_IPI_THRESHOLD, |
| TM_CHANGED_IPI_PERIOD, |
| TM_CHANGED_IPI_RESET, |
| + TM_CHANGED_TXBF_ACT, |
| |
| /* must be last */ |
| NUM_TM_CHANGED |
| @@ -41,25 +42,31 @@ static const u8 tm_change_map[] = { |
| [TM_CHANGED_IPI_THRESHOLD] = MT76_TM_ATTR_IPI_THRESHOLD, |
| [TM_CHANGED_IPI_PERIOD] = MT76_TM_ATTR_IPI_PERIOD, |
| [TM_CHANGED_IPI_RESET] = MT76_TM_ATTR_IPI_RESET, |
| + [TM_CHANGED_TXBF_ACT] = MT76_TM_ATTR_TXBF_ACT, |
| }; |
| |
| static void mt7996_tm_ipi_work(struct work_struct *work); |
| +static int mt7996_tm_txbf_apply_tx(struct mt7996_phy *phy, u16 wlan_idx, |
| + bool ebf, bool ibf, bool phase_cal); |
| |
| static u32 mt7996_tm_bw_mapping(enum nl80211_chan_width width, enum bw_mapping_method method) |
| { |
| static const u32 width_to_bw[][NUM_BW_MAP] = { |
| - [NL80211_CHAN_WIDTH_40] = {FW_CDBW_40MHZ, TM_CBW_40MHZ, 40, |
| + [NL80211_CHAN_WIDTH_40] = {FW_CDBW_40MHZ, TM_CBW_40MHZ, BF_CDBW_40MHZ, 40, |
| FIRST_CONTROL_CHAN_BITMAP_BW40}, |
| - [NL80211_CHAN_WIDTH_80] = {FW_CDBW_80MHZ, TM_CBW_80MHZ, 80, |
| + [NL80211_CHAN_WIDTH_80] = {FW_CDBW_80MHZ, TM_CBW_80MHZ, BF_CDBW_80MHZ, 80, |
| FIRST_CONTROL_CHAN_BITMAP_BW80}, |
| - [NL80211_CHAN_WIDTH_80P80] = {FW_CDBW_8080MHZ, TM_CBW_8080MHZ, 80, 0x0}, |
| - [NL80211_CHAN_WIDTH_160] = {FW_CDBW_160MHZ, TM_CBW_160MHZ, 160, |
| + [NL80211_CHAN_WIDTH_80P80] = {FW_CDBW_8080MHZ, TM_CBW_8080MHZ, BF_CDBW_8080MHZ, |
| + 80, 0x0}, |
| + [NL80211_CHAN_WIDTH_160] = {FW_CDBW_160MHZ, TM_CBW_160MHZ, BF_CDBW_160MHZ, 160, |
| FIRST_CONTROL_CHAN_BITMAP_BW160}, |
| - [NL80211_CHAN_WIDTH_5] = {FW_CDBW_5MHZ, TM_CBW_5MHZ, 5, 0x0}, |
| - [NL80211_CHAN_WIDTH_10] = {FW_CDBW_10MHZ, TM_CBW_10MHZ, 10, 0x0}, |
| - [NL80211_CHAN_WIDTH_20] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, 20, 0x0}, |
| - [NL80211_CHAN_WIDTH_20_NOHT] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, 20, 0x0}, |
| - [NL80211_CHAN_WIDTH_320] = {FW_CDBW_320MHZ, TM_CBW_320MHZ, 320, 0x0}, |
| + [NL80211_CHAN_WIDTH_5] = {FW_CDBW_5MHZ, TM_CBW_5MHZ, BF_CDBW_5MHZ, 5, 0x0}, |
| + [NL80211_CHAN_WIDTH_10] = {FW_CDBW_10MHZ, TM_CBW_10MHZ, BF_CDBW_10MHZ, 10, 0x0}, |
| + [NL80211_CHAN_WIDTH_20] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, BF_CDBW_20MHZ, 20, 0x0}, |
| + [NL80211_CHAN_WIDTH_20_NOHT] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, BF_CDBW_20MHZ, |
| + 20, 0x0}, |
| + [NL80211_CHAN_WIDTH_320] = {FW_CDBW_320MHZ, TM_CBW_320MHZ, BF_CDBW_320MHZ, |
| + 320, 0x0}, |
| }; |
| |
| if (width >= ARRAY_SIZE(width_to_bw)) |
| @@ -68,26 +75,26 @@ static u32 mt7996_tm_bw_mapping(enum nl80211_chan_width width, enum bw_mapping_m |
| return width_to_bw[width][method]; |
| } |
| |
| -static u8 mt7996_tm_rate_to_phy(u8 tx_rate_mode) |
| +static u8 mt7996_tm_rate_mapping(u8 tx_rate_mode, enum rate_mapping_type type) |
| { |
| - static const u8 rate_to_phy[] = { |
| - [MT76_TM_TX_MODE_CCK] = MT_PHY_TYPE_CCK, |
| - [MT76_TM_TX_MODE_OFDM] = MT_PHY_TYPE_OFDM, |
| - [MT76_TM_TX_MODE_HT] = MT_PHY_TYPE_HT, |
| - [MT76_TM_TX_MODE_VHT] = MT_PHY_TYPE_VHT, |
| - [MT76_TM_TX_MODE_HE_SU] = MT_PHY_TYPE_HE_SU, |
| - [MT76_TM_TX_MODE_HE_EXT_SU] = MT_PHY_TYPE_HE_EXT_SU, |
| - [MT76_TM_TX_MODE_HE_TB] = MT_PHY_TYPE_HE_TB, |
| - [MT76_TM_TX_MODE_HE_MU] = MT_PHY_TYPE_HE_MU, |
| - [MT76_TM_TX_MODE_EHT_SU] = MT_PHY_TYPE_EHT_SU, |
| - [MT76_TM_TX_MODE_EHT_TRIG] = MT_PHY_TYPE_EHT_TRIG, |
| - [MT76_TM_TX_MODE_EHT_MU] = MT_PHY_TYPE_EHT_MU, |
| + static const u8 rate_to_phy[][NUM_RATE_MAP] = { |
| + [MT76_TM_TX_MODE_CCK] = {MT_PHY_TYPE_CCK, BF_LM_LEGACY}, |
| + [MT76_TM_TX_MODE_OFDM] = {MT_PHY_TYPE_OFDM, BF_LM_LEGACY}, |
| + [MT76_TM_TX_MODE_HT] = {MT_PHY_TYPE_HT, BF_LM_HT}, |
| + [MT76_TM_TX_MODE_VHT] = {MT_PHY_TYPE_VHT, BF_LM_VHT}, |
| + [MT76_TM_TX_MODE_HE_SU] = {MT_PHY_TYPE_HE_SU, BF_LM_HE}, |
| + [MT76_TM_TX_MODE_HE_EXT_SU] = {MT_PHY_TYPE_HE_EXT_SU, BF_LM_HE}, |
| + [MT76_TM_TX_MODE_HE_TB] = {MT_PHY_TYPE_HE_TB, BF_LM_HE}, |
| + [MT76_TM_TX_MODE_HE_MU] = {MT_PHY_TYPE_HE_MU, BF_LM_HE}, |
| + [MT76_TM_TX_MODE_EHT_SU] = {MT_PHY_TYPE_EHT_SU, BF_LM_EHT}, |
| + [MT76_TM_TX_MODE_EHT_TRIG] = {MT_PHY_TYPE_EHT_TRIG, BF_LM_EHT}, |
| + [MT76_TM_TX_MODE_EHT_MU] = {MT_PHY_TYPE_EHT_MU, BF_LM_EHT}, |
| }; |
| |
| if (tx_rate_mode > MT76_TM_TX_MODE_MAX) |
| return -EINVAL; |
| |
| - return rate_to_phy[tx_rate_mode]; |
| + return rate_to_phy[tx_rate_mode][type]; |
| } |
| |
| static int |
| @@ -239,7 +246,7 @@ mt7996_tm_init(struct mt7996_phy *phy, bool en) |
| INIT_DELAYED_WORK(&phy->ipi_work, mt7996_tm_ipi_work); |
| } |
| |
| -static void |
| +void |
| mt7996_tm_update_channel(struct mt7996_phy *phy) |
| { |
| #define CHAN_FREQ_BW_80P80_TAG (SET_ID(CHAN_FREQ) | BIT(16)) |
| @@ -303,7 +310,8 @@ mt7996_tm_set_tx_frames(struct mt7996_phy *phy, bool en) |
| mt7996_tm_set(dev, SET_ID(MAC_HEADER), FRAME_CONTROL); |
| mt7996_tm_set(dev, SET_ID(SEQ_CTRL), 0); |
| mt7996_tm_set(dev, SET_ID(TX_COUNT), td->tx_count); |
| - mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode)); |
| + mt7996_tm_set(dev, SET_ID(TX_MODE), |
| + mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_PHY)); |
| mt7996_tm_set(dev, SET_ID(TX_RATE), td->tx_rate_idx); |
| |
| if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER)) |
| @@ -331,7 +339,8 @@ mt7996_tm_set_tx_frames(struct mt7996_phy *phy, bool en) |
| |
| mt7996_tm_set(dev, SET_ID(MAX_PE), 2); |
| mt7996_tm_set(dev, SET_ID(HW_TX_MODE), 0); |
| - mt7996_tm_update_channel(phy); |
| + if (!td->bf_en) |
| + mt7996_tm_update_channel(phy); |
| |
| /* trigger firmware to start TX */ |
| mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(START_TX)); |
| @@ -373,7 +382,8 @@ mt7996_tm_set_rx_frames(struct mt7996_phy *phy, bool en) |
| return; |
| } |
| |
| - mt7996_tm_update_channel(phy); |
| + if (!td->bf_en) |
| + mt7996_tm_update_channel(phy); |
| |
| if (td->tx_rate_mode >= MT76_TM_TX_MODE_HE_MU) { |
| if (td->aid) |
| @@ -381,7 +391,8 @@ mt7996_tm_set_rx_frames(struct mt7996_phy *phy, bool en) |
| else |
| ret = mt7996_tm_set(dev, SET_ID(RX_MU_AID), RX_MU_DISABLE); |
| } |
| - mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode)); |
| + mt7996_tm_set(dev, SET_ID(TX_MODE), |
| + mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_PHY)); |
| mt7996_tm_set(dev, SET_ID(GI), td->tx_rate_sgi); |
| mt7996_tm_set_antenna(phy, SET_ID(RX_PATH)); |
| mt7996_tm_set(dev, SET_ID(MAX_PE), 2); |
| @@ -405,7 +416,8 @@ mt7996_tm_set_tx_cont(struct mt7996_phy *phy, bool en) |
| |
| if (en) { |
| mt7996_tm_update_channel(phy); |
| - mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode)); |
| + mt7996_tm_set(dev, SET_ID(TX_MODE), |
| + mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_PHY)); |
| mt7996_tm_set(dev, SET_ID(TX_RATE), td->tx_rate_idx); |
| /* fix payload is OFDM */ |
| mt7996_tm_set(dev, SET_ID(CONT_WAVE_MODE), CONT_WAVE_MODE_OFDM); |
| @@ -1047,6 +1059,678 @@ mt7996_tm_set_ipi(struct mt7996_phy *phy) |
| return 0; |
| } |
| |
| +static int |
| +mt7996_tm_set_trx_mac(struct mt7996_phy *phy, u8 type, bool en) |
| +{ |
| +#define UNI_TM_TRX_CTRL 0 |
| + struct mt7996_dev *dev = phy->dev; |
| + struct mt7996_tm_trx_req req = { |
| + .param_num = 1, |
| + .tag = cpu_to_le16(UNI_TM_TRX_CTRL), |
| + .len = cpu_to_le16(sizeof(req) - 4), |
| + .param_idx = cpu_to_le16(TM_TRX_PARAM_SET_TRX), |
| + .band_idx = phy->mt76->band_idx, |
| + .testmode_en = 1, |
| + .action = TM_TRX_ACTION_SET, |
| + .set_trx = { |
| + .type = type, |
| + .enable = en, |
| + .band_idx = phy->mt76->band_idx, |
| + } |
| + }; |
| + |
| + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_TRX_PARAM), |
| + &req, sizeof(req), false); |
| +} |
| + |
| +static int |
| +mt7996_tm_txbf_init(struct mt7996_phy *phy, u16 *val) |
| +{ |
| +#define EBF_BBP_RX_OFFSET 0x10280 |
| +#define EBF_BBP_RX_ENABLE (BIT(0) | BIT(15)) |
| + struct mt7996_dev *dev = phy->dev; |
| + struct mt76_testmode_data *td = &phy->mt76->test; |
| + bool enable = val[0]; |
| + void *phase_cal, *pfmu_data, *pfmu_tag; |
| + u8 nss, band_idx = phy->mt76->band_idx; |
| + enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20; |
| + u8 sub_addr = td->is_txbf_dut ? TXBF_DUT_MAC_SUBADDR : TXBF_GOLDEN_MAC_SUBADDR; |
| + u8 peer_addr = td->is_txbf_dut ? TXBF_GOLDEN_MAC_SUBADDR : TXBF_DUT_MAC_SUBADDR; |
| + u8 bss_addr = TXBF_DUT_MAC_SUBADDR; |
| + u8 addr[ETH_ALEN] = {0x00, sub_addr, sub_addr, sub_addr, sub_addr, sub_addr}; |
| + u8 bssid[ETH_ALEN] = {0x00, bss_addr, bss_addr, bss_addr, bss_addr, bss_addr}; |
| + u8 peer_addrs[ETH_ALEN] = {0x00, peer_addr, peer_addr, peer_addr, peer_addr, peer_addr}; |
| + struct mt7996_vif *mvif = (struct mt7996_vif *)phy->monitor_vif->drv_priv; |
| + |
| + if (!enable) { |
| + td->bf_en = false; |
| + return 0; |
| + } |
| + |
| + if (!dev->test.txbf_phase_cal) { |
| + phase_cal = devm_kzalloc(dev->mt76.dev, |
| + sizeof(struct mt7996_txbf_phase) * |
| + MAX_PHASE_GROUP_NUM, |
| + GFP_KERNEL); |
| + if (!phase_cal) |
| + return -ENOMEM; |
| + |
| + dev->test.txbf_phase_cal = phase_cal; |
| + } |
| + |
| + if (!dev->test.txbf_pfmu_data) { |
| + pfmu_data = devm_kzalloc(dev->mt76.dev, |
| + sizeof(struct mt7996_pfmu_data) * |
| + MT7996_TXBF_SUBCAR_NUM, |
| + GFP_KERNEL); |
| + if (!pfmu_data) |
| + return -ENOMEM; |
| + |
| + dev->test.txbf_pfmu_data = pfmu_data; |
| + } |
| + |
| + if (!dev->test.txbf_pfmu_tag) { |
| + pfmu_tag = devm_kzalloc(dev->mt76.dev, |
| + sizeof(struct mt7996_pfmu_tag), GFP_KERNEL); |
| + if (!pfmu_tag) |
| + return -ENOMEM; |
| + |
| + dev->test.txbf_pfmu_tag = pfmu_tag; |
| + } |
| + |
| + td->bf_en = true; |
| + dev->ibf = td->ibf; |
| + memcpy(td->addr[0], peer_addrs, ETH_ALEN); |
| + memcpy(td->addr[1], addr, ETH_ALEN); |
| + memcpy(td->addr[2], bssid, ETH_ALEN); |
| + memcpy(phy->monitor_vif->addr, addr, ETH_ALEN); |
| + mt7996_tm_set_mac_addr(dev, td->addr[0], SET_ID(DA)); |
| + mt7996_tm_set_mac_addr(dev, td->addr[1], SET_ID(SA)); |
| + mt7996_tm_set_mac_addr(dev, td->addr[2], SET_ID(BSSID)); |
| + |
| + /* bss idx & omac idx should be set to band idx for ibf cal */ |
| + mvif->mt76.idx = band_idx; |
| + dev->mt76.vif_mask |= BIT_ULL(mvif->mt76.idx); |
| + mvif->mt76.omac_idx = band_idx; |
| + phy->omac_mask |= BIT_ULL(mvif->mt76.omac_idx); |
| + |
| + mt7996_mcu_add_dev_info(phy, phy->monitor_vif, true); |
| + mt7996_mcu_add_bss_info(phy, phy->monitor_vif, true); |
| + |
| + if (td->ibf) { |
| + if (td->is_txbf_dut) { |
| + /* Enable ITxBF Capability */ |
| + mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE); |
| + mt7996_tm_set_trx_mac(phy, TM_TRX_MAC_TX, true); |
| + |
| + td->tx_ipg = 999; |
| + td->tx_mpdu_len = 1024; |
| + td->tx_antenna_mask = phy->mt76->chainmask >> dev->chainshift[band_idx]; |
| + nss = hweight8(td->tx_antenna_mask); |
| + if (nss > 1 && nss <= 4) |
| + td->tx_rate_idx = 15 + 8 * (nss - 2); |
| + else |
| + td->tx_rate_idx = 31; |
| + } else { |
| + td->tx_antenna_mask = 1; |
| + td->tx_mpdu_len = 1024; |
| + td->tx_rate_idx = 0; |
| + mt76_set(dev, EBF_BBP_RX_OFFSET, EBF_BBP_RX_ENABLE); |
| + dev_info(dev->mt76.dev, "Set BBP RX CR = %x\n", |
| + mt76_rr(dev, EBF_BBP_RX_OFFSET)); |
| + } |
| + |
| + td->tx_rate_mode = MT76_TM_TX_MODE_HT; |
| + td->tx_rate_sgi = 0; |
| + } else { |
| + if (td->is_txbf_dut) { |
| + /* Enable ETxBF Capability */ |
| + mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE); |
| + td->tx_antenna_mask = phy->mt76->chainmask >> dev->chainshift[band_idx]; |
| + td->tx_spe_idx = 24 + phy->mt76->band_idx; |
| + if (td->tx_rate_mode == MT76_TM_TX_MODE_VHT || |
| + td->tx_rate_mode == MT76_TM_TX_MODE_HE_SU) |
| + mt7996_tm_set(dev, SET_ID(NSS), td->tx_rate_nss); |
| + |
| + mt7996_tm_set(dev, SET_ID(ENCODE_MODE), td->tx_rate_ldpc); |
| + mt7996_tm_set(dev, SET_ID(TX_COUNT), td->tx_count); |
| + } else { |
| + /* Turn On BBP CR for RX */ |
| + mt76_set(dev, EBF_BBP_RX_OFFSET, EBF_BBP_RX_ENABLE); |
| + dev_info(dev->mt76.dev, "Set BBP RX CR = %x\n", |
| + mt76_rr(dev, EBF_BBP_RX_OFFSET)); |
| + |
| + td->tx_antenna_mask = 1; |
| + } |
| + width = phy->mt76->chandef.width; |
| + |
| + if (td->tx_rate_mode == MT76_TM_TX_MODE_EHT_MU) |
| + td->tx_rate_mode = MT76_TM_TX_MODE_EHT_SU; |
| + } |
| + mt76_testmode_param_set(td, MT76_TM_ATTR_TX_ANTENNA); |
| + |
| + mt7996_tm_set(dev, SET_ID(TX_MODE), |
| + mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_PHY)); |
| + mt7996_tm_set(dev, SET_ID(TX_RATE), td->tx_rate_idx); |
| + mt7996_tm_set(dev, SET_ID(GI), td->tx_rate_sgi); |
| + mt7996_tm_set(dev, SET_ID(CBW), |
| + mt7996_tm_bw_mapping(width, BW_MAP_NL_TO_FW)); |
| + mt7996_tm_set(dev, SET_ID(DBW), |
| + mt7996_tm_bw_mapping(width, BW_MAP_NL_TO_FW)); |
| + mt7996_tm_set_antenna(phy, SET_ID(TX_PATH)); |
| + mt7996_tm_set_antenna(phy, SET_ID(RX_PATH)); |
| + mt7996_tm_set(dev, SET_ID(IPG), td->tx_ipg); |
| + mt7996_tm_set(dev, SET_ID(TX_LEN), td->tx_mpdu_len); |
| + mt7996_tm_set(dev, SET_ID(TX_TIME), 0); |
| + mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(TX_COMMIT)); |
| + |
| + return 0; |
| +} |
| + |
| +static int |
| +mt7996_tm_txbf_phase_comp(struct mt7996_phy *phy, u16 *val) |
| +{ |
| + struct mt7996_dev *dev = phy->dev; |
| + struct mt7996_tm_bf_req req = { |
| + .phase_comp = { |
| + .tag = cpu_to_le16(BF_IBF_PHASE_COMP), |
| + .len = cpu_to_le16(sizeof(req.phase_comp)), |
| + .bw = val[0], |
| + .jp_band = (val[2] == 1) ? 1 : 0, |
| + .band_idx = phy->mt76->band_idx, |
| + .read_from_e2p = val[3], |
| + .disable = val[4], |
| + .group = val[2], |
| + } |
| + }; |
| + struct mt7996_txbf_phase *phase = (struct mt7996_txbf_phase *)dev->test.txbf_phase_cal; |
| + |
| + wait_event_timeout(dev->mt76.tx_wait, phase[val[2]].status != 0, HZ); |
| + if (val[2]) |
| + memcpy(req.phase_comp.buf, &phase[val[2]].phase_5g, sizeof(req.phase_comp.buf)); |
| + else |
| + memcpy(req.phase_comp.buf, &phase[val[2]].phase_2g, sizeof(req.phase_comp.buf)); |
| + |
| + pr_info("ibf cal process: phase comp info\n"); |
| + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, |
| + &req, sizeof(req), 0); |
| + |
| + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &req, |
| + sizeof(req), false); |
| +} |
| + |
| +static int |
| +mt7996_tm_txbf_profile_tag_write(struct mt7996_phy *phy, u8 pfmu_idx, struct mt7996_pfmu_tag *tag) |
| +{ |
| + struct mt7996_dev *dev = phy->dev; |
| + struct mt7996_tm_bf_req req = { |
| + .pfmu_tag = { |
| + .tag = cpu_to_le16(BF_PFMU_TAG_WRITE), |
| + .len = cpu_to_le16(sizeof(req.pfmu_tag)), |
| + .pfmu_id = pfmu_idx, |
| + .bfer = true, |
| + .band_idx = phy->mt76->band_idx, |
| + } |
| + }; |
| + |
| + memcpy(req.pfmu_tag.buf, tag, sizeof(*tag)); |
| + wait_event_timeout(dev->mt76.tx_wait, tag->t1.pfmu_idx != 0, HZ); |
| + |
| + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &req, |
| + sizeof(req), false); |
| +} |
| + |
| +static int |
| +mt7996_tm_add_txbf_sta(struct mt7996_phy *phy, u8 pfmu_idx, u8 nr, u8 nc, bool ebf) |
| +{ |
| + struct mt7996_dev *dev = phy->dev; |
| + struct mt76_testmode_data *td = &phy->mt76->test; |
| + struct { |
| + struct sta_req_hdr hdr; |
| + struct sta_rec_bf bf; |
| + } __packed req = { |
| + .hdr = { |
| + .bss_idx = phy->mt76->band_idx, |
| + .wlan_idx_lo = to_wcid_lo(phy->mt76->band_idx + 1), |
| + .tlv_num = 1, |
| + .is_tlv_append = 1, |
| + .muar_idx = 0, |
| + .wlan_idx_hi = to_wcid_hi(phy->mt76->band_idx + 1), |
| + }, |
| + .bf = { |
| + .tag = cpu_to_le16(STA_REC_BF), |
| + .len = cpu_to_le16(sizeof(req.bf)), |
| + .pfmu = cpu_to_le16(pfmu_idx), |
| + .sounding_phy = 1, |
| + .bf_cap = ebf, |
| + .ncol = nc, |
| + .nrow = nr, |
| + .ibf_timeout = 0xff, |
| + .tx_mode = mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_PHY), |
| + }, |
| + }; |
| + u8 ndp_rate, ndpa_rate, rept_poll_rate, bf_bw; |
| + |
| + if (td->tx_rate_mode == MT76_TM_TX_MODE_HE_SU || |
| + td->tx_rate_mode == MT76_TM_TX_MODE_EHT_SU) { |
| + rept_poll_rate = 0x49; |
| + ndpa_rate = 0x49; |
| + ndp_rate = 0; |
| + } else if (td->tx_rate_mode == MT76_TM_TX_MODE_VHT) { |
| + rept_poll_rate = 0x9; |
| + ndpa_rate = 0x9; |
| + ndp_rate = 0; |
| + } else { |
| + rept_poll_rate = 0; |
| + ndpa_rate = 0; |
| + if (nr == 1) |
| + ndp_rate = 8; |
| + else if (nr == 2) |
| + ndp_rate = 16; |
| + else |
| + ndp_rate = 24; |
| + } |
| + |
| + bf_bw = mt7996_tm_bw_mapping(phy->mt76->chandef.width, BW_MAP_NL_TO_BF); |
| + req.bf.ndp_rate = ndp_rate; |
| + req.bf.ndpa_rate = ndpa_rate; |
| + req.bf.rept_poll_rate = rept_poll_rate; |
| + req.bf.bw = bf_bw; |
| + req.bf.tx_mode = (td->tx_rate_mode == MT76_TM_TX_MODE_EHT_SU) ? 0xf : req.bf.tx_mode; |
| + |
| + if (ebf) { |
| + req.bf.mem[0].row = 0; |
| + req.bf.mem[1].row = 1; |
| + req.bf.mem[2].row = 2; |
| + req.bf.mem[3].row = 3; |
| + } else { |
| + req.bf.mem[0].row = 4; |
| + req.bf.mem[1].row = 5; |
| + req.bf.mem[2].row = 6; |
| + req.bf.mem[3].row = 7; |
| + } |
| + |
| + return mt76_mcu_send_msg(&dev->mt76, MCU_WMWA_UNI_CMD(STA_REC_UPDATE), &req, |
| + sizeof(req), true); |
| +} |
| + |
| +static int |
| +mt7996_tm_txbf_profile_update(struct mt7996_phy *phy, u16 *val, bool ebf) |
| +{ |
| +#define MT_ARB_IBF_ENABLE (BIT(0) | GENMASK(9, 8)) |
| + struct mt76_testmode_data *td = &phy->mt76->test; |
| + struct mt7996_dev *dev = phy->dev; |
| + struct mt7996_pfmu_tag *tag = dev->test.txbf_pfmu_tag; |
| + u8 pfmu_idx = val[0], nc = val[2], nr; |
| + int ret; |
| + bool is_atenl = val[5]; |
| + |
| + if (td->tx_antenna_mask == 3) |
| + nr = 1; |
| + else if (td->tx_antenna_mask == 7) |
| + nr = 2; |
| + else |
| + nr = 3; |
| + |
| + memset(tag, 0, sizeof(*tag)); |
| + tag->t1.pfmu_idx = pfmu_idx; |
| + tag->t1.ebf = ebf; |
| + tag->t1.nr = nr; |
| + tag->t1.nc = nc; |
| + tag->t1.invalid_prof = true; |
| + tag->t1.data_bw = mt7996_tm_bw_mapping(phy->mt76->chandef.width, BW_MAP_NL_TO_BF); |
| + tag->t2.se_idx = td->tx_spe_idx; |
| + |
| + if (ebf) { |
| + tag->t1.row_id1 = 0; |
| + tag->t1.row_id2 = 1; |
| + tag->t1.row_id3 = 2; |
| + tag->t1.row_id4 = 3; |
| + tag->t1.lm = mt7996_tm_rate_mapping(td->tx_rate_mode, RATE_MODE_TO_LM); |
| + } else { |
| + tag->t1.row_id1 = 4; |
| + tag->t1.row_id2 = 5; |
| + tag->t1.row_id3 = 6; |
| + tag->t1.row_id4 = 7; |
| + tag->t1.lm = mt7996_tm_rate_mapping(MT76_TM_TX_MODE_OFDM, RATE_MODE_TO_LM); |
| + |
| + tag->t2.ibf_timeout = 0xff; |
| + tag->t2.ibf_nr = nr; |
| + tag->t2.ibf_nc = nc; |
| + } |
| + |
| + ret = mt7996_tm_txbf_profile_tag_write(phy, pfmu_idx, tag); |
| + if (ret) |
| + return ret; |
| + |
| + ret = mt7996_tm_add_txbf_sta(phy, pfmu_idx, nr, nc, ebf); |
| + if (ret) |
| + return ret; |
| + |
| + if (!is_atenl && !td->ibf) { |
| + mt76_set(dev, MT_ARB_TQSAXM0(phy->mt76->band_idx), MT_ARB_TQSAXM_ALTX_START_MASK); |
| + dev_info(dev->mt76.dev, "Set TX queue start CR for AX management (0x%x) = 0x%x\n", |
| + MT_ARB_TQSAXM0(phy->mt76->band_idx), |
| + mt76_rr(dev, MT_ARB_TQSAXM0(phy->mt76->band_idx))); |
| + } else if (!is_atenl && td->ibf && ebf) { |
| + /* iBF's ebf profile update */ |
| + mt76_set(dev, MT_ARB_TQSAXM0(phy->mt76->band_idx), MT_ARB_IBF_ENABLE); |
| + dev_info(dev->mt76.dev, "Set TX queue start CR for AX management (0x%x) = 0x%x\n", |
| + MT_ARB_TQSAXM0(phy->mt76->band_idx), |
| + mt76_rr(dev, MT_ARB_TQSAXM0(phy->mt76->band_idx))); |
| + } |
| + |
| + if (!ebf && is_atenl) |
| + return mt7996_tm_txbf_apply_tx(phy, 1, false, true, true); |
| + |
| + return 0; |
| +} |
| + |
| +static int |
| +mt7996_tm_txbf_phase_cal(struct mt7996_phy *phy, u16 *val) |
| +{ |
| + struct mt7996_dev *dev = phy->dev; |
| + struct mt7996_tm_bf_req req = { |
| + .phase_cal = { |
| + .tag = cpu_to_le16(BF_PHASE_CALIBRATION), |
| + .len = cpu_to_le16(sizeof(req.phase_cal)), |
| + .group = val[0], |
| + .group_l_m_n = val[1], |
| + .sx2 = val[2], |
| + .cal_type = val[3], |
| + .lna_gain_level = val[4], |
| + .band_idx = phy->mt76->band_idx, |
| + }, |
| + }; |
| + struct mt7996_txbf_phase *phase = (struct mt7996_txbf_phase *)dev->test.txbf_phase_cal; |
| + |
| + /* reset phase status before update phase cal data */ |
| + phase[req.phase_cal.group].status = 0; |
| + |
| + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &req, |
| + sizeof(req), false); |
| +} |
| + |
| +static int |
| +mt7996_tm_txbf_profile_update_all(struct mt7996_phy *phy, u16 *val) |
| +{ |
| +#define MT7996_TXBF_PFMU_DATA_LEN (MT7996_TXBF_SUBCAR_NUM * sizeof(struct mt7996_pfmu_data)) |
| + struct mt76_testmode_data *td = &phy->mt76->test; |
| + u8 nss = hweight8(td->tx_antenna_mask); |
| + u16 pfmu_idx = val[0]; |
| + u16 subc_id = val[1]; |
| + u16 angle11 = val[2]; |
| + u16 angle21 = val[3]; |
| + u16 angle31 = val[4]; |
| + u16 angle41 = val[5]; |
| + s16 phi11 = 0, phi21 = 0, phi31 = 0; |
| + struct mt7996_pfmu_data *pfmu_data; |
| + |
| + if (subc_id > MT7996_TXBF_SUBCAR_NUM - 1) |
| + return -EINVAL; |
| + |
| + if (nss == 2) { |
| + phi11 = (s16)(angle21 - angle11); |
| + } else if (nss == 3) { |
| + phi11 = (s16)(angle31 - angle11); |
| + phi21 = (s16)(angle31 - angle21); |
| + } else { |
| + phi11 = (s16)(angle41 - angle11); |
| + phi21 = (s16)(angle41 - angle21); |
| + phi31 = (s16)(angle41 - angle31); |
| + } |
| + |
| + pfmu_data = (struct mt7996_pfmu_data *)phy->dev->test.txbf_pfmu_data; |
| + pfmu_data = &pfmu_data[subc_id]; |
| + |
| + if (subc_id < 32) |
| + pfmu_data->subc_idx = cpu_to_le16(subc_id + 224); |
| + else |
| + pfmu_data->subc_idx = cpu_to_le16(subc_id - 32); |
| + |
| + pfmu_data->phi11 = cpu_to_le16(phi11); |
| + pfmu_data->phi21 = cpu_to_le16(phi21); |
| + pfmu_data->phi31 = cpu_to_le16(phi31); |
| + if (subc_id == MT7996_TXBF_SUBCAR_NUM - 1) { |
| + struct mt7996_dev *dev = phy->dev; |
| + struct mt7996_tm_bf_req req = { |
| + .pfmu_data_all = { |
| + .tag = cpu_to_le16(BF_PROFILE_WRITE_20M_ALL), |
| + .len = cpu_to_le16(sizeof(req.pfmu_data_all)), |
| + .pfmu_id = pfmu_idx, |
| + .band_idx = phy->mt76->band_idx, |
| + }, |
| + }; |
| + |
| + memcpy(req.pfmu_data_all.buf, dev->test.txbf_pfmu_data, MT7996_TXBF_PFMU_DATA_LEN); |
| + |
| + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), |
| + &req, sizeof(req), true); |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +static int |
| +mt7996_tm_txbf_e2p_update(struct mt7996_phy *phy) |
| +{ |
| +#define TXBF_PHASE_EEPROM_START_OFFSET 0xc00 |
| +#define TXBF_PHASE_GROUP_EEPROM_OFFSET 0x2e |
| + struct mt7996_txbf_phase *phase, *p; |
| + struct mt7996_dev *dev = phy->dev; |
| + u8 *eeprom = dev->mt76.eeprom.data; |
| + u16 offset; |
| + int i; |
| + |
| + offset = TXBF_PHASE_EEPROM_START_OFFSET; |
| + phase = (struct mt7996_txbf_phase *)dev->test.txbf_phase_cal; |
| + for (i = 0; i < MAX_PHASE_GROUP_NUM; i++) { |
| + p = &phase[i]; |
| + |
| + if (!p->status) |
| + continue; |
| + |
| + /* copy phase cal data to eeprom */ |
| + if (i) |
| + memcpy(eeprom + offset, &p->phase_5g, sizeof(p->phase_5g)); |
| + else |
| + memcpy(eeprom + offset, &p->phase_2g, sizeof(p->phase_2g)); |
| + offset += TXBF_PHASE_GROUP_EEPROM_OFFSET; |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +static int |
| +mt7996_tm_txbf_apply_tx(struct mt7996_phy *phy, u16 wlan_idx, bool ebf, |
| + bool ibf, bool phase_cal) |
| +{ |
| + struct mt7996_dev *dev = phy->dev; |
| + struct mt7996_tm_bf_req req = { |
| + .tx_apply = { |
| + .tag = cpu_to_le16(BF_DATA_PACKET_APPLY), |
| + .len = cpu_to_le16(sizeof(req.tx_apply)), |
| + .wlan_idx = cpu_to_le16(wlan_idx), |
| + .ebf = ebf, |
| + .ibf = ibf, |
| + .phase_cal = phase_cal, |
| + }, |
| + }; |
| + |
| + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &req, sizeof(req), false); |
| +} |
| + |
| +static int |
| +mt7996_tm_txbf_set_tx(struct mt7996_phy *phy, u16 *val) |
| +{ |
| + bool bf_on = val[0], update = val[3]; |
| + struct mt7996_dev *dev = phy->dev; |
| + struct mt7996_pfmu_tag *tag = dev->test.txbf_pfmu_tag; |
| + struct mt76_testmode_data *td = &phy->mt76->test; |
| + |
| + if (bf_on) { |
| + mt7996_tm_set_rx_frames(phy, false); |
| + mt7996_tm_set_tx_frames(phy, false); |
| + mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, 2, true); |
| + tag->t1.invalid_prof = false; |
| + mt7996_tm_txbf_profile_tag_write(phy, 2, tag); |
| + td->bf_ever_en = true; |
| + |
| + if (update) |
| + mt7996_tm_txbf_apply_tx(phy, 1, 0, 1, 1); |
| + } else { |
| + if (!td->bf_ever_en) { |
| + mt7996_tm_set_rx_frames(phy, false); |
| + mt7996_tm_set_tx_frames(phy, false); |
| + td->ibf = false; |
| + td->ebf = false; |
| + |
| + if (update) |
| + mt7996_tm_txbf_apply_tx(phy, 1, 0, 0, 0); |
| + } else { |
| + td->bf_ever_en = false; |
| + |
| + mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, 2, true); |
| + tag->t1.invalid_prof = true; |
| + mt7996_tm_txbf_profile_tag_write(phy, 2, tag); |
| + } |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +static int |
| +mt7996_tm_trigger_sounding(struct mt7996_phy *phy, u16 *val, bool en) |
| +{ |
| + struct mt7996_dev *dev = phy->dev; |
| + u8 sounding_mode = val[0]; |
| + u8 sta_num = val[1]; |
| + u32 sounding_interval = (u32)val[2] << 2; /* input unit: 4ms */ |
| + u16 tag = en ? BF_SOUNDING_ON : BF_SOUNDING_OFF; |
| + struct mt7996_tm_bf_req req = { |
| + .sounding = { |
| + .tag = cpu_to_le16(tag), |
| + .len = cpu_to_le16(sizeof(req.sounding)), |
| + .snd_mode = sounding_mode, |
| + .sta_num = sta_num, |
| + .wlan_id = { |
| + cpu_to_le16(val[3]), |
| + cpu_to_le16(val[4]), |
| + cpu_to_le16(val[5]), |
| + cpu_to_le16(val[6]) |
| + }, |
| + .snd_period = cpu_to_le32(sounding_interval), |
| + }, |
| + }; |
| + |
| + if (sounding_mode > SOUNDING_MODE_MAX) |
| + return -EINVAL; |
| + |
| + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), |
| + &req, sizeof(req), false); |
| +} |
| + |
| +static int |
| +mt7996_tm_txbf_txcmd(struct mt7996_phy *phy, u16 *val) |
| +{ |
| + struct mt7996_dev *dev = phy->dev; |
| + struct mt7996_tm_bf_req req = { |
| + .txcmd = { |
| + .tag = cpu_to_le16(BF_CMD_TXCMD), |
| + .len = cpu_to_le16(sizeof(req.txcmd)), |
| + .action = val[0], |
| + .bf_manual = val[1], |
| + .bf_bit = val[2], |
| + }, |
| + }; |
| + |
| + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(BF), &req, sizeof(req), false); |
| +} |
| + |
| +static int |
| +mt7996_tm_set_txbf(struct mt7996_phy *phy) |
| +{ |
| +#define TXBF_IS_DUT_MASK BIT(0) |
| +#define TXBF_IBF_MASK BIT(1) |
| + struct mt76_testmode_data *td = &phy->mt76->test; |
| + u16 *val = td->txbf_param; |
| + |
| + dev_info(phy->dev->mt76.dev, |
| + "ibf cal process: act = %u, val = %u, %u, %u, %u, %u, %u, %u, %u\n", |
| + td->txbf_act, val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]); |
| + |
| + switch (td->txbf_act) { |
| + case MT76_TM_TXBF_ACT_GOLDEN_INIT: |
| + case MT76_TM_TXBF_ACT_INIT: |
| + case MT76_TM_TX_EBF_ACT_GOLDEN_INIT: |
| + case MT76_TM_TX_EBF_ACT_INIT: |
| + td->ibf = !u32_get_bits(td->txbf_act, TXBF_IBF_MASK); |
| + td->ebf = true; |
| + td->is_txbf_dut = !!u32_get_bits(td->txbf_act, TXBF_IS_DUT_MASK); |
| + return mt7996_tm_txbf_init(phy, val); |
| + case MT76_TM_TXBF_ACT_UPDATE_CH: |
| + mt7996_tm_update_channel(phy); |
| + break; |
| + case MT76_TM_TXBF_ACT_PHASE_COMP: |
| + return mt7996_tm_txbf_phase_comp(phy, val); |
| + case MT76_TM_TXBF_ACT_TX_PREP: |
| + return mt7996_tm_txbf_set_tx(phy, val); |
| + case MT76_TM_TXBF_ACT_IBF_PROF_UPDATE: |
| + return mt7996_tm_txbf_profile_update(phy, val, false); |
| + case MT76_TM_TXBF_ACT_EBF_PROF_UPDATE: |
| + return mt7996_tm_txbf_profile_update(phy, val, true); |
| + case MT76_TM_TXBF_ACT_PHASE_CAL: |
| + return mt7996_tm_txbf_phase_cal(phy, val); |
| + case MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD: |
| + case MT76_TM_TXBF_ACT_PROF_UPDATE_ALL: |
| + return mt7996_tm_txbf_profile_update_all(phy, val); |
| + case MT76_TM_TXBF_ACT_E2P_UPDATE: |
| + return mt7996_tm_txbf_e2p_update(phy); |
| + case MT76_TM_TXBF_ACT_APPLY_TX: { |
| + u16 wlan_idx = val[0]; |
| + bool ebf = !!val[1], ibf = !!val[2], phase_cal = !!val[4]; |
| + |
| + return mt7996_tm_txbf_apply_tx(phy, wlan_idx, ebf, ibf, phase_cal); |
| + } |
| + case MT76_TM_TXBF_ACT_TRIGGER_SOUNDING: |
| + return mt7996_tm_trigger_sounding(phy, val, true); |
| + case MT76_TM_TXBF_ACT_STOP_SOUNDING: |
| + memset(val, 0, sizeof(td->txbf_param)); |
| + return mt7996_tm_trigger_sounding(phy, val, false); |
| + case MT76_TM_TXBF_ACT_PROFILE_TAG_READ: |
| + case MT76_TM_TXBF_ACT_PROFILE_TAG_WRITE: |
| + case MT76_TM_TXBF_ACT_PROFILE_TAG_INVALID: { |
| + u8 pfmu_idx = val[0]; |
| + bool bfer = !!val[1]; |
| + struct mt7996_dev *dev = phy->dev; |
| + struct mt7996_pfmu_tag *tag = dev->test.txbf_pfmu_tag; |
| + |
| + if (!tag) { |
| + dev_err(dev->mt76.dev, |
| + "pfmu tag is not initialized!\n"); |
| + return 0; |
| + } |
| + |
| + if (td->txbf_act == MT76_TM_TXBF_ACT_PROFILE_TAG_WRITE) |
| + return mt7996_tm_txbf_profile_tag_write(phy, pfmu_idx, tag); |
| + else if (td->txbf_act == MT76_TM_TXBF_ACT_PROFILE_TAG_READ) |
| + return mt7996_mcu_set_txbf_internal(phy, BF_PFMU_TAG_READ, pfmu_idx, bfer); |
| + |
| + tag->t1.invalid_prof = !!val[0]; |
| + |
| + return 0; |
| + } |
| + case MT76_TM_TXBF_ACT_STA_REC_READ: |
| + return mt7996_mcu_set_txbf_internal(phy, BF_STA_REC_READ, val[0], 0); |
| + case MT76_TM_TXBF_ACT_TXCMD: |
| + return mt7996_tm_txbf_txcmd(phy, val); |
| + default: |
| + break; |
| + }; |
| + |
| + return 0; |
| +} |
| + |
| static void |
| mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed) |
| { |
| @@ -1086,6 +1770,8 @@ mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed) |
| mt7996_tm_set_ipi(phy); |
| if (changed & BIT(TM_CHANGED_IPI_RESET)) |
| mt7996_tm_ipi_hist_ctrl(phy, NULL, RDD_SET_IPI_HIST_RESET); |
| + if (changed & BIT(TM_CHANGED_TXBF_ACT)) |
| + mt7996_tm_set_txbf(phy); |
| } |
| |
| static int |
| diff --git a/mt7996/testmode.h b/mt7996/testmode.h |
| index 78662b2e..f97ccb26 100644 |
| --- a/mt7996/testmode.h |
| +++ b/mt7996/testmode.h |
| @@ -27,6 +27,17 @@ enum { |
| FW_CDBW_8080MHZ, |
| }; |
| |
| +enum { |
| + BF_CDBW_20MHZ, |
| + BF_CDBW_40MHZ, |
| + BF_CDBW_80MHZ, |
| + BF_CDBW_160MHZ, |
| + BF_CDBW_320MHZ, |
| + BF_CDBW_10MHZ = BF_CDBW_320MHZ, |
| + BF_CDBW_5MHZ, |
| + BF_CDBW_8080MHZ, |
| +}; |
| + |
| #define FIRST_CONTROL_CHAN_BITMAP_BW40 0x5555555 |
| #define FIRST_CONTROL_CHAN_BITMAP_BW80 0x111111 |
| #define FIRST_CONTROL_CHAN_BITMAP_BW160 0x100101 |
| @@ -34,12 +45,20 @@ enum { |
| enum bw_mapping_method { |
| BW_MAP_NL_TO_FW, |
| BW_MAP_NL_TO_TM, |
| + BW_MAP_NL_TO_BF, |
| BW_MAP_NL_TO_MHZ, |
| BW_MAP_NL_TO_CONTROL_BITMAP_5G, |
| |
| NUM_BW_MAP, |
| }; |
| |
| +enum rate_mapping_type { |
| + RATE_MODE_TO_PHY, |
| + RATE_MODE_TO_LM, |
| + |
| + NUM_RATE_MAP, |
| +}; |
| + |
| struct tm_cal_param { |
| __le32 func_data; |
| u8 band_idx; |
| diff --git a/testmode.c b/testmode.c |
| index 805ad83c..7b8f9e66 100644 |
| --- a/testmode.c |
| +++ b/testmode.c |
| @@ -462,6 +462,42 @@ out: |
| return err; |
| } |
| |
| +static int |
| +mt76_testmode_txbf_profile_update_all_cmd(struct mt76_phy *phy, struct nlattr **tb, u32 state) |
| +{ |
| +#define PARAM_UNIT 5 |
| + static u8 pfmu_idx; |
| + struct mt76_testmode_data *td = &phy->test; |
| + struct mt76_dev *dev = phy->dev; |
| + struct nlattr *cur; |
| + u16 tmp_val[PARAM_UNIT], *val = td->txbf_param; |
| + int idx, rem, ret, i = 0; |
| + |
| + memset(td->txbf_param, 0, sizeof(td->txbf_param)); |
| + nla_for_each_nested(cur, tb[MT76_TM_ATTR_TXBF_PARAM], rem) { |
| + if (nla_len(cur) != 2) |
| + return -EINVAL; |
| + idx = i % PARAM_UNIT; |
| + tmp_val[idx] = nla_get_u16(cur); |
| + if (idx == 1 && (tmp_val[idx] == 0xf0 || tmp_val[idx] == 0xff)) { |
| + pfmu_idx = tmp_val[0]; |
| + return 0; |
| + } |
| + if (idx == PARAM_UNIT - 1) { |
| + val[0] = pfmu_idx; |
| + memcpy(val + 1, tmp_val, sizeof(tmp_val)); |
| + if (dev->test_ops->set_params) { |
| + ret = dev->test_ops->set_params(phy, tb, state); |
| + if (ret) |
| + return ret; |
| + } |
| + } |
| + i++; |
| + } |
| + |
| + return 0; |
| +} |
| + |
| int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, |
| void *data, int len) |
| { |
| @@ -607,6 +643,30 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, |
| } |
| } |
| |
| + if (tb[MT76_TM_ATTR_TXBF_ACT]) { |
| + struct nlattr *cur; |
| + int rem, idx = 0; |
| + |
| + if (!tb[MT76_TM_ATTR_TXBF_PARAM] || |
| + mt76_tm_get_u8(tb[MT76_TM_ATTR_TXBF_ACT], &td->txbf_act, |
| + 0, MT76_TM_TXBF_ACT_MAX)) |
| + goto out; |
| + |
| + if (td->txbf_act == MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD) { |
| + err = mt76_testmode_txbf_profile_update_all_cmd(phy, tb, state); |
| + goto out; |
| + } |
| + |
| + memset(td->txbf_param, 0, sizeof(td->txbf_param)); |
| + nla_for_each_nested(cur, tb[MT76_TM_ATTR_TXBF_PARAM], rem) { |
| + if (nla_len(cur) != 2 || |
| + idx >= ARRAY_SIZE(td->txbf_param)) |
| + goto out; |
| + |
| + td->txbf_param[idx++] = nla_get_u16(cur); |
| + } |
| + } |
| + |
| if (dev->test_ops->set_params) { |
| err = dev->test_ops->set_params(phy, tb, state); |
| if (err) |
| diff --git a/testmode.h b/testmode.h |
| index 5d677f8c..bda7624a 100644 |
| --- a/testmode.h |
| +++ b/testmode.h |
| @@ -286,6 +286,59 @@ enum mt76_testmode_eeprom_action { |
| MT76_TM_EEPROM_ACTION_MAX = NUM_MT76_TM_EEPROM_ACTION - 1, |
| }; |
| |
| +/** |
| + * enum mt76_testmode_txbf_act - txbf action |
| + * |
| + * @MT76_TM_TXBF_ACT_GOLDEN_INIT: init ibf setting for golden device |
| + * @MT76_TM_TXBF_ACT_INIT: init ibf setting for DUT |
| + * @MT76_TM_TX_EBF_ACT_GOLDEN_INIT: init ebf setting for golden device |
| + * @MT76_TM_TX_EBF_ACT_INIT: init ebf setting for DUT |
| + * @MT76_TM_TXBF_ACT_UPDATE_CH: update channel info |
| + * @MT76_TM_TXBF_ACT_PHASE_COMP: txbf phase compensation |
| + * @MT76_TM_TXBF_ACT_TX_PREP: TX preparation for txbf |
| + * @MT76_TM_TXBF_ACT_IBF_PROF_UPDATE: update ibf profile (pfmu tag, bf sta record) |
| + * @MT76_TM_TXBF_ACT_EBF_PROF_UPDATE: update ebf profile |
| + * @MT76_TM_TXBF_ACT_APPLY_TX: apply TX setting for txbf |
| + * @MT76_TM_TXBF_ACT_PHASE_CAL: perform txbf phase calibration |
| + * @MT76_TM_TXBF_ACT_PROF_UPDATE_ALL: update bf profile via instrument |
| + * @MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD: update bf profile via instrument |
| + * @MT76_TM_TXBF_ACT_E2P_UPDATE: write back txbf calibration result to eeprom |
| + * @MT76_TM_TXBF_ACT_TRIGGER_SOUNDING: trigger beamformer to send sounding packet |
| + * @MT76_TM_TXBF_ACT_STOP_SOUNDING: stop sending sounding packet |
| + * @MT76_TM_TXBF_ACT_PROFILE_TAG_READ: read pfmu tag |
| + * @MT76_TM_TXBF_ACT_PROFILE_TAG_WRITE: update pfmu tag |
| + * @MT76_TM_TXBF_ACT_PROFILE_TAG_INVALID: invalidate pfmu tag |
| + * @MT76_TM_TXBF_ACT_STA_REC_READ: read bf sta record |
| + * @MT76_TM_TXBF_ACT_TXCMD: configure txcmd bf bit manually |
| + */ |
| +enum mt76_testmode_txbf_act { |
| + MT76_TM_TXBF_ACT_GOLDEN_INIT, |
| + MT76_TM_TXBF_ACT_INIT, |
| + MT76_TM_TX_EBF_ACT_GOLDEN_INIT, |
| + MT76_TM_TX_EBF_ACT_INIT, |
| + MT76_TM_TXBF_ACT_UPDATE_CH, |
| + MT76_TM_TXBF_ACT_PHASE_COMP, |
| + MT76_TM_TXBF_ACT_TX_PREP, |
| + MT76_TM_TXBF_ACT_IBF_PROF_UPDATE, |
| + MT76_TM_TXBF_ACT_EBF_PROF_UPDATE, |
| + MT76_TM_TXBF_ACT_APPLY_TX, |
| + MT76_TM_TXBF_ACT_PHASE_CAL, |
| + MT76_TM_TXBF_ACT_PROF_UPDATE_ALL, |
| + MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD, |
| + MT76_TM_TXBF_ACT_E2P_UPDATE, |
| + MT76_TM_TXBF_ACT_TRIGGER_SOUNDING, |
| + MT76_TM_TXBF_ACT_STOP_SOUNDING, |
| + MT76_TM_TXBF_ACT_PROFILE_TAG_READ, |
| + MT76_TM_TXBF_ACT_PROFILE_TAG_WRITE, |
| + MT76_TM_TXBF_ACT_PROFILE_TAG_INVALID, |
| + MT76_TM_TXBF_ACT_STA_REC_READ, |
| + MT76_TM_TXBF_ACT_TXCMD, |
| + |
| + /* keep last */ |
| + NUM_MT76_TM_TXBF_ACT, |
| + MT76_TM_TXBF_ACT_MAX = NUM_MT76_TM_TXBF_ACT - 1, |
| +}; |
| + |
| extern const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS]; |
| |
| #endif |
| diff --git a/tools/fields.c b/tools/fields.c |
| index 77696ce7..f793d1a5 100644 |
| --- a/tools/fields.c |
| +++ b/tools/fields.c |
| @@ -44,6 +44,30 @@ static const char * const testmode_offchan_bw[] = { |
| [NL80211_CHAN_WIDTH_160] = "160", |
| }; |
| |
| +static const char * const testmode_txbf_act[] = { |
| + [MT76_TM_TXBF_ACT_GOLDEN_INIT] = "golden_init", |
| + [MT76_TM_TXBF_ACT_INIT] = "init", |
| + [MT76_TM_TX_EBF_ACT_GOLDEN_INIT] = "ebf_golden_init", |
| + [MT76_TM_TX_EBF_ACT_INIT] = "ebf_init", |
| + [MT76_TM_TXBF_ACT_UPDATE_CH] = "update_ch", |
| + [MT76_TM_TXBF_ACT_PHASE_COMP] = "phase_comp", |
| + [MT76_TM_TXBF_ACT_TX_PREP] = "tx_prep", |
| + [MT76_TM_TXBF_ACT_IBF_PROF_UPDATE] = "ibf_prof_update", |
| + [MT76_TM_TXBF_ACT_EBF_PROF_UPDATE] = "ebf_prof_update", |
| + [MT76_TM_TXBF_ACT_APPLY_TX] = "apply_tx", |
| + [MT76_TM_TXBF_ACT_PHASE_CAL] = "phase_cal", |
| + [MT76_TM_TXBF_ACT_PROF_UPDATE_ALL] = "prof_update", |
| + [MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD] = "prof_update_all", |
| + [MT76_TM_TXBF_ACT_E2P_UPDATE] = "e2p_update", |
| + [MT76_TM_TXBF_ACT_TRIGGER_SOUNDING] = "trigger_sounding", |
| + [MT76_TM_TXBF_ACT_STOP_SOUNDING] = "stop_sounding", |
| + [MT76_TM_TXBF_ACT_PROFILE_TAG_READ] = "pfmu_tag_read", |
| + [MT76_TM_TXBF_ACT_PROFILE_TAG_WRITE] = "pfmu_tag_write", |
| + [MT76_TM_TXBF_ACT_PROFILE_TAG_INVALID] = "set_invalid_prof", |
| + [MT76_TM_TXBF_ACT_STA_REC_READ] = "sta_rec_read", |
| + [MT76_TM_TXBF_ACT_TXCMD] = "txcmd", |
| +}; |
| + |
| static void print_enum(const struct tm_field *field, struct nlattr *attr) |
| { |
| unsigned int i = nla_get_u8(attr); |
| @@ -94,6 +118,17 @@ static void print_s8(const struct tm_field *field, struct nlattr *attr) |
| printf("%d", (int8_t)nla_get_u8(attr)); |
| } |
| |
| +static bool parse_u16_hex(const struct tm_field *field, int idx, |
| + struct nl_msg *msg, const char *val) |
| +{ |
| + return !nla_put_u16(msg, idx, strtoul(val, NULL, 16)); |
| +} |
| + |
| +static void print_u16_hex(const struct tm_field *field, struct nlattr *attr) |
| +{ |
| + printf("%d", nla_get_u16(attr)); |
| +} |
| + |
| static bool parse_u32(const struct tm_field *field, int idx, |
| struct nl_msg *msg, const char *val) |
| { |
| @@ -399,6 +434,8 @@ static const struct tm_field testdata_fields[NUM_MT76_TM_ATTRS] = { |
| FIELD(u8, AID, "aid"), |
| FIELD(u8, RU_ALLOC, "ru_alloc"), |
| FIELD(u8, RU_IDX, "ru_idx"), |
| + FIELD_ENUM(TXBF_ACT, "txbf_act", testmode_txbf_act), |
| + FIELD_ARRAY(u16_hex, TXBF_PARAM, "txbf_param"), |
| FIELD(u8, OFF_CH_SCAN_CH, "offchan_ch"), |
| FIELD(u8, OFF_CH_SCAN_CENTER_CH, "offchan_center_ch"), |
| FIELD_ENUM(OFF_CH_SCAN_BW, "offchan_bw", testmode_offchan_bw), |
| -- |
| 2.18.0 |
| |