18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: ISC 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com> 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/module.h> 78c2ecf20Sopenharmony_ci#include "mt76.h" 88c2ecf20Sopenharmony_ci#include "usb_trace.h" 98c2ecf20Sopenharmony_ci#include "dma.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define MT_VEND_REQ_MAX_RETRY 10 128c2ecf20Sopenharmony_ci#define MT_VEND_REQ_TOUT_MS 300 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistatic bool disable_usb_sg; 158c2ecf20Sopenharmony_cimodule_param_named(disable_usb_sg, disable_usb_sg, bool, 0644); 168c2ecf20Sopenharmony_ciMODULE_PARM_DESC(disable_usb_sg, "Disable usb scatter-gather support"); 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic int __mt76u_vendor_request(struct mt76_dev *dev, u8 req, 198c2ecf20Sopenharmony_ci u8 req_type, u16 val, u16 offset, 208c2ecf20Sopenharmony_ci void *buf, size_t len) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci struct usb_interface *uintf = to_usb_interface(dev->dev); 238c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(uintf); 248c2ecf20Sopenharmony_ci unsigned int pipe; 258c2ecf20Sopenharmony_ci int i, ret; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci lockdep_assert_held(&dev->usb.usb_ctrl_mtx); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci pipe = (req_type & USB_DIR_IN) ? usb_rcvctrlpipe(udev, 0) 308c2ecf20Sopenharmony_ci : usb_sndctrlpipe(udev, 0); 318c2ecf20Sopenharmony_ci for (i = 0; i < MT_VEND_REQ_MAX_RETRY; i++) { 328c2ecf20Sopenharmony_ci if (test_bit(MT76_REMOVED, &dev->phy.state)) 338c2ecf20Sopenharmony_ci return -EIO; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci ret = usb_control_msg(udev, pipe, req, req_type, val, 368c2ecf20Sopenharmony_ci offset, buf, len, MT_VEND_REQ_TOUT_MS); 378c2ecf20Sopenharmony_ci if (ret == -ENODEV) 388c2ecf20Sopenharmony_ci set_bit(MT76_REMOVED, &dev->phy.state); 398c2ecf20Sopenharmony_ci if (ret >= 0 || ret == -ENODEV) 408c2ecf20Sopenharmony_ci return ret; 418c2ecf20Sopenharmony_ci usleep_range(5000, 10000); 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci dev_err(dev->dev, "vendor request req:%02x off:%04x failed:%d\n", 458c2ecf20Sopenharmony_ci req, offset, ret); 468c2ecf20Sopenharmony_ci return ret; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ciint mt76u_vendor_request(struct mt76_dev *dev, u8 req, 508c2ecf20Sopenharmony_ci u8 req_type, u16 val, u16 offset, 518c2ecf20Sopenharmony_ci void *buf, size_t len) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci int ret; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci mutex_lock(&dev->usb.usb_ctrl_mtx); 568c2ecf20Sopenharmony_ci ret = __mt76u_vendor_request(dev, req, req_type, 578c2ecf20Sopenharmony_ci val, offset, buf, len); 588c2ecf20Sopenharmony_ci trace_usb_reg_wr(dev, offset, val); 598c2ecf20Sopenharmony_ci mutex_unlock(&dev->usb.usb_ctrl_mtx); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci return ret; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76u_vendor_request); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic u32 ___mt76u_rr(struct mt76_dev *dev, u8 req, u32 addr) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct mt76_usb *usb = &dev->usb; 688c2ecf20Sopenharmony_ci u32 data = ~0; 698c2ecf20Sopenharmony_ci int ret; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci ret = __mt76u_vendor_request(dev, req, 728c2ecf20Sopenharmony_ci USB_DIR_IN | USB_TYPE_VENDOR, 738c2ecf20Sopenharmony_ci addr >> 16, addr, usb->data, 748c2ecf20Sopenharmony_ci sizeof(__le32)); 758c2ecf20Sopenharmony_ci if (ret == sizeof(__le32)) 768c2ecf20Sopenharmony_ci data = get_unaligned_le32(usb->data); 778c2ecf20Sopenharmony_ci trace_usb_reg_rr(dev, addr, data); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci return data; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic u32 __mt76u_rr(struct mt76_dev *dev, u32 addr) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci u8 req; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci switch (addr & MT_VEND_TYPE_MASK) { 878c2ecf20Sopenharmony_ci case MT_VEND_TYPE_EEPROM: 888c2ecf20Sopenharmony_ci req = MT_VEND_READ_EEPROM; 898c2ecf20Sopenharmony_ci break; 908c2ecf20Sopenharmony_ci case MT_VEND_TYPE_CFG: 918c2ecf20Sopenharmony_ci req = MT_VEND_READ_CFG; 928c2ecf20Sopenharmony_ci break; 938c2ecf20Sopenharmony_ci default: 948c2ecf20Sopenharmony_ci req = MT_VEND_MULTI_READ; 958c2ecf20Sopenharmony_ci break; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci return ___mt76u_rr(dev, req, addr & ~MT_VEND_TYPE_MASK); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic u32 mt76u_rr(struct mt76_dev *dev, u32 addr) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci u32 ret; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci mutex_lock(&dev->usb.usb_ctrl_mtx); 1068c2ecf20Sopenharmony_ci ret = __mt76u_rr(dev, addr); 1078c2ecf20Sopenharmony_ci mutex_unlock(&dev->usb.usb_ctrl_mtx); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return ret; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic u32 mt76u_rr_ext(struct mt76_dev *dev, u32 addr) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci u32 ret; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci mutex_lock(&dev->usb.usb_ctrl_mtx); 1178c2ecf20Sopenharmony_ci ret = ___mt76u_rr(dev, MT_VEND_READ_EXT, addr); 1188c2ecf20Sopenharmony_ci mutex_unlock(&dev->usb.usb_ctrl_mtx); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci return ret; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic void ___mt76u_wr(struct mt76_dev *dev, u8 req, 1248c2ecf20Sopenharmony_ci u32 addr, u32 val) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct mt76_usb *usb = &dev->usb; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci put_unaligned_le32(val, usb->data); 1298c2ecf20Sopenharmony_ci __mt76u_vendor_request(dev, req, 1308c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR, 1318c2ecf20Sopenharmony_ci addr >> 16, addr, usb->data, 1328c2ecf20Sopenharmony_ci sizeof(__le32)); 1338c2ecf20Sopenharmony_ci trace_usb_reg_wr(dev, addr, val); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic void __mt76u_wr(struct mt76_dev *dev, u32 addr, u32 val) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci u8 req; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci switch (addr & MT_VEND_TYPE_MASK) { 1418c2ecf20Sopenharmony_ci case MT_VEND_TYPE_CFG: 1428c2ecf20Sopenharmony_ci req = MT_VEND_WRITE_CFG; 1438c2ecf20Sopenharmony_ci break; 1448c2ecf20Sopenharmony_ci default: 1458c2ecf20Sopenharmony_ci req = MT_VEND_MULTI_WRITE; 1468c2ecf20Sopenharmony_ci break; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci ___mt76u_wr(dev, req, addr & ~MT_VEND_TYPE_MASK, val); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic void mt76u_wr(struct mt76_dev *dev, u32 addr, u32 val) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci mutex_lock(&dev->usb.usb_ctrl_mtx); 1548c2ecf20Sopenharmony_ci __mt76u_wr(dev, addr, val); 1558c2ecf20Sopenharmony_ci mutex_unlock(&dev->usb.usb_ctrl_mtx); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic void mt76u_wr_ext(struct mt76_dev *dev, u32 addr, u32 val) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci mutex_lock(&dev->usb.usb_ctrl_mtx); 1618c2ecf20Sopenharmony_ci ___mt76u_wr(dev, MT_VEND_WRITE_EXT, addr, val); 1628c2ecf20Sopenharmony_ci mutex_unlock(&dev->usb.usb_ctrl_mtx); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic u32 mt76u_rmw(struct mt76_dev *dev, u32 addr, 1668c2ecf20Sopenharmony_ci u32 mask, u32 val) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci mutex_lock(&dev->usb.usb_ctrl_mtx); 1698c2ecf20Sopenharmony_ci val |= __mt76u_rr(dev, addr) & ~mask; 1708c2ecf20Sopenharmony_ci __mt76u_wr(dev, addr, val); 1718c2ecf20Sopenharmony_ci mutex_unlock(&dev->usb.usb_ctrl_mtx); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return val; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic u32 mt76u_rmw_ext(struct mt76_dev *dev, u32 addr, 1778c2ecf20Sopenharmony_ci u32 mask, u32 val) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci mutex_lock(&dev->usb.usb_ctrl_mtx); 1808c2ecf20Sopenharmony_ci val |= ___mt76u_rr(dev, MT_VEND_READ_EXT, addr) & ~mask; 1818c2ecf20Sopenharmony_ci ___mt76u_wr(dev, MT_VEND_WRITE_EXT, addr, val); 1828c2ecf20Sopenharmony_ci mutex_unlock(&dev->usb.usb_ctrl_mtx); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci return val; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic void mt76u_copy(struct mt76_dev *dev, u32 offset, 1888c2ecf20Sopenharmony_ci const void *data, int len) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct mt76_usb *usb = &dev->usb; 1918c2ecf20Sopenharmony_ci const u8 *val = data; 1928c2ecf20Sopenharmony_ci int ret; 1938c2ecf20Sopenharmony_ci int current_batch_size; 1948c2ecf20Sopenharmony_ci int i = 0; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* Assure that always a multiple of 4 bytes are copied, 1978c2ecf20Sopenharmony_ci * otherwise beacons can be corrupted. 1988c2ecf20Sopenharmony_ci * See: "mt76: round up length on mt76_wr_copy" 1998c2ecf20Sopenharmony_ci * Commit 850e8f6fbd5d0003b0 2008c2ecf20Sopenharmony_ci */ 2018c2ecf20Sopenharmony_ci len = round_up(len, 4); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci mutex_lock(&usb->usb_ctrl_mtx); 2048c2ecf20Sopenharmony_ci while (i < len) { 2058c2ecf20Sopenharmony_ci current_batch_size = min_t(int, usb->data_len, len - i); 2068c2ecf20Sopenharmony_ci memcpy(usb->data, val + i, current_batch_size); 2078c2ecf20Sopenharmony_ci ret = __mt76u_vendor_request(dev, MT_VEND_MULTI_WRITE, 2088c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR, 2098c2ecf20Sopenharmony_ci 0, offset + i, usb->data, 2108c2ecf20Sopenharmony_ci current_batch_size); 2118c2ecf20Sopenharmony_ci if (ret < 0) 2128c2ecf20Sopenharmony_ci break; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci i += current_batch_size; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci mutex_unlock(&usb->usb_ctrl_mtx); 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic void mt76u_copy_ext(struct mt76_dev *dev, u32 offset, 2208c2ecf20Sopenharmony_ci const void *data, int len) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct mt76_usb *usb = &dev->usb; 2238c2ecf20Sopenharmony_ci int ret, i = 0, batch_len; 2248c2ecf20Sopenharmony_ci const u8 *val = data; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci len = round_up(len, 4); 2278c2ecf20Sopenharmony_ci mutex_lock(&usb->usb_ctrl_mtx); 2288c2ecf20Sopenharmony_ci while (i < len) { 2298c2ecf20Sopenharmony_ci batch_len = min_t(int, usb->data_len, len - i); 2308c2ecf20Sopenharmony_ci memcpy(usb->data, val + i, batch_len); 2318c2ecf20Sopenharmony_ci ret = __mt76u_vendor_request(dev, MT_VEND_WRITE_EXT, 2328c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR, 2338c2ecf20Sopenharmony_ci (offset + i) >> 16, offset + i, 2348c2ecf20Sopenharmony_ci usb->data, batch_len); 2358c2ecf20Sopenharmony_ci if (ret < 0) 2368c2ecf20Sopenharmony_ci break; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci i += batch_len; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci mutex_unlock(&usb->usb_ctrl_mtx); 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic void 2448c2ecf20Sopenharmony_cimt76u_read_copy_ext(struct mt76_dev *dev, u32 offset, 2458c2ecf20Sopenharmony_ci void *data, int len) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct mt76_usb *usb = &dev->usb; 2488c2ecf20Sopenharmony_ci int i = 0, batch_len, ret; 2498c2ecf20Sopenharmony_ci u8 *val = data; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci len = round_up(len, 4); 2528c2ecf20Sopenharmony_ci mutex_lock(&usb->usb_ctrl_mtx); 2538c2ecf20Sopenharmony_ci while (i < len) { 2548c2ecf20Sopenharmony_ci batch_len = min_t(int, usb->data_len, len - i); 2558c2ecf20Sopenharmony_ci ret = __mt76u_vendor_request(dev, MT_VEND_READ_EXT, 2568c2ecf20Sopenharmony_ci USB_DIR_IN | USB_TYPE_VENDOR, 2578c2ecf20Sopenharmony_ci (offset + i) >> 16, offset + i, 2588c2ecf20Sopenharmony_ci usb->data, batch_len); 2598c2ecf20Sopenharmony_ci if (ret < 0) 2608c2ecf20Sopenharmony_ci break; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci memcpy(val + i, usb->data, batch_len); 2638c2ecf20Sopenharmony_ci i += batch_len; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci mutex_unlock(&usb->usb_ctrl_mtx); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_civoid mt76u_single_wr(struct mt76_dev *dev, const u8 req, 2698c2ecf20Sopenharmony_ci const u16 offset, const u32 val) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci mutex_lock(&dev->usb.usb_ctrl_mtx); 2728c2ecf20Sopenharmony_ci __mt76u_vendor_request(dev, req, 2738c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR, 2748c2ecf20Sopenharmony_ci val & 0xffff, offset, NULL, 0); 2758c2ecf20Sopenharmony_ci __mt76u_vendor_request(dev, req, 2768c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR, 2778c2ecf20Sopenharmony_ci val >> 16, offset + 2, NULL, 0); 2788c2ecf20Sopenharmony_ci mutex_unlock(&dev->usb.usb_ctrl_mtx); 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76u_single_wr); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic int 2838c2ecf20Sopenharmony_cimt76u_req_wr_rp(struct mt76_dev *dev, u32 base, 2848c2ecf20Sopenharmony_ci const struct mt76_reg_pair *data, int len) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct mt76_usb *usb = &dev->usb; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci mutex_lock(&usb->usb_ctrl_mtx); 2898c2ecf20Sopenharmony_ci while (len > 0) { 2908c2ecf20Sopenharmony_ci __mt76u_wr(dev, base + data->reg, data->value); 2918c2ecf20Sopenharmony_ci len--; 2928c2ecf20Sopenharmony_ci data++; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci mutex_unlock(&usb->usb_ctrl_mtx); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci return 0; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic int 3008c2ecf20Sopenharmony_cimt76u_wr_rp(struct mt76_dev *dev, u32 base, 3018c2ecf20Sopenharmony_ci const struct mt76_reg_pair *data, int n) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci if (test_bit(MT76_STATE_MCU_RUNNING, &dev->phy.state)) 3048c2ecf20Sopenharmony_ci return dev->mcu_ops->mcu_wr_rp(dev, base, data, n); 3058c2ecf20Sopenharmony_ci else 3068c2ecf20Sopenharmony_ci return mt76u_req_wr_rp(dev, base, data, n); 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic int 3108c2ecf20Sopenharmony_cimt76u_req_rd_rp(struct mt76_dev *dev, u32 base, struct mt76_reg_pair *data, 3118c2ecf20Sopenharmony_ci int len) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci struct mt76_usb *usb = &dev->usb; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci mutex_lock(&usb->usb_ctrl_mtx); 3168c2ecf20Sopenharmony_ci while (len > 0) { 3178c2ecf20Sopenharmony_ci data->value = __mt76u_rr(dev, base + data->reg); 3188c2ecf20Sopenharmony_ci len--; 3198c2ecf20Sopenharmony_ci data++; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci mutex_unlock(&usb->usb_ctrl_mtx); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci return 0; 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic int 3278c2ecf20Sopenharmony_cimt76u_rd_rp(struct mt76_dev *dev, u32 base, 3288c2ecf20Sopenharmony_ci struct mt76_reg_pair *data, int n) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci if (test_bit(MT76_STATE_MCU_RUNNING, &dev->phy.state)) 3318c2ecf20Sopenharmony_ci return dev->mcu_ops->mcu_rd_rp(dev, base, data, n); 3328c2ecf20Sopenharmony_ci else 3338c2ecf20Sopenharmony_ci return mt76u_req_rd_rp(dev, base, data, n); 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic bool mt76u_check_sg(struct mt76_dev *dev) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci struct usb_interface *uintf = to_usb_interface(dev->dev); 3398c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(uintf); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci return (!disable_usb_sg && udev->bus->sg_tablesize > 0 && 3428c2ecf20Sopenharmony_ci (udev->bus->no_sg_constraint || 3438c2ecf20Sopenharmony_ci udev->speed == USB_SPEED_WIRELESS)); 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic int 3478c2ecf20Sopenharmony_cimt76u_set_endpoints(struct usb_interface *intf, 3488c2ecf20Sopenharmony_ci struct mt76_usb *usb) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci struct usb_host_interface *intf_desc = intf->cur_altsetting; 3518c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *ep_desc; 3528c2ecf20Sopenharmony_ci int i, in_ep = 0, out_ep = 0; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) { 3558c2ecf20Sopenharmony_ci ep_desc = &intf_desc->endpoint[i].desc; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (usb_endpoint_is_bulk_in(ep_desc) && 3588c2ecf20Sopenharmony_ci in_ep < __MT_EP_IN_MAX) { 3598c2ecf20Sopenharmony_ci usb->in_ep[in_ep] = usb_endpoint_num(ep_desc); 3608c2ecf20Sopenharmony_ci in_ep++; 3618c2ecf20Sopenharmony_ci } else if (usb_endpoint_is_bulk_out(ep_desc) && 3628c2ecf20Sopenharmony_ci out_ep < __MT_EP_OUT_MAX) { 3638c2ecf20Sopenharmony_ci usb->out_ep[out_ep] = usb_endpoint_num(ep_desc); 3648c2ecf20Sopenharmony_ci out_ep++; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (in_ep != __MT_EP_IN_MAX || out_ep != __MT_EP_OUT_MAX) 3698c2ecf20Sopenharmony_ci return -EINVAL; 3708c2ecf20Sopenharmony_ci return 0; 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic int 3748c2ecf20Sopenharmony_cimt76u_fill_rx_sg(struct mt76_dev *dev, struct mt76_queue *q, struct urb *urb, 3758c2ecf20Sopenharmony_ci int nsgs, gfp_t gfp) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci int i; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci for (i = 0; i < nsgs; i++) { 3808c2ecf20Sopenharmony_ci struct page *page; 3818c2ecf20Sopenharmony_ci void *data; 3828c2ecf20Sopenharmony_ci int offset; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci data = page_frag_alloc(&q->rx_page, q->buf_size, gfp); 3858c2ecf20Sopenharmony_ci if (!data) 3868c2ecf20Sopenharmony_ci break; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci page = virt_to_head_page(data); 3898c2ecf20Sopenharmony_ci offset = data - page_address(page); 3908c2ecf20Sopenharmony_ci sg_set_page(&urb->sg[i], page, q->buf_size, offset); 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci if (i < nsgs) { 3948c2ecf20Sopenharmony_ci int j; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci for (j = nsgs; j < urb->num_sgs; j++) 3978c2ecf20Sopenharmony_ci skb_free_frag(sg_virt(&urb->sg[j])); 3988c2ecf20Sopenharmony_ci urb->num_sgs = i; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci urb->num_sgs = max_t(int, i, urb->num_sgs); 4028c2ecf20Sopenharmony_ci urb->transfer_buffer_length = urb->num_sgs * q->buf_size; 4038c2ecf20Sopenharmony_ci sg_init_marker(urb->sg, urb->num_sgs); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci return i ? : -ENOMEM; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic int 4098c2ecf20Sopenharmony_cimt76u_refill_rx(struct mt76_dev *dev, struct mt76_queue *q, 4108c2ecf20Sopenharmony_ci struct urb *urb, int nsgs, gfp_t gfp) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci enum mt76_rxq_id qid = q - &dev->q_rx[MT_RXQ_MAIN]; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (qid == MT_RXQ_MAIN && dev->usb.sg_en) 4158c2ecf20Sopenharmony_ci return mt76u_fill_rx_sg(dev, q, urb, nsgs, gfp); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci urb->transfer_buffer_length = q->buf_size; 4188c2ecf20Sopenharmony_ci urb->transfer_buffer = page_frag_alloc(&q->rx_page, q->buf_size, gfp); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci return urb->transfer_buffer ? 0 : -ENOMEM; 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic int 4248c2ecf20Sopenharmony_cimt76u_urb_alloc(struct mt76_dev *dev, struct mt76_queue_entry *e, 4258c2ecf20Sopenharmony_ci int sg_max_size) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci unsigned int size = sizeof(struct urb); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if (dev->usb.sg_en) 4308c2ecf20Sopenharmony_ci size += sg_max_size * sizeof(struct scatterlist); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci e->urb = kzalloc(size, GFP_KERNEL); 4338c2ecf20Sopenharmony_ci if (!e->urb) 4348c2ecf20Sopenharmony_ci return -ENOMEM; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci usb_init_urb(e->urb); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci if (dev->usb.sg_en && sg_max_size > 0) 4398c2ecf20Sopenharmony_ci e->urb->sg = (struct scatterlist *)(e->urb + 1); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci return 0; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic int 4458c2ecf20Sopenharmony_cimt76u_rx_urb_alloc(struct mt76_dev *dev, struct mt76_queue *q, 4468c2ecf20Sopenharmony_ci struct mt76_queue_entry *e) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci enum mt76_rxq_id qid = q - &dev->q_rx[MT_RXQ_MAIN]; 4498c2ecf20Sopenharmony_ci int err, sg_size; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci sg_size = qid == MT_RXQ_MAIN ? MT_RX_SG_MAX_SIZE : 0; 4528c2ecf20Sopenharmony_ci err = mt76u_urb_alloc(dev, e, sg_size); 4538c2ecf20Sopenharmony_ci if (err) 4548c2ecf20Sopenharmony_ci return err; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci return mt76u_refill_rx(dev, q, e->urb, sg_size, GFP_KERNEL); 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cistatic void mt76u_urb_free(struct urb *urb) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci int i; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci for (i = 0; i < urb->num_sgs; i++) 4648c2ecf20Sopenharmony_ci skb_free_frag(sg_virt(&urb->sg[i])); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if (urb->transfer_buffer) 4678c2ecf20Sopenharmony_ci skb_free_frag(urb->transfer_buffer); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci usb_free_urb(urb); 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic void 4738c2ecf20Sopenharmony_cimt76u_fill_bulk_urb(struct mt76_dev *dev, int dir, int index, 4748c2ecf20Sopenharmony_ci struct urb *urb, usb_complete_t complete_fn, 4758c2ecf20Sopenharmony_ci void *context) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci struct usb_interface *uintf = to_usb_interface(dev->dev); 4788c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(uintf); 4798c2ecf20Sopenharmony_ci unsigned int pipe; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (dir == USB_DIR_IN) 4828c2ecf20Sopenharmony_ci pipe = usb_rcvbulkpipe(udev, dev->usb.in_ep[index]); 4838c2ecf20Sopenharmony_ci else 4848c2ecf20Sopenharmony_ci pipe = usb_sndbulkpipe(udev, dev->usb.out_ep[index]); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci urb->dev = udev; 4878c2ecf20Sopenharmony_ci urb->pipe = pipe; 4888c2ecf20Sopenharmony_ci urb->complete = complete_fn; 4898c2ecf20Sopenharmony_ci urb->context = context; 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cistatic struct urb * 4938c2ecf20Sopenharmony_cimt76u_get_next_rx_entry(struct mt76_queue *q) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci struct urb *urb = NULL; 4968c2ecf20Sopenharmony_ci unsigned long flags; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci spin_lock_irqsave(&q->lock, flags); 4998c2ecf20Sopenharmony_ci if (q->queued > 0) { 5008c2ecf20Sopenharmony_ci urb = q->entry[q->tail].urb; 5018c2ecf20Sopenharmony_ci q->tail = (q->tail + 1) % q->ndesc; 5028c2ecf20Sopenharmony_ci q->queued--; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&q->lock, flags); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci return urb; 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_cistatic int 5108c2ecf20Sopenharmony_cimt76u_get_rx_entry_len(struct mt76_dev *dev, u8 *data, 5118c2ecf20Sopenharmony_ci u32 data_len) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci u16 dma_len, min_len; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci dma_len = get_unaligned_le16(data); 5168c2ecf20Sopenharmony_ci if (dev->drv->drv_flags & MT_DRV_RX_DMA_HDR) 5178c2ecf20Sopenharmony_ci return dma_len; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci min_len = MT_DMA_HDR_LEN + MT_RX_RXWI_LEN + MT_FCE_INFO_LEN; 5208c2ecf20Sopenharmony_ci if (data_len < min_len || !dma_len || 5218c2ecf20Sopenharmony_ci dma_len + MT_DMA_HDR_LEN > data_len || 5228c2ecf20Sopenharmony_ci (dma_len & 0x3)) 5238c2ecf20Sopenharmony_ci return -EINVAL; 5248c2ecf20Sopenharmony_ci return dma_len; 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic struct sk_buff * 5288c2ecf20Sopenharmony_cimt76u_build_rx_skb(struct mt76_dev *dev, void *data, 5298c2ecf20Sopenharmony_ci int len, int buf_size) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci int head_room, drv_flags = dev->drv->drv_flags; 5328c2ecf20Sopenharmony_ci struct sk_buff *skb; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci head_room = drv_flags & MT_DRV_RX_DMA_HDR ? 0 : MT_DMA_HDR_LEN; 5358c2ecf20Sopenharmony_ci if (SKB_WITH_OVERHEAD(buf_size) < head_room + len) { 5368c2ecf20Sopenharmony_ci struct page *page; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci /* slow path, not enough space for data and 5398c2ecf20Sopenharmony_ci * skb_shared_info 5408c2ecf20Sopenharmony_ci */ 5418c2ecf20Sopenharmony_ci skb = alloc_skb(MT_SKB_HEAD_LEN, GFP_ATOMIC); 5428c2ecf20Sopenharmony_ci if (!skb) 5438c2ecf20Sopenharmony_ci return NULL; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci skb_put_data(skb, data + head_room, MT_SKB_HEAD_LEN); 5468c2ecf20Sopenharmony_ci data += head_room + MT_SKB_HEAD_LEN; 5478c2ecf20Sopenharmony_ci page = virt_to_head_page(data); 5488c2ecf20Sopenharmony_ci skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, 5498c2ecf20Sopenharmony_ci page, data - page_address(page), 5508c2ecf20Sopenharmony_ci len - MT_SKB_HEAD_LEN, buf_size); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci return skb; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci /* fast path */ 5568c2ecf20Sopenharmony_ci skb = build_skb(data, buf_size); 5578c2ecf20Sopenharmony_ci if (!skb) 5588c2ecf20Sopenharmony_ci return NULL; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci skb_reserve(skb, head_room); 5618c2ecf20Sopenharmony_ci __skb_put(skb, len); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci return skb; 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic int 5678c2ecf20Sopenharmony_cimt76u_process_rx_entry(struct mt76_dev *dev, struct urb *urb, 5688c2ecf20Sopenharmony_ci int buf_size) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci u8 *data = urb->num_sgs ? sg_virt(&urb->sg[0]) : urb->transfer_buffer; 5718c2ecf20Sopenharmony_ci int data_len = urb->num_sgs ? urb->sg[0].length : urb->actual_length; 5728c2ecf20Sopenharmony_ci int len, nsgs = 1, head_room, drv_flags = dev->drv->drv_flags; 5738c2ecf20Sopenharmony_ci struct sk_buff *skb; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci if (!test_bit(MT76_STATE_INITIALIZED, &dev->phy.state)) 5768c2ecf20Sopenharmony_ci return 0; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci len = mt76u_get_rx_entry_len(dev, data, urb->actual_length); 5798c2ecf20Sopenharmony_ci if (len < 0) 5808c2ecf20Sopenharmony_ci return 0; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci head_room = drv_flags & MT_DRV_RX_DMA_HDR ? 0 : MT_DMA_HDR_LEN; 5838c2ecf20Sopenharmony_ci data_len = min_t(int, len, data_len - head_room); 5848c2ecf20Sopenharmony_ci skb = mt76u_build_rx_skb(dev, data, data_len, buf_size); 5858c2ecf20Sopenharmony_ci if (!skb) 5868c2ecf20Sopenharmony_ci return 0; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci len -= data_len; 5898c2ecf20Sopenharmony_ci while (len > 0 && nsgs < urb->num_sgs) { 5908c2ecf20Sopenharmony_ci data_len = min_t(int, len, urb->sg[nsgs].length); 5918c2ecf20Sopenharmony_ci skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, 5928c2ecf20Sopenharmony_ci sg_page(&urb->sg[nsgs]), 5938c2ecf20Sopenharmony_ci urb->sg[nsgs].offset, data_len, 5948c2ecf20Sopenharmony_ci buf_size); 5958c2ecf20Sopenharmony_ci len -= data_len; 5968c2ecf20Sopenharmony_ci nsgs++; 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci dev->drv->rx_skb(dev, MT_RXQ_MAIN, skb); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci return nsgs; 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic void mt76u_complete_rx(struct urb *urb) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci struct mt76_dev *dev = dev_get_drvdata(&urb->dev->dev); 6068c2ecf20Sopenharmony_ci struct mt76_queue *q = urb->context; 6078c2ecf20Sopenharmony_ci unsigned long flags; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci trace_rx_urb(dev, urb); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci switch (urb->status) { 6128c2ecf20Sopenharmony_ci case -ECONNRESET: 6138c2ecf20Sopenharmony_ci case -ESHUTDOWN: 6148c2ecf20Sopenharmony_ci case -ENOENT: 6158c2ecf20Sopenharmony_ci return; 6168c2ecf20Sopenharmony_ci default: 6178c2ecf20Sopenharmony_ci dev_err_ratelimited(dev->dev, "rx urb failed: %d\n", 6188c2ecf20Sopenharmony_ci urb->status); 6198c2ecf20Sopenharmony_ci fallthrough; 6208c2ecf20Sopenharmony_ci case 0: 6218c2ecf20Sopenharmony_ci break; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci spin_lock_irqsave(&q->lock, flags); 6258c2ecf20Sopenharmony_ci if (WARN_ONCE(q->entry[q->head].urb != urb, "rx urb mismatch")) 6268c2ecf20Sopenharmony_ci goto out; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci q->head = (q->head + 1) % q->ndesc; 6298c2ecf20Sopenharmony_ci q->queued++; 6308c2ecf20Sopenharmony_ci tasklet_schedule(&dev->usb.rx_tasklet); 6318c2ecf20Sopenharmony_ciout: 6328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&q->lock, flags); 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_cistatic int 6368c2ecf20Sopenharmony_cimt76u_submit_rx_buf(struct mt76_dev *dev, enum mt76_rxq_id qid, 6378c2ecf20Sopenharmony_ci struct urb *urb) 6388c2ecf20Sopenharmony_ci{ 6398c2ecf20Sopenharmony_ci int ep = qid == MT_RXQ_MAIN ? MT_EP_IN_PKT_RX : MT_EP_IN_CMD_RESP; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci mt76u_fill_bulk_urb(dev, USB_DIR_IN, ep, urb, 6428c2ecf20Sopenharmony_ci mt76u_complete_rx, &dev->q_rx[qid]); 6438c2ecf20Sopenharmony_ci trace_submit_urb(dev, urb); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci return usb_submit_urb(urb, GFP_ATOMIC); 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_cistatic void 6498c2ecf20Sopenharmony_cimt76u_process_rx_queue(struct mt76_dev *dev, struct mt76_queue *q) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci int qid = q - &dev->q_rx[MT_RXQ_MAIN]; 6528c2ecf20Sopenharmony_ci struct urb *urb; 6538c2ecf20Sopenharmony_ci int err, count; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci while (true) { 6568c2ecf20Sopenharmony_ci urb = mt76u_get_next_rx_entry(q); 6578c2ecf20Sopenharmony_ci if (!urb) 6588c2ecf20Sopenharmony_ci break; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci count = mt76u_process_rx_entry(dev, urb, q->buf_size); 6618c2ecf20Sopenharmony_ci if (count > 0) { 6628c2ecf20Sopenharmony_ci err = mt76u_refill_rx(dev, q, urb, count, GFP_ATOMIC); 6638c2ecf20Sopenharmony_ci if (err < 0) 6648c2ecf20Sopenharmony_ci break; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci mt76u_submit_rx_buf(dev, qid, urb); 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci if (qid == MT_RXQ_MAIN) 6698c2ecf20Sopenharmony_ci mt76_rx_poll_complete(dev, MT_RXQ_MAIN, NULL); 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic void mt76u_rx_tasklet(unsigned long data) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci struct mt76_dev *dev = (struct mt76_dev *)data; 6758c2ecf20Sopenharmony_ci int i; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci rcu_read_lock(); 6788c2ecf20Sopenharmony_ci mt76_for_each_q_rx(dev, i) 6798c2ecf20Sopenharmony_ci mt76u_process_rx_queue(dev, &dev->q_rx[i]); 6808c2ecf20Sopenharmony_ci rcu_read_unlock(); 6818c2ecf20Sopenharmony_ci} 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_cistatic int 6848c2ecf20Sopenharmony_cimt76u_submit_rx_buffers(struct mt76_dev *dev, enum mt76_rxq_id qid) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci struct mt76_queue *q = &dev->q_rx[qid]; 6878c2ecf20Sopenharmony_ci unsigned long flags; 6888c2ecf20Sopenharmony_ci int i, err = 0; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci spin_lock_irqsave(&q->lock, flags); 6918c2ecf20Sopenharmony_ci for (i = 0; i < q->ndesc; i++) { 6928c2ecf20Sopenharmony_ci err = mt76u_submit_rx_buf(dev, qid, q->entry[i].urb); 6938c2ecf20Sopenharmony_ci if (err < 0) 6948c2ecf20Sopenharmony_ci break; 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci q->head = q->tail = 0; 6978c2ecf20Sopenharmony_ci q->queued = 0; 6988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&q->lock, flags); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci return err; 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_cistatic int 7048c2ecf20Sopenharmony_cimt76u_alloc_rx_queue(struct mt76_dev *dev, enum mt76_rxq_id qid) 7058c2ecf20Sopenharmony_ci{ 7068c2ecf20Sopenharmony_ci struct mt76_queue *q = &dev->q_rx[qid]; 7078c2ecf20Sopenharmony_ci int i, err; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci spin_lock_init(&q->lock); 7108c2ecf20Sopenharmony_ci q->entry = devm_kcalloc(dev->dev, 7118c2ecf20Sopenharmony_ci MT_NUM_RX_ENTRIES, sizeof(*q->entry), 7128c2ecf20Sopenharmony_ci GFP_KERNEL); 7138c2ecf20Sopenharmony_ci if (!q->entry) 7148c2ecf20Sopenharmony_ci return -ENOMEM; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci q->ndesc = MT_NUM_RX_ENTRIES; 7178c2ecf20Sopenharmony_ci q->buf_size = PAGE_SIZE; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci for (i = 0; i < q->ndesc; i++) { 7208c2ecf20Sopenharmony_ci err = mt76u_rx_urb_alloc(dev, q, &q->entry[i]); 7218c2ecf20Sopenharmony_ci if (err < 0) 7228c2ecf20Sopenharmony_ci return err; 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci return mt76u_submit_rx_buffers(dev, qid); 7268c2ecf20Sopenharmony_ci} 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ciint mt76u_alloc_mcu_queue(struct mt76_dev *dev) 7298c2ecf20Sopenharmony_ci{ 7308c2ecf20Sopenharmony_ci return mt76u_alloc_rx_queue(dev, MT_RXQ_MCU); 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76u_alloc_mcu_queue); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_cistatic void 7358c2ecf20Sopenharmony_cimt76u_free_rx_queue(struct mt76_dev *dev, struct mt76_queue *q) 7368c2ecf20Sopenharmony_ci{ 7378c2ecf20Sopenharmony_ci struct page *page; 7388c2ecf20Sopenharmony_ci int i; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci for (i = 0; i < q->ndesc; i++) 7418c2ecf20Sopenharmony_ci mt76u_urb_free(q->entry[i].urb); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci if (!q->rx_page.va) 7448c2ecf20Sopenharmony_ci return; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci page = virt_to_page(q->rx_page.va); 7478c2ecf20Sopenharmony_ci __page_frag_cache_drain(page, q->rx_page.pagecnt_bias); 7488c2ecf20Sopenharmony_ci memset(&q->rx_page, 0, sizeof(q->rx_page)); 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_cistatic void mt76u_free_rx(struct mt76_dev *dev) 7528c2ecf20Sopenharmony_ci{ 7538c2ecf20Sopenharmony_ci int i; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci mt76_for_each_q_rx(dev, i) 7568c2ecf20Sopenharmony_ci mt76u_free_rx_queue(dev, &dev->q_rx[i]); 7578c2ecf20Sopenharmony_ci} 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_civoid mt76u_stop_rx(struct mt76_dev *dev) 7608c2ecf20Sopenharmony_ci{ 7618c2ecf20Sopenharmony_ci int i; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci mt76_for_each_q_rx(dev, i) { 7648c2ecf20Sopenharmony_ci struct mt76_queue *q = &dev->q_rx[i]; 7658c2ecf20Sopenharmony_ci int j; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci for (j = 0; j < q->ndesc; j++) 7688c2ecf20Sopenharmony_ci usb_poison_urb(q->entry[j].urb); 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci tasklet_kill(&dev->usb.rx_tasklet); 7728c2ecf20Sopenharmony_ci} 7738c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76u_stop_rx); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ciint mt76u_resume_rx(struct mt76_dev *dev) 7768c2ecf20Sopenharmony_ci{ 7778c2ecf20Sopenharmony_ci int i; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci mt76_for_each_q_rx(dev, i) { 7808c2ecf20Sopenharmony_ci struct mt76_queue *q = &dev->q_rx[i]; 7818c2ecf20Sopenharmony_ci int err, j; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci for (j = 0; j < q->ndesc; j++) 7848c2ecf20Sopenharmony_ci usb_unpoison_urb(q->entry[j].urb); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci err = mt76u_submit_rx_buffers(dev, i); 7878c2ecf20Sopenharmony_ci if (err < 0) 7888c2ecf20Sopenharmony_ci return err; 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci return 0; 7928c2ecf20Sopenharmony_ci} 7938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76u_resume_rx); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_cistatic void mt76u_tx_worker(struct mt76_worker *w) 7968c2ecf20Sopenharmony_ci{ 7978c2ecf20Sopenharmony_ci struct mt76_dev *dev = container_of(w, struct mt76_dev, tx_worker); 7988c2ecf20Sopenharmony_ci struct mt76_queue_entry entry; 7998c2ecf20Sopenharmony_ci struct mt76_queue *q; 8008c2ecf20Sopenharmony_ci bool wake; 8018c2ecf20Sopenharmony_ci int i; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci for (i = 0; i < IEEE80211_NUM_ACS; i++) { 8048c2ecf20Sopenharmony_ci q = dev->q_tx[i]; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci while (q->queued > 0) { 8078c2ecf20Sopenharmony_ci if (!q->entry[q->tail].done) 8088c2ecf20Sopenharmony_ci break; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci entry = q->entry[q->tail]; 8118c2ecf20Sopenharmony_ci q->entry[q->tail].done = false; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci mt76_queue_tx_complete(dev, q, &entry); 8148c2ecf20Sopenharmony_ci } 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci wake = q->stopped && q->queued < q->ndesc - 8; 8178c2ecf20Sopenharmony_ci if (wake) 8188c2ecf20Sopenharmony_ci q->stopped = false; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci if (!q->queued) 8218c2ecf20Sopenharmony_ci wake_up(&dev->tx_wait); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci mt76_txq_schedule(&dev->phy, i); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci if (dev->drv->tx_status_data && 8268c2ecf20Sopenharmony_ci !test_and_set_bit(MT76_READING_STATS, &dev->phy.state)) 8278c2ecf20Sopenharmony_ci queue_work(dev->wq, &dev->usb.stat_work); 8288c2ecf20Sopenharmony_ci if (wake) 8298c2ecf20Sopenharmony_ci ieee80211_wake_queue(dev->hw, i); 8308c2ecf20Sopenharmony_ci } 8318c2ecf20Sopenharmony_ci} 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_cistatic void mt76u_tx_status_data(struct work_struct *work) 8348c2ecf20Sopenharmony_ci{ 8358c2ecf20Sopenharmony_ci struct mt76_usb *usb; 8368c2ecf20Sopenharmony_ci struct mt76_dev *dev; 8378c2ecf20Sopenharmony_ci u8 update = 1; 8388c2ecf20Sopenharmony_ci u16 count = 0; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci usb = container_of(work, struct mt76_usb, stat_work); 8418c2ecf20Sopenharmony_ci dev = container_of(usb, struct mt76_dev, usb); 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci while (true) { 8448c2ecf20Sopenharmony_ci if (test_bit(MT76_REMOVED, &dev->phy.state)) 8458c2ecf20Sopenharmony_ci break; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci if (!dev->drv->tx_status_data(dev, &update)) 8488c2ecf20Sopenharmony_ci break; 8498c2ecf20Sopenharmony_ci count++; 8508c2ecf20Sopenharmony_ci } 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci if (count && test_bit(MT76_STATE_RUNNING, &dev->phy.state)) 8538c2ecf20Sopenharmony_ci queue_work(dev->wq, &usb->stat_work); 8548c2ecf20Sopenharmony_ci else 8558c2ecf20Sopenharmony_ci clear_bit(MT76_READING_STATS, &dev->phy.state); 8568c2ecf20Sopenharmony_ci} 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_cistatic void mt76u_complete_tx(struct urb *urb) 8598c2ecf20Sopenharmony_ci{ 8608c2ecf20Sopenharmony_ci struct mt76_dev *dev = dev_get_drvdata(&urb->dev->dev); 8618c2ecf20Sopenharmony_ci struct mt76_queue_entry *e = urb->context; 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci if (mt76u_urb_error(urb)) 8648c2ecf20Sopenharmony_ci dev_err(dev->dev, "tx urb failed: %d\n", urb->status); 8658c2ecf20Sopenharmony_ci e->done = true; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci mt76_worker_schedule(&dev->tx_worker); 8688c2ecf20Sopenharmony_ci} 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_cistatic int 8718c2ecf20Sopenharmony_cimt76u_tx_setup_buffers(struct mt76_dev *dev, struct sk_buff *skb, 8728c2ecf20Sopenharmony_ci struct urb *urb) 8738c2ecf20Sopenharmony_ci{ 8748c2ecf20Sopenharmony_ci urb->transfer_buffer_length = skb->len; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci if (!dev->usb.sg_en) { 8778c2ecf20Sopenharmony_ci urb->transfer_buffer = skb->data; 8788c2ecf20Sopenharmony_ci return 0; 8798c2ecf20Sopenharmony_ci } 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci sg_init_table(urb->sg, MT_TX_SG_MAX_SIZE); 8828c2ecf20Sopenharmony_ci urb->num_sgs = skb_to_sgvec(skb, urb->sg, 0, skb->len); 8838c2ecf20Sopenharmony_ci if (!urb->num_sgs) 8848c2ecf20Sopenharmony_ci return -ENOMEM; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci return urb->num_sgs; 8878c2ecf20Sopenharmony_ci} 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_cistatic int 8908c2ecf20Sopenharmony_cimt76u_tx_queue_skb(struct mt76_dev *dev, enum mt76_txq_id qid, 8918c2ecf20Sopenharmony_ci struct sk_buff *skb, struct mt76_wcid *wcid, 8928c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 8938c2ecf20Sopenharmony_ci{ 8948c2ecf20Sopenharmony_ci struct mt76_queue *q = dev->q_tx[qid]; 8958c2ecf20Sopenharmony_ci struct mt76_tx_info tx_info = { 8968c2ecf20Sopenharmony_ci .skb = skb, 8978c2ecf20Sopenharmony_ci }; 8988c2ecf20Sopenharmony_ci u16 idx = q->head; 8998c2ecf20Sopenharmony_ci int err; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci if (q->queued == q->ndesc) 9028c2ecf20Sopenharmony_ci return -ENOSPC; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci skb->prev = skb->next = NULL; 9058c2ecf20Sopenharmony_ci err = dev->drv->tx_prepare_skb(dev, NULL, qid, wcid, sta, &tx_info); 9068c2ecf20Sopenharmony_ci if (err < 0) 9078c2ecf20Sopenharmony_ci return err; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci err = mt76u_tx_setup_buffers(dev, tx_info.skb, q->entry[idx].urb); 9108c2ecf20Sopenharmony_ci if (err < 0) 9118c2ecf20Sopenharmony_ci return err; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci mt76u_fill_bulk_urb(dev, USB_DIR_OUT, q2ep(q->hw_idx), 9148c2ecf20Sopenharmony_ci q->entry[idx].urb, mt76u_complete_tx, 9158c2ecf20Sopenharmony_ci &q->entry[idx]); 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci q->head = (q->head + 1) % q->ndesc; 9188c2ecf20Sopenharmony_ci q->entry[idx].skb = tx_info.skb; 9198c2ecf20Sopenharmony_ci q->queued++; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci return idx; 9228c2ecf20Sopenharmony_ci} 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_cistatic void mt76u_tx_kick(struct mt76_dev *dev, struct mt76_queue *q) 9258c2ecf20Sopenharmony_ci{ 9268c2ecf20Sopenharmony_ci struct urb *urb; 9278c2ecf20Sopenharmony_ci int err; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci while (q->first != q->head) { 9308c2ecf20Sopenharmony_ci urb = q->entry[q->first].urb; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci trace_submit_urb(dev, urb); 9338c2ecf20Sopenharmony_ci err = usb_submit_urb(urb, GFP_ATOMIC); 9348c2ecf20Sopenharmony_ci if (err < 0) { 9358c2ecf20Sopenharmony_ci if (err == -ENODEV) 9368c2ecf20Sopenharmony_ci set_bit(MT76_REMOVED, &dev->phy.state); 9378c2ecf20Sopenharmony_ci else 9388c2ecf20Sopenharmony_ci dev_err(dev->dev, "tx urb submit failed:%d\n", 9398c2ecf20Sopenharmony_ci err); 9408c2ecf20Sopenharmony_ci break; 9418c2ecf20Sopenharmony_ci } 9428c2ecf20Sopenharmony_ci q->first = (q->first + 1) % q->ndesc; 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci} 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_cistatic u8 mt76u_ac_to_hwq(struct mt76_dev *dev, u8 ac) 9478c2ecf20Sopenharmony_ci{ 9488c2ecf20Sopenharmony_ci if (mt76_chip(dev) == 0x7663) { 9498c2ecf20Sopenharmony_ci static const u8 lmac_queue_map[] = { 9508c2ecf20Sopenharmony_ci /* ac to lmac mapping */ 9518c2ecf20Sopenharmony_ci [IEEE80211_AC_BK] = 0, 9528c2ecf20Sopenharmony_ci [IEEE80211_AC_BE] = 1, 9538c2ecf20Sopenharmony_ci [IEEE80211_AC_VI] = 2, 9548c2ecf20Sopenharmony_ci [IEEE80211_AC_VO] = 4, 9558c2ecf20Sopenharmony_ci }; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci if (WARN_ON(ac >= ARRAY_SIZE(lmac_queue_map))) 9588c2ecf20Sopenharmony_ci return 1; /* BE */ 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci return lmac_queue_map[ac]; 9618c2ecf20Sopenharmony_ci } 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci return mt76_ac_to_hwq(ac); 9648c2ecf20Sopenharmony_ci} 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_cistatic int mt76u_alloc_tx(struct mt76_dev *dev) 9678c2ecf20Sopenharmony_ci{ 9688c2ecf20Sopenharmony_ci struct mt76_queue *q; 9698c2ecf20Sopenharmony_ci int i, j, err; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci for (i = 0; i <= MT_TXQ_PSD; i++) { 9728c2ecf20Sopenharmony_ci if (i >= IEEE80211_NUM_ACS) { 9738c2ecf20Sopenharmony_ci dev->q_tx[i] = dev->q_tx[0]; 9748c2ecf20Sopenharmony_ci continue; 9758c2ecf20Sopenharmony_ci } 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci q = devm_kzalloc(dev->dev, sizeof(*q), GFP_KERNEL); 9788c2ecf20Sopenharmony_ci if (!q) 9798c2ecf20Sopenharmony_ci return -ENOMEM; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci spin_lock_init(&q->lock); 9828c2ecf20Sopenharmony_ci q->hw_idx = mt76u_ac_to_hwq(dev, i); 9838c2ecf20Sopenharmony_ci dev->q_tx[i] = q; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci q->entry = devm_kcalloc(dev->dev, 9868c2ecf20Sopenharmony_ci MT_NUM_TX_ENTRIES, sizeof(*q->entry), 9878c2ecf20Sopenharmony_ci GFP_KERNEL); 9888c2ecf20Sopenharmony_ci if (!q->entry) 9898c2ecf20Sopenharmony_ci return -ENOMEM; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci q->ndesc = MT_NUM_TX_ENTRIES; 9928c2ecf20Sopenharmony_ci for (j = 0; j < q->ndesc; j++) { 9938c2ecf20Sopenharmony_ci err = mt76u_urb_alloc(dev, &q->entry[j], 9948c2ecf20Sopenharmony_ci MT_TX_SG_MAX_SIZE); 9958c2ecf20Sopenharmony_ci if (err < 0) 9968c2ecf20Sopenharmony_ci return err; 9978c2ecf20Sopenharmony_ci } 9988c2ecf20Sopenharmony_ci } 9998c2ecf20Sopenharmony_ci return 0; 10008c2ecf20Sopenharmony_ci} 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_cistatic void mt76u_free_tx(struct mt76_dev *dev) 10038c2ecf20Sopenharmony_ci{ 10048c2ecf20Sopenharmony_ci int i; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci for (i = 0; i < IEEE80211_NUM_ACS; i++) { 10078c2ecf20Sopenharmony_ci struct mt76_queue *q; 10088c2ecf20Sopenharmony_ci int j; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci q = dev->q_tx[i]; 10118c2ecf20Sopenharmony_ci if (!q) 10128c2ecf20Sopenharmony_ci continue; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci for (j = 0; j < q->ndesc; j++) 10158c2ecf20Sopenharmony_ci usb_free_urb(q->entry[j].urb); 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci} 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_civoid mt76u_stop_tx(struct mt76_dev *dev) 10208c2ecf20Sopenharmony_ci{ 10218c2ecf20Sopenharmony_ci int ret; 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci ret = wait_event_timeout(dev->tx_wait, !mt76_has_tx_pending(&dev->phy), 10248c2ecf20Sopenharmony_ci HZ / 5); 10258c2ecf20Sopenharmony_ci if (!ret) { 10268c2ecf20Sopenharmony_ci struct mt76_queue_entry entry; 10278c2ecf20Sopenharmony_ci struct mt76_queue *q; 10288c2ecf20Sopenharmony_ci int i, j; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci dev_err(dev->dev, "timed out waiting for pending tx\n"); 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci for (i = 0; i < IEEE80211_NUM_ACS; i++) { 10338c2ecf20Sopenharmony_ci q = dev->q_tx[i]; 10348c2ecf20Sopenharmony_ci if (!q) 10358c2ecf20Sopenharmony_ci continue; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci for (j = 0; j < q->ndesc; j++) 10388c2ecf20Sopenharmony_ci usb_kill_urb(q->entry[j].urb); 10398c2ecf20Sopenharmony_ci } 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci mt76_worker_disable(&dev->tx_worker); 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci /* On device removal we maight queue skb's, but mt76u_tx_kick() 10448c2ecf20Sopenharmony_ci * will fail to submit urb, cleanup those skb's manually. 10458c2ecf20Sopenharmony_ci */ 10468c2ecf20Sopenharmony_ci for (i = 0; i < IEEE80211_NUM_ACS; i++) { 10478c2ecf20Sopenharmony_ci q = dev->q_tx[i]; 10488c2ecf20Sopenharmony_ci if (!q) 10498c2ecf20Sopenharmony_ci continue; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci while (q->queued > 0) { 10528c2ecf20Sopenharmony_ci entry = q->entry[q->tail]; 10538c2ecf20Sopenharmony_ci q->entry[q->tail].done = false; 10548c2ecf20Sopenharmony_ci mt76_queue_tx_complete(dev, q, &entry); 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci } 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci mt76_worker_enable(&dev->tx_worker); 10598c2ecf20Sopenharmony_ci } 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci cancel_work_sync(&dev->usb.stat_work); 10628c2ecf20Sopenharmony_ci clear_bit(MT76_READING_STATS, &dev->phy.state); 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci mt76_tx_status_check(dev, NULL, true); 10658c2ecf20Sopenharmony_ci} 10668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76u_stop_tx); 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_civoid mt76u_queues_deinit(struct mt76_dev *dev) 10698c2ecf20Sopenharmony_ci{ 10708c2ecf20Sopenharmony_ci mt76u_stop_rx(dev); 10718c2ecf20Sopenharmony_ci mt76u_stop_tx(dev); 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci mt76u_free_rx(dev); 10748c2ecf20Sopenharmony_ci mt76u_free_tx(dev); 10758c2ecf20Sopenharmony_ci} 10768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76u_queues_deinit); 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ciint mt76u_alloc_queues(struct mt76_dev *dev) 10798c2ecf20Sopenharmony_ci{ 10808c2ecf20Sopenharmony_ci int err; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci err = mt76u_alloc_rx_queue(dev, MT_RXQ_MAIN); 10838c2ecf20Sopenharmony_ci if (err < 0) 10848c2ecf20Sopenharmony_ci return err; 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci return mt76u_alloc_tx(dev); 10878c2ecf20Sopenharmony_ci} 10888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76u_alloc_queues); 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_cistatic const struct mt76_queue_ops usb_queue_ops = { 10918c2ecf20Sopenharmony_ci .tx_queue_skb = mt76u_tx_queue_skb, 10928c2ecf20Sopenharmony_ci .kick = mt76u_tx_kick, 10938c2ecf20Sopenharmony_ci}; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ciint mt76u_init(struct mt76_dev *dev, 10968c2ecf20Sopenharmony_ci struct usb_interface *intf, bool ext) 10978c2ecf20Sopenharmony_ci{ 10988c2ecf20Sopenharmony_ci static struct mt76_bus_ops mt76u_ops = { 10998c2ecf20Sopenharmony_ci .read_copy = mt76u_read_copy_ext, 11008c2ecf20Sopenharmony_ci .wr_rp = mt76u_wr_rp, 11018c2ecf20Sopenharmony_ci .rd_rp = mt76u_rd_rp, 11028c2ecf20Sopenharmony_ci .type = MT76_BUS_USB, 11038c2ecf20Sopenharmony_ci }; 11048c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(intf); 11058c2ecf20Sopenharmony_ci struct mt76_usb *usb = &dev->usb; 11068c2ecf20Sopenharmony_ci int err = -ENOMEM; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci mt76u_ops.rr = ext ? mt76u_rr_ext : mt76u_rr; 11098c2ecf20Sopenharmony_ci mt76u_ops.wr = ext ? mt76u_wr_ext : mt76u_wr; 11108c2ecf20Sopenharmony_ci mt76u_ops.rmw = ext ? mt76u_rmw_ext : mt76u_rmw; 11118c2ecf20Sopenharmony_ci mt76u_ops.write_copy = ext ? mt76u_copy_ext : mt76u_copy; 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci dev->tx_worker.fn = mt76u_tx_worker; 11148c2ecf20Sopenharmony_ci tasklet_init(&usb->rx_tasklet, mt76u_rx_tasklet, (unsigned long)dev); 11158c2ecf20Sopenharmony_ci INIT_WORK(&usb->stat_work, mt76u_tx_status_data); 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci usb->data_len = usb_maxpacket(udev, usb_sndctrlpipe(udev, 0), 1); 11188c2ecf20Sopenharmony_ci if (usb->data_len < 32) 11198c2ecf20Sopenharmony_ci usb->data_len = 32; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci usb->data = devm_kmalloc(dev->dev, usb->data_len, GFP_KERNEL); 11228c2ecf20Sopenharmony_ci if (!usb->data) 11238c2ecf20Sopenharmony_ci goto error; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci mutex_init(&usb->usb_ctrl_mtx); 11268c2ecf20Sopenharmony_ci dev->bus = &mt76u_ops; 11278c2ecf20Sopenharmony_ci dev->queue_ops = &usb_queue_ops; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci dev_set_drvdata(&udev->dev, dev); 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci usb->sg_en = mt76u_check_sg(dev); 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci err = mt76u_set_endpoints(intf, usb); 11348c2ecf20Sopenharmony_ci if (err < 0) 11358c2ecf20Sopenharmony_ci goto error; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci return 0; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_cierror: 11408c2ecf20Sopenharmony_ci destroy_workqueue(dev->wq); 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci return err; 11438c2ecf20Sopenharmony_ci} 11448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76u_init); 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>"); 11478c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 1148