18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: ISC 28c2ecf20Sopenharmony_ci/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include "mt7615.h" 58c2ecf20Sopenharmony_ci#include "eeprom.h" 68c2ecf20Sopenharmony_ci#include "mcu.h" 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_cienum { 98c2ecf20Sopenharmony_ci TM_CHANGED_TXPOWER_CTRL, 108c2ecf20Sopenharmony_ci TM_CHANGED_TXPOWER, 118c2ecf20Sopenharmony_ci TM_CHANGED_FREQ_OFFSET, 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci /* must be last */ 148c2ecf20Sopenharmony_ci NUM_TM_CHANGED 158c2ecf20Sopenharmony_ci}; 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic const u8 tm_change_map[] = { 198c2ecf20Sopenharmony_ci [TM_CHANGED_TXPOWER_CTRL] = MT76_TM_ATTR_TX_POWER_CONTROL, 208c2ecf20Sopenharmony_ci [TM_CHANGED_TXPOWER] = MT76_TM_ATTR_TX_POWER, 218c2ecf20Sopenharmony_ci [TM_CHANGED_FREQ_OFFSET] = MT76_TM_ATTR_FREQ_OFFSET, 228c2ecf20Sopenharmony_ci}; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic const u32 reg_backup_list[] = { 258c2ecf20Sopenharmony_ci MT_WF_PHY_RFINTF3_0(0), 268c2ecf20Sopenharmony_ci MT_WF_PHY_RFINTF3_0(1), 278c2ecf20Sopenharmony_ci MT_WF_PHY_RFINTF3_0(2), 288c2ecf20Sopenharmony_ci MT_WF_PHY_RFINTF3_0(3), 298c2ecf20Sopenharmony_ci MT_ANT_SWITCH_CON(2), 308c2ecf20Sopenharmony_ci MT_ANT_SWITCH_CON(3), 318c2ecf20Sopenharmony_ci MT_ANT_SWITCH_CON(4), 328c2ecf20Sopenharmony_ci MT_ANT_SWITCH_CON(6), 338c2ecf20Sopenharmony_ci MT_ANT_SWITCH_CON(7), 348c2ecf20Sopenharmony_ci MT_ANT_SWITCH_CON(8), 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic const struct { 388c2ecf20Sopenharmony_ci u16 wf; 398c2ecf20Sopenharmony_ci u16 reg; 408c2ecf20Sopenharmony_ci} rf_backup_list[] = { 418c2ecf20Sopenharmony_ci { 0, 0x48 }, 428c2ecf20Sopenharmony_ci { 1, 0x48 }, 438c2ecf20Sopenharmony_ci { 2, 0x48 }, 448c2ecf20Sopenharmony_ci { 3, 0x48 }, 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic int 488c2ecf20Sopenharmony_cimt7615_tm_set_tx_power(struct mt7615_phy *phy) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct mt7615_dev *dev = phy->dev; 518c2ecf20Sopenharmony_ci struct mt76_phy *mphy = phy->mt76; 528c2ecf20Sopenharmony_ci int i, ret, n_chains = hweight8(mphy->antenna_mask); 538c2ecf20Sopenharmony_ci struct cfg80211_chan_def *chandef = &mphy->chandef; 548c2ecf20Sopenharmony_ci int freq = chandef->center_freq1, len, target_chains; 558c2ecf20Sopenharmony_ci u8 *data, *eep = (u8 *)dev->mt76.eeprom.data; 568c2ecf20Sopenharmony_ci enum nl80211_band band = chandef->chan->band; 578c2ecf20Sopenharmony_ci struct sk_buff *skb; 588c2ecf20Sopenharmony_ci struct { 598c2ecf20Sopenharmony_ci u8 center_chan; 608c2ecf20Sopenharmony_ci u8 dbdc_idx; 618c2ecf20Sopenharmony_ci u8 band; 628c2ecf20Sopenharmony_ci u8 rsv; 638c2ecf20Sopenharmony_ci } __packed req_hdr = { 648c2ecf20Sopenharmony_ci .center_chan = ieee80211_frequency_to_channel(freq), 658c2ecf20Sopenharmony_ci .band = band, 668c2ecf20Sopenharmony_ci .dbdc_idx = phy != &dev->phy, 678c2ecf20Sopenharmony_ci }; 688c2ecf20Sopenharmony_ci u8 *tx_power = NULL; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (dev->mt76.test.state != MT76_TM_STATE_OFF) 718c2ecf20Sopenharmony_ci tx_power = dev->mt76.test.tx_power; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci len = MT7615_EE_MAX - MT_EE_NIC_CONF_0; 748c2ecf20Sopenharmony_ci skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, sizeof(req_hdr) + len); 758c2ecf20Sopenharmony_ci if (!skb) 768c2ecf20Sopenharmony_ci return -ENOMEM; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci skb_put_data(skb, &req_hdr, sizeof(req_hdr)); 798c2ecf20Sopenharmony_ci data = skb_put_data(skb, eep + MT_EE_NIC_CONF_0, len); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci target_chains = mt7615_ext_pa_enabled(dev, band) ? 1 : n_chains; 828c2ecf20Sopenharmony_ci for (i = 0; i < target_chains; i++) { 838c2ecf20Sopenharmony_ci ret = mt7615_eeprom_get_target_power_index(dev, chandef->chan, i); 848c2ecf20Sopenharmony_ci if (ret < 0) { 858c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 868c2ecf20Sopenharmony_ci return -EINVAL; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (tx_power && tx_power[i]) 908c2ecf20Sopenharmony_ci data[ret - MT_EE_NIC_CONF_0] = tx_power[i]; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci return __mt76_mcu_skb_send_msg(&dev->mt76, skb, 948c2ecf20Sopenharmony_ci MCU_EXT_CMD_SET_TX_POWER_CTRL, false); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic void 988c2ecf20Sopenharmony_cimt7615_tm_reg_backup_restore(struct mt7615_dev *dev) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci u32 *b = dev->test.reg_backup; 1018c2ecf20Sopenharmony_ci int n_regs = ARRAY_SIZE(reg_backup_list); 1028c2ecf20Sopenharmony_ci int n_rf_regs = ARRAY_SIZE(rf_backup_list); 1038c2ecf20Sopenharmony_ci int i; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (dev->mt76.test.state == MT76_TM_STATE_OFF) { 1068c2ecf20Sopenharmony_ci for (i = 0; i < n_regs; i++) 1078c2ecf20Sopenharmony_ci mt76_wr(dev, reg_backup_list[i], b[i]); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci for (i = 0; i < n_rf_regs; i++) 1108c2ecf20Sopenharmony_ci mt7615_rf_wr(dev, rf_backup_list[i].wf, 1118c2ecf20Sopenharmony_ci rf_backup_list[i].reg, b[n_regs + i]); 1128c2ecf20Sopenharmony_ci return; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (b) 1168c2ecf20Sopenharmony_ci return; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci b = devm_kzalloc(dev->mt76.dev, 4 * (n_regs + n_rf_regs), 1198c2ecf20Sopenharmony_ci GFP_KERNEL); 1208c2ecf20Sopenharmony_ci if (!b) 1218c2ecf20Sopenharmony_ci return; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci dev->test.reg_backup = b; 1248c2ecf20Sopenharmony_ci for (i = 0; i < n_regs; i++) 1258c2ecf20Sopenharmony_ci b[i] = mt76_rr(dev, reg_backup_list[i]); 1268c2ecf20Sopenharmony_ci for (i = 0; i < n_rf_regs; i++) 1278c2ecf20Sopenharmony_ci b[n_regs + i] = mt7615_rf_rr(dev, rf_backup_list[i].wf, 1288c2ecf20Sopenharmony_ci rf_backup_list[i].reg); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void 1338c2ecf20Sopenharmony_cimt7615_tm_init_phy(struct mt7615_dev *dev, struct mt7615_phy *phy) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci unsigned int total_flags = ~0; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state)) 1388c2ecf20Sopenharmony_ci return; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci mutex_unlock(&dev->mt76.mutex); 1418c2ecf20Sopenharmony_ci mt7615_set_channel(phy); 1428c2ecf20Sopenharmony_ci mt7615_ops.configure_filter(phy->mt76->hw, 0, &total_flags, 0); 1438c2ecf20Sopenharmony_ci mutex_lock(&dev->mt76.mutex); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci mt7615_tm_reg_backup_restore(dev); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic void 1498c2ecf20Sopenharmony_cimt7615_tm_init(struct mt7615_dev *dev) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci mt7615_tm_init_phy(dev, &dev->phy); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (dev->mt76.phy2) 1548c2ecf20Sopenharmony_ci mt7615_tm_init_phy(dev, dev->mt76.phy2->priv); 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic void 1588c2ecf20Sopenharmony_cimt7615_tm_set_rx_enable(struct mt7615_dev *dev, bool en) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci u32 rqcr_mask = (MT_ARB_RQCR_RX_START | 1618c2ecf20Sopenharmony_ci MT_ARB_RQCR_RXV_START | 1628c2ecf20Sopenharmony_ci MT_ARB_RQCR_RXV_R_EN | 1638c2ecf20Sopenharmony_ci MT_ARB_RQCR_RXV_T_EN) * 1648c2ecf20Sopenharmony_ci (BIT(0) | BIT(MT_ARB_RQCR_BAND_SHIFT)); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (en) { 1678c2ecf20Sopenharmony_ci mt76_clear(dev, MT_ARB_SCR, 1688c2ecf20Sopenharmony_ci MT_ARB_SCR_RX0_DISABLE | MT_ARB_SCR_RX1_DISABLE); 1698c2ecf20Sopenharmony_ci mt76_set(dev, MT_ARB_RQCR, rqcr_mask); 1708c2ecf20Sopenharmony_ci } else { 1718c2ecf20Sopenharmony_ci mt76_set(dev, MT_ARB_SCR, 1728c2ecf20Sopenharmony_ci MT_ARB_SCR_RX0_DISABLE | MT_ARB_SCR_RX1_DISABLE); 1738c2ecf20Sopenharmony_ci mt76_clear(dev, MT_ARB_RQCR, rqcr_mask); 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic void 1788c2ecf20Sopenharmony_cimt7615_tm_set_tx_antenna(struct mt7615_dev *dev, bool en) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct mt76_testmode_data *td = &dev->mt76.test; 1818c2ecf20Sopenharmony_ci u8 mask = td->tx_antenna_mask; 1828c2ecf20Sopenharmony_ci int i; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (!mask) 1858c2ecf20Sopenharmony_ci return; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (!en) 1888c2ecf20Sopenharmony_ci mask = dev->phy.chainmask; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 1918c2ecf20Sopenharmony_ci mt76_rmw_field(dev, MT_WF_PHY_RFINTF3_0(i), 1928c2ecf20Sopenharmony_ci MT_WF_PHY_RFINTF3_0_ANT, 1938c2ecf20Sopenharmony_ci (td->tx_antenna_mask & BIT(i)) ? 0 : 0xa); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci /* 2.4 GHz band */ 1988c2ecf20Sopenharmony_ci mt76_rmw_field(dev, MT_ANT_SWITCH_CON(3), MT_ANT_SWITCH_CON_MODE(0), 1998c2ecf20Sopenharmony_ci (td->tx_antenna_mask & BIT(0)) ? 0x8 : 0x1b); 2008c2ecf20Sopenharmony_ci mt76_rmw_field(dev, MT_ANT_SWITCH_CON(4), MT_ANT_SWITCH_CON_MODE(2), 2018c2ecf20Sopenharmony_ci (td->tx_antenna_mask & BIT(1)) ? 0xe : 0x1b); 2028c2ecf20Sopenharmony_ci mt76_rmw_field(dev, MT_ANT_SWITCH_CON(6), MT_ANT_SWITCH_CON_MODE1(0), 2038c2ecf20Sopenharmony_ci (td->tx_antenna_mask & BIT(2)) ? 0x0 : 0xf); 2048c2ecf20Sopenharmony_ci mt76_rmw_field(dev, MT_ANT_SWITCH_CON(7), MT_ANT_SWITCH_CON_MODE1(2), 2058c2ecf20Sopenharmony_ci (td->tx_antenna_mask & BIT(3)) ? 0x6 : 0xf); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* 5 GHz band */ 2088c2ecf20Sopenharmony_ci mt76_rmw_field(dev, MT_ANT_SWITCH_CON(4), MT_ANT_SWITCH_CON_MODE(1), 2098c2ecf20Sopenharmony_ci (td->tx_antenna_mask & BIT(0)) ? 0xd : 0x1b); 2108c2ecf20Sopenharmony_ci mt76_rmw_field(dev, MT_ANT_SWITCH_CON(2), MT_ANT_SWITCH_CON_MODE(3), 2118c2ecf20Sopenharmony_ci (td->tx_antenna_mask & BIT(1)) ? 0x13 : 0x1b); 2128c2ecf20Sopenharmony_ci mt76_rmw_field(dev, MT_ANT_SWITCH_CON(7), MT_ANT_SWITCH_CON_MODE1(1), 2138c2ecf20Sopenharmony_ci (td->tx_antenna_mask & BIT(2)) ? 0x5 : 0xf); 2148c2ecf20Sopenharmony_ci mt76_rmw_field(dev, MT_ANT_SWITCH_CON(8), MT_ANT_SWITCH_CON_MODE1(3), 2158c2ecf20Sopenharmony_ci (td->tx_antenna_mask & BIT(3)) ? 0xb : 0xf); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 2188c2ecf20Sopenharmony_ci u32 val; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci val = mt7615_rf_rr(dev, i, 0x48); 2218c2ecf20Sopenharmony_ci val &= ~(0x3ff << 20); 2228c2ecf20Sopenharmony_ci if (td->tx_antenna_mask & BIT(i)) 2238c2ecf20Sopenharmony_ci val |= 3 << 20; 2248c2ecf20Sopenharmony_ci else 2258c2ecf20Sopenharmony_ci val |= (2 << 28) | (2 << 26) | (8 << 20); 2268c2ecf20Sopenharmony_ci mt7615_rf_wr(dev, i, 0x48, val); 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic void 2318c2ecf20Sopenharmony_cimt7615_tm_set_tx_frames(struct mt7615_dev *dev, bool en) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info; 2348c2ecf20Sopenharmony_ci struct sk_buff *skb = dev->mt76.test.tx_skb; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci mt7615_mcu_set_chan_info(&dev->phy, MCU_EXT_CMD_SET_RX_PATH); 2378c2ecf20Sopenharmony_ci mt7615_tm_set_tx_antenna(dev, en); 2388c2ecf20Sopenharmony_ci mt7615_tm_set_rx_enable(dev, !en); 2398c2ecf20Sopenharmony_ci if (!en || !skb) 2408c2ecf20Sopenharmony_ci return; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci info = IEEE80211_SKB_CB(skb); 2438c2ecf20Sopenharmony_ci info->control.vif = dev->phy.monitor_vif; 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic void 2478c2ecf20Sopenharmony_cimt7615_tm_update_params(struct mt7615_dev *dev, u32 changed) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct mt76_testmode_data *td = &dev->mt76.test; 2508c2ecf20Sopenharmony_ci bool en = dev->mt76.test.state != MT76_TM_STATE_OFF; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (changed & BIT(TM_CHANGED_TXPOWER_CTRL)) 2538c2ecf20Sopenharmony_ci mt7615_mcu_set_test_param(dev, MCU_ATE_SET_TX_POWER_CONTROL, 2548c2ecf20Sopenharmony_ci en, en && td->tx_power_control); 2558c2ecf20Sopenharmony_ci if (changed & BIT(TM_CHANGED_FREQ_OFFSET)) 2568c2ecf20Sopenharmony_ci mt7615_mcu_set_test_param(dev, MCU_ATE_SET_FREQ_OFFSET, 2578c2ecf20Sopenharmony_ci en, en ? td->freq_offset : 0); 2588c2ecf20Sopenharmony_ci if (changed & BIT(TM_CHANGED_TXPOWER)) 2598c2ecf20Sopenharmony_ci mt7615_tm_set_tx_power(&dev->phy); 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic int 2638c2ecf20Sopenharmony_cimt7615_tm_set_state(struct mt76_dev *mdev, enum mt76_testmode_state state) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); 2668c2ecf20Sopenharmony_ci struct mt76_testmode_data *td = &mdev->test; 2678c2ecf20Sopenharmony_ci enum mt76_testmode_state prev_state = td->state; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci mdev->test.state = state; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (prev_state == MT76_TM_STATE_TX_FRAMES) 2728c2ecf20Sopenharmony_ci mt7615_tm_set_tx_frames(dev, false); 2738c2ecf20Sopenharmony_ci else if (state == MT76_TM_STATE_TX_FRAMES) 2748c2ecf20Sopenharmony_ci mt7615_tm_set_tx_frames(dev, true); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (state <= MT76_TM_STATE_IDLE) 2778c2ecf20Sopenharmony_ci mt7615_tm_init(dev); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if ((state == MT76_TM_STATE_IDLE && 2808c2ecf20Sopenharmony_ci prev_state == MT76_TM_STATE_OFF) || 2818c2ecf20Sopenharmony_ci (state == MT76_TM_STATE_OFF && 2828c2ecf20Sopenharmony_ci prev_state == MT76_TM_STATE_IDLE)) { 2838c2ecf20Sopenharmony_ci u32 changed = 0; 2848c2ecf20Sopenharmony_ci int i; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) { 2878c2ecf20Sopenharmony_ci u16 cur = tm_change_map[i]; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (td->param_set[cur / 32] & BIT(cur % 32)) 2908c2ecf20Sopenharmony_ci changed |= BIT(i); 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci mt7615_tm_update_params(dev, changed); 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci return 0; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic int 3008c2ecf20Sopenharmony_cimt7615_tm_set_params(struct mt76_dev *mdev, struct nlattr **tb, 3018c2ecf20Sopenharmony_ci enum mt76_testmode_state new_state) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); 3048c2ecf20Sopenharmony_ci struct mt76_testmode_data *td = &dev->mt76.test; 3058c2ecf20Sopenharmony_ci u32 changed = 0; 3068c2ecf20Sopenharmony_ci int i; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci BUILD_BUG_ON(NUM_TM_CHANGED >= 32); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (new_state == MT76_TM_STATE_OFF || 3118c2ecf20Sopenharmony_ci td->state == MT76_TM_STATE_OFF) 3128c2ecf20Sopenharmony_ci return 0; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (td->tx_antenna_mask & ~dev->phy.chainmask) 3158c2ecf20Sopenharmony_ci return -EINVAL; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) { 3188c2ecf20Sopenharmony_ci if (tb[tm_change_map[i]]) 3198c2ecf20Sopenharmony_ci changed |= BIT(i); 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci mt7615_tm_update_params(dev, changed); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci return 0; 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic int 3288c2ecf20Sopenharmony_cimt7615_tm_dump_stats(struct mt76_dev *mdev, struct sk_buff *msg) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); 3318c2ecf20Sopenharmony_ci void *rx, *rssi; 3328c2ecf20Sopenharmony_ci int i; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci rx = nla_nest_start(msg, MT76_TM_STATS_ATTR_LAST_RX); 3358c2ecf20Sopenharmony_ci if (!rx) 3368c2ecf20Sopenharmony_ci return -ENOMEM; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (nla_put_s32(msg, MT76_TM_RX_ATTR_FREQ_OFFSET, dev->test.last_freq_offset) || 3398c2ecf20Sopenharmony_ci nla_put_s32(msg, MT76_TM_RX_ATTR_IB_RSSI, dev->test.last_ib_rssi) || 3408c2ecf20Sopenharmony_ci nla_put_s32(msg, MT76_TM_RX_ATTR_WB_RSSI, dev->test.last_wb_rssi)) 3418c2ecf20Sopenharmony_ci return -ENOMEM; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_RCPI); 3448c2ecf20Sopenharmony_ci if (!rssi) 3458c2ecf20Sopenharmony_ci return -ENOMEM; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dev->test.last_rcpi); i++) 3488c2ecf20Sopenharmony_ci if (nla_put_u8(msg, i, dev->test.last_rcpi[i])) 3498c2ecf20Sopenharmony_ci return -ENOMEM; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci nla_nest_end(msg, rssi); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci nla_nest_end(msg, rx); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci return 0; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ciconst struct mt76_testmode_ops mt7615_testmode_ops = { 3598c2ecf20Sopenharmony_ci .set_state = mt7615_tm_set_state, 3608c2ecf20Sopenharmony_ci .set_params = mt7615_tm_set_params, 3618c2ecf20Sopenharmony_ci .dump_stats = mt7615_tm_dump_stats, 3628c2ecf20Sopenharmony_ci}; 363