18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * (c) Copyright 2002-2010, Ralink Technology, Inc. 48c2ecf20Sopenharmony_ci * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org> 58c2ecf20Sopenharmony_ci * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/firmware.h> 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/usb.h> 128c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "mt7601u.h" 158c2ecf20Sopenharmony_ci#include "dma.h" 168c2ecf20Sopenharmony_ci#include "mcu.h" 178c2ecf20Sopenharmony_ci#include "usb.h" 188c2ecf20Sopenharmony_ci#include "trace.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define MCU_FW_URB_MAX_PAYLOAD 0x3800 218c2ecf20Sopenharmony_ci#define MCU_FW_URB_SIZE (MCU_FW_URB_MAX_PAYLOAD + 12) 228c2ecf20Sopenharmony_ci#define MCU_RESP_URB_SIZE 1024 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic inline int firmware_running(struct mt7601u_dev *dev) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci return mt7601u_rr(dev, MT_MCU_COM_REG0) == 1; 278c2ecf20Sopenharmony_ci} 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic inline void skb_put_le32(struct sk_buff *skb, u32 val) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci put_unaligned_le32(val, skb_put(skb, 4)); 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic inline void mt7601u_dma_skb_wrap_cmd(struct sk_buff *skb, 358c2ecf20Sopenharmony_ci u8 seq, enum mcu_cmd cmd) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci WARN_ON(mt7601u_dma_skb_wrap(skb, CPU_TX_PORT, DMA_COMMAND, 388c2ecf20Sopenharmony_ci FIELD_PREP(MT_TXD_CMD_INFO_SEQ, seq) | 398c2ecf20Sopenharmony_ci FIELD_PREP(MT_TXD_CMD_INFO_TYPE, cmd))); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic inline void trace_mt_mcu_msg_send_cs(struct mt7601u_dev *dev, 438c2ecf20Sopenharmony_ci struct sk_buff *skb, bool need_resp) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci u32 i, csum = 0; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci for (i = 0; i < skb->len / 4; i++) 488c2ecf20Sopenharmony_ci csum ^= get_unaligned_le32(skb->data + i * 4); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci trace_mt_mcu_msg_send(dev, skb, csum, need_resp); 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic struct sk_buff *mt7601u_mcu_msg_alloc(const void *data, int len) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci struct sk_buff *skb; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci WARN_ON(len % 4); /* if length is not divisible by 4 we need to pad */ 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci skb = alloc_skb(len + MT_DMA_HDR_LEN + 4, GFP_KERNEL); 608c2ecf20Sopenharmony_ci if (skb) { 618c2ecf20Sopenharmony_ci skb_reserve(skb, MT_DMA_HDR_LEN); 628c2ecf20Sopenharmony_ci skb_put_data(skb, data, len); 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci return skb; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic int mt7601u_mcu_wait_resp(struct mt7601u_dev *dev, u8 seq) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct urb *urb = dev->mcu.resp.urb; 718c2ecf20Sopenharmony_ci u32 rxfce; 728c2ecf20Sopenharmony_ci int urb_status, ret, i = 5; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci while (i--) { 758c2ecf20Sopenharmony_ci if (!wait_for_completion_timeout(&dev->mcu.resp_cmpl, 768c2ecf20Sopenharmony_ci msecs_to_jiffies(300))) { 778c2ecf20Sopenharmony_ci dev_warn(dev->dev, "Warning: %s retrying\n", __func__); 788c2ecf20Sopenharmony_ci continue; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /* Make copies of important data before reusing the urb */ 828c2ecf20Sopenharmony_ci rxfce = get_unaligned_le32(dev->mcu.resp.buf); 838c2ecf20Sopenharmony_ci urb_status = urb->status * mt7601u_urb_has_error(urb); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci ret = mt7601u_usb_submit_buf(dev, USB_DIR_IN, MT_EP_IN_CMD_RESP, 868c2ecf20Sopenharmony_ci &dev->mcu.resp, GFP_KERNEL, 878c2ecf20Sopenharmony_ci mt7601u_complete_urb, 888c2ecf20Sopenharmony_ci &dev->mcu.resp_cmpl); 898c2ecf20Sopenharmony_ci if (ret) 908c2ecf20Sopenharmony_ci return ret; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (urb_status) 938c2ecf20Sopenharmony_ci dev_err(dev->dev, "Error: MCU resp urb failed:%d\n", 948c2ecf20Sopenharmony_ci urb_status); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (FIELD_GET(MT_RXD_CMD_INFO_CMD_SEQ, rxfce) == seq && 978c2ecf20Sopenharmony_ci FIELD_GET(MT_RXD_CMD_INFO_EVT_TYPE, rxfce) == CMD_DONE) 988c2ecf20Sopenharmony_ci return 0; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci dev_err(dev->dev, "Error: MCU resp evt:%lx seq:%hhx-%lx!\n", 1018c2ecf20Sopenharmony_ci FIELD_GET(MT_RXD_CMD_INFO_EVT_TYPE, rxfce), 1028c2ecf20Sopenharmony_ci seq, FIELD_GET(MT_RXD_CMD_INFO_CMD_SEQ, rxfce)); 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci dev_err(dev->dev, "Error: %s timed out\n", __func__); 1068c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic int 1108c2ecf20Sopenharmony_cimt7601u_mcu_msg_send(struct mt7601u_dev *dev, struct sk_buff *skb, 1118c2ecf20Sopenharmony_ci enum mcu_cmd cmd, bool wait_resp) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct usb_device *usb_dev = mt7601u_to_usb_dev(dev); 1148c2ecf20Sopenharmony_ci unsigned cmd_pipe = usb_sndbulkpipe(usb_dev, 1158c2ecf20Sopenharmony_ci dev->out_eps[MT_EP_OUT_INBAND_CMD]); 1168c2ecf20Sopenharmony_ci int sent, ret; 1178c2ecf20Sopenharmony_ci u8 seq = 0; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (test_bit(MT7601U_STATE_REMOVED, &dev->state)) { 1208c2ecf20Sopenharmony_ci consume_skb(skb); 1218c2ecf20Sopenharmony_ci return 0; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci mutex_lock(&dev->mcu.mutex); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (wait_resp) 1278c2ecf20Sopenharmony_ci while (!seq) 1288c2ecf20Sopenharmony_ci seq = ++dev->mcu.msg_seq & 0xf; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci mt7601u_dma_skb_wrap_cmd(skb, seq, cmd); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (dev->mcu.resp_cmpl.done) 1338c2ecf20Sopenharmony_ci dev_err(dev->dev, "Error: MCU response pre-completed!\n"); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci trace_mt_mcu_msg_send_cs(dev, skb, wait_resp); 1368c2ecf20Sopenharmony_ci trace_mt_submit_urb_sync(dev, cmd_pipe, skb->len); 1378c2ecf20Sopenharmony_ci ret = usb_bulk_msg(usb_dev, cmd_pipe, skb->data, skb->len, &sent, 500); 1388c2ecf20Sopenharmony_ci if (ret) { 1398c2ecf20Sopenharmony_ci dev_err(dev->dev, "Error: send MCU cmd failed:%d\n", ret); 1408c2ecf20Sopenharmony_ci goto out; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci if (sent != skb->len) 1438c2ecf20Sopenharmony_ci dev_err(dev->dev, "Error: %s sent != skb->len\n", __func__); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (wait_resp) 1468c2ecf20Sopenharmony_ci ret = mt7601u_mcu_wait_resp(dev, seq); 1478c2ecf20Sopenharmony_ciout: 1488c2ecf20Sopenharmony_ci mutex_unlock(&dev->mcu.mutex); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci consume_skb(skb); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return ret; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int mt7601u_mcu_function_select(struct mt7601u_dev *dev, 1568c2ecf20Sopenharmony_ci enum mcu_function func, u32 val) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct sk_buff *skb; 1598c2ecf20Sopenharmony_ci struct { 1608c2ecf20Sopenharmony_ci __le32 id; 1618c2ecf20Sopenharmony_ci __le32 value; 1628c2ecf20Sopenharmony_ci } __packed __aligned(4) msg = { 1638c2ecf20Sopenharmony_ci .id = cpu_to_le32(func), 1648c2ecf20Sopenharmony_ci .value = cpu_to_le32(val), 1658c2ecf20Sopenharmony_ci }; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci skb = mt7601u_mcu_msg_alloc(&msg, sizeof(msg)); 1688c2ecf20Sopenharmony_ci if (!skb) 1698c2ecf20Sopenharmony_ci return -ENOMEM; 1708c2ecf20Sopenharmony_ci return mt7601u_mcu_msg_send(dev, skb, CMD_FUN_SET_OP, func == 5); 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ciint mt7601u_mcu_tssi_read_kick(struct mt7601u_dev *dev, int use_hvga) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci int ret; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (!test_bit(MT7601U_STATE_MCU_RUNNING, &dev->state)) 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci ret = mt7601u_mcu_function_select(dev, ATOMIC_TSSI_SETTING, 1818c2ecf20Sopenharmony_ci use_hvga); 1828c2ecf20Sopenharmony_ci if (ret) { 1838c2ecf20Sopenharmony_ci dev_warn(dev->dev, "Warning: MCU TSSI read kick failed\n"); 1848c2ecf20Sopenharmony_ci return ret; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci dev->tssi_read_trig = true; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return 0; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ciint 1938c2ecf20Sopenharmony_cimt7601u_mcu_calibrate(struct mt7601u_dev *dev, enum mcu_calibrate cal, u32 val) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct sk_buff *skb; 1968c2ecf20Sopenharmony_ci struct { 1978c2ecf20Sopenharmony_ci __le32 id; 1988c2ecf20Sopenharmony_ci __le32 value; 1998c2ecf20Sopenharmony_ci } __packed __aligned(4) msg = { 2008c2ecf20Sopenharmony_ci .id = cpu_to_le32(cal), 2018c2ecf20Sopenharmony_ci .value = cpu_to_le32(val), 2028c2ecf20Sopenharmony_ci }; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci skb = mt7601u_mcu_msg_alloc(&msg, sizeof(msg)); 2058c2ecf20Sopenharmony_ci if (!skb) 2068c2ecf20Sopenharmony_ci return -ENOMEM; 2078c2ecf20Sopenharmony_ci return mt7601u_mcu_msg_send(dev, skb, CMD_CALIBRATION_OP, true); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ciint mt7601u_write_reg_pairs(struct mt7601u_dev *dev, u32 base, 2118c2ecf20Sopenharmony_ci const struct mt76_reg_pair *data, int n) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci const int max_vals_per_cmd = INBAND_PACKET_MAX_LEN / 8; 2148c2ecf20Sopenharmony_ci struct sk_buff *skb; 2158c2ecf20Sopenharmony_ci int cnt, i, ret; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (!n) 2188c2ecf20Sopenharmony_ci return 0; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci cnt = min(max_vals_per_cmd, n); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci skb = alloc_skb(cnt * 8 + MT_DMA_HDR_LEN + 4, GFP_KERNEL); 2238c2ecf20Sopenharmony_ci if (!skb) 2248c2ecf20Sopenharmony_ci return -ENOMEM; 2258c2ecf20Sopenharmony_ci skb_reserve(skb, MT_DMA_HDR_LEN); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci for (i = 0; i < cnt; i++) { 2288c2ecf20Sopenharmony_ci skb_put_le32(skb, base + data[i].reg); 2298c2ecf20Sopenharmony_ci skb_put_le32(skb, data[i].value); 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci ret = mt7601u_mcu_msg_send(dev, skb, CMD_RANDOM_WRITE, cnt == n); 2338c2ecf20Sopenharmony_ci if (ret) 2348c2ecf20Sopenharmony_ci return ret; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return mt7601u_write_reg_pairs(dev, base, data + cnt, n - cnt); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ciint mt7601u_burst_write_regs(struct mt7601u_dev *dev, u32 offset, 2408c2ecf20Sopenharmony_ci const u32 *data, int n) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci const int max_regs_per_cmd = INBAND_PACKET_MAX_LEN / 4 - 1; 2438c2ecf20Sopenharmony_ci struct sk_buff *skb; 2448c2ecf20Sopenharmony_ci int cnt, i, ret; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (!n) 2478c2ecf20Sopenharmony_ci return 0; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci cnt = min(max_regs_per_cmd, n); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci skb = alloc_skb(cnt * 4 + MT_DMA_HDR_LEN + 4, GFP_KERNEL); 2528c2ecf20Sopenharmony_ci if (!skb) 2538c2ecf20Sopenharmony_ci return -ENOMEM; 2548c2ecf20Sopenharmony_ci skb_reserve(skb, MT_DMA_HDR_LEN); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci skb_put_le32(skb, MT_MCU_MEMMAP_WLAN + offset); 2578c2ecf20Sopenharmony_ci for (i = 0; i < cnt; i++) 2588c2ecf20Sopenharmony_ci skb_put_le32(skb, data[i]); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci ret = mt7601u_mcu_msg_send(dev, skb, CMD_BURST_WRITE, cnt == n); 2618c2ecf20Sopenharmony_ci if (ret) 2628c2ecf20Sopenharmony_ci return ret; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci return mt7601u_burst_write_regs(dev, offset + cnt * 4, 2658c2ecf20Sopenharmony_ci data + cnt, n - cnt); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistruct mt76_fw_header { 2698c2ecf20Sopenharmony_ci __le32 ilm_len; 2708c2ecf20Sopenharmony_ci __le32 dlm_len; 2718c2ecf20Sopenharmony_ci __le16 build_ver; 2728c2ecf20Sopenharmony_ci __le16 fw_ver; 2738c2ecf20Sopenharmony_ci u8 pad[4]; 2748c2ecf20Sopenharmony_ci char build_time[16]; 2758c2ecf20Sopenharmony_ci}; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistruct mt76_fw { 2788c2ecf20Sopenharmony_ci struct mt76_fw_header hdr; 2798c2ecf20Sopenharmony_ci u8 ivb[MT_MCU_IVB_SIZE]; 2808c2ecf20Sopenharmony_ci u8 ilm[]; 2818c2ecf20Sopenharmony_ci}; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic int __mt7601u_dma_fw(struct mt7601u_dev *dev, 2848c2ecf20Sopenharmony_ci const struct mt7601u_dma_buf *dma_buf, 2858c2ecf20Sopenharmony_ci const void *data, u32 len, u32 dst_addr) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(cmpl); 2888c2ecf20Sopenharmony_ci struct mt7601u_dma_buf buf = *dma_buf; /* we need to fake length */ 2898c2ecf20Sopenharmony_ci __le32 reg; 2908c2ecf20Sopenharmony_ci u32 val; 2918c2ecf20Sopenharmony_ci int ret; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci reg = cpu_to_le32(FIELD_PREP(MT_TXD_INFO_TYPE, DMA_PACKET) | 2948c2ecf20Sopenharmony_ci FIELD_PREP(MT_TXD_INFO_D_PORT, CPU_TX_PORT) | 2958c2ecf20Sopenharmony_ci FIELD_PREP(MT_TXD_INFO_LEN, len)); 2968c2ecf20Sopenharmony_ci memcpy(buf.buf, ®, sizeof(reg)); 2978c2ecf20Sopenharmony_ci memcpy(buf.buf + sizeof(reg), data, len); 2988c2ecf20Sopenharmony_ci memset(buf.buf + sizeof(reg) + len, 0, 8); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci ret = mt7601u_vendor_single_wr(dev, MT_VEND_WRITE_FCE, 3018c2ecf20Sopenharmony_ci MT_FCE_DMA_ADDR, dst_addr); 3028c2ecf20Sopenharmony_ci if (ret) 3038c2ecf20Sopenharmony_ci return ret; 3048c2ecf20Sopenharmony_ci len = roundup(len, 4); 3058c2ecf20Sopenharmony_ci ret = mt7601u_vendor_single_wr(dev, MT_VEND_WRITE_FCE, 3068c2ecf20Sopenharmony_ci MT_FCE_DMA_LEN, len << 16); 3078c2ecf20Sopenharmony_ci if (ret) 3088c2ecf20Sopenharmony_ci return ret; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci buf.len = MT_DMA_HDR_LEN + len + 4; 3118c2ecf20Sopenharmony_ci ret = mt7601u_usb_submit_buf(dev, USB_DIR_OUT, MT_EP_OUT_INBAND_CMD, 3128c2ecf20Sopenharmony_ci &buf, GFP_KERNEL, 3138c2ecf20Sopenharmony_ci mt7601u_complete_urb, &cmpl); 3148c2ecf20Sopenharmony_ci if (ret) 3158c2ecf20Sopenharmony_ci return ret; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if (!wait_for_completion_timeout(&cmpl, msecs_to_jiffies(1000))) { 3188c2ecf20Sopenharmony_ci dev_err(dev->dev, "Error: firmware upload timed out\n"); 3198c2ecf20Sopenharmony_ci usb_kill_urb(buf.urb); 3208c2ecf20Sopenharmony_ci return -ETIMEDOUT; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci if (mt7601u_urb_has_error(buf.urb)) { 3238c2ecf20Sopenharmony_ci dev_err(dev->dev, "Error: firmware upload urb failed:%d\n", 3248c2ecf20Sopenharmony_ci buf.urb->status); 3258c2ecf20Sopenharmony_ci return buf.urb->status; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci val = mt7601u_rr(dev, MT_TX_CPU_FROM_FCE_CPU_DESC_IDX); 3298c2ecf20Sopenharmony_ci val++; 3308c2ecf20Sopenharmony_ci mt7601u_wr(dev, MT_TX_CPU_FROM_FCE_CPU_DESC_IDX, val); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci return 0; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic int 3368c2ecf20Sopenharmony_cimt7601u_dma_fw(struct mt7601u_dev *dev, struct mt7601u_dma_buf *dma_buf, 3378c2ecf20Sopenharmony_ci const void *data, int len, u32 dst_addr) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci int n, ret; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (len == 0) 3428c2ecf20Sopenharmony_ci return 0; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci n = min(MCU_FW_URB_MAX_PAYLOAD, len); 3458c2ecf20Sopenharmony_ci ret = __mt7601u_dma_fw(dev, dma_buf, data, n, dst_addr); 3468c2ecf20Sopenharmony_ci if (ret) 3478c2ecf20Sopenharmony_ci return ret; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (!mt76_poll_msec(dev, MT_MCU_COM_REG1, BIT(31), BIT(31), 500)) 3508c2ecf20Sopenharmony_ci return -ETIMEDOUT; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci return mt7601u_dma_fw(dev, dma_buf, data + n, len - n, dst_addr + n); 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic int 3568c2ecf20Sopenharmony_cimt7601u_upload_firmware(struct mt7601u_dev *dev, const struct mt76_fw *fw) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci struct mt7601u_dma_buf dma_buf; 3598c2ecf20Sopenharmony_ci void *ivb; 3608c2ecf20Sopenharmony_ci u32 ilm_len, dlm_len; 3618c2ecf20Sopenharmony_ci int i, ret; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci ivb = kmemdup(fw->ivb, sizeof(fw->ivb), GFP_KERNEL); 3648c2ecf20Sopenharmony_ci if (!ivb) 3658c2ecf20Sopenharmony_ci return -ENOMEM; 3668c2ecf20Sopenharmony_ci if (mt7601u_usb_alloc_buf(dev, MCU_FW_URB_SIZE, &dma_buf)) { 3678c2ecf20Sopenharmony_ci ret = -ENOMEM; 3688c2ecf20Sopenharmony_ci goto error; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci ilm_len = le32_to_cpu(fw->hdr.ilm_len) - sizeof(fw->ivb); 3728c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "loading FW - ILM %u + IVB %zu\n", 3738c2ecf20Sopenharmony_ci ilm_len, sizeof(fw->ivb)); 3748c2ecf20Sopenharmony_ci ret = mt7601u_dma_fw(dev, &dma_buf, fw->ilm, ilm_len, sizeof(fw->ivb)); 3758c2ecf20Sopenharmony_ci if (ret) 3768c2ecf20Sopenharmony_ci goto error; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci dlm_len = le32_to_cpu(fw->hdr.dlm_len); 3798c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "loading FW - DLM %u\n", dlm_len); 3808c2ecf20Sopenharmony_ci ret = mt7601u_dma_fw(dev, &dma_buf, fw->ilm + ilm_len, 3818c2ecf20Sopenharmony_ci dlm_len, MT_MCU_DLM_OFFSET); 3828c2ecf20Sopenharmony_ci if (ret) 3838c2ecf20Sopenharmony_ci goto error; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci ret = mt7601u_vendor_request(dev, MT_VEND_DEV_MODE, USB_DIR_OUT, 3868c2ecf20Sopenharmony_ci 0x12, 0, ivb, sizeof(fw->ivb)); 3878c2ecf20Sopenharmony_ci if (ret < 0) 3888c2ecf20Sopenharmony_ci goto error; 3898c2ecf20Sopenharmony_ci ret = 0; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci for (i = 100; i && !firmware_running(dev); i--) 3928c2ecf20Sopenharmony_ci msleep(10); 3938c2ecf20Sopenharmony_ci if (!i) { 3948c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 3958c2ecf20Sopenharmony_ci goto error; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "Firmware running!\n"); 3998c2ecf20Sopenharmony_cierror: 4008c2ecf20Sopenharmony_ci kfree(ivb); 4018c2ecf20Sopenharmony_ci mt7601u_usb_free_buf(dev, &dma_buf); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci return ret; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic int mt7601u_load_firmware(struct mt7601u_dev *dev) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci const struct firmware *fw; 4098c2ecf20Sopenharmony_ci const struct mt76_fw_header *hdr; 4108c2ecf20Sopenharmony_ci int len, ret; 4118c2ecf20Sopenharmony_ci u32 val; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci mt7601u_wr(dev, MT_USB_DMA_CFG, (MT_USB_DMA_CFG_RX_BULK_EN | 4148c2ecf20Sopenharmony_ci MT_USB_DMA_CFG_TX_BULK_EN)); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci if (firmware_running(dev)) 4178c2ecf20Sopenharmony_ci return firmware_request_cache(dev->dev, MT7601U_FIRMWARE); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci ret = request_firmware(&fw, MT7601U_FIRMWARE, dev->dev); 4208c2ecf20Sopenharmony_ci if (ret) 4218c2ecf20Sopenharmony_ci return ret; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (!fw || !fw->data || fw->size < sizeof(*hdr)) 4248c2ecf20Sopenharmony_ci goto err_inv_fw; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci hdr = (const struct mt76_fw_header *) fw->data; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (le32_to_cpu(hdr->ilm_len) <= MT_MCU_IVB_SIZE) 4298c2ecf20Sopenharmony_ci goto err_inv_fw; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci len = sizeof(*hdr); 4328c2ecf20Sopenharmony_ci len += le32_to_cpu(hdr->ilm_len); 4338c2ecf20Sopenharmony_ci len += le32_to_cpu(hdr->dlm_len); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (fw->size != len) 4368c2ecf20Sopenharmony_ci goto err_inv_fw; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci val = le16_to_cpu(hdr->fw_ver); 4398c2ecf20Sopenharmony_ci dev_info(dev->dev, 4408c2ecf20Sopenharmony_ci "Firmware Version: %d.%d.%02d Build: %x Build time: %.16s\n", 4418c2ecf20Sopenharmony_ci (val >> 12) & 0xf, (val >> 8) & 0xf, val & 0xf, 4428c2ecf20Sopenharmony_ci le16_to_cpu(hdr->build_ver), hdr->build_time); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci len = le32_to_cpu(hdr->ilm_len); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci mt7601u_wr(dev, 0x94c, 0); 4478c2ecf20Sopenharmony_ci mt7601u_wr(dev, MT_FCE_PSE_CTRL, 0); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci mt7601u_vendor_reset(dev); 4508c2ecf20Sopenharmony_ci msleep(5); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci mt7601u_wr(dev, 0xa44, 0); 4538c2ecf20Sopenharmony_ci mt7601u_wr(dev, 0x230, 0x84210); 4548c2ecf20Sopenharmony_ci mt7601u_wr(dev, 0x400, 0x80c00); 4558c2ecf20Sopenharmony_ci mt7601u_wr(dev, 0x800, 1); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci mt7601u_rmw(dev, MT_PBF_CFG, 0, (MT_PBF_CFG_TX0Q_EN | 4588c2ecf20Sopenharmony_ci MT_PBF_CFG_TX1Q_EN | 4598c2ecf20Sopenharmony_ci MT_PBF_CFG_TX2Q_EN | 4608c2ecf20Sopenharmony_ci MT_PBF_CFG_TX3Q_EN)); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci mt7601u_wr(dev, MT_FCE_PSE_CTRL, 1); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci mt7601u_wr(dev, MT_USB_DMA_CFG, (MT_USB_DMA_CFG_RX_BULK_EN | 4658c2ecf20Sopenharmony_ci MT_USB_DMA_CFG_TX_BULK_EN)); 4668c2ecf20Sopenharmony_ci val = mt76_set(dev, MT_USB_DMA_CFG, MT_USB_DMA_CFG_TX_CLR); 4678c2ecf20Sopenharmony_ci val &= ~MT_USB_DMA_CFG_TX_CLR; 4688c2ecf20Sopenharmony_ci mt7601u_wr(dev, MT_USB_DMA_CFG, val); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci /* FCE tx_fs_base_ptr */ 4718c2ecf20Sopenharmony_ci mt7601u_wr(dev, MT_TX_CPU_FROM_FCE_BASE_PTR, 0x400230); 4728c2ecf20Sopenharmony_ci /* FCE tx_fs_max_cnt */ 4738c2ecf20Sopenharmony_ci mt7601u_wr(dev, MT_TX_CPU_FROM_FCE_MAX_COUNT, 1); 4748c2ecf20Sopenharmony_ci /* FCE pdma enable */ 4758c2ecf20Sopenharmony_ci mt7601u_wr(dev, MT_FCE_PDMA_GLOBAL_CONF, 0x44); 4768c2ecf20Sopenharmony_ci /* FCE skip_fs_en */ 4778c2ecf20Sopenharmony_ci mt7601u_wr(dev, MT_FCE_SKIP_FS, 3); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci ret = mt7601u_upload_firmware(dev, (const struct mt76_fw *)fw->data); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci release_firmware(fw); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci return ret; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cierr_inv_fw: 4868c2ecf20Sopenharmony_ci dev_err(dev->dev, "Invalid firmware image\n"); 4878c2ecf20Sopenharmony_ci release_firmware(fw); 4888c2ecf20Sopenharmony_ci return -ENOENT; 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ciint mt7601u_mcu_init(struct mt7601u_dev *dev) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci int ret; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci mutex_init(&dev->mcu.mutex); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci ret = mt7601u_load_firmware(dev); 4988c2ecf20Sopenharmony_ci if (ret) 4998c2ecf20Sopenharmony_ci return ret; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci set_bit(MT7601U_STATE_MCU_RUNNING, &dev->state); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci return 0; 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ciint mt7601u_mcu_cmd_init(struct mt7601u_dev *dev) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci int ret; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci ret = mt7601u_mcu_function_select(dev, Q_SELECT, 1); 5118c2ecf20Sopenharmony_ci if (ret) 5128c2ecf20Sopenharmony_ci return ret; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci init_completion(&dev->mcu.resp_cmpl); 5158c2ecf20Sopenharmony_ci if (mt7601u_usb_alloc_buf(dev, MCU_RESP_URB_SIZE, &dev->mcu.resp)) { 5168c2ecf20Sopenharmony_ci mt7601u_usb_free_buf(dev, &dev->mcu.resp); 5178c2ecf20Sopenharmony_ci return -ENOMEM; 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci ret = mt7601u_usb_submit_buf(dev, USB_DIR_IN, MT_EP_IN_CMD_RESP, 5218c2ecf20Sopenharmony_ci &dev->mcu.resp, GFP_KERNEL, 5228c2ecf20Sopenharmony_ci mt7601u_complete_urb, &dev->mcu.resp_cmpl); 5238c2ecf20Sopenharmony_ci if (ret) { 5248c2ecf20Sopenharmony_ci mt7601u_usb_free_buf(dev, &dev->mcu.resp); 5258c2ecf20Sopenharmony_ci return ret; 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci return 0; 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_civoid mt7601u_mcu_cmd_deinit(struct mt7601u_dev *dev) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci usb_kill_urb(dev->mcu.resp.urb); 5348c2ecf20Sopenharmony_ci mt7601u_usb_free_buf(dev, &dev->mcu.resp); 5358c2ecf20Sopenharmony_ci} 536