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/kernel.h>
78c2ecf20Sopenharmony_ci#include <linux/module.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include "../mt76x02_usb.h"
108c2ecf20Sopenharmony_ci#include "mt76x2u.h"
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_cistatic const struct usb_device_id mt76x2u_device_table[] = {
138c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0b05, 0x1833) },	/* Asus USB-AC54 */
148c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0b05, 0x17eb) },	/* Asus USB-AC55 */
158c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0b05, 0x180b) },	/* Asus USB-N53 B1 */
168c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0e8d, 0x7612) },	/* Aukey USBAC1200 - Alfa AWUS036ACM */
178c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x057c, 0x8503) },	/* Avm FRITZ!WLAN AC860 */
188c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x7392, 0xb711) },	/* Edimax EW 7722 UAC */
198c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0e8d, 0x7632) },	/* HC-M7662BU1 */
208c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x2c4e, 0x0103) },	/* Mercury UD13 */
218c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0846, 0x9053) },	/* Netgear A6210 */
228c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x045e, 0x02e6) },	/* XBox One Wireless Adapter */
238c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x045e, 0x02fe) },	/* XBox One Wireless Adapter */
248c2ecf20Sopenharmony_ci	{ },
258c2ecf20Sopenharmony_ci};
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic int mt76x2u_probe(struct usb_interface *intf,
288c2ecf20Sopenharmony_ci			 const struct usb_device_id *id)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	static const struct mt76_driver_ops drv_ops = {
318c2ecf20Sopenharmony_ci		.drv_flags = MT_DRV_SW_RX_AIRTIME,
328c2ecf20Sopenharmony_ci		.survey_flags = SURVEY_INFO_TIME_TX,
338c2ecf20Sopenharmony_ci		.update_survey = mt76x02_update_channel,
348c2ecf20Sopenharmony_ci		.tx_prepare_skb = mt76x02u_tx_prepare_skb,
358c2ecf20Sopenharmony_ci		.tx_complete_skb = mt76x02u_tx_complete_skb,
368c2ecf20Sopenharmony_ci		.tx_status_data = mt76x02_tx_status_data,
378c2ecf20Sopenharmony_ci		.rx_skb = mt76x02_queue_rx_skb,
388c2ecf20Sopenharmony_ci		.sta_ps = mt76x02_sta_ps,
398c2ecf20Sopenharmony_ci		.sta_add = mt76x02_sta_add,
408c2ecf20Sopenharmony_ci		.sta_remove = mt76x02_sta_remove,
418c2ecf20Sopenharmony_ci	};
428c2ecf20Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(intf);
438c2ecf20Sopenharmony_ci	struct mt76x02_dev *dev;
448c2ecf20Sopenharmony_ci	struct mt76_dev *mdev;
458c2ecf20Sopenharmony_ci	int err;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	mdev = mt76_alloc_device(&intf->dev, sizeof(*dev), &mt76x2u_ops,
488c2ecf20Sopenharmony_ci				 &drv_ops);
498c2ecf20Sopenharmony_ci	if (!mdev)
508c2ecf20Sopenharmony_ci		return -ENOMEM;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	dev = container_of(mdev, struct mt76x02_dev, mt76);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	udev = usb_get_dev(udev);
558c2ecf20Sopenharmony_ci	usb_reset_device(udev);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	usb_set_intfdata(intf, dev);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	mt76x02u_init_mcu(mdev);
608c2ecf20Sopenharmony_ci	err = mt76u_init(mdev, intf, false);
618c2ecf20Sopenharmony_ci	if (err < 0)
628c2ecf20Sopenharmony_ci		goto err;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	mdev->rev = mt76_rr(dev, MT_ASIC_VERSION);
658c2ecf20Sopenharmony_ci	dev_info(mdev->dev, "ASIC revision: %08x\n", mdev->rev);
668c2ecf20Sopenharmony_ci	if (!is_mt76x2(dev)) {
678c2ecf20Sopenharmony_ci		err = -ENODEV;
688c2ecf20Sopenharmony_ci		goto err;
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	err = mt76x2u_register_device(dev);
728c2ecf20Sopenharmony_ci	if (err < 0)
738c2ecf20Sopenharmony_ci		goto err;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	return 0;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cierr:
788c2ecf20Sopenharmony_ci	mt76_free_device(&dev->mt76);
798c2ecf20Sopenharmony_ci	usb_set_intfdata(intf, NULL);
808c2ecf20Sopenharmony_ci	usb_put_dev(udev);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	return err;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic void mt76x2u_disconnect(struct usb_interface *intf)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(intf);
888c2ecf20Sopenharmony_ci	struct mt76x02_dev *dev = usb_get_intfdata(intf);
898c2ecf20Sopenharmony_ci	struct ieee80211_hw *hw = mt76_hw(dev);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	set_bit(MT76_REMOVED, &dev->mphy.state);
928c2ecf20Sopenharmony_ci	ieee80211_unregister_hw(hw);
938c2ecf20Sopenharmony_ci	mt76x2u_cleanup(dev);
948c2ecf20Sopenharmony_ci	mt76_free_device(&dev->mt76);
958c2ecf20Sopenharmony_ci	usb_set_intfdata(intf, NULL);
968c2ecf20Sopenharmony_ci	usb_put_dev(udev);
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic int __maybe_unused mt76x2u_suspend(struct usb_interface *intf,
1008c2ecf20Sopenharmony_ci					  pm_message_t state)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	struct mt76x02_dev *dev = usb_get_intfdata(intf);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	mt76u_stop_rx(&dev->mt76);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	return 0;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic int __maybe_unused mt76x2u_resume(struct usb_interface *intf)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	struct mt76x02_dev *dev = usb_get_intfdata(intf);
1128c2ecf20Sopenharmony_ci	int err;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	err = mt76u_resume_rx(&dev->mt76);
1158c2ecf20Sopenharmony_ci	if (err < 0)
1168c2ecf20Sopenharmony_ci		goto err;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	err = mt76x2u_init_hardware(dev);
1198c2ecf20Sopenharmony_ci	if (err < 0)
1208c2ecf20Sopenharmony_ci		goto err;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	return 0;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cierr:
1258c2ecf20Sopenharmony_ci	mt76x2u_cleanup(dev);
1268c2ecf20Sopenharmony_ci	return err;
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, mt76x2u_device_table);
1308c2ecf20Sopenharmony_ciMODULE_FIRMWARE(MT7662_FIRMWARE);
1318c2ecf20Sopenharmony_ciMODULE_FIRMWARE(MT7662_ROM_PATCH);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic struct usb_driver mt76x2u_driver = {
1348c2ecf20Sopenharmony_ci	.name		= KBUILD_MODNAME,
1358c2ecf20Sopenharmony_ci	.id_table	= mt76x2u_device_table,
1368c2ecf20Sopenharmony_ci	.probe		= mt76x2u_probe,
1378c2ecf20Sopenharmony_ci	.disconnect	= mt76x2u_disconnect,
1388c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
1398c2ecf20Sopenharmony_ci	.suspend	= mt76x2u_suspend,
1408c2ecf20Sopenharmony_ci	.resume		= mt76x2u_resume,
1418c2ecf20Sopenharmony_ci	.reset_resume	= mt76x2u_resume,
1428c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */
1438c2ecf20Sopenharmony_ci	.soft_unbind	= 1,
1448c2ecf20Sopenharmony_ci	.disable_hub_initiated_lpm = 1,
1458c2ecf20Sopenharmony_ci};
1468c2ecf20Sopenharmony_cimodule_usb_driver(mt76x2u_driver);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>");
1498c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
150