1c869f77dSJakub Kicinski /* 2c869f77dSJakub Kicinski * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl> 3c869f77dSJakub Kicinski * 4c869f77dSJakub Kicinski * This program is free software; you can redistribute it and/or modify 5c869f77dSJakub Kicinski * it under the terms of the GNU General Public License version 2 6c869f77dSJakub Kicinski * as published by the Free Software Foundation 7c869f77dSJakub Kicinski * 8c869f77dSJakub Kicinski * This program is distributed in the hope that it will be useful, 9c869f77dSJakub Kicinski * but WITHOUT ANY WARRANTY; without even the implied warranty of 10c869f77dSJakub Kicinski * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11c869f77dSJakub Kicinski * GNU General Public License for more details. 12c869f77dSJakub Kicinski */ 13c869f77dSJakub Kicinski 14c869f77dSJakub Kicinski #include <linux/kernel.h> 15c869f77dSJakub Kicinski #include <linux/module.h> 16c869f77dSJakub Kicinski #include <linux/usb.h> 17c869f77dSJakub Kicinski 18c869f77dSJakub Kicinski #include "mt7601u.h" 19c869f77dSJakub Kicinski #include "usb.h" 20c869f77dSJakub Kicinski #include "trace.h" 21c869f77dSJakub Kicinski 22c869f77dSJakub Kicinski static struct usb_device_id mt7601u_device_table[] = { 23c869f77dSJakub Kicinski { USB_DEVICE(0x0b05, 0x17d3) }, 24c869f77dSJakub Kicinski { USB_DEVICE(0x0e8d, 0x760a) }, 25c869f77dSJakub Kicinski { USB_DEVICE(0x0e8d, 0x760b) }, 26c869f77dSJakub Kicinski { USB_DEVICE(0x13d3, 0x3431) }, 27c869f77dSJakub Kicinski { USB_DEVICE(0x13d3, 0x3434) }, 28c869f77dSJakub Kicinski { USB_DEVICE(0x148f, 0x7601) }, 29c869f77dSJakub Kicinski { USB_DEVICE(0x148f, 0x760a) }, 30c869f77dSJakub Kicinski { USB_DEVICE(0x148f, 0x760b) }, 31c869f77dSJakub Kicinski { USB_DEVICE(0x148f, 0x760c) }, 32c869f77dSJakub Kicinski { USB_DEVICE(0x148f, 0x760d) }, 33c869f77dSJakub Kicinski { USB_DEVICE(0x2001, 0x3d04) }, 34c869f77dSJakub Kicinski { USB_DEVICE(0x2717, 0x4106) }, 35c869f77dSJakub Kicinski { USB_DEVICE(0x2955, 0x0001) }, 36c869f77dSJakub Kicinski { USB_DEVICE(0x2955, 0x1001) }, 37c869f77dSJakub Kicinski { USB_DEVICE(0x2a5f, 0x1000) }, 38c869f77dSJakub Kicinski { USB_DEVICE(0x7392, 0x7710) }, 39c869f77dSJakub Kicinski { 0, } 40c869f77dSJakub Kicinski }; 41c869f77dSJakub Kicinski 42c869f77dSJakub Kicinski bool mt7601u_usb_alloc_buf(struct mt7601u_dev *dev, size_t len, 43c869f77dSJakub Kicinski struct mt7601u_dma_buf *buf) 44c869f77dSJakub Kicinski { 45c869f77dSJakub Kicinski struct usb_device *usb_dev = mt7601u_to_usb_dev(dev); 46c869f77dSJakub Kicinski 47c869f77dSJakub Kicinski buf->len = len; 48c869f77dSJakub Kicinski buf->urb = usb_alloc_urb(0, GFP_KERNEL); 49c869f77dSJakub Kicinski buf->buf = usb_alloc_coherent(usb_dev, buf->len, GFP_KERNEL, &buf->dma); 50c869f77dSJakub Kicinski 51c869f77dSJakub Kicinski return !buf->urb || !buf->buf; 52c869f77dSJakub Kicinski } 53c869f77dSJakub Kicinski 54c869f77dSJakub Kicinski void mt7601u_usb_free_buf(struct mt7601u_dev *dev, struct mt7601u_dma_buf *buf) 55c869f77dSJakub Kicinski { 56c869f77dSJakub Kicinski struct usb_device *usb_dev = mt7601u_to_usb_dev(dev); 57c869f77dSJakub Kicinski 58c869f77dSJakub Kicinski usb_free_coherent(usb_dev, buf->len, buf->buf, buf->dma); 59c869f77dSJakub Kicinski usb_free_urb(buf->urb); 60c869f77dSJakub Kicinski } 61c869f77dSJakub Kicinski 62c869f77dSJakub Kicinski int mt7601u_usb_submit_buf(struct mt7601u_dev *dev, int dir, int ep_idx, 63c869f77dSJakub Kicinski struct mt7601u_dma_buf *buf, gfp_t gfp, 64c869f77dSJakub Kicinski usb_complete_t complete_fn, void *context) 65c869f77dSJakub Kicinski { 66c869f77dSJakub Kicinski struct usb_device *usb_dev = mt7601u_to_usb_dev(dev); 67c869f77dSJakub Kicinski unsigned pipe; 68c869f77dSJakub Kicinski int ret; 69c869f77dSJakub Kicinski 70c869f77dSJakub Kicinski if (dir == USB_DIR_IN) 71c869f77dSJakub Kicinski pipe = usb_rcvbulkpipe(usb_dev, dev->in_eps[ep_idx]); 72c869f77dSJakub Kicinski else 73c869f77dSJakub Kicinski pipe = usb_sndbulkpipe(usb_dev, dev->out_eps[ep_idx]); 74c869f77dSJakub Kicinski 75c869f77dSJakub Kicinski usb_fill_bulk_urb(buf->urb, usb_dev, pipe, buf->buf, buf->len, 76c869f77dSJakub Kicinski complete_fn, context); 77c869f77dSJakub Kicinski buf->urb->transfer_dma = buf->dma; 78c869f77dSJakub Kicinski buf->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 79c869f77dSJakub Kicinski 80c869f77dSJakub Kicinski trace_mt_submit_urb(dev, buf->urb); 81c869f77dSJakub Kicinski ret = usb_submit_urb(buf->urb, gfp); 82c869f77dSJakub Kicinski if (ret) 83c869f77dSJakub Kicinski dev_err(dev->dev, "Error: submit URB dir:%d ep:%d failed:%d\n", 84c869f77dSJakub Kicinski dir, ep_idx, ret); 85c869f77dSJakub Kicinski return ret; 86c869f77dSJakub Kicinski } 87c869f77dSJakub Kicinski 88c869f77dSJakub Kicinski void mt7601u_complete_urb(struct urb *urb) 89c869f77dSJakub Kicinski { 90c869f77dSJakub Kicinski struct completion *cmpl = urb->context; 91c869f77dSJakub Kicinski 92c869f77dSJakub Kicinski complete(cmpl); 93c869f77dSJakub Kicinski } 94c869f77dSJakub Kicinski 95bed429e1SJakub Kicinski int mt7601u_vendor_request(struct mt7601u_dev *dev, const u8 req, 96c869f77dSJakub Kicinski const u8 direction, const u16 val, const u16 offset, 97c869f77dSJakub Kicinski void *buf, const size_t buflen) 98c869f77dSJakub Kicinski { 99c869f77dSJakub Kicinski int i, ret; 100c869f77dSJakub Kicinski struct usb_device *usb_dev = mt7601u_to_usb_dev(dev); 101c869f77dSJakub Kicinski const u8 req_type = direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE; 102c869f77dSJakub Kicinski const unsigned int pipe = (direction == USB_DIR_IN) ? 103c869f77dSJakub Kicinski usb_rcvctrlpipe(usb_dev, 0) : usb_sndctrlpipe(usb_dev, 0); 104c869f77dSJakub Kicinski 105c869f77dSJakub Kicinski for (i = 0; i < MT_VEND_REQ_MAX_RETRY; i++) { 106c869f77dSJakub Kicinski ret = usb_control_msg(usb_dev, pipe, req, req_type, 107c869f77dSJakub Kicinski val, offset, buf, buflen, 108c869f77dSJakub Kicinski MT_VEND_REQ_TOUT_MS); 109c869f77dSJakub Kicinski trace_mt_vend_req(dev, pipe, req, req_type, val, offset, 110c869f77dSJakub Kicinski buf, buflen, ret); 111c869f77dSJakub Kicinski 112bed429e1SJakub Kicinski if (ret == -ENODEV) 113bed429e1SJakub Kicinski set_bit(MT7601U_STATE_REMOVED, &dev->state); 114c869f77dSJakub Kicinski if (ret >= 0 || ret == -ENODEV) 115c869f77dSJakub Kicinski return ret; 116c869f77dSJakub Kicinski 117c869f77dSJakub Kicinski msleep(5); 118c869f77dSJakub Kicinski } 119c869f77dSJakub Kicinski 120c869f77dSJakub Kicinski dev_err(dev->dev, "Vendor request req:%02x off:%04x failed:%d\n", 121c869f77dSJakub Kicinski req, offset, ret); 122c869f77dSJakub Kicinski 123c869f77dSJakub Kicinski return ret; 124c869f77dSJakub Kicinski } 125c869f77dSJakub Kicinski 126c869f77dSJakub Kicinski void mt7601u_vendor_reset(struct mt7601u_dev *dev) 127c869f77dSJakub Kicinski { 128c869f77dSJakub Kicinski mt7601u_vendor_request(dev, MT_VEND_DEV_MODE, USB_DIR_OUT, 129c869f77dSJakub Kicinski MT_VEND_DEV_MODE_RESET, 0, NULL, 0); 130c869f77dSJakub Kicinski } 131c869f77dSJakub Kicinski 132c869f77dSJakub Kicinski u32 mt7601u_rr(struct mt7601u_dev *dev, u32 offset) 133c869f77dSJakub Kicinski { 134c869f77dSJakub Kicinski int ret; 135bed429e1SJakub Kicinski u32 val = ~0; 136c869f77dSJakub Kicinski 137c869f77dSJakub Kicinski WARN_ONCE(offset > USHRT_MAX, "read high off:%08x", offset); 138c869f77dSJakub Kicinski 139bed429e1SJakub Kicinski mutex_lock(&dev->vendor_req_mutex); 140bed429e1SJakub Kicinski 141c869f77dSJakub Kicinski ret = mt7601u_vendor_request(dev, MT_VEND_MULTI_READ, USB_DIR_IN, 142bed429e1SJakub Kicinski 0, offset, dev->vend_buf, MT_VEND_BUF); 143bed429e1SJakub Kicinski if (ret == MT_VEND_BUF) 144bed429e1SJakub Kicinski val = get_unaligned_le32(dev->vend_buf); 145bed429e1SJakub Kicinski else if (ret > 0) 146c869f77dSJakub Kicinski dev_err(dev->dev, "Error: wrong size read:%d off:%08x\n", 147c869f77dSJakub Kicinski ret, offset); 148bed429e1SJakub Kicinski 149bed429e1SJakub Kicinski mutex_unlock(&dev->vendor_req_mutex); 150c869f77dSJakub Kicinski 151c869f77dSJakub Kicinski trace_reg_read(dev, offset, val); 152c869f77dSJakub Kicinski return val; 153c869f77dSJakub Kicinski } 154c869f77dSJakub Kicinski 155c869f77dSJakub Kicinski int mt7601u_vendor_single_wr(struct mt7601u_dev *dev, const u8 req, 156c869f77dSJakub Kicinski const u16 offset, const u32 val) 157c869f77dSJakub Kicinski { 158c869f77dSJakub Kicinski int ret; 159c869f77dSJakub Kicinski 160bed429e1SJakub Kicinski mutex_lock(&dev->vendor_req_mutex); 161bed429e1SJakub Kicinski 162c869f77dSJakub Kicinski ret = mt7601u_vendor_request(dev, req, USB_DIR_OUT, 163c869f77dSJakub Kicinski val & 0xffff, offset, NULL, 0); 164bed429e1SJakub Kicinski if (!ret) 165bed429e1SJakub Kicinski ret = mt7601u_vendor_request(dev, req, USB_DIR_OUT, 166c869f77dSJakub Kicinski val >> 16, offset + 2, NULL, 0); 167bed429e1SJakub Kicinski 168bed429e1SJakub Kicinski mutex_unlock(&dev->vendor_req_mutex); 169bed429e1SJakub Kicinski 170bed429e1SJakub Kicinski return ret; 171c869f77dSJakub Kicinski } 172c869f77dSJakub Kicinski 173c869f77dSJakub Kicinski void mt7601u_wr(struct mt7601u_dev *dev, u32 offset, u32 val) 174c869f77dSJakub Kicinski { 175c869f77dSJakub Kicinski WARN_ONCE(offset > USHRT_MAX, "write high off:%08x", offset); 176c869f77dSJakub Kicinski 177c869f77dSJakub Kicinski mt7601u_vendor_single_wr(dev, MT_VEND_WRITE, offset, val); 178c869f77dSJakub Kicinski trace_reg_write(dev, offset, val); 179c869f77dSJakub Kicinski } 180c869f77dSJakub Kicinski 181c869f77dSJakub Kicinski u32 mt7601u_rmw(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val) 182c869f77dSJakub Kicinski { 183c869f77dSJakub Kicinski val |= mt7601u_rr(dev, offset) & ~mask; 184c869f77dSJakub Kicinski mt7601u_wr(dev, offset, val); 185c869f77dSJakub Kicinski return val; 186c869f77dSJakub Kicinski } 187c869f77dSJakub Kicinski 188c869f77dSJakub Kicinski u32 mt7601u_rmc(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val) 189c869f77dSJakub Kicinski { 190c869f77dSJakub Kicinski u32 reg = mt7601u_rr(dev, offset); 191c869f77dSJakub Kicinski 192c869f77dSJakub Kicinski val |= reg & ~mask; 193c869f77dSJakub Kicinski if (reg != val) 194c869f77dSJakub Kicinski mt7601u_wr(dev, offset, val); 195c869f77dSJakub Kicinski return val; 196c869f77dSJakub Kicinski } 197c869f77dSJakub Kicinski 198c869f77dSJakub Kicinski void mt7601u_wr_copy(struct mt7601u_dev *dev, u32 offset, 199c869f77dSJakub Kicinski const void *data, int len) 200c869f77dSJakub Kicinski { 201c869f77dSJakub Kicinski WARN_ONCE(offset & 3, "unaligned write copy off:%08x", offset); 202c869f77dSJakub Kicinski WARN_ONCE(len & 3, "short write copy off:%08x", offset); 203c869f77dSJakub Kicinski 204c869f77dSJakub Kicinski mt7601u_burst_write_regs(dev, offset, data, len / 4); 205c869f77dSJakub Kicinski } 206c869f77dSJakub Kicinski 207c869f77dSJakub Kicinski void mt7601u_addr_wr(struct mt7601u_dev *dev, const u32 offset, const u8 *addr) 208c869f77dSJakub Kicinski { 209c869f77dSJakub Kicinski mt7601u_wr(dev, offset, get_unaligned_le32(addr)); 210c869f77dSJakub Kicinski mt7601u_wr(dev, offset + 4, addr[4] | addr[5] << 8); 211c869f77dSJakub Kicinski } 212c869f77dSJakub Kicinski 213c869f77dSJakub Kicinski static int mt7601u_assign_pipes(struct usb_interface *usb_intf, 214c869f77dSJakub Kicinski struct mt7601u_dev *dev) 215c869f77dSJakub Kicinski { 216c869f77dSJakub Kicinski struct usb_endpoint_descriptor *ep_desc; 217c869f77dSJakub Kicinski struct usb_host_interface *intf_desc = usb_intf->cur_altsetting; 218c869f77dSJakub Kicinski unsigned i, ep_i = 0, ep_o = 0; 219c869f77dSJakub Kicinski 220c869f77dSJakub Kicinski BUILD_BUG_ON(sizeof(dev->in_eps) < __MT_EP_IN_MAX); 221c869f77dSJakub Kicinski BUILD_BUG_ON(sizeof(dev->out_eps) < __MT_EP_OUT_MAX); 222c869f77dSJakub Kicinski 223c869f77dSJakub Kicinski for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) { 224c869f77dSJakub Kicinski ep_desc = &intf_desc->endpoint[i].desc; 225c869f77dSJakub Kicinski 226c869f77dSJakub Kicinski if (usb_endpoint_is_bulk_in(ep_desc) && 227c869f77dSJakub Kicinski ep_i++ < __MT_EP_IN_MAX) { 228c869f77dSJakub Kicinski dev->in_eps[ep_i - 1] = usb_endpoint_num(ep_desc); 229c869f77dSJakub Kicinski dev->in_max_packet = usb_endpoint_maxp(ep_desc); 230c869f77dSJakub Kicinski /* Note: this is ignored by usb sub-system but vendor 231c869f77dSJakub Kicinski * code does it. We can drop this at some point. 232c869f77dSJakub Kicinski */ 233c869f77dSJakub Kicinski dev->in_eps[ep_i - 1] |= USB_DIR_IN; 234c869f77dSJakub Kicinski } else if (usb_endpoint_is_bulk_out(ep_desc) && 235c869f77dSJakub Kicinski ep_o++ < __MT_EP_OUT_MAX) { 236c869f77dSJakub Kicinski dev->out_eps[ep_o - 1] = usb_endpoint_num(ep_desc); 237c869f77dSJakub Kicinski dev->out_max_packet = usb_endpoint_maxp(ep_desc); 238c869f77dSJakub Kicinski } 239c869f77dSJakub Kicinski } 240c869f77dSJakub Kicinski 241c869f77dSJakub Kicinski if (ep_i != __MT_EP_IN_MAX || ep_o != __MT_EP_OUT_MAX) { 242c869f77dSJakub Kicinski dev_err(dev->dev, "Error: wrong pipe number in:%d out:%d\n", 243c869f77dSJakub Kicinski ep_i, ep_o); 244c869f77dSJakub Kicinski return -EINVAL; 245c869f77dSJakub Kicinski } 246c869f77dSJakub Kicinski 247c869f77dSJakub Kicinski return 0; 248c869f77dSJakub Kicinski } 249c869f77dSJakub Kicinski 250c869f77dSJakub Kicinski static int mt7601u_probe(struct usb_interface *usb_intf, 251c869f77dSJakub Kicinski const struct usb_device_id *id) 252c869f77dSJakub Kicinski { 253c869f77dSJakub Kicinski struct usb_device *usb_dev = interface_to_usbdev(usb_intf); 254c869f77dSJakub Kicinski struct mt7601u_dev *dev; 255c869f77dSJakub Kicinski u32 asic_rev, mac_rev; 256c869f77dSJakub Kicinski int ret; 257c869f77dSJakub Kicinski 258c869f77dSJakub Kicinski dev = mt7601u_alloc_device(&usb_intf->dev); 259c869f77dSJakub Kicinski if (!dev) 260c869f77dSJakub Kicinski return -ENOMEM; 261c869f77dSJakub Kicinski 262c869f77dSJakub Kicinski usb_dev = usb_get_dev(usb_dev); 263c869f77dSJakub Kicinski usb_reset_device(usb_dev); 264c869f77dSJakub Kicinski 265c869f77dSJakub Kicinski usb_set_intfdata(usb_intf, dev); 266c869f77dSJakub Kicinski 267bed429e1SJakub Kicinski dev->vend_buf = devm_kmalloc(dev->dev, MT_VEND_BUF, GFP_KERNEL); 268bed429e1SJakub Kicinski if (!dev->vend_buf) { 269bed429e1SJakub Kicinski ret = -ENOMEM; 270bed429e1SJakub Kicinski goto err; 271bed429e1SJakub Kicinski } 272bed429e1SJakub Kicinski 273c869f77dSJakub Kicinski ret = mt7601u_assign_pipes(usb_intf, dev); 274c869f77dSJakub Kicinski if (ret) 275c869f77dSJakub Kicinski goto err; 276c869f77dSJakub Kicinski ret = mt7601u_wait_asic_ready(dev); 277c869f77dSJakub Kicinski if (ret) 278c869f77dSJakub Kicinski goto err; 279c869f77dSJakub Kicinski 280c869f77dSJakub Kicinski asic_rev = mt7601u_rr(dev, MT_ASIC_VERSION); 281c869f77dSJakub Kicinski mac_rev = mt7601u_rr(dev, MT_MAC_CSR0); 282c869f77dSJakub Kicinski dev_info(dev->dev, "ASIC revision: %08x MAC revision: %08x\n", 283c869f77dSJakub Kicinski asic_rev, mac_rev); 284c869f77dSJakub Kicinski 285c869f77dSJakub Kicinski /* Note: vendor driver skips this check for MT7601U */ 286c869f77dSJakub Kicinski if (!(mt7601u_rr(dev, MT_EFUSE_CTRL) & MT_EFUSE_CTRL_SEL)) 287c869f77dSJakub Kicinski dev_warn(dev->dev, "Warning: eFUSE not present\n"); 288c869f77dSJakub Kicinski 289c869f77dSJakub Kicinski ret = mt7601u_init_hardware(dev); 290c869f77dSJakub Kicinski if (ret) 291c869f77dSJakub Kicinski goto err; 292c869f77dSJakub Kicinski ret = mt7601u_register_device(dev); 293c869f77dSJakub Kicinski if (ret) 294c869f77dSJakub Kicinski goto err_hw; 295c869f77dSJakub Kicinski 296c869f77dSJakub Kicinski set_bit(MT7601U_STATE_INITIALIZED, &dev->state); 297c869f77dSJakub Kicinski 298c869f77dSJakub Kicinski return 0; 299c869f77dSJakub Kicinski err_hw: 300c869f77dSJakub Kicinski mt7601u_cleanup(dev); 301c869f77dSJakub Kicinski err: 302c869f77dSJakub Kicinski usb_set_intfdata(usb_intf, NULL); 303c869f77dSJakub Kicinski usb_put_dev(interface_to_usbdev(usb_intf)); 304c869f77dSJakub Kicinski 305c869f77dSJakub Kicinski destroy_workqueue(dev->stat_wq); 306c869f77dSJakub Kicinski ieee80211_free_hw(dev->hw); 307c869f77dSJakub Kicinski return ret; 308c869f77dSJakub Kicinski } 309c869f77dSJakub Kicinski 310c869f77dSJakub Kicinski static void mt7601u_disconnect(struct usb_interface *usb_intf) 311c869f77dSJakub Kicinski { 312c869f77dSJakub Kicinski struct mt7601u_dev *dev = usb_get_intfdata(usb_intf); 313c869f77dSJakub Kicinski 314c869f77dSJakub Kicinski ieee80211_unregister_hw(dev->hw); 315c869f77dSJakub Kicinski mt7601u_cleanup(dev); 316c869f77dSJakub Kicinski 317c869f77dSJakub Kicinski usb_set_intfdata(usb_intf, NULL); 318c869f77dSJakub Kicinski usb_put_dev(interface_to_usbdev(usb_intf)); 319c869f77dSJakub Kicinski 320c869f77dSJakub Kicinski destroy_workqueue(dev->stat_wq); 321c869f77dSJakub Kicinski ieee80211_free_hw(dev->hw); 322c869f77dSJakub Kicinski } 323c869f77dSJakub Kicinski 324c869f77dSJakub Kicinski static int mt7601u_suspend(struct usb_interface *usb_intf, pm_message_t state) 325c869f77dSJakub Kicinski { 326c869f77dSJakub Kicinski struct mt7601u_dev *dev = usb_get_intfdata(usb_intf); 327c869f77dSJakub Kicinski 328c869f77dSJakub Kicinski mt7601u_cleanup(dev); 329c869f77dSJakub Kicinski 330c869f77dSJakub Kicinski return 0; 331c869f77dSJakub Kicinski } 332c869f77dSJakub Kicinski 333c869f77dSJakub Kicinski static int mt7601u_resume(struct usb_interface *usb_intf) 334c869f77dSJakub Kicinski { 335c869f77dSJakub Kicinski struct mt7601u_dev *dev = usb_get_intfdata(usb_intf); 3369a15b57eSJakub Kicinski int ret; 337c869f77dSJakub Kicinski 3389a15b57eSJakub Kicinski ret = mt7601u_init_hardware(dev); 3399a15b57eSJakub Kicinski if (ret) 3409a15b57eSJakub Kicinski return ret; 3419a15b57eSJakub Kicinski 3429a15b57eSJakub Kicinski set_bit(MT7601U_STATE_INITIALIZED, &dev->state); 3439a15b57eSJakub Kicinski 3449a15b57eSJakub Kicinski return 0; 345c869f77dSJakub Kicinski } 346c869f77dSJakub Kicinski 347c869f77dSJakub Kicinski MODULE_DEVICE_TABLE(usb, mt7601u_device_table); 348c869f77dSJakub Kicinski MODULE_FIRMWARE(MT7601U_FIRMWARE); 349c869f77dSJakub Kicinski MODULE_LICENSE("GPL"); 350c869f77dSJakub Kicinski 351c869f77dSJakub Kicinski static struct usb_driver mt7601u_driver = { 352c869f77dSJakub Kicinski .name = KBUILD_MODNAME, 353c869f77dSJakub Kicinski .id_table = mt7601u_device_table, 354c869f77dSJakub Kicinski .probe = mt7601u_probe, 355c869f77dSJakub Kicinski .disconnect = mt7601u_disconnect, 356c869f77dSJakub Kicinski .suspend = mt7601u_suspend, 357c869f77dSJakub Kicinski .resume = mt7601u_resume, 358c869f77dSJakub Kicinski .reset_resume = mt7601u_resume, 359c869f77dSJakub Kicinski .soft_unbind = 1, 360c869f77dSJakub Kicinski .disable_hub_initiated_lpm = 1, 361c869f77dSJakub Kicinski }; 362c869f77dSJakub Kicinski module_usb_driver(mt7601u_driver); 363