18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: ISC 28c2ecf20Sopenharmony_ci/* Copyright (C) 2019 MediaTek Inc. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Author: Felix Fietkau <nbd@nbd.name> 58c2ecf20Sopenharmony_ci * Lorenzo Bianconi <lorenzo@kernel.org> 68c2ecf20Sopenharmony_ci * Sean Wang <sean.wang@mediatek.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/usb.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "mt7615.h" 148c2ecf20Sopenharmony_ci#include "mac.h" 158c2ecf20Sopenharmony_ci#include "mcu.h" 168c2ecf20Sopenharmony_ci#include "regs.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic const struct usb_device_id mt7615_device_table[] = { 198c2ecf20Sopenharmony_ci { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7663, 0xff, 0xff, 0xff) }, 208c2ecf20Sopenharmony_ci { }, 218c2ecf20Sopenharmony_ci}; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic void mt7663u_stop(struct ieee80211_hw *hw) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci struct mt7615_phy *phy = mt7615_hw_phy(hw); 268c2ecf20Sopenharmony_ci struct mt7615_dev *dev = hw->priv; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci clear_bit(MT76_STATE_RUNNING, &dev->mphy.state); 298c2ecf20Sopenharmony_ci del_timer_sync(&phy->roc_timer); 308c2ecf20Sopenharmony_ci cancel_work_sync(&phy->roc_work); 318c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&phy->scan_work); 328c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&phy->mac_work); 338c2ecf20Sopenharmony_ci mt76u_stop_tx(&dev->mt76); 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic void mt7663u_cleanup(struct mt7615_dev *dev) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci clear_bit(MT76_STATE_INITIALIZED, &dev->mphy.state); 398c2ecf20Sopenharmony_ci mt76u_queues_deinit(&dev->mt76); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic void mt7663u_init_work(struct work_struct *work) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct mt7615_dev *dev; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci dev = container_of(work, struct mt7615_dev, mcu_work); 478c2ecf20Sopenharmony_ci if (mt7663u_mcu_init(dev)) 488c2ecf20Sopenharmony_ci return; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci mt7615_mcu_set_eeprom(dev); 518c2ecf20Sopenharmony_ci mt7615_mac_init(dev); 528c2ecf20Sopenharmony_ci mt7615_phy_init(dev); 538c2ecf20Sopenharmony_ci mt7615_mcu_del_wtbl_all(dev); 548c2ecf20Sopenharmony_ci mt7615_check_offload_capability(dev); 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic int mt7663u_probe(struct usb_interface *usb_intf, 588c2ecf20Sopenharmony_ci const struct usb_device_id *id) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci static const struct mt76_driver_ops drv_ops = { 618c2ecf20Sopenharmony_ci .txwi_size = MT_USB_TXD_SIZE, 628c2ecf20Sopenharmony_ci .drv_flags = MT_DRV_RX_DMA_HDR | MT_DRV_HW_MGMT_TXQ, 638c2ecf20Sopenharmony_ci .tx_prepare_skb = mt7663_usb_sdio_tx_prepare_skb, 648c2ecf20Sopenharmony_ci .tx_complete_skb = mt7663_usb_sdio_tx_complete_skb, 658c2ecf20Sopenharmony_ci .tx_status_data = mt7663_usb_sdio_tx_status_data, 668c2ecf20Sopenharmony_ci .rx_skb = mt7615_queue_rx_skb, 678c2ecf20Sopenharmony_ci .sta_ps = mt7615_sta_ps, 688c2ecf20Sopenharmony_ci .sta_add = mt7615_mac_sta_add, 698c2ecf20Sopenharmony_ci .sta_remove = mt7615_mac_sta_remove, 708c2ecf20Sopenharmony_ci .update_survey = mt7615_update_channel, 718c2ecf20Sopenharmony_ci }; 728c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(usb_intf); 738c2ecf20Sopenharmony_ci struct ieee80211_ops *ops; 748c2ecf20Sopenharmony_ci struct mt7615_dev *dev; 758c2ecf20Sopenharmony_ci struct mt76_dev *mdev; 768c2ecf20Sopenharmony_ci int ret; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci ops = devm_kmemdup(&usb_intf->dev, &mt7615_ops, sizeof(mt7615_ops), 798c2ecf20Sopenharmony_ci GFP_KERNEL); 808c2ecf20Sopenharmony_ci if (!ops) 818c2ecf20Sopenharmony_ci return -ENOMEM; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci ops->stop = mt7663u_stop; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci mdev = mt76_alloc_device(&usb_intf->dev, sizeof(*dev), ops, &drv_ops); 868c2ecf20Sopenharmony_ci if (!mdev) 878c2ecf20Sopenharmony_ci return -ENOMEM; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci dev = container_of(mdev, struct mt7615_dev, mt76); 908c2ecf20Sopenharmony_ci udev = usb_get_dev(udev); 918c2ecf20Sopenharmony_ci usb_reset_device(udev); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci usb_set_intfdata(usb_intf, dev); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci INIT_WORK(&dev->mcu_work, mt7663u_init_work); 968c2ecf20Sopenharmony_ci dev->reg_map = mt7663_usb_sdio_reg_map; 978c2ecf20Sopenharmony_ci dev->ops = ops; 988c2ecf20Sopenharmony_ci ret = mt76u_init(mdev, usb_intf, true); 998c2ecf20Sopenharmony_ci if (ret < 0) 1008c2ecf20Sopenharmony_ci goto error; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci mdev->rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) | 1038c2ecf20Sopenharmony_ci (mt76_rr(dev, MT_HW_REV) & 0xff); 1048c2ecf20Sopenharmony_ci dev_dbg(mdev->dev, "ASIC revision: %04x\n", mdev->rev); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (mt76_poll_msec(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_PWR_ON, 1078c2ecf20Sopenharmony_ci FW_STATE_PWR_ON << 1, 500)) { 1088c2ecf20Sopenharmony_ci dev_dbg(dev->mt76.dev, "Usb device already powered on\n"); 1098c2ecf20Sopenharmony_ci set_bit(MT76_STATE_POWER_OFF, &dev->mphy.state); 1108c2ecf20Sopenharmony_ci goto alloc_queues; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci ret = mt76u_vendor_request(&dev->mt76, MT_VEND_POWER_ON, 1148c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR, 1158c2ecf20Sopenharmony_ci 0x0, 0x1, NULL, 0); 1168c2ecf20Sopenharmony_ci if (ret) 1178c2ecf20Sopenharmony_ci goto error; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (!mt76_poll_msec(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_PWR_ON, 1208c2ecf20Sopenharmony_ci FW_STATE_PWR_ON << 1, 500)) { 1218c2ecf20Sopenharmony_ci dev_err(dev->mt76.dev, "Timeout for power on\n"); 1228c2ecf20Sopenharmony_ci ret = -EIO; 1238c2ecf20Sopenharmony_ci goto error; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cialloc_queues: 1278c2ecf20Sopenharmony_ci ret = mt76u_alloc_mcu_queue(&dev->mt76); 1288c2ecf20Sopenharmony_ci if (ret) 1298c2ecf20Sopenharmony_ci goto error_free_q; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci ret = mt76u_alloc_queues(&dev->mt76); 1328c2ecf20Sopenharmony_ci if (ret) 1338c2ecf20Sopenharmony_ci goto error_free_q; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci ret = mt7663_usb_sdio_register_device(dev); 1368c2ecf20Sopenharmony_ci if (ret) 1378c2ecf20Sopenharmony_ci goto error_free_q; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return 0; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cierror_free_q: 1428c2ecf20Sopenharmony_ci mt76u_queues_deinit(&dev->mt76); 1438c2ecf20Sopenharmony_cierror: 1448c2ecf20Sopenharmony_ci usb_set_intfdata(usb_intf, NULL); 1458c2ecf20Sopenharmony_ci usb_put_dev(interface_to_usbdev(usb_intf)); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci mt76_free_device(&dev->mt76); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return ret; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic void mt7663u_disconnect(struct usb_interface *usb_intf) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct mt7615_dev *dev = usb_get_intfdata(usb_intf); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state)) 1578c2ecf20Sopenharmony_ci return; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci ieee80211_unregister_hw(dev->mt76.hw); 1608c2ecf20Sopenharmony_ci mt7663u_cleanup(dev); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci usb_set_intfdata(usb_intf, NULL); 1638c2ecf20Sopenharmony_ci usb_put_dev(interface_to_usbdev(usb_intf)); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci mt76_free_device(&dev->mt76); 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 1698c2ecf20Sopenharmony_cistatic int mt7663u_suspend(struct usb_interface *intf, pm_message_t state) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct mt7615_dev *dev = usb_get_intfdata(intf); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (!test_bit(MT76_STATE_SUSPEND, &dev->mphy.state) && 1748c2ecf20Sopenharmony_ci mt7615_firmware_offload(dev)) { 1758c2ecf20Sopenharmony_ci int err; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci err = mt7615_mcu_set_hif_suspend(dev, true); 1788c2ecf20Sopenharmony_ci if (err < 0) 1798c2ecf20Sopenharmony_ci return err; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci mt76u_stop_rx(&dev->mt76); 1838c2ecf20Sopenharmony_ci mt76u_stop_tx(&dev->mt76); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci return 0; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int mt7663u_resume(struct usb_interface *intf) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct mt7615_dev *dev = usb_get_intfdata(intf); 1918c2ecf20Sopenharmony_ci int err; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci err = mt76u_vendor_request(&dev->mt76, MT_VEND_FEATURE_SET, 1948c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR, 1958c2ecf20Sopenharmony_ci 0x5, 0x0, NULL, 0); 1968c2ecf20Sopenharmony_ci if (err) 1978c2ecf20Sopenharmony_ci return err; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci err = mt76u_resume_rx(&dev->mt76); 2008c2ecf20Sopenharmony_ci if (err < 0) 2018c2ecf20Sopenharmony_ci return err; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (!test_bit(MT76_STATE_SUSPEND, &dev->mphy.state) && 2048c2ecf20Sopenharmony_ci mt7615_firmware_offload(dev)) 2058c2ecf20Sopenharmony_ci err = mt7615_mcu_set_hif_suspend(dev, false); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci return err; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, mt7615_device_table); 2128c2ecf20Sopenharmony_ciMODULE_FIRMWARE(MT7663_OFFLOAD_FIRMWARE_N9); 2138c2ecf20Sopenharmony_ciMODULE_FIRMWARE(MT7663_OFFLOAD_ROM_PATCH); 2148c2ecf20Sopenharmony_ciMODULE_FIRMWARE(MT7663_FIRMWARE_N9); 2158c2ecf20Sopenharmony_ciMODULE_FIRMWARE(MT7663_ROM_PATCH); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic struct usb_driver mt7663u_driver = { 2188c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 2198c2ecf20Sopenharmony_ci .id_table = mt7615_device_table, 2208c2ecf20Sopenharmony_ci .probe = mt7663u_probe, 2218c2ecf20Sopenharmony_ci .disconnect = mt7663u_disconnect, 2228c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 2238c2ecf20Sopenharmony_ci .suspend = mt7663u_suspend, 2248c2ecf20Sopenharmony_ci .resume = mt7663u_resume, 2258c2ecf20Sopenharmony_ci .reset_resume = mt7663u_resume, 2268c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 2278c2ecf20Sopenharmony_ci .soft_unbind = 1, 2288c2ecf20Sopenharmony_ci .disable_hub_initiated_lpm = 1, 2298c2ecf20Sopenharmony_ci}; 2308c2ecf20Sopenharmony_cimodule_usb_driver(mt7663u_driver); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); 2338c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>"); 2348c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 235