18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl> 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/kernel.h> 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/usb.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "mt7601u.h" 118c2ecf20Sopenharmony_ci#include "usb.h" 128c2ecf20Sopenharmony_ci#include "trace.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistatic const struct usb_device_id mt7601u_device_table[] = { 158c2ecf20Sopenharmony_ci { USB_DEVICE(0x0b05, 0x17d3) }, 168c2ecf20Sopenharmony_ci { USB_DEVICE(0x0e8d, 0x760a) }, 178c2ecf20Sopenharmony_ci { USB_DEVICE(0x0e8d, 0x760b) }, 188c2ecf20Sopenharmony_ci { USB_DEVICE(0x13d3, 0x3431) }, 198c2ecf20Sopenharmony_ci { USB_DEVICE(0x13d3, 0x3434) }, 208c2ecf20Sopenharmony_ci { USB_DEVICE(0x148f, 0x7601) }, 218c2ecf20Sopenharmony_ci { USB_DEVICE(0x148f, 0x760a) }, 228c2ecf20Sopenharmony_ci { USB_DEVICE(0x148f, 0x760b) }, 238c2ecf20Sopenharmony_ci { USB_DEVICE(0x148f, 0x760c) }, 248c2ecf20Sopenharmony_ci { USB_DEVICE(0x148f, 0x760d) }, 258c2ecf20Sopenharmony_ci { USB_DEVICE(0x2001, 0x3d04) }, 268c2ecf20Sopenharmony_ci { USB_DEVICE(0x2717, 0x4106) }, 278c2ecf20Sopenharmony_ci { USB_DEVICE(0x2955, 0x0001) }, 288c2ecf20Sopenharmony_ci { USB_DEVICE(0x2955, 0x1001) }, 298c2ecf20Sopenharmony_ci { USB_DEVICE(0x2955, 0x1003) }, 308c2ecf20Sopenharmony_ci { USB_DEVICE(0x2a5f, 0x1000) }, 318c2ecf20Sopenharmony_ci { USB_DEVICE(0x7392, 0x7710) }, 328c2ecf20Sopenharmony_ci { 0, } 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cibool mt7601u_usb_alloc_buf(struct mt7601u_dev *dev, size_t len, 368c2ecf20Sopenharmony_ci struct mt7601u_dma_buf *buf) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci struct usb_device *usb_dev = mt7601u_to_usb_dev(dev); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci buf->len = len; 418c2ecf20Sopenharmony_ci buf->urb = usb_alloc_urb(0, GFP_KERNEL); 428c2ecf20Sopenharmony_ci buf->buf = usb_alloc_coherent(usb_dev, buf->len, GFP_KERNEL, &buf->dma); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci return !buf->urb || !buf->buf; 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_civoid mt7601u_usb_free_buf(struct mt7601u_dev *dev, struct mt7601u_dma_buf *buf) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct usb_device *usb_dev = mt7601u_to_usb_dev(dev); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci usb_free_coherent(usb_dev, buf->len, buf->buf, buf->dma); 528c2ecf20Sopenharmony_ci usb_free_urb(buf->urb); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ciint mt7601u_usb_submit_buf(struct mt7601u_dev *dev, int dir, int ep_idx, 568c2ecf20Sopenharmony_ci struct mt7601u_dma_buf *buf, gfp_t gfp, 578c2ecf20Sopenharmony_ci usb_complete_t complete_fn, void *context) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci struct usb_device *usb_dev = mt7601u_to_usb_dev(dev); 608c2ecf20Sopenharmony_ci unsigned pipe; 618c2ecf20Sopenharmony_ci int ret; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (dir == USB_DIR_IN) 648c2ecf20Sopenharmony_ci pipe = usb_rcvbulkpipe(usb_dev, dev->in_eps[ep_idx]); 658c2ecf20Sopenharmony_ci else 668c2ecf20Sopenharmony_ci pipe = usb_sndbulkpipe(usb_dev, dev->out_eps[ep_idx]); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci usb_fill_bulk_urb(buf->urb, usb_dev, pipe, buf->buf, buf->len, 698c2ecf20Sopenharmony_ci complete_fn, context); 708c2ecf20Sopenharmony_ci buf->urb->transfer_dma = buf->dma; 718c2ecf20Sopenharmony_ci buf->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci trace_mt_submit_urb(dev, buf->urb); 748c2ecf20Sopenharmony_ci ret = usb_submit_urb(buf->urb, gfp); 758c2ecf20Sopenharmony_ci if (ret) 768c2ecf20Sopenharmony_ci dev_err(dev->dev, "Error: submit URB dir:%d ep:%d failed:%d\n", 778c2ecf20Sopenharmony_ci dir, ep_idx, ret); 788c2ecf20Sopenharmony_ci return ret; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_civoid mt7601u_complete_urb(struct urb *urb) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct completion *cmpl = urb->context; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci complete(cmpl); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ciint mt7601u_vendor_request(struct mt7601u_dev *dev, const u8 req, 898c2ecf20Sopenharmony_ci const u8 direction, const u16 val, const u16 offset, 908c2ecf20Sopenharmony_ci void *buf, const size_t buflen) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci int i, ret; 938c2ecf20Sopenharmony_ci struct usb_device *usb_dev = mt7601u_to_usb_dev(dev); 948c2ecf20Sopenharmony_ci const u8 req_type = direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE; 958c2ecf20Sopenharmony_ci const unsigned int pipe = (direction == USB_DIR_IN) ? 968c2ecf20Sopenharmony_ci usb_rcvctrlpipe(usb_dev, 0) : usb_sndctrlpipe(usb_dev, 0); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci for (i = 0; i < MT_VEND_REQ_MAX_RETRY; i++) { 998c2ecf20Sopenharmony_ci ret = usb_control_msg(usb_dev, pipe, req, req_type, 1008c2ecf20Sopenharmony_ci val, offset, buf, buflen, 1018c2ecf20Sopenharmony_ci MT_VEND_REQ_TOUT_MS); 1028c2ecf20Sopenharmony_ci trace_mt_vend_req(dev, pipe, req, req_type, val, offset, 1038c2ecf20Sopenharmony_ci buf, buflen, ret); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (ret == -ENODEV) 1068c2ecf20Sopenharmony_ci set_bit(MT7601U_STATE_REMOVED, &dev->state); 1078c2ecf20Sopenharmony_ci if (ret >= 0 || ret == -ENODEV) 1088c2ecf20Sopenharmony_ci return ret; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci msleep(5); 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci dev_err(dev->dev, "Vendor request req:%02x off:%04x failed:%d\n", 1148c2ecf20Sopenharmony_ci req, offset, ret); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci return ret; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_civoid mt7601u_vendor_reset(struct mt7601u_dev *dev) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci mt7601u_vendor_request(dev, MT_VEND_DEV_MODE, USB_DIR_OUT, 1228c2ecf20Sopenharmony_ci MT_VEND_DEV_MODE_RESET, 0, NULL, 0); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/* should be called with vendor_req_mutex held */ 1268c2ecf20Sopenharmony_cistatic u32 __mt7601u_rr(struct mt7601u_dev *dev, u32 offset) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci int ret; 1298c2ecf20Sopenharmony_ci u32 val = ~0; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci WARN_ONCE(offset > USHRT_MAX, "read high off:%08x", offset); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci ret = mt7601u_vendor_request(dev, MT_VEND_MULTI_READ, USB_DIR_IN, 1348c2ecf20Sopenharmony_ci 0, offset, dev->vend_buf, MT_VEND_BUF); 1358c2ecf20Sopenharmony_ci if (ret == MT_VEND_BUF) 1368c2ecf20Sopenharmony_ci val = get_unaligned_le32(dev->vend_buf); 1378c2ecf20Sopenharmony_ci else if (ret > 0) 1388c2ecf20Sopenharmony_ci dev_err(dev->dev, "Error: wrong size read:%d off:%08x\n", 1398c2ecf20Sopenharmony_ci ret, offset); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci trace_reg_read(dev, offset, val); 1428c2ecf20Sopenharmony_ci return val; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ciu32 mt7601u_rr(struct mt7601u_dev *dev, u32 offset) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci u32 ret; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci mutex_lock(&dev->vendor_req_mutex); 1508c2ecf20Sopenharmony_ci ret = __mt7601u_rr(dev, offset); 1518c2ecf20Sopenharmony_ci mutex_unlock(&dev->vendor_req_mutex); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return ret; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/* should be called with vendor_req_mutex held */ 1578c2ecf20Sopenharmony_cistatic int __mt7601u_vendor_single_wr(struct mt7601u_dev *dev, const u8 req, 1588c2ecf20Sopenharmony_ci const u16 offset, const u32 val) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci int ret = mt7601u_vendor_request(dev, req, USB_DIR_OUT, 1618c2ecf20Sopenharmony_ci val & 0xffff, offset, NULL, 0); 1628c2ecf20Sopenharmony_ci if (!ret) 1638c2ecf20Sopenharmony_ci ret = mt7601u_vendor_request(dev, req, USB_DIR_OUT, 1648c2ecf20Sopenharmony_ci val >> 16, offset + 2, NULL, 0); 1658c2ecf20Sopenharmony_ci trace_reg_write(dev, offset, val); 1668c2ecf20Sopenharmony_ci return ret; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ciint mt7601u_vendor_single_wr(struct mt7601u_dev *dev, const u8 req, 1708c2ecf20Sopenharmony_ci const u16 offset, const u32 val) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci int ret; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci mutex_lock(&dev->vendor_req_mutex); 1758c2ecf20Sopenharmony_ci ret = __mt7601u_vendor_single_wr(dev, req, offset, val); 1768c2ecf20Sopenharmony_ci mutex_unlock(&dev->vendor_req_mutex); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return ret; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_civoid mt7601u_wr(struct mt7601u_dev *dev, u32 offset, u32 val) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci WARN_ONCE(offset > USHRT_MAX, "write high off:%08x", offset); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci mt7601u_vendor_single_wr(dev, MT_VEND_WRITE, offset, val); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ciu32 mt7601u_rmw(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci mutex_lock(&dev->vendor_req_mutex); 1918c2ecf20Sopenharmony_ci val |= __mt7601u_rr(dev, offset) & ~mask; 1928c2ecf20Sopenharmony_ci __mt7601u_vendor_single_wr(dev, MT_VEND_WRITE, offset, val); 1938c2ecf20Sopenharmony_ci mutex_unlock(&dev->vendor_req_mutex); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return val; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ciu32 mt7601u_rmc(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci u32 reg; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci mutex_lock(&dev->vendor_req_mutex); 2038c2ecf20Sopenharmony_ci reg = __mt7601u_rr(dev, offset); 2048c2ecf20Sopenharmony_ci val |= reg & ~mask; 2058c2ecf20Sopenharmony_ci if (reg != val) 2068c2ecf20Sopenharmony_ci __mt7601u_vendor_single_wr(dev, MT_VEND_WRITE, 2078c2ecf20Sopenharmony_ci offset, val); 2088c2ecf20Sopenharmony_ci mutex_unlock(&dev->vendor_req_mutex); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return val; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_civoid mt7601u_wr_copy(struct mt7601u_dev *dev, u32 offset, 2148c2ecf20Sopenharmony_ci const void *data, int len) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci WARN_ONCE(offset & 3, "unaligned write copy off:%08x", offset); 2178c2ecf20Sopenharmony_ci WARN_ONCE(len & 3, "short write copy off:%08x", offset); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci mt7601u_burst_write_regs(dev, offset, data, len / 4); 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_civoid mt7601u_addr_wr(struct mt7601u_dev *dev, const u32 offset, const u8 *addr) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci mt7601u_wr(dev, offset, get_unaligned_le32(addr)); 2258c2ecf20Sopenharmony_ci mt7601u_wr(dev, offset + 4, addr[4] | addr[5] << 8); 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic int mt7601u_assign_pipes(struct usb_interface *usb_intf, 2298c2ecf20Sopenharmony_ci struct mt7601u_dev *dev) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *ep_desc; 2328c2ecf20Sopenharmony_ci struct usb_host_interface *intf_desc = usb_intf->cur_altsetting; 2338c2ecf20Sopenharmony_ci unsigned i, ep_i = 0, ep_o = 0; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(dev->in_eps) < __MT_EP_IN_MAX); 2368c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(dev->out_eps) < __MT_EP_OUT_MAX); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) { 2398c2ecf20Sopenharmony_ci ep_desc = &intf_desc->endpoint[i].desc; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (usb_endpoint_is_bulk_in(ep_desc) && 2428c2ecf20Sopenharmony_ci ep_i++ < __MT_EP_IN_MAX) { 2438c2ecf20Sopenharmony_ci dev->in_eps[ep_i - 1] = usb_endpoint_num(ep_desc); 2448c2ecf20Sopenharmony_ci dev->in_max_packet = usb_endpoint_maxp(ep_desc); 2458c2ecf20Sopenharmony_ci /* Note: this is ignored by usb sub-system but vendor 2468c2ecf20Sopenharmony_ci * code does it. We can drop this at some point. 2478c2ecf20Sopenharmony_ci */ 2488c2ecf20Sopenharmony_ci dev->in_eps[ep_i - 1] |= USB_DIR_IN; 2498c2ecf20Sopenharmony_ci } else if (usb_endpoint_is_bulk_out(ep_desc) && 2508c2ecf20Sopenharmony_ci ep_o++ < __MT_EP_OUT_MAX) { 2518c2ecf20Sopenharmony_ci dev->out_eps[ep_o - 1] = usb_endpoint_num(ep_desc); 2528c2ecf20Sopenharmony_ci dev->out_max_packet = usb_endpoint_maxp(ep_desc); 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (ep_i != __MT_EP_IN_MAX || ep_o != __MT_EP_OUT_MAX) { 2578c2ecf20Sopenharmony_ci dev_err(dev->dev, "Error: wrong pipe number in:%d out:%d\n", 2588c2ecf20Sopenharmony_ci ep_i, ep_o); 2598c2ecf20Sopenharmony_ci return -EINVAL; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci return 0; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic int mt7601u_probe(struct usb_interface *usb_intf, 2668c2ecf20Sopenharmony_ci const struct usb_device_id *id) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci struct usb_device *usb_dev = interface_to_usbdev(usb_intf); 2698c2ecf20Sopenharmony_ci struct mt7601u_dev *dev; 2708c2ecf20Sopenharmony_ci u32 asic_rev, mac_rev; 2718c2ecf20Sopenharmony_ci int ret; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci dev = mt7601u_alloc_device(&usb_intf->dev); 2748c2ecf20Sopenharmony_ci if (!dev) 2758c2ecf20Sopenharmony_ci return -ENOMEM; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci usb_dev = usb_get_dev(usb_dev); 2788c2ecf20Sopenharmony_ci usb_reset_device(usb_dev); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci usb_set_intfdata(usb_intf, dev); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci dev->vend_buf = devm_kmalloc(dev->dev, MT_VEND_BUF, GFP_KERNEL); 2838c2ecf20Sopenharmony_ci if (!dev->vend_buf) { 2848c2ecf20Sopenharmony_ci ret = -ENOMEM; 2858c2ecf20Sopenharmony_ci goto err; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci ret = mt7601u_assign_pipes(usb_intf, dev); 2898c2ecf20Sopenharmony_ci if (ret) 2908c2ecf20Sopenharmony_ci goto err; 2918c2ecf20Sopenharmony_ci ret = mt7601u_wait_asic_ready(dev); 2928c2ecf20Sopenharmony_ci if (ret) 2938c2ecf20Sopenharmony_ci goto err; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci asic_rev = mt7601u_rr(dev, MT_ASIC_VERSION); 2968c2ecf20Sopenharmony_ci mac_rev = mt7601u_rr(dev, MT_MAC_CSR0); 2978c2ecf20Sopenharmony_ci dev_info(dev->dev, "ASIC revision: %08x MAC revision: %08x\n", 2988c2ecf20Sopenharmony_ci asic_rev, mac_rev); 2998c2ecf20Sopenharmony_ci if ((asic_rev >> 16) != 0x7601) { 3008c2ecf20Sopenharmony_ci ret = -ENODEV; 3018c2ecf20Sopenharmony_ci goto err; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* Note: vendor driver skips this check for MT7601U */ 3058c2ecf20Sopenharmony_ci if (!(mt7601u_rr(dev, MT_EFUSE_CTRL) & MT_EFUSE_CTRL_SEL)) 3068c2ecf20Sopenharmony_ci dev_warn(dev->dev, "Warning: eFUSE not present\n"); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci ret = mt7601u_init_hardware(dev); 3098c2ecf20Sopenharmony_ci if (ret) 3108c2ecf20Sopenharmony_ci goto err; 3118c2ecf20Sopenharmony_ci ret = mt7601u_register_device(dev); 3128c2ecf20Sopenharmony_ci if (ret) 3138c2ecf20Sopenharmony_ci goto err_hw; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci set_bit(MT7601U_STATE_INITIALIZED, &dev->state); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return 0; 3188c2ecf20Sopenharmony_cierr_hw: 3198c2ecf20Sopenharmony_ci mt7601u_cleanup(dev); 3208c2ecf20Sopenharmony_cierr: 3218c2ecf20Sopenharmony_ci usb_set_intfdata(usb_intf, NULL); 3228c2ecf20Sopenharmony_ci usb_put_dev(interface_to_usbdev(usb_intf)); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci destroy_workqueue(dev->stat_wq); 3258c2ecf20Sopenharmony_ci ieee80211_free_hw(dev->hw); 3268c2ecf20Sopenharmony_ci return ret; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic void mt7601u_disconnect(struct usb_interface *usb_intf) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci struct mt7601u_dev *dev = usb_get_intfdata(usb_intf); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci ieee80211_unregister_hw(dev->hw); 3348c2ecf20Sopenharmony_ci mt7601u_cleanup(dev); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci usb_set_intfdata(usb_intf, NULL); 3378c2ecf20Sopenharmony_ci usb_put_dev(interface_to_usbdev(usb_intf)); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci destroy_workqueue(dev->stat_wq); 3408c2ecf20Sopenharmony_ci ieee80211_free_hw(dev->hw); 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic int mt7601u_suspend(struct usb_interface *usb_intf, pm_message_t state) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci struct mt7601u_dev *dev = usb_get_intfdata(usb_intf); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci mt7601u_cleanup(dev); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci return 0; 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic int mt7601u_resume(struct usb_interface *usb_intf) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci struct mt7601u_dev *dev = usb_get_intfdata(usb_intf); 3558c2ecf20Sopenharmony_ci int ret; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci ret = mt7601u_init_hardware(dev); 3588c2ecf20Sopenharmony_ci if (ret) 3598c2ecf20Sopenharmony_ci return ret; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci set_bit(MT7601U_STATE_INITIALIZED, &dev->state); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci return 0; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, mt7601u_device_table); 3678c2ecf20Sopenharmony_ciMODULE_FIRMWARE(MT7601U_FIRMWARE); 3688c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic struct usb_driver mt7601u_driver = { 3718c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 3728c2ecf20Sopenharmony_ci .id_table = mt7601u_device_table, 3738c2ecf20Sopenharmony_ci .probe = mt7601u_probe, 3748c2ecf20Sopenharmony_ci .disconnect = mt7601u_disconnect, 3758c2ecf20Sopenharmony_ci .suspend = mt7601u_suspend, 3768c2ecf20Sopenharmony_ci .resume = mt7601u_resume, 3778c2ecf20Sopenharmony_ci .reset_resume = mt7601u_resume, 3788c2ecf20Sopenharmony_ci .soft_unbind = 1, 3798c2ecf20Sopenharmony_ci .disable_hub_initiated_lpm = 1, 3808c2ecf20Sopenharmony_ci}; 3818c2ecf20Sopenharmony_cimodule_usb_driver(mt7601u_driver); 382