162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include "mt7921.h" 462306a36Sopenharmony_ci#include "mcu.h" 562306a36Sopenharmony_ci 662306a36Sopenharmony_cienum mt7921_testmode_attr { 762306a36Sopenharmony_ci MT7921_TM_ATTR_UNSPEC, 862306a36Sopenharmony_ci MT7921_TM_ATTR_SET, 962306a36Sopenharmony_ci MT7921_TM_ATTR_QUERY, 1062306a36Sopenharmony_ci MT7921_TM_ATTR_RSP, 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci /* keep last */ 1362306a36Sopenharmony_ci NUM_MT7921_TM_ATTRS, 1462306a36Sopenharmony_ci MT7921_TM_ATTR_MAX = NUM_MT7921_TM_ATTRS - 1, 1562306a36Sopenharmony_ci}; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistruct mt7921_tm_cmd { 1862306a36Sopenharmony_ci u8 action; 1962306a36Sopenharmony_ci u32 param0; 2062306a36Sopenharmony_ci u32 param1; 2162306a36Sopenharmony_ci}; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistruct mt7921_tm_evt { 2462306a36Sopenharmony_ci u32 param0; 2562306a36Sopenharmony_ci u32 param1; 2662306a36Sopenharmony_ci}; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic const struct nla_policy mt7921_tm_policy[NUM_MT7921_TM_ATTRS] = { 2962306a36Sopenharmony_ci [MT7921_TM_ATTR_SET] = NLA_POLICY_EXACT_LEN(sizeof(struct mt7921_tm_cmd)), 3062306a36Sopenharmony_ci [MT7921_TM_ATTR_QUERY] = NLA_POLICY_EXACT_LEN(sizeof(struct mt7921_tm_cmd)), 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic int 3462306a36Sopenharmony_cimt7921_tm_set(struct mt792x_dev *dev, struct mt7921_tm_cmd *req) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci struct mt7921_rftest_cmd cmd = { 3762306a36Sopenharmony_ci .action = req->action, 3862306a36Sopenharmony_ci .param0 = cpu_to_le32(req->param0), 3962306a36Sopenharmony_ci .param1 = cpu_to_le32(req->param1), 4062306a36Sopenharmony_ci }; 4162306a36Sopenharmony_ci bool testmode = false, normal = false; 4262306a36Sopenharmony_ci struct mt76_connac_pm *pm = &dev->pm; 4362306a36Sopenharmony_ci struct mt76_phy *phy = &dev->mphy; 4462306a36Sopenharmony_ci int ret = -ENOTCONN; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci mutex_lock(&dev->mt76.mutex); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci if (req->action == TM_SWITCH_MODE) { 4962306a36Sopenharmony_ci if (req->param0 == MT7921_TM_NORMAL) 5062306a36Sopenharmony_ci normal = true; 5162306a36Sopenharmony_ci else 5262306a36Sopenharmony_ci testmode = true; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (testmode) { 5662306a36Sopenharmony_ci /* Make sure testmode running on full power mode */ 5762306a36Sopenharmony_ci pm->enable = false; 5862306a36Sopenharmony_ci cancel_delayed_work_sync(&pm->ps_work); 5962306a36Sopenharmony_ci cancel_work_sync(&pm->wake_work); 6062306a36Sopenharmony_ci __mt792x_mcu_drv_pmctrl(dev); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci phy->test.state = MT76_TM_STATE_ON; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (!mt76_testmode_enabled(phy)) 6662306a36Sopenharmony_ci goto out; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci ret = mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(TEST_CTRL), &cmd, 6962306a36Sopenharmony_ci sizeof(cmd), false); 7062306a36Sopenharmony_ci if (ret) 7162306a36Sopenharmony_ci goto out; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (normal) { 7462306a36Sopenharmony_ci /* Switch back to the normal world */ 7562306a36Sopenharmony_ci phy->test.state = MT76_TM_STATE_OFF; 7662306a36Sopenharmony_ci pm->enable = true; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ciout: 7962306a36Sopenharmony_ci mutex_unlock(&dev->mt76.mutex); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci return ret; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int 8562306a36Sopenharmony_cimt7921_tm_query(struct mt792x_dev *dev, struct mt7921_tm_cmd *req, 8662306a36Sopenharmony_ci struct mt7921_tm_evt *evt_resp) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct mt7921_rftest_cmd cmd = { 8962306a36Sopenharmony_ci .action = req->action, 9062306a36Sopenharmony_ci .param0 = cpu_to_le32(req->param0), 9162306a36Sopenharmony_ci .param1 = cpu_to_le32(req->param1), 9262306a36Sopenharmony_ci }; 9362306a36Sopenharmony_ci struct mt7921_rftest_evt *evt; 9462306a36Sopenharmony_ci struct sk_buff *skb; 9562306a36Sopenharmony_ci int ret; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_CE_CMD(TEST_CTRL), 9862306a36Sopenharmony_ci &cmd, sizeof(cmd), true, &skb); 9962306a36Sopenharmony_ci if (ret) 10062306a36Sopenharmony_ci goto out; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci evt = (struct mt7921_rftest_evt *)skb->data; 10362306a36Sopenharmony_ci evt_resp->param0 = le32_to_cpu(evt->param0); 10462306a36Sopenharmony_ci evt_resp->param1 = le32_to_cpu(evt->param1); 10562306a36Sopenharmony_ciout: 10662306a36Sopenharmony_ci dev_kfree_skb(skb); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci return ret; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ciint mt7921_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 11262306a36Sopenharmony_ci void *data, int len) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci struct nlattr *tb[NUM_MT76_TM_ATTRS]; 11562306a36Sopenharmony_ci struct mt76_phy *mphy = hw->priv; 11662306a36Sopenharmony_ci struct mt792x_phy *phy = mphy->priv; 11762306a36Sopenharmony_ci int err; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (!test_bit(MT76_STATE_RUNNING, &mphy->state) || 12062306a36Sopenharmony_ci !(hw->conf.flags & IEEE80211_CONF_MONITOR)) 12162306a36Sopenharmony_ci return -ENOTCONN; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci err = nla_parse_deprecated(tb, MT76_TM_ATTR_MAX, data, len, 12462306a36Sopenharmony_ci mt76_tm_policy, NULL); 12562306a36Sopenharmony_ci if (err) 12662306a36Sopenharmony_ci return err; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (tb[MT76_TM_ATTR_DRV_DATA]) { 12962306a36Sopenharmony_ci struct nlattr *drv_tb[NUM_MT7921_TM_ATTRS], *data; 13062306a36Sopenharmony_ci int ret; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci data = tb[MT76_TM_ATTR_DRV_DATA]; 13362306a36Sopenharmony_ci ret = nla_parse_nested_deprecated(drv_tb, 13462306a36Sopenharmony_ci MT7921_TM_ATTR_MAX, 13562306a36Sopenharmony_ci data, mt7921_tm_policy, 13662306a36Sopenharmony_ci NULL); 13762306a36Sopenharmony_ci if (ret) 13862306a36Sopenharmony_ci return ret; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci data = drv_tb[MT7921_TM_ATTR_SET]; 14162306a36Sopenharmony_ci if (data) 14262306a36Sopenharmony_ci return mt7921_tm_set(phy->dev, nla_data(data)); 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return -EINVAL; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ciint mt7921_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg, 14962306a36Sopenharmony_ci struct netlink_callback *cb, void *data, int len) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci struct nlattr *tb[NUM_MT76_TM_ATTRS]; 15262306a36Sopenharmony_ci struct mt76_phy *mphy = hw->priv; 15362306a36Sopenharmony_ci struct mt792x_phy *phy = mphy->priv; 15462306a36Sopenharmony_ci int err; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (!test_bit(MT76_STATE_RUNNING, &mphy->state) || 15762306a36Sopenharmony_ci !(hw->conf.flags & IEEE80211_CONF_MONITOR) || 15862306a36Sopenharmony_ci !mt76_testmode_enabled(mphy)) 15962306a36Sopenharmony_ci return -ENOTCONN; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (cb->args[2]++ > 0) 16262306a36Sopenharmony_ci return -ENOENT; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci err = nla_parse_deprecated(tb, MT76_TM_ATTR_MAX, data, len, 16562306a36Sopenharmony_ci mt76_tm_policy, NULL); 16662306a36Sopenharmony_ci if (err) 16762306a36Sopenharmony_ci return err; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (tb[MT76_TM_ATTR_DRV_DATA]) { 17062306a36Sopenharmony_ci struct nlattr *drv_tb[NUM_MT7921_TM_ATTRS], *data; 17162306a36Sopenharmony_ci int ret; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci data = tb[MT76_TM_ATTR_DRV_DATA]; 17462306a36Sopenharmony_ci ret = nla_parse_nested_deprecated(drv_tb, 17562306a36Sopenharmony_ci MT7921_TM_ATTR_MAX, 17662306a36Sopenharmony_ci data, mt7921_tm_policy, 17762306a36Sopenharmony_ci NULL); 17862306a36Sopenharmony_ci if (ret) 17962306a36Sopenharmony_ci return ret; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci data = drv_tb[MT7921_TM_ATTR_QUERY]; 18262306a36Sopenharmony_ci if (data) { 18362306a36Sopenharmony_ci struct mt7921_tm_evt evt_resp; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci err = mt7921_tm_query(phy->dev, nla_data(data), 18662306a36Sopenharmony_ci &evt_resp); 18762306a36Sopenharmony_ci if (err) 18862306a36Sopenharmony_ci return err; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci return nla_put(msg, MT7921_TM_ATTR_RSP, 19162306a36Sopenharmony_ci sizeof(evt_resp), &evt_resp); 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci return -EINVAL; 19662306a36Sopenharmony_ci} 197