11c099ab4SSean Wang // SPDX-License-Identifier: ISC 21c099ab4SSean Wang /* Copyright (C) 2020 MediaTek Inc. */ 31c099ab4SSean Wang 41c099ab4SSean Wang #include <linux/firmware.h> 51c099ab4SSean Wang #include <linux/fs.h> 61c099ab4SSean Wang #include "mt7921.h" 72afd17b4SLorenzo Bianconi #include "mt7921_trace.h" 81c099ab4SSean Wang #include "mcu.h" 91c099ab4SSean Wang #include "mac.h" 101c099ab4SSean Wang 111c099ab4SSean Wang #define MT_STA_BFER BIT(0) 121c099ab4SSean Wang #define MT_STA_BFEE BIT(1) 131c099ab4SSean Wang 1468808872SDeren Wu #define PATCH_SEC_ENC_TYPE_MASK GENMASK(31, 24) 1568808872SDeren Wu #define PATCH_SEC_ENC_TYPE_PLAIN 0x00 1668808872SDeren Wu #define PATCH_SEC_ENC_TYPE_AES 0x01 1768808872SDeren Wu #define PATCH_SEC_ENC_TYPE_SCRAMBLE 0x02 1868808872SDeren Wu #define PATCH_SEC_ENC_SCRAMBLE_INFO_MASK GENMASK(15, 0) 1968808872SDeren Wu #define PATCH_SEC_ENC_AES_KEY_MASK GENMASK(7, 0) 2068808872SDeren Wu 211c099ab4SSean Wang static int 221c099ab4SSean Wang mt7921_mcu_parse_eeprom(struct mt76_dev *dev, struct sk_buff *skb) 231c099ab4SSean Wang { 241c099ab4SSean Wang struct mt7921_mcu_eeprom_info *res; 251c099ab4SSean Wang u8 *buf; 261c099ab4SSean Wang 271c099ab4SSean Wang if (!skb) 281c099ab4SSean Wang return -EINVAL; 291c099ab4SSean Wang 301c099ab4SSean Wang skb_pull(skb, sizeof(struct mt7921_mcu_rxd)); 311c099ab4SSean Wang 321c099ab4SSean Wang res = (struct mt7921_mcu_eeprom_info *)skb->data; 331c099ab4SSean Wang buf = dev->eeprom.data + le32_to_cpu(res->addr); 341c099ab4SSean Wang memcpy(buf, res->data, 16); 351c099ab4SSean Wang 361c099ab4SSean Wang return 0; 371c099ab4SSean Wang } 381c099ab4SSean Wang 39dfc7743dSSean Wang int mt7921_mcu_parse_response(struct mt76_dev *mdev, int cmd, 401c099ab4SSean Wang struct sk_buff *skb, int seq) 411c099ab4SSean Wang { 42680a2eadSLorenzo Bianconi int mcu_cmd = FIELD_GET(__MCU_CMD_FIELD_ID, cmd); 431c099ab4SSean Wang struct mt7921_mcu_rxd *rxd; 441c099ab4SSean Wang int ret = 0; 451c099ab4SSean Wang 461c099ab4SSean Wang if (!skb) { 4753d35b1aSLorenzo Bianconi dev_err(mdev->dev, "Message %08x (seq %d) timeout\n", 481c099ab4SSean Wang cmd, seq); 49d43b3257SLorenzo Bianconi mt7921_reset(mdev); 50d43b3257SLorenzo Bianconi 511c099ab4SSean Wang return -ETIMEDOUT; 521c099ab4SSean Wang } 531c099ab4SSean Wang 541c099ab4SSean Wang rxd = (struct mt7921_mcu_rxd *)skb->data; 551c099ab4SSean Wang if (seq != rxd->seq) 561c099ab4SSean Wang return -EAGAIN; 571c099ab4SSean Wang 5845b6f9cbSYN Chen if (cmd == MCU_CMD(PATCH_SEM_CONTROL) || 5945b6f9cbSYN Chen cmd == MCU_CMD(PATCH_FINISH_REQ)) { 601c099ab4SSean Wang skb_pull(skb, sizeof(*rxd) - 4); 611c099ab4SSean Wang ret = *skb->data; 629d8d136cSLorenzo Bianconi } else if (cmd == MCU_EXT_CMD(THERMAL_CTRL)) { 631c099ab4SSean Wang skb_pull(skb, sizeof(*rxd) + 4); 641c099ab4SSean Wang ret = le32_to_cpu(*(__le32 *)skb->data); 65e6d2070dSLorenzo Bianconi } else if (cmd == MCU_EXT_CMD(EFUSE_ACCESS)) { 661c099ab4SSean Wang ret = mt7921_mcu_parse_eeprom(mdev, skb); 6754722402SLorenzo Bianconi } else if (cmd == MCU_UNI_CMD(DEV_INFO_UPDATE) || 6854722402SLorenzo Bianconi cmd == MCU_UNI_CMD(BSS_INFO_UPDATE) || 6954722402SLorenzo Bianconi cmd == MCU_UNI_CMD(STA_REC_UPDATE) || 7054722402SLorenzo Bianconi cmd == MCU_UNI_CMD(HIF_CTRL) || 7154722402SLorenzo Bianconi cmd == MCU_UNI_CMD(OFFLOAD) || 7254722402SLorenzo Bianconi cmd == MCU_UNI_CMD(SUSPEND)) { 731c099ab4SSean Wang struct mt7921_mcu_uni_event *event; 741c099ab4SSean Wang 751c099ab4SSean Wang skb_pull(skb, sizeof(*rxd)); 761c099ab4SSean Wang event = (struct mt7921_mcu_uni_event *)skb->data; 771c099ab4SSean Wang ret = le32_to_cpu(event->status); 78cd3f3873SDeren Wu /* skip invalid event */ 79cd3f3873SDeren Wu if (mcu_cmd != event->cid) 80cd3f3873SDeren Wu ret = -EAGAIN; 81680a2eadSLorenzo Bianconi } else if (cmd == MCU_CE_QUERY(REG_READ)) { 821c099ab4SSean Wang struct mt7921_mcu_reg_event *event; 831c099ab4SSean Wang 841c099ab4SSean Wang skb_pull(skb, sizeof(*rxd)); 851c099ab4SSean Wang event = (struct mt7921_mcu_reg_event *)skb->data; 861c099ab4SSean Wang ret = (int)le32_to_cpu(event->val); 87e6d2070dSLorenzo Bianconi } else { 881c099ab4SSean Wang skb_pull(skb, sizeof(struct mt7921_mcu_rxd)); 891c099ab4SSean Wang } 901c099ab4SSean Wang 911c099ab4SSean Wang return ret; 921c099ab4SSean Wang } 938910a4e5SSean Wang EXPORT_SYMBOL_GPL(mt7921_mcu_parse_response); 941c099ab4SSean Wang 95fe0195f7SSean Wang int mt7921_mcu_fill_message(struct mt76_dev *mdev, struct sk_buff *skb, 961c099ab4SSean Wang int cmd, int *wait_seq) 971c099ab4SSean Wang { 981c099ab4SSean Wang struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76); 99e6d2070dSLorenzo Bianconi int txd_len, mcu_cmd = FIELD_GET(__MCU_CMD_FIELD_ID, cmd); 1001c099ab4SSean Wang struct mt7921_uni_txd *uni_txd; 1011c099ab4SSean Wang struct mt7921_mcu_txd *mcu_txd; 1021c099ab4SSean Wang __le32 *txd; 1031c099ab4SSean Wang u32 val; 1041c099ab4SSean Wang u8 seq; 1051c099ab4SSean Wang 10654722402SLorenzo Bianconi if (cmd == MCU_UNI_CMD(HIF_CTRL) || 10754722402SLorenzo Bianconi cmd == MCU_UNI_CMD(SUSPEND) || 10854722402SLorenzo Bianconi cmd == MCU_UNI_CMD(OFFLOAD)) 1091bb42a35SSean Wang mdev->mcu.timeout = HZ; 11054722402SLorenzo Bianconi else 111a2a6cd54SLorenzo Bianconi mdev->mcu.timeout = 3 * HZ; 1121c099ab4SSean Wang 1131c099ab4SSean Wang seq = ++dev->mt76.mcu.msg_seq & 0xf; 1141c099ab4SSean Wang if (!seq) 1151c099ab4SSean Wang seq = ++dev->mt76.mcu.msg_seq & 0xf; 1161c099ab4SSean Wang 117ffc2198dSLorenzo Bianconi if (cmd == MCU_CMD(FW_SCATTER)) 1181c099ab4SSean Wang goto exit; 1191c099ab4SSean Wang 12054722402SLorenzo Bianconi txd_len = cmd & __MCU_CMD_FIELD_UNI ? sizeof(*uni_txd) : sizeof(*mcu_txd); 1211c099ab4SSean Wang txd = (__le32 *)skb_push(skb, txd_len); 1221c099ab4SSean Wang 1231c099ab4SSean Wang val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len) | 1241c099ab4SSean Wang FIELD_PREP(MT_TXD0_PKT_FMT, MT_TX_TYPE_CMD) | 1251c099ab4SSean Wang FIELD_PREP(MT_TXD0_Q_IDX, MT_TX_MCU_PORT_RX_Q0); 1261c099ab4SSean Wang txd[0] = cpu_to_le32(val); 1271c099ab4SSean Wang 1281c099ab4SSean Wang val = MT_TXD1_LONG_FORMAT | 1291c099ab4SSean Wang FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_CMD); 1301c099ab4SSean Wang txd[1] = cpu_to_le32(val); 1311c099ab4SSean Wang 13254722402SLorenzo Bianconi if (cmd & __MCU_CMD_FIELD_UNI) { 1331c099ab4SSean Wang uni_txd = (struct mt7921_uni_txd *)txd; 1341c099ab4SSean Wang uni_txd->len = cpu_to_le16(skb->len - sizeof(uni_txd->txd)); 1351c099ab4SSean Wang uni_txd->option = MCU_CMD_UNI_EXT_ACK; 1361c099ab4SSean Wang uni_txd->cid = cpu_to_le16(mcu_cmd); 1371c099ab4SSean Wang uni_txd->s2d_index = MCU_S2D_H2N; 1381c099ab4SSean Wang uni_txd->pkt_type = MCU_PKT_ID; 1391c099ab4SSean Wang uni_txd->seq = seq; 1401c099ab4SSean Wang 1411c099ab4SSean Wang goto exit; 1421c099ab4SSean Wang } 1431c099ab4SSean Wang 1441c099ab4SSean Wang mcu_txd = (struct mt7921_mcu_txd *)txd; 1451c099ab4SSean Wang mcu_txd->len = cpu_to_le16(skb->len - sizeof(mcu_txd->txd)); 1461c099ab4SSean Wang mcu_txd->pq_id = cpu_to_le16(MCU_PQ_ID(MT_TX_PORT_IDX_MCU, 1471c099ab4SSean Wang MT_TX_MCU_PORT_RX_Q0)); 1481c099ab4SSean Wang mcu_txd->pkt_type = MCU_PKT_ID; 1491c099ab4SSean Wang mcu_txd->seq = seq; 1501c099ab4SSean Wang mcu_txd->cid = mcu_cmd; 1511c099ab4SSean Wang mcu_txd->s2d_index = MCU_S2D_H2N; 152e6d2070dSLorenzo Bianconi mcu_txd->ext_cid = FIELD_GET(__MCU_CMD_FIELD_EXT_ID, cmd); 153e6d2070dSLorenzo Bianconi 154680a2eadSLorenzo Bianconi if (mcu_txd->ext_cid || (cmd & __MCU_CMD_FIELD_CE)) { 155e6d2070dSLorenzo Bianconi if (cmd & __MCU_CMD_FIELD_QUERY) 156e6d2070dSLorenzo Bianconi mcu_txd->set_query = MCU_Q_QUERY; 157e6d2070dSLorenzo Bianconi else 158e6d2070dSLorenzo Bianconi mcu_txd->set_query = MCU_Q_SET; 159e6d2070dSLorenzo Bianconi mcu_txd->ext_cid_ack = !!mcu_txd->ext_cid; 160e6d2070dSLorenzo Bianconi } else { 161e6d2070dSLorenzo Bianconi mcu_txd->set_query = MCU_Q_NA; 162e6d2070dSLorenzo Bianconi } 1631c099ab4SSean Wang 1641c099ab4SSean Wang exit: 1651c099ab4SSean Wang if (wait_seq) 1661c099ab4SSean Wang *wait_seq = seq; 1671c099ab4SSean Wang 168fe0195f7SSean Wang return 0; 1691c099ab4SSean Wang } 170fe0195f7SSean Wang EXPORT_SYMBOL_GPL(mt7921_mcu_fill_message); 1711c099ab4SSean Wang 1725fc201aaSDeren Wu #ifdef CONFIG_PM 1735fc201aaSDeren Wu 1745fc201aaSDeren Wu static int 1755fc201aaSDeren Wu mt7921_mcu_set_ipv6_ns_filter(struct mt76_dev *dev, 1765fc201aaSDeren Wu struct ieee80211_vif *vif, bool suspend) 1775fc201aaSDeren Wu { 1785fc201aaSDeren Wu struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; 1795fc201aaSDeren Wu struct { 1805fc201aaSDeren Wu struct { 1815fc201aaSDeren Wu u8 bss_idx; 1825fc201aaSDeren Wu u8 pad[3]; 1835fc201aaSDeren Wu } __packed hdr; 1845fc201aaSDeren Wu struct mt76_connac_arpns_tlv arpns; 1855fc201aaSDeren Wu } req = { 1865fc201aaSDeren Wu .hdr = { 1875fc201aaSDeren Wu .bss_idx = mvif->mt76.idx, 1885fc201aaSDeren Wu }, 1895fc201aaSDeren Wu .arpns = { 1905fc201aaSDeren Wu .tag = cpu_to_le16(UNI_OFFLOAD_OFFLOAD_ND), 1915fc201aaSDeren Wu .len = cpu_to_le16(sizeof(struct mt76_connac_arpns_tlv)), 1925fc201aaSDeren Wu .mode = suspend, 1935fc201aaSDeren Wu }, 1945fc201aaSDeren Wu }; 1955fc201aaSDeren Wu 1965fc201aaSDeren Wu return mt76_mcu_send_msg(dev, MCU_UNI_CMD_OFFLOAD, &req, sizeof(req), 1975fc201aaSDeren Wu true); 1985fc201aaSDeren Wu } 1995fc201aaSDeren Wu 2005fc201aaSDeren Wu void mt7921_mcu_set_suspend_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) 2015fc201aaSDeren Wu { 2025fc201aaSDeren Wu if (IS_ENABLED(CONFIG_IPV6)) { 2035fc201aaSDeren Wu struct mt76_phy *phy = priv; 2045fc201aaSDeren Wu 2055fc201aaSDeren Wu mt7921_mcu_set_ipv6_ns_filter(phy->dev, vif, 2065fc201aaSDeren Wu !test_bit(MT76_STATE_RUNNING, 2075fc201aaSDeren Wu &phy->state)); 2085fc201aaSDeren Wu } 2095fc201aaSDeren Wu 2105fc201aaSDeren Wu mt76_connac_mcu_set_suspend_iter(priv, mac, vif); 2115fc201aaSDeren Wu } 2125fc201aaSDeren Wu 2135fc201aaSDeren Wu #endif /* CONFIG_PM */ 2145fc201aaSDeren Wu 2151c099ab4SSean Wang static void 2161c099ab4SSean Wang mt7921_mcu_scan_event(struct mt7921_dev *dev, struct sk_buff *skb) 2171c099ab4SSean Wang { 2181c099ab4SSean Wang struct mt76_phy *mphy = &dev->mt76.phy; 2191c099ab4SSean Wang struct mt7921_phy *phy = (struct mt7921_phy *)mphy->priv; 2201c099ab4SSean Wang 2211c099ab4SSean Wang spin_lock_bh(&dev->mt76.lock); 2221c099ab4SSean Wang __skb_queue_tail(&phy->scan_event_list, skb); 2231c099ab4SSean Wang spin_unlock_bh(&dev->mt76.lock); 2241c099ab4SSean Wang 2251c099ab4SSean Wang ieee80211_queue_delayed_work(mphy->hw, &phy->scan_work, 2261c099ab4SSean Wang MT7921_HW_SCAN_TIMEOUT); 2271c099ab4SSean Wang } 2281c099ab4SSean Wang 2291c099ab4SSean Wang static void 23010de032aSSean Wang mt7921_mcu_connection_loss_iter(void *priv, u8 *mac, 23110de032aSSean Wang struct ieee80211_vif *vif) 23210de032aSSean Wang { 23310de032aSSean Wang struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv; 23410de032aSSean Wang struct mt76_connac_beacon_loss_event *event = priv; 23510de032aSSean Wang 23610de032aSSean Wang if (mvif->idx != event->bss_idx) 23710de032aSSean Wang return; 23810de032aSSean Wang 239116c6960SSean Wang if (!(vif->driver_flags & IEEE80211_VIF_BEACON_FILTER) || 240116c6960SSean Wang vif->type != NL80211_IFTYPE_STATION) 24110de032aSSean Wang return; 24210de032aSSean Wang 24310de032aSSean Wang ieee80211_connection_loss(vif); 24410de032aSSean Wang } 24510de032aSSean Wang 24610de032aSSean Wang static void 24710de032aSSean Wang mt7921_mcu_connection_loss_event(struct mt7921_dev *dev, struct sk_buff *skb) 248b88f5c64SSean Wang { 24967aa2743SLorenzo Bianconi struct mt76_connac_beacon_loss_event *event; 25010de032aSSean Wang struct mt76_phy *mphy = &dev->mt76.phy; 251b88f5c64SSean Wang 252b88f5c64SSean Wang skb_pull(skb, sizeof(struct mt7921_mcu_rxd)); 25367aa2743SLorenzo Bianconi event = (struct mt76_connac_beacon_loss_event *)skb->data; 254b88f5c64SSean Wang 255b88f5c64SSean Wang ieee80211_iterate_active_interfaces_atomic(mphy->hw, 256b88f5c64SSean Wang IEEE80211_IFACE_ITER_RESUME_ALL, 25710de032aSSean Wang mt7921_mcu_connection_loss_iter, event); 258b88f5c64SSean Wang } 259b88f5c64SSean Wang 260b88f5c64SSean Wang static void 2611c099ab4SSean Wang mt7921_mcu_bss_event(struct mt7921_dev *dev, struct sk_buff *skb) 2621c099ab4SSean Wang { 2631c099ab4SSean Wang struct mt76_phy *mphy = &dev->mt76.phy; 26467aa2743SLorenzo Bianconi struct mt76_connac_mcu_bss_event *event; 2651c099ab4SSean Wang 26667aa2743SLorenzo Bianconi skb_pull(skb, sizeof(struct mt7921_mcu_rxd)); 26767aa2743SLorenzo Bianconi event = (struct mt76_connac_mcu_bss_event *)skb->data; 2681c099ab4SSean Wang if (event->is_absent) 2691c099ab4SSean Wang ieee80211_stop_queues(mphy->hw); 2701c099ab4SSean Wang else 2711c099ab4SSean Wang ieee80211_wake_queues(mphy->hw); 2721c099ab4SSean Wang } 2731c099ab4SSean Wang 2741c099ab4SSean Wang static void 2751c099ab4SSean Wang mt7921_mcu_debug_msg_event(struct mt7921_dev *dev, struct sk_buff *skb) 2761c099ab4SSean Wang { 277c7cc5ec5SLorenzo Bianconi struct mt7921_debug_msg { 2781c099ab4SSean Wang __le16 id; 2791c099ab4SSean Wang u8 type; 2801c099ab4SSean Wang u8 flag; 2811c099ab4SSean Wang __le32 value; 2821c099ab4SSean Wang __le16 len; 2831c099ab4SSean Wang u8 content[512]; 284c7cc5ec5SLorenzo Bianconi } __packed * msg; 285c7cc5ec5SLorenzo Bianconi 286c7cc5ec5SLorenzo Bianconi skb_pull(skb, sizeof(struct mt7921_mcu_rxd)); 287c7cc5ec5SLorenzo Bianconi msg = (struct mt7921_debug_msg *)skb->data; 288c7cc5ec5SLorenzo Bianconi 289c7cc5ec5SLorenzo Bianconi if (msg->type == 3) { /* fw log */ 290c7cc5ec5SLorenzo Bianconi u16 len = min_t(u16, le16_to_cpu(msg->len), 512); 2911c099ab4SSean Wang int i; 2921c099ab4SSean Wang 293c7cc5ec5SLorenzo Bianconi for (i = 0 ; i < len; i++) { 294c7cc5ec5SLorenzo Bianconi if (!msg->content[i]) 295c7cc5ec5SLorenzo Bianconi msg->content[i] = ' '; 296c7cc5ec5SLorenzo Bianconi } 2972bf301bcSDan Carpenter wiphy_info(mt76_hw(dev)->wiphy, "%.*s", len, msg->content); 2981c099ab4SSean Wang } 2991c099ab4SSean Wang } 3001c099ab4SSean Wang 3011c099ab4SSean Wang static void 3022afd17b4SLorenzo Bianconi mt7921_mcu_low_power_event(struct mt7921_dev *dev, struct sk_buff *skb) 3032afd17b4SLorenzo Bianconi { 3042afd17b4SLorenzo Bianconi struct mt7921_mcu_lp_event { 3052afd17b4SLorenzo Bianconi u8 state; 3062afd17b4SLorenzo Bianconi u8 reserved[3]; 3072afd17b4SLorenzo Bianconi } __packed * event; 3082afd17b4SLorenzo Bianconi 3092afd17b4SLorenzo Bianconi skb_pull(skb, sizeof(struct mt7921_mcu_rxd)); 3102afd17b4SLorenzo Bianconi event = (struct mt7921_mcu_lp_event *)skb->data; 3112afd17b4SLorenzo Bianconi 3122afd17b4SLorenzo Bianconi trace_lp_event(dev, event->state); 3132afd17b4SLorenzo Bianconi } 3142afd17b4SLorenzo Bianconi 3152afd17b4SLorenzo Bianconi static void 316e0bf699aSDeren Wu mt7921_mcu_tx_done_event(struct mt7921_dev *dev, struct sk_buff *skb) 317e0bf699aSDeren Wu { 318e0bf699aSDeren Wu struct mt7921_mcu_tx_done_event *event; 319e0bf699aSDeren Wu 320e0bf699aSDeren Wu skb_pull(skb, sizeof(struct mt7921_mcu_rxd)); 321e0bf699aSDeren Wu event = (struct mt7921_mcu_tx_done_event *)skb->data; 322e0bf699aSDeren Wu 323e0bf699aSDeren Wu mt7921_mac_add_txs(dev, event->txs); 324e0bf699aSDeren Wu } 325e0bf699aSDeren Wu 326e0bf699aSDeren Wu static void 3271c099ab4SSean Wang mt7921_mcu_rx_unsolicited_event(struct mt7921_dev *dev, struct sk_buff *skb) 3281c099ab4SSean Wang { 3291c099ab4SSean Wang struct mt7921_mcu_rxd *rxd = (struct mt7921_mcu_rxd *)skb->data; 3301c099ab4SSean Wang 3311c099ab4SSean Wang switch (rxd->eid) { 3321c099ab4SSean Wang case MCU_EVENT_BSS_BEACON_LOSS: 33310de032aSSean Wang mt7921_mcu_connection_loss_event(dev, skb); 3341c099ab4SSean Wang break; 3351c099ab4SSean Wang case MCU_EVENT_SCHED_SCAN_DONE: 3361c099ab4SSean Wang case MCU_EVENT_SCAN_DONE: 3371c099ab4SSean Wang mt7921_mcu_scan_event(dev, skb); 3381c099ab4SSean Wang return; 3391c099ab4SSean Wang case MCU_EVENT_BSS_ABSENCE: 3401c099ab4SSean Wang mt7921_mcu_bss_event(dev, skb); 3411c099ab4SSean Wang break; 3421c099ab4SSean Wang case MCU_EVENT_DBG_MSG: 3431c099ab4SSean Wang mt7921_mcu_debug_msg_event(dev, skb); 3441c099ab4SSean Wang break; 3450da3c795SSean Wang case MCU_EVENT_COREDUMP: 346ca74b9b9SSean Wang dev->fw_assert = true; 3470da3c795SSean Wang mt76_connac_mcu_coredump_event(&dev->mt76, skb, 3480da3c795SSean Wang &dev->coredump); 3490da3c795SSean Wang return; 3502afd17b4SLorenzo Bianconi case MCU_EVENT_LP_INFO: 3512afd17b4SLorenzo Bianconi mt7921_mcu_low_power_event(dev, skb); 3522afd17b4SLorenzo Bianconi break; 353e0bf699aSDeren Wu case MCU_EVENT_TX_DONE: 354e0bf699aSDeren Wu mt7921_mcu_tx_done_event(dev, skb); 355e0bf699aSDeren Wu break; 3561c099ab4SSean Wang default: 3571c099ab4SSean Wang break; 3581c099ab4SSean Wang } 3591c099ab4SSean Wang dev_kfree_skb(skb); 3601c099ab4SSean Wang } 3611c099ab4SSean Wang 3621c099ab4SSean Wang void mt7921_mcu_rx_event(struct mt7921_dev *dev, struct sk_buff *skb) 3631c099ab4SSean Wang { 364f0ff5d3aSSean Wang struct mt7921_mcu_rxd *rxd; 365f0ff5d3aSSean Wang 366f0ff5d3aSSean Wang if (skb_linearize(skb)) 367f0ff5d3aSSean Wang return; 368f0ff5d3aSSean Wang 369f0ff5d3aSSean Wang rxd = (struct mt7921_mcu_rxd *)skb->data; 3701c099ab4SSean Wang 3711c099ab4SSean Wang if (rxd->eid == 0x6) { 3721c099ab4SSean Wang mt76_mcu_rx_event(&dev->mt76, skb); 3731c099ab4SSean Wang return; 3741c099ab4SSean Wang } 3751c099ab4SSean Wang 3761c099ab4SSean Wang if (rxd->ext_eid == MCU_EXT_EVENT_RATE_REPORT || 3771c099ab4SSean Wang rxd->eid == MCU_EVENT_BSS_BEACON_LOSS || 3781c099ab4SSean Wang rxd->eid == MCU_EVENT_SCHED_SCAN_DONE || 3791c099ab4SSean Wang rxd->eid == MCU_EVENT_BSS_ABSENCE || 3801c099ab4SSean Wang rxd->eid == MCU_EVENT_SCAN_DONE || 3813cce2b98SDeren Wu rxd->eid == MCU_EVENT_TX_DONE || 3821c099ab4SSean Wang rxd->eid == MCU_EVENT_DBG_MSG || 3830da3c795SSean Wang rxd->eid == MCU_EVENT_COREDUMP || 3842afd17b4SLorenzo Bianconi rxd->eid == MCU_EVENT_LP_INFO || 3851c099ab4SSean Wang !rxd->seq) 3861c099ab4SSean Wang mt7921_mcu_rx_unsolicited_event(dev, skb); 3871c099ab4SSean Wang else 3881c099ab4SSean Wang mt76_mcu_rx_event(&dev->mt76, skb); 3891c099ab4SSean Wang } 3901c099ab4SSean Wang 3911c099ab4SSean Wang /** starec & wtbl **/ 3921c099ab4SSean Wang int mt7921_mcu_uni_tx_ba(struct mt7921_dev *dev, 3931c099ab4SSean Wang struct ieee80211_ampdu_params *params, 3941c099ab4SSean Wang bool enable) 3951c099ab4SSean Wang { 39667aa2743SLorenzo Bianconi struct mt7921_sta *msta = (struct mt7921_sta *)params->sta->drv_priv; 39767aa2743SLorenzo Bianconi 39867aa2743SLorenzo Bianconi if (enable && !params->amsdu) 39967aa2743SLorenzo Bianconi msta->wcid.amsdu = false; 40067aa2743SLorenzo Bianconi 40167aa2743SLorenzo Bianconi return mt76_connac_mcu_sta_ba(&dev->mt76, &msta->vif->mt76, params, 402b5322e44SLorenzo Bianconi MCU_UNI_CMD(STA_REC_UPDATE), 40367aa2743SLorenzo Bianconi enable, true); 4041c099ab4SSean Wang } 4051c099ab4SSean Wang 4061c099ab4SSean Wang int mt7921_mcu_uni_rx_ba(struct mt7921_dev *dev, 4071c099ab4SSean Wang struct ieee80211_ampdu_params *params, 4081c099ab4SSean Wang bool enable) 4091c099ab4SSean Wang { 41067aa2743SLorenzo Bianconi struct mt7921_sta *msta = (struct mt7921_sta *)params->sta->drv_priv; 4111c099ab4SSean Wang 41267aa2743SLorenzo Bianconi return mt76_connac_mcu_sta_ba(&dev->mt76, &msta->vif->mt76, params, 413b5322e44SLorenzo Bianconi MCU_UNI_CMD(STA_REC_UPDATE), 41467aa2743SLorenzo Bianconi enable, false); 4151c099ab4SSean Wang } 4161c099ab4SSean Wang 41768808872SDeren Wu static u32 mt7921_get_data_mode(struct mt7921_dev *dev, u32 info) 41868808872SDeren Wu { 41968808872SDeren Wu u32 mode = DL_MODE_NEED_RSP; 42068808872SDeren Wu 42168808872SDeren Wu if (info == PATCH_SEC_NOT_SUPPORT) 42268808872SDeren Wu return mode; 42368808872SDeren Wu 42468808872SDeren Wu switch (FIELD_GET(PATCH_SEC_ENC_TYPE_MASK, info)) { 42568808872SDeren Wu case PATCH_SEC_ENC_TYPE_PLAIN: 42668808872SDeren Wu break; 42768808872SDeren Wu case PATCH_SEC_ENC_TYPE_AES: 42868808872SDeren Wu mode |= DL_MODE_ENCRYPT; 42968808872SDeren Wu mode |= FIELD_PREP(DL_MODE_KEY_IDX, 43068808872SDeren Wu (info & PATCH_SEC_ENC_AES_KEY_MASK)) & DL_MODE_KEY_IDX; 43168808872SDeren Wu mode |= DL_MODE_RESET_SEC_IV; 43268808872SDeren Wu break; 43368808872SDeren Wu case PATCH_SEC_ENC_TYPE_SCRAMBLE: 43468808872SDeren Wu mode |= DL_MODE_ENCRYPT; 43568808872SDeren Wu mode |= DL_CONFIG_ENCRY_MODE_SEL; 43668808872SDeren Wu mode |= DL_MODE_RESET_SEC_IV; 43768808872SDeren Wu break; 43868808872SDeren Wu default: 43968808872SDeren Wu dev_err(dev->mt76.dev, "Encryption type not support!\n"); 44068808872SDeren Wu } 44168808872SDeren Wu 44268808872SDeren Wu return mode; 44368808872SDeren Wu } 44468808872SDeren Wu 44568808872SDeren Wu static char *mt7921_patch_name(struct mt7921_dev *dev) 44668808872SDeren Wu { 44768808872SDeren Wu char *ret; 44868808872SDeren Wu 44968808872SDeren Wu if (is_mt7922(&dev->mt76)) 45068808872SDeren Wu ret = MT7922_ROM_PATCH; 45168808872SDeren Wu else 45268808872SDeren Wu ret = MT7921_ROM_PATCH; 45368808872SDeren Wu 45468808872SDeren Wu return ret; 45568808872SDeren Wu } 45668808872SDeren Wu 4571c099ab4SSean Wang static int mt7921_load_patch(struct mt7921_dev *dev) 4581c099ab4SSean Wang { 4593d8c636cSLorenzo Bianconi const struct mt76_connac2_patch_hdr *hdr; 4601c099ab4SSean Wang const struct firmware *fw = NULL; 46148fab5bbSSean Wang int i, ret, sem, max_len; 46248fab5bbSSean Wang 46348fab5bbSSean Wang max_len = mt76_is_sdio(&dev->mt76) ? 2048 : 4096; 4641c099ab4SSean Wang 46567aa2743SLorenzo Bianconi sem = mt76_connac_mcu_patch_sem_ctrl(&dev->mt76, true); 4661c099ab4SSean Wang switch (sem) { 4671c099ab4SSean Wang case PATCH_IS_DL: 4681c099ab4SSean Wang return 0; 4691c099ab4SSean Wang case PATCH_NOT_DL_SEM_SUCCESS: 4701c099ab4SSean Wang break; 4711c099ab4SSean Wang default: 4721c099ab4SSean Wang dev_err(dev->mt76.dev, "Failed to get patch semaphore\n"); 4731c099ab4SSean Wang return -EAGAIN; 4741c099ab4SSean Wang } 4751c099ab4SSean Wang 47668808872SDeren Wu ret = request_firmware(&fw, mt7921_patch_name(dev), dev->mt76.dev); 4771c099ab4SSean Wang if (ret) 4781c099ab4SSean Wang goto out; 4791c099ab4SSean Wang 4801c099ab4SSean Wang if (!fw || !fw->data || fw->size < sizeof(*hdr)) { 4811c099ab4SSean Wang dev_err(dev->mt76.dev, "Invalid firmware\n"); 4821c099ab4SSean Wang ret = -EINVAL; 4831c099ab4SSean Wang goto out; 4841c099ab4SSean Wang } 4851c099ab4SSean Wang 4863d8c636cSLorenzo Bianconi hdr = (const struct mt76_connac2_patch_hdr *)fw->data; 4871c099ab4SSean Wang 4881c099ab4SSean Wang dev_info(dev->mt76.dev, "HW/SW Version: 0x%x, Build Time: %.16s\n", 4891c099ab4SSean Wang be32_to_cpu(hdr->hw_sw_ver), hdr->build_date); 4901c099ab4SSean Wang 4911c099ab4SSean Wang for (i = 0; i < be32_to_cpu(hdr->desc.n_region); i++) { 4923d8c636cSLorenzo Bianconi struct mt76_connac2_patch_sec *sec; 4931c099ab4SSean Wang const u8 *dl; 49468808872SDeren Wu u32 len, addr, mode; 49568808872SDeren Wu u32 sec_info = 0; 4961c099ab4SSean Wang 4973d8c636cSLorenzo Bianconi sec = (void *)(fw->data + sizeof(*hdr) + i * sizeof(*sec)); 4981c099ab4SSean Wang if ((be32_to_cpu(sec->type) & PATCH_SEC_TYPE_MASK) != 4991c099ab4SSean Wang PATCH_SEC_TYPE_INFO) { 5001c099ab4SSean Wang ret = -EINVAL; 5011c099ab4SSean Wang goto out; 5021c099ab4SSean Wang } 5031c099ab4SSean Wang 5041c099ab4SSean Wang addr = be32_to_cpu(sec->info.addr); 5051c099ab4SSean Wang len = be32_to_cpu(sec->info.len); 5061c099ab4SSean Wang dl = fw->data + be32_to_cpu(sec->offs); 50768808872SDeren Wu sec_info = be32_to_cpu(sec->info.sec_key_idx); 50868808872SDeren Wu mode = mt7921_get_data_mode(dev, sec_info); 5091c099ab4SSean Wang 51067aa2743SLorenzo Bianconi ret = mt76_connac_mcu_init_download(&dev->mt76, addr, len, 51168808872SDeren Wu mode); 5121c099ab4SSean Wang if (ret) { 5131c099ab4SSean Wang dev_err(dev->mt76.dev, "Download request failed\n"); 5141c099ab4SSean Wang goto out; 5151c099ab4SSean Wang } 5161c099ab4SSean Wang 517ffc2198dSLorenzo Bianconi ret = __mt76_mcu_send_firmware(&dev->mt76, MCU_CMD(FW_SCATTER), 51848fab5bbSSean Wang dl, len, max_len); 5191c099ab4SSean Wang if (ret) { 5201c099ab4SSean Wang dev_err(dev->mt76.dev, "Failed to send patch\n"); 5211c099ab4SSean Wang goto out; 5221c099ab4SSean Wang } 5231c099ab4SSean Wang } 5241c099ab4SSean Wang 52567aa2743SLorenzo Bianconi ret = mt76_connac_mcu_start_patch(&dev->mt76); 5261c099ab4SSean Wang if (ret) 5271c099ab4SSean Wang dev_err(dev->mt76.dev, "Failed to start patch\n"); 5281c099ab4SSean Wang 5291c099ab4SSean Wang out: 53067aa2743SLorenzo Bianconi sem = mt76_connac_mcu_patch_sem_ctrl(&dev->mt76, false); 5311c099ab4SSean Wang switch (sem) { 5321c099ab4SSean Wang case PATCH_REL_SEM_SUCCESS: 5331c099ab4SSean Wang break; 5341c099ab4SSean Wang default: 5351c099ab4SSean Wang ret = -EAGAIN; 5361c099ab4SSean Wang dev_err(dev->mt76.dev, "Failed to release patch semaphore\n"); 53702d1c7d4SSean Wang break; 5381c099ab4SSean Wang } 539a55a0c70SYN Chen 5401c099ab4SSean Wang release_firmware(fw); 5411c099ab4SSean Wang 5421c099ab4SSean Wang return ret; 5431c099ab4SSean Wang } 5441c099ab4SSean Wang 54568808872SDeren Wu static char *mt7921_ram_name(struct mt7921_dev *dev) 54668808872SDeren Wu { 54768808872SDeren Wu char *ret; 54868808872SDeren Wu 54968808872SDeren Wu if (is_mt7922(&dev->mt76)) 55068808872SDeren Wu ret = MT7922_FIRMWARE_WM; 55168808872SDeren Wu else 55268808872SDeren Wu ret = MT7921_FIRMWARE_WM; 55368808872SDeren Wu 55468808872SDeren Wu return ret; 55568808872SDeren Wu } 55668808872SDeren Wu 5571c099ab4SSean Wang static int mt7921_load_firmware(struct mt7921_dev *dev) 5581c099ab4SSean Wang { 5591c099ab4SSean Wang int ret; 5601c099ab4SSean Wang 5611c099ab4SSean Wang ret = mt76_get_field(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_N9_RDY); 56248fab5bbSSean Wang if (ret && mt76_is_mmio(&dev->mt76)) { 5631c099ab4SSean Wang dev_dbg(dev->mt76.dev, "Firmware is already download\n"); 564c3426904SAaron Ma goto fw_loaded; 5651c099ab4SSean Wang } 5661c099ab4SSean Wang 5671c099ab4SSean Wang ret = mt7921_load_patch(dev); 5681c099ab4SSean Wang if (ret) 5691c099ab4SSean Wang return ret; 5701c099ab4SSean Wang 571c132fc7dSLorenzo Bianconi if (mt76_is_sdio(&dev->mt76)) { 572c132fc7dSLorenzo Bianconi /* activate again */ 573c132fc7dSLorenzo Bianconi ret = __mt7921_mcu_fw_pmctrl(dev); 574c132fc7dSLorenzo Bianconi if (!ret) 575c132fc7dSLorenzo Bianconi ret = __mt7921_mcu_drv_pmctrl(dev); 576c132fc7dSLorenzo Bianconi } 577c132fc7dSLorenzo Bianconi 578*b9ec2710SLorenzo Bianconi ret = mt76_connac2_load_ram(&dev->mt76, mt7921_ram_name(dev), NULL); 5791c099ab4SSean Wang if (ret) 5801c099ab4SSean Wang return ret; 5811c099ab4SSean Wang 5821c099ab4SSean Wang if (!mt76_poll_msec(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_N9_RDY, 5831c099ab4SSean Wang MT_TOP_MISC2_FW_N9_RDY, 1500)) { 5841c099ab4SSean Wang dev_err(dev->mt76.dev, "Timeout for initializing firmware\n"); 5851c099ab4SSean Wang 5861c099ab4SSean Wang return -EIO; 5871c099ab4SSean Wang } 5881c099ab4SSean Wang 589c3426904SAaron Ma fw_loaded: 5901c099ab4SSean Wang 591ffa1bf97SSean Wang #ifdef CONFIG_PM 592022159b0SLorenzo Bianconi dev->mt76.hw->wiphy->wowlan = &mt76_connac_wowlan_support; 593ffa1bf97SSean Wang #endif /* CONFIG_PM */ 594ffa1bf97SSean Wang 59581a88b1eSTzung-Bi Shih dev_dbg(dev->mt76.dev, "Firmware init done\n"); 5961c099ab4SSean Wang 5971c099ab4SSean Wang return 0; 5981c099ab4SSean Wang } 5991c099ab4SSean Wang 6001c099ab4SSean Wang int mt7921_mcu_fw_log_2_host(struct mt7921_dev *dev, u8 ctrl) 6011c099ab4SSean Wang { 6021c099ab4SSean Wang struct { 6031c099ab4SSean Wang u8 ctrl_val; 6041c099ab4SSean Wang u8 pad[3]; 6051c099ab4SSean Wang } data = { 6061c099ab4SSean Wang .ctrl_val = ctrl 6071c099ab4SSean Wang }; 6081c099ab4SSean Wang 609680a2eadSLorenzo Bianconi return mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(FWLOG_2_HOST), 610680a2eadSLorenzo Bianconi &data, sizeof(data), false); 6111c099ab4SSean Wang } 6121c099ab4SSean Wang 613d32464e6SLorenzo Bianconi int mt7921_run_firmware(struct mt7921_dev *dev) 614d32464e6SLorenzo Bianconi { 615d32464e6SLorenzo Bianconi int err; 616d32464e6SLorenzo Bianconi 617d32464e6SLorenzo Bianconi err = mt7921_load_firmware(dev); 618d32464e6SLorenzo Bianconi if (err) 619d32464e6SLorenzo Bianconi return err; 620d32464e6SLorenzo Bianconi 62116d98b54SSean Wang err = mt76_connac_mcu_get_nic_capability(&dev->mphy); 62216d98b54SSean Wang if (err) 62316d98b54SSean Wang return err; 624d32464e6SLorenzo Bianconi 62516d98b54SSean Wang set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); 62616d98b54SSean Wang return mt7921_mcu_fw_log_2_host(dev, 1); 627d32464e6SLorenzo Bianconi } 6288910a4e5SSean Wang EXPORT_SYMBOL_GPL(mt7921_run_firmware); 629d32464e6SLorenzo Bianconi 6301c099ab4SSean Wang int mt7921_mcu_set_tx(struct mt7921_dev *dev, struct ieee80211_vif *vif) 6311c099ab4SSean Wang { 63266ca1a7bSSean Wang struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; 6331c099ab4SSean Wang struct edca { 63466ca1a7bSSean Wang __le16 cw_min; 6351c099ab4SSean Wang __le16 cw_max; 6361c099ab4SSean Wang __le16 txop; 63766ca1a7bSSean Wang __le16 aifs; 63866ca1a7bSSean Wang u8 guardtime; 63966ca1a7bSSean Wang u8 acm; 64066ca1a7bSSean Wang } __packed; 6411c099ab4SSean Wang struct mt7921_mcu_tx { 6421c099ab4SSean Wang struct edca edca[IEEE80211_NUM_ACS]; 64366ca1a7bSSean Wang u8 bss_idx; 64466ca1a7bSSean Wang u8 qos; 64566ca1a7bSSean Wang u8 wmm_idx; 64666ca1a7bSSean Wang u8 pad; 6471c099ab4SSean Wang } __packed req = { 64866ca1a7bSSean Wang .bss_idx = mvif->mt76.idx, 64966ca1a7bSSean Wang .qos = vif->bss_conf.qos, 65066ca1a7bSSean Wang .wmm_idx = mvif->mt76.wmm_idx, 6511c099ab4SSean Wang }; 652bb0ae4cfSSean Wang struct mu_edca { 653bb0ae4cfSSean Wang u8 cw_min; 654bb0ae4cfSSean Wang u8 cw_max; 655bb0ae4cfSSean Wang u8 aifsn; 656bb0ae4cfSSean Wang u8 acm; 657bb0ae4cfSSean Wang u8 timer; 658bb0ae4cfSSean Wang u8 padding[3]; 659bb0ae4cfSSean Wang }; 660bb0ae4cfSSean Wang struct mt7921_mcu_mu_tx { 661bb0ae4cfSSean Wang u8 ver; 662bb0ae4cfSSean Wang u8 pad0; 663bb0ae4cfSSean Wang __le16 len; 664bb0ae4cfSSean Wang u8 bss_idx; 665bb0ae4cfSSean Wang u8 qos; 666bb0ae4cfSSean Wang u8 wmm_idx; 667bb0ae4cfSSean Wang u8 pad1; 668bb0ae4cfSSean Wang struct mu_edca edca[IEEE80211_NUM_ACS]; 669bb0ae4cfSSean Wang u8 pad3[32]; 670bb0ae4cfSSean Wang } __packed req_mu = { 671bb0ae4cfSSean Wang .bss_idx = mvif->mt76.idx, 672bb0ae4cfSSean Wang .qos = vif->bss_conf.qos, 673bb0ae4cfSSean Wang .wmm_idx = mvif->mt76.wmm_idx, 674bb0ae4cfSSean Wang }; 6754abe5b92SLorenzo Bianconi static const int to_aci[] = { 1, 0, 2, 3 }; 676bb0ae4cfSSean Wang int ac, ret; 6771c099ab4SSean Wang 6781c099ab4SSean Wang for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { 6791c099ab4SSean Wang struct ieee80211_tx_queue_params *q = &mvif->queue_params[ac]; 68066ca1a7bSSean Wang struct edca *e = &req.edca[to_aci[ac]]; 6811c099ab4SSean Wang 6824abe5b92SLorenzo Bianconi e->aifs = cpu_to_le16(q->aifs); 6831c099ab4SSean Wang e->txop = cpu_to_le16(q->txop); 6841c099ab4SSean Wang 6851c099ab4SSean Wang if (q->cw_min) 68666ca1a7bSSean Wang e->cw_min = cpu_to_le16(q->cw_min); 6871c099ab4SSean Wang else 6884abe5b92SLorenzo Bianconi e->cw_min = cpu_to_le16(5); 6891c099ab4SSean Wang 6901c099ab4SSean Wang if (q->cw_max) 69166ca1a7bSSean Wang e->cw_max = cpu_to_le16(q->cw_max); 6921c099ab4SSean Wang else 6931c099ab4SSean Wang e->cw_max = cpu_to_le16(10); 6941c099ab4SSean Wang } 695bb0ae4cfSSean Wang 69666ca1a7bSSean Wang ret = mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(SET_EDCA_PARMS), &req, 69766ca1a7bSSean Wang sizeof(req), false); 698bb0ae4cfSSean Wang if (ret) 699bb0ae4cfSSean Wang return ret; 700bb0ae4cfSSean Wang 701bb0ae4cfSSean Wang if (!vif->bss_conf.he_support) 702bb0ae4cfSSean Wang return 0; 703bb0ae4cfSSean Wang 704bb0ae4cfSSean Wang for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { 705bb0ae4cfSSean Wang struct ieee80211_he_mu_edca_param_ac_rec *q; 706bb0ae4cfSSean Wang struct mu_edca *e; 707bb0ae4cfSSean Wang 708bb0ae4cfSSean Wang if (!mvif->queue_params[ac].mu_edca) 709bb0ae4cfSSean Wang break; 710bb0ae4cfSSean Wang 711bb0ae4cfSSean Wang q = &mvif->queue_params[ac].mu_edca_param_rec; 712bb0ae4cfSSean Wang e = &(req_mu.edca[to_aci[ac]]); 713bb0ae4cfSSean Wang 714bb0ae4cfSSean Wang e->cw_min = q->ecw_min_max & 0xf; 715bb0ae4cfSSean Wang e->cw_max = (q->ecw_min_max & 0xf0) >> 4; 716bb0ae4cfSSean Wang e->aifsn = q->aifsn; 717bb0ae4cfSSean Wang e->timer = q->mu_edca_timer; 718bb0ae4cfSSean Wang } 719bb0ae4cfSSean Wang 720680a2eadSLorenzo Bianconi return mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(SET_MU_EDCA_PARMS), 721680a2eadSLorenzo Bianconi &req_mu, sizeof(req_mu), false); 7221c099ab4SSean Wang } 7231c099ab4SSean Wang 7241c099ab4SSean Wang int mt7921_mcu_set_chan_info(struct mt7921_phy *phy, int cmd) 7251c099ab4SSean Wang { 7261c099ab4SSean Wang struct mt7921_dev *dev = phy->dev; 7271c099ab4SSean Wang struct cfg80211_chan_def *chandef = &phy->mt76->chandef; 7281c099ab4SSean Wang int freq1 = chandef->center_freq1; 7291c099ab4SSean Wang struct { 7301c099ab4SSean Wang u8 control_ch; 7311c099ab4SSean Wang u8 center_ch; 7321c099ab4SSean Wang u8 bw; 7331c099ab4SSean Wang u8 tx_streams_num; 7341c099ab4SSean Wang u8 rx_streams; /* mask or num */ 7351c099ab4SSean Wang u8 switch_reason; 7361c099ab4SSean Wang u8 band_idx; 7371c099ab4SSean Wang u8 center_ch2; /* for 80+80 only */ 7381c099ab4SSean Wang __le16 cac_case; 7391c099ab4SSean Wang u8 channel_band; 7401c099ab4SSean Wang u8 rsv0; 7411c099ab4SSean Wang __le32 outband_freq; 7421c099ab4SSean Wang u8 txpower_drop; 7431c099ab4SSean Wang u8 ap_bw; 7441c099ab4SSean Wang u8 ap_center_ch; 7451c099ab4SSean Wang u8 rsv1[57]; 7461c099ab4SSean Wang } __packed req = { 7471c099ab4SSean Wang .control_ch = chandef->chan->hw_value, 7481c099ab4SSean Wang .center_ch = ieee80211_frequency_to_channel(freq1), 74944c73d17SLorenzo Bianconi .bw = mt76_connac_chan_bw(chandef), 7501c099ab4SSean Wang .tx_streams_num = hweight8(phy->mt76->antenna_mask), 7511c099ab4SSean Wang .rx_streams = phy->mt76->antenna_mask, 7521c099ab4SSean Wang .band_idx = phy != &dev->phy, 7531c099ab4SSean Wang }; 7541c099ab4SSean Wang 75550ac15a5SLorenzo Bianconi if (chandef->chan->band == NL80211_BAND_6GHZ) 75650ac15a5SLorenzo Bianconi req.channel_band = 2; 75750ac15a5SLorenzo Bianconi else 75850ac15a5SLorenzo Bianconi req.channel_band = chandef->chan->band; 75950ac15a5SLorenzo Bianconi 76024e69f6bSDeren Wu if (cmd == MCU_EXT_CMD(SET_RX_PATH) || 76124e69f6bSDeren Wu dev->mt76.hw->conf.flags & IEEE80211_CONF_MONITOR) 76200a883e6SFelix Fietkau req.switch_reason = CH_SWITCH_NORMAL; 76300a883e6SFelix Fietkau else if (dev->mt76.hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) 7641c099ab4SSean Wang req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD; 76500a883e6SFelix Fietkau else if (!cfg80211_reg_can_beacon(dev->mt76.hw->wiphy, chandef, 76600a883e6SFelix Fietkau NL80211_IFTYPE_AP)) 7671c099ab4SSean Wang req.switch_reason = CH_SWITCH_DFS; 7681c099ab4SSean Wang else 7691c099ab4SSean Wang req.switch_reason = CH_SWITCH_NORMAL; 7701c099ab4SSean Wang 771e6d2070dSLorenzo Bianconi if (cmd == MCU_EXT_CMD(CHANNEL_SWITCH)) 7721c099ab4SSean Wang req.rx_streams = hweight8(req.rx_streams); 7731c099ab4SSean Wang 7741c099ab4SSean Wang if (chandef->width == NL80211_CHAN_WIDTH_80P80) { 7751c099ab4SSean Wang int freq2 = chandef->center_freq2; 7761c099ab4SSean Wang 7771c099ab4SSean Wang req.center_ch2 = ieee80211_frequency_to_channel(freq2); 7781c099ab4SSean Wang } 7791c099ab4SSean Wang 7801c099ab4SSean Wang return mt76_mcu_send_msg(&dev->mt76, cmd, &req, sizeof(req), true); 7811c099ab4SSean Wang } 7821c099ab4SSean Wang 7831c099ab4SSean Wang int mt7921_mcu_set_eeprom(struct mt7921_dev *dev) 7841c099ab4SSean Wang { 7851c099ab4SSean Wang struct req_hdr { 7861c099ab4SSean Wang u8 buffer_mode; 7871c099ab4SSean Wang u8 format; 7881c099ab4SSean Wang __le16 len; 7891c099ab4SSean Wang } __packed req = { 7901c099ab4SSean Wang .buffer_mode = EE_MODE_EFUSE, 7911c099ab4SSean Wang .format = EE_FORMAT_WHOLE, 7921c099ab4SSean Wang }; 7931c099ab4SSean Wang 794e6d2070dSLorenzo Bianconi return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(EFUSE_BUFFER_MODE), 7951c099ab4SSean Wang &req, sizeof(req), true); 7961c099ab4SSean Wang } 7978910a4e5SSean Wang EXPORT_SYMBOL_GPL(mt7921_mcu_set_eeprom); 7981c099ab4SSean Wang 79956d965daSSean Wang int mt7921_mcu_uni_bss_ps(struct mt7921_dev *dev, struct ieee80211_vif *vif) 80056d965daSSean Wang { 80156d965daSSean Wang struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; 80256d965daSSean Wang struct { 80356d965daSSean Wang struct { 80456d965daSSean Wang u8 bss_idx; 80556d965daSSean Wang u8 pad[3]; 80656d965daSSean Wang } __packed hdr; 80756d965daSSean Wang struct ps_tlv { 80856d965daSSean Wang __le16 tag; 80956d965daSSean Wang __le16 len; 81056d965daSSean Wang u8 ps_state; /* 0: device awake 81156d965daSSean Wang * 1: static power save 81256d965daSSean Wang * 2: dynamic power saving 81356d965daSSean Wang * 3: enter TWT power saving 81456d965daSSean Wang * 4: leave TWT power saving 81556d965daSSean Wang */ 81656d965daSSean Wang u8 pad[3]; 81756d965daSSean Wang } __packed ps; 81856d965daSSean Wang } __packed ps_req = { 81956d965daSSean Wang .hdr = { 82056d965daSSean Wang .bss_idx = mvif->mt76.idx, 82156d965daSSean Wang }, 82256d965daSSean Wang .ps = { 82356d965daSSean Wang .tag = cpu_to_le16(UNI_BSS_INFO_PS), 82456d965daSSean Wang .len = cpu_to_le16(sizeof(struct ps_tlv)), 82556d965daSSean Wang .ps_state = vif->bss_conf.ps ? 2 : 0, 82656d965daSSean Wang }, 82756d965daSSean Wang }; 82856d965daSSean Wang 82956d965daSSean Wang if (vif->type != NL80211_IFTYPE_STATION) 83056d965daSSean Wang return -EOPNOTSUPP; 83156d965daSSean Wang 83254722402SLorenzo Bianconi return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE), 83356d965daSSean Wang &ps_req, sizeof(ps_req), true); 83456d965daSSean Wang } 8354086ee28SSean Wang 836890809caSLorenzo Bianconi static int 837890809caSLorenzo Bianconi mt7921_mcu_uni_bss_bcnft(struct mt7921_dev *dev, struct ieee80211_vif *vif, 8384086ee28SSean Wang bool enable) 8394086ee28SSean Wang { 8404086ee28SSean Wang struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; 8414086ee28SSean Wang struct { 8424086ee28SSean Wang struct { 8434086ee28SSean Wang u8 bss_idx; 8444086ee28SSean Wang u8 pad[3]; 8454086ee28SSean Wang } __packed hdr; 8464086ee28SSean Wang struct bcnft_tlv { 8474086ee28SSean Wang __le16 tag; 8484086ee28SSean Wang __le16 len; 8494086ee28SSean Wang __le16 bcn_interval; 8504086ee28SSean Wang u8 dtim_period; 8514086ee28SSean Wang u8 pad; 8524086ee28SSean Wang } __packed bcnft; 8534086ee28SSean Wang } __packed bcnft_req = { 8544086ee28SSean Wang .hdr = { 8554086ee28SSean Wang .bss_idx = mvif->mt76.idx, 8564086ee28SSean Wang }, 8574086ee28SSean Wang .bcnft = { 8584086ee28SSean Wang .tag = cpu_to_le16(UNI_BSS_INFO_BCNFT), 8594086ee28SSean Wang .len = cpu_to_le16(sizeof(struct bcnft_tlv)), 8604086ee28SSean Wang .bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int), 8614086ee28SSean Wang .dtim_period = vif->bss_conf.dtim_period, 8624086ee28SSean Wang }, 8634086ee28SSean Wang }; 8644086ee28SSean Wang 8654086ee28SSean Wang if (vif->type != NL80211_IFTYPE_STATION) 8664086ee28SSean Wang return 0; 8674086ee28SSean Wang 86854722402SLorenzo Bianconi return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE), 8694086ee28SSean Wang &bcnft_req, sizeof(bcnft_req), true); 8704086ee28SSean Wang } 8714086ee28SSean Wang 872890809caSLorenzo Bianconi static int 873890809caSLorenzo Bianconi mt7921_mcu_set_bss_pm(struct mt7921_dev *dev, struct ieee80211_vif *vif, 8744086ee28SSean Wang bool enable) 8754086ee28SSean Wang { 8764086ee28SSean Wang struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; 8774086ee28SSean Wang struct { 8784086ee28SSean Wang u8 bss_idx; 8794086ee28SSean Wang u8 dtim_period; 8804086ee28SSean Wang __le16 aid; 8814086ee28SSean Wang __le16 bcn_interval; 8824086ee28SSean Wang __le16 atim_window; 8834086ee28SSean Wang u8 uapsd; 8844086ee28SSean Wang u8 bmc_delivered_ac; 8854086ee28SSean Wang u8 bmc_triggered_ac; 8864086ee28SSean Wang u8 pad; 8874086ee28SSean Wang } req = { 8884086ee28SSean Wang .bss_idx = mvif->mt76.idx, 889f276e20bSJohannes Berg .aid = cpu_to_le16(vif->cfg.aid), 8904086ee28SSean Wang .dtim_period = vif->bss_conf.dtim_period, 8914086ee28SSean Wang .bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int), 8924086ee28SSean Wang }; 8934086ee28SSean Wang struct { 8944086ee28SSean Wang u8 bss_idx; 8954086ee28SSean Wang u8 pad[3]; 8964086ee28SSean Wang } req_hdr = { 8974086ee28SSean Wang .bss_idx = mvif->mt76.idx, 8984086ee28SSean Wang }; 8994086ee28SSean Wang int err; 9004086ee28SSean Wang 9014086ee28SSean Wang if (vif->type != NL80211_IFTYPE_STATION) 9024086ee28SSean Wang return 0; 9034086ee28SSean Wang 904680a2eadSLorenzo Bianconi err = mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(SET_BSS_ABORT), 905680a2eadSLorenzo Bianconi &req_hdr, sizeof(req_hdr), false); 9064086ee28SSean Wang if (err < 0 || !enable) 9074086ee28SSean Wang return err; 9084086ee28SSean Wang 909680a2eadSLorenzo Bianconi return mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(SET_BSS_CONNECTED), 910680a2eadSLorenzo Bianconi &req, sizeof(req), false); 9114086ee28SSean Wang } 9121d8efc74SSean Wang 913f5056657SSean Wang int mt7921_mcu_sta_update(struct mt7921_dev *dev, struct ieee80211_sta *sta, 914f5056657SSean Wang struct ieee80211_vif *vif, bool enable, 915f5056657SSean Wang enum mt76_sta_info_state state) 91636fcc8cfSLorenzo Bianconi { 91736fcc8cfSLorenzo Bianconi struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; 91836fcc8cfSLorenzo Bianconi int rssi = -ewma_rssi_read(&mvif->rssi); 91936fcc8cfSLorenzo Bianconi struct mt76_sta_cmd_info info = { 92036fcc8cfSLorenzo Bianconi .sta = sta, 92136fcc8cfSLorenzo Bianconi .vif = vif, 92236fcc8cfSLorenzo Bianconi .enable = enable, 92354722402SLorenzo Bianconi .cmd = MCU_UNI_CMD(STA_REC_UPDATE), 924f5056657SSean Wang .state = state, 92582453b1cSLorenzo Bianconi .offload_fw = true, 92636fcc8cfSLorenzo Bianconi .rcpi = to_rcpi(rssi), 92736fcc8cfSLorenzo Bianconi }; 92836fcc8cfSLorenzo Bianconi struct mt7921_sta *msta; 92936fcc8cfSLorenzo Bianconi 93036fcc8cfSLorenzo Bianconi msta = sta ? (struct mt7921_sta *)sta->drv_priv : NULL; 93136fcc8cfSLorenzo Bianconi info.wcid = msta ? &msta->wcid : &mvif->sta.wcid; 932f5056657SSean Wang info.newly = msta ? state != MT76_STA_INFO_STATE_ASSOC : true; 93336fcc8cfSLorenzo Bianconi 934f5056657SSean Wang return mt76_connac_mcu_sta_cmd(&dev->mphy, &info); 93536fcc8cfSLorenzo Bianconi } 93636fcc8cfSLorenzo Bianconi 9377bf0a71eSSean Wang int mt7921_mcu_drv_pmctrl(struct mt7921_dev *dev) 9387bf0a71eSSean Wang { 9397bf0a71eSSean Wang struct mt76_phy *mphy = &dev->mt76.phy; 9407bf0a71eSSean Wang struct mt76_connac_pm *pm = &dev->pm; 9417bf0a71eSSean Wang int err = 0; 9427bf0a71eSSean Wang 9437bf0a71eSSean Wang mutex_lock(&pm->mutex); 9447bf0a71eSSean Wang 9457bf0a71eSSean Wang if (!test_bit(MT76_STATE_PM, &mphy->state)) 9467bf0a71eSSean Wang goto out; 9477bf0a71eSSean Wang 9487bf0a71eSSean Wang err = __mt7921_mcu_drv_pmctrl(dev); 9497bf0a71eSSean Wang out: 95036873246SLorenzo Bianconi mutex_unlock(&pm->mutex); 9511d8efc74SSean Wang 952fad90e43SLorenzo Bianconi if (err) 953fad90e43SLorenzo Bianconi mt7921_reset(&dev->mt76); 954fad90e43SLorenzo Bianconi 955fad90e43SLorenzo Bianconi return err; 9561d8efc74SSean Wang } 9578910a4e5SSean Wang EXPORT_SYMBOL_GPL(mt7921_mcu_drv_pmctrl); 9581d8efc74SSean Wang 9591d8efc74SSean Wang int mt7921_mcu_fw_pmctrl(struct mt7921_dev *dev) 9601d8efc74SSean Wang { 9611d8efc74SSean Wang struct mt76_phy *mphy = &dev->mt76.phy; 96236873246SLorenzo Bianconi struct mt76_connac_pm *pm = &dev->pm; 963dfc7743dSSean Wang int err = 0; 964fad90e43SLorenzo Bianconi 96536873246SLorenzo Bianconi mutex_lock(&pm->mutex); 9661d8efc74SSean Wang 96736873246SLorenzo Bianconi if (mt76_connac_skip_fw_pmctrl(mphy, pm)) 968fad90e43SLorenzo Bianconi goto out; 9691d8efc74SSean Wang 970dfc7743dSSean Wang err = __mt7921_mcu_fw_pmctrl(dev); 971fad90e43SLorenzo Bianconi out: 97236873246SLorenzo Bianconi mutex_unlock(&pm->mutex); 9731d8efc74SSean Wang 974fad90e43SLorenzo Bianconi if (err) 975fad90e43SLorenzo Bianconi mt7921_reset(&dev->mt76); 976fad90e43SLorenzo Bianconi 977fad90e43SLorenzo Bianconi return err; 9781d8efc74SSean Wang } 9798910a4e5SSean Wang EXPORT_SYMBOL_GPL(mt7921_mcu_fw_pmctrl); 9801d8efc74SSean Wang 981890809caSLorenzo Bianconi int mt7921_mcu_set_beacon_filter(struct mt7921_dev *dev, 982890809caSLorenzo Bianconi struct ieee80211_vif *vif, 983890809caSLorenzo Bianconi bool enable) 9841d8efc74SSean Wang { 98510de032aSSean Wang struct ieee80211_hw *hw = mt76_hw(dev); 986890809caSLorenzo Bianconi int err; 9871d8efc74SSean Wang 988890809caSLorenzo Bianconi if (enable) { 989890809caSLorenzo Bianconi err = mt7921_mcu_uni_bss_bcnft(dev, vif, true); 990890809caSLorenzo Bianconi if (err) 991890809caSLorenzo Bianconi return err; 992159f6dd6SSean Wang 9931d8efc74SSean Wang vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; 99410de032aSSean Wang ieee80211_hw_set(hw, CONNECTION_MONITOR); 9951d8efc74SSean Wang mt76_set(dev, MT_WF_RFCR(0), MT_WF_RFCR_DROP_OTHER_BEACON); 996890809caSLorenzo Bianconi 997890809caSLorenzo Bianconi return 0; 998890809caSLorenzo Bianconi } 999890809caSLorenzo Bianconi 1000890809caSLorenzo Bianconi err = mt7921_mcu_set_bss_pm(dev, vif, false); 1001890809caSLorenzo Bianconi if (err) 1002890809caSLorenzo Bianconi return err; 1003890809caSLorenzo Bianconi 10041d8efc74SSean Wang vif->driver_flags &= ~IEEE80211_VIF_BEACON_FILTER; 100510de032aSSean Wang __clear_bit(IEEE80211_HW_CONNECTION_MONITOR, hw->flags); 10061d8efc74SSean Wang mt76_clear(dev, MT_WF_RFCR(0), MT_WF_RFCR_DROP_OTHER_BEACON); 1007890809caSLorenzo Bianconi 1008890809caSLorenzo Bianconi return 0; 10091d8efc74SSean Wang } 10109c9d8321SSean Wang 1011ea29acc9SSean Wang int mt7921_get_txpwr_info(struct mt7921_dev *dev, struct mt7921_txpwr *txpwr) 1012ea29acc9SSean Wang { 1013ea29acc9SSean Wang struct mt7921_txpwr_event *event; 1014ea29acc9SSean Wang struct mt7921_txpwr_req req = { 1015ea29acc9SSean Wang .dbdc_idx = 0, 1016ea29acc9SSean Wang }; 1017ea29acc9SSean Wang struct sk_buff *skb; 1018ea29acc9SSean Wang int ret; 1019ea29acc9SSean Wang 1020680a2eadSLorenzo Bianconi ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_CE_CMD(GET_TXPWR), 1021ea29acc9SSean Wang &req, sizeof(req), true, &skb); 1022ea29acc9SSean Wang if (ret) 1023ea29acc9SSean Wang return ret; 1024ea29acc9SSean Wang 1025ea29acc9SSean Wang event = (struct mt7921_txpwr_event *)skb->data; 1026ea29acc9SSean Wang WARN_ON(skb->len != le16_to_cpu(event->len)); 1027ea29acc9SSean Wang memcpy(txpwr, &event->txpwr, sizeof(event->txpwr)); 1028ea29acc9SSean Wang 1029ea29acc9SSean Wang dev_kfree_skb(skb); 1030ea29acc9SSean Wang 1031ea29acc9SSean Wang return 0; 1032ea29acc9SSean Wang } 1033cbaa0a40SSean Wang 1034cbaa0a40SSean Wang int mt7921_mcu_set_sniffer(struct mt7921_dev *dev, struct ieee80211_vif *vif, 1035cbaa0a40SSean Wang bool enable) 1036cbaa0a40SSean Wang { 1037cbaa0a40SSean Wang struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv; 1038cbaa0a40SSean Wang struct { 1039cbaa0a40SSean Wang struct { 1040cbaa0a40SSean Wang u8 band_idx; 1041cbaa0a40SSean Wang u8 pad[3]; 1042cbaa0a40SSean Wang } __packed hdr; 1043cbaa0a40SSean Wang struct sniffer_enable_tlv { 1044cbaa0a40SSean Wang __le16 tag; 1045cbaa0a40SSean Wang __le16 len; 1046cbaa0a40SSean Wang u8 enable; 1047cbaa0a40SSean Wang u8 pad[3]; 1048cbaa0a40SSean Wang } __packed enable; 1049cbaa0a40SSean Wang } req = { 1050cbaa0a40SSean Wang .hdr = { 1051cbaa0a40SSean Wang .band_idx = mvif->band_idx, 1052cbaa0a40SSean Wang }, 1053cbaa0a40SSean Wang .enable = { 1054cbaa0a40SSean Wang .tag = cpu_to_le16(0), 1055cbaa0a40SSean Wang .len = cpu_to_le16(sizeof(struct sniffer_enable_tlv)), 1056cbaa0a40SSean Wang .enable = enable, 1057cbaa0a40SSean Wang }, 1058cbaa0a40SSean Wang }; 1059cbaa0a40SSean Wang 1060cbaa0a40SSean Wang return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(SNIFFER), &req, sizeof(req), 1061cbaa0a40SSean Wang true); 1062cbaa0a40SSean Wang } 1063116c6960SSean Wang 1064116c6960SSean Wang int 1065116c6960SSean Wang mt7921_mcu_uni_add_beacon_offload(struct mt7921_dev *dev, 1066116c6960SSean Wang struct ieee80211_hw *hw, 1067116c6960SSean Wang struct ieee80211_vif *vif, 1068116c6960SSean Wang bool enable) 1069116c6960SSean Wang { 1070116c6960SSean Wang struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; 1071116c6960SSean Wang struct mt76_wcid *wcid = &dev->mt76.global_wcid; 1072116c6960SSean Wang struct ieee80211_mutable_offsets offs; 1073116c6960SSean Wang struct { 1074116c6960SSean Wang struct req_hdr { 1075116c6960SSean Wang u8 bss_idx; 1076116c6960SSean Wang u8 pad[3]; 1077116c6960SSean Wang } __packed hdr; 1078116c6960SSean Wang struct bcn_content_tlv { 1079116c6960SSean Wang __le16 tag; 1080116c6960SSean Wang __le16 len; 1081116c6960SSean Wang __le16 tim_ie_pos; 1082116c6960SSean Wang __le16 csa_ie_pos; 1083116c6960SSean Wang __le16 bcc_ie_pos; 1084116c6960SSean Wang /* 0: disable beacon offload 1085116c6960SSean Wang * 1: enable beacon offload 1086116c6960SSean Wang * 2: update probe respond offload 1087116c6960SSean Wang */ 1088116c6960SSean Wang u8 enable; 1089116c6960SSean Wang /* 0: legacy format (TXD + payload) 1090116c6960SSean Wang * 1: only cap field IE 1091116c6960SSean Wang */ 1092116c6960SSean Wang u8 type; 1093116c6960SSean Wang __le16 pkt_len; 1094116c6960SSean Wang u8 pkt[512]; 1095116c6960SSean Wang } __packed beacon_tlv; 1096116c6960SSean Wang } req = { 1097116c6960SSean Wang .hdr = { 1098116c6960SSean Wang .bss_idx = mvif->mt76.idx, 1099116c6960SSean Wang }, 1100116c6960SSean Wang .beacon_tlv = { 1101116c6960SSean Wang .tag = cpu_to_le16(UNI_BSS_INFO_BCN_CONTENT), 1102116c6960SSean Wang .len = cpu_to_le16(sizeof(struct bcn_content_tlv)), 1103116c6960SSean Wang .enable = enable, 1104116c6960SSean Wang }, 1105116c6960SSean Wang }; 1106116c6960SSean Wang struct sk_buff *skb; 1107116c6960SSean Wang 1108116c6960SSean Wang if (!enable) 1109116c6960SSean Wang goto out; 1110116c6960SSean Wang 11116e8912a5SShaul Triebitz skb = ieee80211_beacon_get_template(mt76_hw(dev), vif, &offs, 0); 1112116c6960SSean Wang if (!skb) 1113116c6960SSean Wang return -EINVAL; 1114116c6960SSean Wang 1115116c6960SSean Wang if (skb->len > 512 - MT_TXD_SIZE) { 1116116c6960SSean Wang dev_err(dev->mt76.dev, "beacon size limit exceed\n"); 1117116c6960SSean Wang dev_kfree_skb(skb); 1118116c6960SSean Wang return -EINVAL; 1119116c6960SSean Wang } 1120116c6960SSean Wang 1121116c6960SSean Wang mt7921_mac_write_txwi(dev, (__le32 *)(req.beacon_tlv.pkt), skb, 1122116c6960SSean Wang wcid, NULL, 0, true); 1123116c6960SSean Wang memcpy(req.beacon_tlv.pkt + MT_TXD_SIZE, skb->data, skb->len); 1124116c6960SSean Wang req.beacon_tlv.pkt_len = cpu_to_le16(MT_TXD_SIZE + skb->len); 1125116c6960SSean Wang req.beacon_tlv.tim_ie_pos = cpu_to_le16(MT_TXD_SIZE + offs.tim_offset); 1126116c6960SSean Wang 1127116c6960SSean Wang if (offs.cntdwn_counter_offs[0]) { 1128116c6960SSean Wang u16 csa_offs; 1129116c6960SSean Wang 1130116c6960SSean Wang csa_offs = MT_TXD_SIZE + offs.cntdwn_counter_offs[0] - 4; 1131116c6960SSean Wang req.beacon_tlv.csa_ie_pos = cpu_to_le16(csa_offs); 1132116c6960SSean Wang } 1133116c6960SSean Wang dev_kfree_skb(skb); 1134116c6960SSean Wang 1135116c6960SSean Wang out: 1136116c6960SSean Wang return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE), 1137116c6960SSean Wang &req, sizeof(req), true); 1138116c6960SSean Wang } 1139