162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include "mt76.h"
862306a36Sopenharmony_ci#include "usb_trace.h"
962306a36Sopenharmony_ci#include "dma.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define MT_VEND_REQ_MAX_RETRY	10
1262306a36Sopenharmony_ci#define MT_VEND_REQ_TOUT_MS	300
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic bool disable_usb_sg;
1562306a36Sopenharmony_cimodule_param_named(disable_usb_sg, disable_usb_sg, bool, 0644);
1662306a36Sopenharmony_ciMODULE_PARM_DESC(disable_usb_sg, "Disable usb scatter-gather support");
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ciint __mt76u_vendor_request(struct mt76_dev *dev, u8 req, u8 req_type,
1962306a36Sopenharmony_ci			   u16 val, u16 offset, void *buf, size_t len)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	struct usb_interface *uintf = to_usb_interface(dev->dev);
2262306a36Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(uintf);
2362306a36Sopenharmony_ci	unsigned int pipe;
2462306a36Sopenharmony_ci	int i, ret;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	lockdep_assert_held(&dev->usb.usb_ctrl_mtx);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	pipe = (req_type & USB_DIR_IN) ? usb_rcvctrlpipe(udev, 0)
2962306a36Sopenharmony_ci				       : usb_sndctrlpipe(udev, 0);
3062306a36Sopenharmony_ci	for (i = 0; i < MT_VEND_REQ_MAX_RETRY; i++) {
3162306a36Sopenharmony_ci		if (test_bit(MT76_REMOVED, &dev->phy.state))
3262306a36Sopenharmony_ci			return -EIO;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci		ret = usb_control_msg(udev, pipe, req, req_type, val,
3562306a36Sopenharmony_ci				      offset, buf, len, MT_VEND_REQ_TOUT_MS);
3662306a36Sopenharmony_ci		if (ret == -ENODEV)
3762306a36Sopenharmony_ci			set_bit(MT76_REMOVED, &dev->phy.state);
3862306a36Sopenharmony_ci		if (ret >= 0 || ret == -ENODEV)
3962306a36Sopenharmony_ci			return ret;
4062306a36Sopenharmony_ci		usleep_range(5000, 10000);
4162306a36Sopenharmony_ci	}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	dev_err(dev->dev, "vendor request req:%02x off:%04x failed:%d\n",
4462306a36Sopenharmony_ci		req, offset, ret);
4562306a36Sopenharmony_ci	return ret;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__mt76u_vendor_request);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ciint mt76u_vendor_request(struct mt76_dev *dev, u8 req,
5062306a36Sopenharmony_ci			 u8 req_type, u16 val, u16 offset,
5162306a36Sopenharmony_ci			 void *buf, size_t len)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	int ret;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	mutex_lock(&dev->usb.usb_ctrl_mtx);
5662306a36Sopenharmony_ci	ret = __mt76u_vendor_request(dev, req, req_type,
5762306a36Sopenharmony_ci				     val, offset, buf, len);
5862306a36Sopenharmony_ci	trace_usb_reg_wr(dev, offset, val);
5962306a36Sopenharmony_ci	mutex_unlock(&dev->usb.usb_ctrl_mtx);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	return ret;
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76u_vendor_request);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ciu32 ___mt76u_rr(struct mt76_dev *dev, u8 req, u8 req_type, u32 addr)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	struct mt76_usb *usb = &dev->usb;
6862306a36Sopenharmony_ci	u32 data = ~0;
6962306a36Sopenharmony_ci	int ret;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	ret = __mt76u_vendor_request(dev, req, req_type, addr >> 16,
7262306a36Sopenharmony_ci				     addr, usb->data, sizeof(__le32));
7362306a36Sopenharmony_ci	if (ret == sizeof(__le32))
7462306a36Sopenharmony_ci		data = get_unaligned_le32(usb->data);
7562306a36Sopenharmony_ci	trace_usb_reg_rr(dev, addr, data);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return data;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(___mt76u_rr);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic u32 __mt76u_rr(struct mt76_dev *dev, u32 addr)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	u8 req;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	switch (addr & MT_VEND_TYPE_MASK) {
8662306a36Sopenharmony_ci	case MT_VEND_TYPE_EEPROM:
8762306a36Sopenharmony_ci		req = MT_VEND_READ_EEPROM;
8862306a36Sopenharmony_ci		break;
8962306a36Sopenharmony_ci	case MT_VEND_TYPE_CFG:
9062306a36Sopenharmony_ci		req = MT_VEND_READ_CFG;
9162306a36Sopenharmony_ci		break;
9262306a36Sopenharmony_ci	default:
9362306a36Sopenharmony_ci		req = MT_VEND_MULTI_READ;
9462306a36Sopenharmony_ci		break;
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	return ___mt76u_rr(dev, req, USB_DIR_IN | USB_TYPE_VENDOR,
9862306a36Sopenharmony_ci			   addr & ~MT_VEND_TYPE_MASK);
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic u32 mt76u_rr(struct mt76_dev *dev, u32 addr)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	u32 ret;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	mutex_lock(&dev->usb.usb_ctrl_mtx);
10662306a36Sopenharmony_ci	ret = __mt76u_rr(dev, addr);
10762306a36Sopenharmony_ci	mutex_unlock(&dev->usb.usb_ctrl_mtx);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	return ret;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_civoid ___mt76u_wr(struct mt76_dev *dev, u8 req, u8 req_type,
11362306a36Sopenharmony_ci		 u32 addr, u32 val)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	struct mt76_usb *usb = &dev->usb;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	put_unaligned_le32(val, usb->data);
11862306a36Sopenharmony_ci	__mt76u_vendor_request(dev, req, req_type, addr >> 16,
11962306a36Sopenharmony_ci			       addr, usb->data, sizeof(__le32));
12062306a36Sopenharmony_ci	trace_usb_reg_wr(dev, addr, val);
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(___mt76u_wr);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic void __mt76u_wr(struct mt76_dev *dev, u32 addr, u32 val)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	u8 req;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	switch (addr & MT_VEND_TYPE_MASK) {
12962306a36Sopenharmony_ci	case MT_VEND_TYPE_CFG:
13062306a36Sopenharmony_ci		req = MT_VEND_WRITE_CFG;
13162306a36Sopenharmony_ci		break;
13262306a36Sopenharmony_ci	default:
13362306a36Sopenharmony_ci		req = MT_VEND_MULTI_WRITE;
13462306a36Sopenharmony_ci		break;
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci	___mt76u_wr(dev, req, USB_DIR_OUT | USB_TYPE_VENDOR,
13762306a36Sopenharmony_ci		    addr & ~MT_VEND_TYPE_MASK, val);
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistatic void mt76u_wr(struct mt76_dev *dev, u32 addr, u32 val)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	mutex_lock(&dev->usb.usb_ctrl_mtx);
14362306a36Sopenharmony_ci	__mt76u_wr(dev, addr, val);
14462306a36Sopenharmony_ci	mutex_unlock(&dev->usb.usb_ctrl_mtx);
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic u32 mt76u_rmw(struct mt76_dev *dev, u32 addr,
14862306a36Sopenharmony_ci		     u32 mask, u32 val)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	mutex_lock(&dev->usb.usb_ctrl_mtx);
15162306a36Sopenharmony_ci	val |= __mt76u_rr(dev, addr) & ~mask;
15262306a36Sopenharmony_ci	__mt76u_wr(dev, addr, val);
15362306a36Sopenharmony_ci	mutex_unlock(&dev->usb.usb_ctrl_mtx);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	return val;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic void mt76u_copy(struct mt76_dev *dev, u32 offset,
15962306a36Sopenharmony_ci		       const void *data, int len)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	struct mt76_usb *usb = &dev->usb;
16262306a36Sopenharmony_ci	const u8 *val = data;
16362306a36Sopenharmony_ci	int ret;
16462306a36Sopenharmony_ci	int current_batch_size;
16562306a36Sopenharmony_ci	int i = 0;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/* Assure that always a multiple of 4 bytes are copied,
16862306a36Sopenharmony_ci	 * otherwise beacons can be corrupted.
16962306a36Sopenharmony_ci	 * See: "mt76: round up length on mt76_wr_copy"
17062306a36Sopenharmony_ci	 * Commit 850e8f6fbd5d0003b0
17162306a36Sopenharmony_ci	 */
17262306a36Sopenharmony_ci	len = round_up(len, 4);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	mutex_lock(&usb->usb_ctrl_mtx);
17562306a36Sopenharmony_ci	while (i < len) {
17662306a36Sopenharmony_ci		current_batch_size = min_t(int, usb->data_len, len - i);
17762306a36Sopenharmony_ci		memcpy(usb->data, val + i, current_batch_size);
17862306a36Sopenharmony_ci		ret = __mt76u_vendor_request(dev, MT_VEND_MULTI_WRITE,
17962306a36Sopenharmony_ci					     USB_DIR_OUT | USB_TYPE_VENDOR,
18062306a36Sopenharmony_ci					     0, offset + i, usb->data,
18162306a36Sopenharmony_ci					     current_batch_size);
18262306a36Sopenharmony_ci		if (ret < 0)
18362306a36Sopenharmony_ci			break;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci		i += current_batch_size;
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci	mutex_unlock(&usb->usb_ctrl_mtx);
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_civoid mt76u_read_copy(struct mt76_dev *dev, u32 offset,
19162306a36Sopenharmony_ci		     void *data, int len)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	struct mt76_usb *usb = &dev->usb;
19462306a36Sopenharmony_ci	int i = 0, batch_len, ret;
19562306a36Sopenharmony_ci	u8 *val = data;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	len = round_up(len, 4);
19862306a36Sopenharmony_ci	mutex_lock(&usb->usb_ctrl_mtx);
19962306a36Sopenharmony_ci	while (i < len) {
20062306a36Sopenharmony_ci		batch_len = min_t(int, usb->data_len, len - i);
20162306a36Sopenharmony_ci		ret = __mt76u_vendor_request(dev, MT_VEND_READ_EXT,
20262306a36Sopenharmony_ci					     USB_DIR_IN | USB_TYPE_VENDOR,
20362306a36Sopenharmony_ci					     (offset + i) >> 16, offset + i,
20462306a36Sopenharmony_ci					     usb->data, batch_len);
20562306a36Sopenharmony_ci		if (ret < 0)
20662306a36Sopenharmony_ci			break;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci		memcpy(val + i, usb->data, batch_len);
20962306a36Sopenharmony_ci		i += batch_len;
21062306a36Sopenharmony_ci	}
21162306a36Sopenharmony_ci	mutex_unlock(&usb->usb_ctrl_mtx);
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76u_read_copy);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_civoid mt76u_single_wr(struct mt76_dev *dev, const u8 req,
21662306a36Sopenharmony_ci		     const u16 offset, const u32 val)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	mutex_lock(&dev->usb.usb_ctrl_mtx);
21962306a36Sopenharmony_ci	__mt76u_vendor_request(dev, req,
22062306a36Sopenharmony_ci			       USB_DIR_OUT | USB_TYPE_VENDOR,
22162306a36Sopenharmony_ci			       val & 0xffff, offset, NULL, 0);
22262306a36Sopenharmony_ci	__mt76u_vendor_request(dev, req,
22362306a36Sopenharmony_ci			       USB_DIR_OUT | USB_TYPE_VENDOR,
22462306a36Sopenharmony_ci			       val >> 16, offset + 2, NULL, 0);
22562306a36Sopenharmony_ci	mutex_unlock(&dev->usb.usb_ctrl_mtx);
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76u_single_wr);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic int
23062306a36Sopenharmony_cimt76u_req_wr_rp(struct mt76_dev *dev, u32 base,
23162306a36Sopenharmony_ci		const struct mt76_reg_pair *data, int len)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	struct mt76_usb *usb = &dev->usb;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	mutex_lock(&usb->usb_ctrl_mtx);
23662306a36Sopenharmony_ci	while (len > 0) {
23762306a36Sopenharmony_ci		__mt76u_wr(dev, base + data->reg, data->value);
23862306a36Sopenharmony_ci		len--;
23962306a36Sopenharmony_ci		data++;
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci	mutex_unlock(&usb->usb_ctrl_mtx);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	return 0;
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic int
24762306a36Sopenharmony_cimt76u_wr_rp(struct mt76_dev *dev, u32 base,
24862306a36Sopenharmony_ci	    const struct mt76_reg_pair *data, int n)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	if (test_bit(MT76_STATE_MCU_RUNNING, &dev->phy.state))
25162306a36Sopenharmony_ci		return dev->mcu_ops->mcu_wr_rp(dev, base, data, n);
25262306a36Sopenharmony_ci	else
25362306a36Sopenharmony_ci		return mt76u_req_wr_rp(dev, base, data, n);
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic int
25762306a36Sopenharmony_cimt76u_req_rd_rp(struct mt76_dev *dev, u32 base, struct mt76_reg_pair *data,
25862306a36Sopenharmony_ci		int len)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct mt76_usb *usb = &dev->usb;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	mutex_lock(&usb->usb_ctrl_mtx);
26362306a36Sopenharmony_ci	while (len > 0) {
26462306a36Sopenharmony_ci		data->value = __mt76u_rr(dev, base + data->reg);
26562306a36Sopenharmony_ci		len--;
26662306a36Sopenharmony_ci		data++;
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci	mutex_unlock(&usb->usb_ctrl_mtx);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return 0;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic int
27462306a36Sopenharmony_cimt76u_rd_rp(struct mt76_dev *dev, u32 base,
27562306a36Sopenharmony_ci	    struct mt76_reg_pair *data, int n)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	if (test_bit(MT76_STATE_MCU_RUNNING, &dev->phy.state))
27862306a36Sopenharmony_ci		return dev->mcu_ops->mcu_rd_rp(dev, base, data, n);
27962306a36Sopenharmony_ci	else
28062306a36Sopenharmony_ci		return mt76u_req_rd_rp(dev, base, data, n);
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic bool mt76u_check_sg(struct mt76_dev *dev)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	struct usb_interface *uintf = to_usb_interface(dev->dev);
28662306a36Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(uintf);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	return (!disable_usb_sg && udev->bus->sg_tablesize > 0 &&
28962306a36Sopenharmony_ci		udev->bus->no_sg_constraint);
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic int
29362306a36Sopenharmony_cimt76u_set_endpoints(struct usb_interface *intf,
29462306a36Sopenharmony_ci		    struct mt76_usb *usb)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	struct usb_host_interface *intf_desc = intf->cur_altsetting;
29762306a36Sopenharmony_ci	struct usb_endpoint_descriptor *ep_desc;
29862306a36Sopenharmony_ci	int i, in_ep = 0, out_ep = 0;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) {
30162306a36Sopenharmony_ci		ep_desc = &intf_desc->endpoint[i].desc;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci		if (usb_endpoint_is_bulk_in(ep_desc) &&
30462306a36Sopenharmony_ci		    in_ep < __MT_EP_IN_MAX) {
30562306a36Sopenharmony_ci			usb->in_ep[in_ep] = usb_endpoint_num(ep_desc);
30662306a36Sopenharmony_ci			in_ep++;
30762306a36Sopenharmony_ci		} else if (usb_endpoint_is_bulk_out(ep_desc) &&
30862306a36Sopenharmony_ci			   out_ep < __MT_EP_OUT_MAX) {
30962306a36Sopenharmony_ci			usb->out_ep[out_ep] = usb_endpoint_num(ep_desc);
31062306a36Sopenharmony_ci			out_ep++;
31162306a36Sopenharmony_ci		}
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	if (in_ep != __MT_EP_IN_MAX || out_ep != __MT_EP_OUT_MAX)
31562306a36Sopenharmony_ci		return -EINVAL;
31662306a36Sopenharmony_ci	return 0;
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic int
32062306a36Sopenharmony_cimt76u_fill_rx_sg(struct mt76_dev *dev, struct mt76_queue *q, struct urb *urb,
32162306a36Sopenharmony_ci		 int nsgs)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	int i;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	for (i = 0; i < nsgs; i++) {
32662306a36Sopenharmony_ci		void *data;
32762306a36Sopenharmony_ci		int offset;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci		data = mt76_get_page_pool_buf(q, &offset, q->buf_size);
33062306a36Sopenharmony_ci		if (!data)
33162306a36Sopenharmony_ci			break;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci		sg_set_page(&urb->sg[i], virt_to_head_page(data), q->buf_size,
33462306a36Sopenharmony_ci			    offset);
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	if (i < nsgs) {
33862306a36Sopenharmony_ci		int j;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci		for (j = nsgs; j < urb->num_sgs; j++)
34162306a36Sopenharmony_ci			mt76_put_page_pool_buf(sg_virt(&urb->sg[j]), false);
34262306a36Sopenharmony_ci		urb->num_sgs = i;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	urb->num_sgs = max_t(int, i, urb->num_sgs);
34662306a36Sopenharmony_ci	urb->transfer_buffer_length = urb->num_sgs * q->buf_size;
34762306a36Sopenharmony_ci	sg_init_marker(urb->sg, urb->num_sgs);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	return i ? : -ENOMEM;
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic int
35362306a36Sopenharmony_cimt76u_refill_rx(struct mt76_dev *dev, struct mt76_queue *q,
35462306a36Sopenharmony_ci		struct urb *urb, int nsgs)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	enum mt76_rxq_id qid = q - &dev->q_rx[MT_RXQ_MAIN];
35762306a36Sopenharmony_ci	int offset;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (qid == MT_RXQ_MAIN && dev->usb.sg_en)
36062306a36Sopenharmony_ci		return mt76u_fill_rx_sg(dev, q, urb, nsgs);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	urb->transfer_buffer_length = q->buf_size;
36362306a36Sopenharmony_ci	urb->transfer_buffer = mt76_get_page_pool_buf(q, &offset, q->buf_size);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	return urb->transfer_buffer ? 0 : -ENOMEM;
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cistatic int
36962306a36Sopenharmony_cimt76u_urb_alloc(struct mt76_dev *dev, struct mt76_queue_entry *e,
37062306a36Sopenharmony_ci		int sg_max_size)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	unsigned int size = sizeof(struct urb);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if (dev->usb.sg_en)
37562306a36Sopenharmony_ci		size += sg_max_size * sizeof(struct scatterlist);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	e->urb = kzalloc(size, GFP_KERNEL);
37862306a36Sopenharmony_ci	if (!e->urb)
37962306a36Sopenharmony_ci		return -ENOMEM;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	usb_init_urb(e->urb);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	if (dev->usb.sg_en && sg_max_size > 0)
38462306a36Sopenharmony_ci		e->urb->sg = (struct scatterlist *)(e->urb + 1);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	return 0;
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cistatic int
39062306a36Sopenharmony_cimt76u_rx_urb_alloc(struct mt76_dev *dev, struct mt76_queue *q,
39162306a36Sopenharmony_ci		   struct mt76_queue_entry *e)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	enum mt76_rxq_id qid = q - &dev->q_rx[MT_RXQ_MAIN];
39462306a36Sopenharmony_ci	int err, sg_size;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	sg_size = qid == MT_RXQ_MAIN ? MT_RX_SG_MAX_SIZE : 0;
39762306a36Sopenharmony_ci	err = mt76u_urb_alloc(dev, e, sg_size);
39862306a36Sopenharmony_ci	if (err)
39962306a36Sopenharmony_ci		return err;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	return mt76u_refill_rx(dev, q, e->urb, sg_size);
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic void mt76u_urb_free(struct urb *urb)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	int i;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	for (i = 0; i < urb->num_sgs; i++)
40962306a36Sopenharmony_ci		mt76_put_page_pool_buf(sg_virt(&urb->sg[i]), false);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	if (urb->transfer_buffer)
41262306a36Sopenharmony_ci		mt76_put_page_pool_buf(urb->transfer_buffer, false);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	usb_free_urb(urb);
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic void
41862306a36Sopenharmony_cimt76u_fill_bulk_urb(struct mt76_dev *dev, int dir, int index,
41962306a36Sopenharmony_ci		    struct urb *urb, usb_complete_t complete_fn,
42062306a36Sopenharmony_ci		    void *context)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	struct usb_interface *uintf = to_usb_interface(dev->dev);
42362306a36Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(uintf);
42462306a36Sopenharmony_ci	unsigned int pipe;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	if (dir == USB_DIR_IN)
42762306a36Sopenharmony_ci		pipe = usb_rcvbulkpipe(udev, dev->usb.in_ep[index]);
42862306a36Sopenharmony_ci	else
42962306a36Sopenharmony_ci		pipe = usb_sndbulkpipe(udev, dev->usb.out_ep[index]);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	urb->dev = udev;
43262306a36Sopenharmony_ci	urb->pipe = pipe;
43362306a36Sopenharmony_ci	urb->complete = complete_fn;
43462306a36Sopenharmony_ci	urb->context = context;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_cistatic struct urb *
43862306a36Sopenharmony_cimt76u_get_next_rx_entry(struct mt76_queue *q)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	struct urb *urb = NULL;
44162306a36Sopenharmony_ci	unsigned long flags;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	spin_lock_irqsave(&q->lock, flags);
44462306a36Sopenharmony_ci	if (q->queued > 0) {
44562306a36Sopenharmony_ci		urb = q->entry[q->tail].urb;
44662306a36Sopenharmony_ci		q->tail = (q->tail + 1) % q->ndesc;
44762306a36Sopenharmony_ci		q->queued--;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci	spin_unlock_irqrestore(&q->lock, flags);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	return urb;
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_cistatic int
45562306a36Sopenharmony_cimt76u_get_rx_entry_len(struct mt76_dev *dev, u8 *data,
45662306a36Sopenharmony_ci		       u32 data_len)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	u16 dma_len, min_len;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	dma_len = get_unaligned_le16(data);
46162306a36Sopenharmony_ci	if (dev->drv->drv_flags & MT_DRV_RX_DMA_HDR)
46262306a36Sopenharmony_ci		return dma_len;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	min_len = MT_DMA_HDR_LEN + MT_RX_RXWI_LEN + MT_FCE_INFO_LEN;
46562306a36Sopenharmony_ci	if (data_len < min_len || !dma_len ||
46662306a36Sopenharmony_ci	    dma_len + MT_DMA_HDR_LEN > data_len ||
46762306a36Sopenharmony_ci	    (dma_len & 0x3))
46862306a36Sopenharmony_ci		return -EINVAL;
46962306a36Sopenharmony_ci	return dma_len;
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic struct sk_buff *
47362306a36Sopenharmony_cimt76u_build_rx_skb(struct mt76_dev *dev, void *data,
47462306a36Sopenharmony_ci		   int len, int buf_size)
47562306a36Sopenharmony_ci{
47662306a36Sopenharmony_ci	int head_room, drv_flags = dev->drv->drv_flags;
47762306a36Sopenharmony_ci	struct sk_buff *skb;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	head_room = drv_flags & MT_DRV_RX_DMA_HDR ? 0 : MT_DMA_HDR_LEN;
48062306a36Sopenharmony_ci	if (SKB_WITH_OVERHEAD(buf_size) < head_room + len) {
48162306a36Sopenharmony_ci		struct page *page;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci		/* slow path, not enough space for data and
48462306a36Sopenharmony_ci		 * skb_shared_info
48562306a36Sopenharmony_ci		 */
48662306a36Sopenharmony_ci		skb = alloc_skb(MT_SKB_HEAD_LEN, GFP_ATOMIC);
48762306a36Sopenharmony_ci		if (!skb)
48862306a36Sopenharmony_ci			return NULL;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci		skb_put_data(skb, data + head_room, MT_SKB_HEAD_LEN);
49162306a36Sopenharmony_ci		data += head_room + MT_SKB_HEAD_LEN;
49262306a36Sopenharmony_ci		page = virt_to_head_page(data);
49362306a36Sopenharmony_ci		skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
49462306a36Sopenharmony_ci				page, data - page_address(page),
49562306a36Sopenharmony_ci				len - MT_SKB_HEAD_LEN, buf_size);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci		return skb;
49862306a36Sopenharmony_ci	}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	/* fast path */
50162306a36Sopenharmony_ci	skb = build_skb(data, buf_size);
50262306a36Sopenharmony_ci	if (!skb)
50362306a36Sopenharmony_ci		return NULL;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	skb_reserve(skb, head_room);
50662306a36Sopenharmony_ci	__skb_put(skb, len);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	return skb;
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_cistatic int
51262306a36Sopenharmony_cimt76u_process_rx_entry(struct mt76_dev *dev, struct urb *urb,
51362306a36Sopenharmony_ci		       int buf_size)
51462306a36Sopenharmony_ci{
51562306a36Sopenharmony_ci	u8 *data = urb->num_sgs ? sg_virt(&urb->sg[0]) : urb->transfer_buffer;
51662306a36Sopenharmony_ci	int data_len = urb->num_sgs ? urb->sg[0].length : urb->actual_length;
51762306a36Sopenharmony_ci	int len, nsgs = 1, head_room, drv_flags = dev->drv->drv_flags;
51862306a36Sopenharmony_ci	struct sk_buff *skb;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	if (!test_bit(MT76_STATE_INITIALIZED, &dev->phy.state))
52162306a36Sopenharmony_ci		return 0;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	len = mt76u_get_rx_entry_len(dev, data, urb->actual_length);
52462306a36Sopenharmony_ci	if (len < 0)
52562306a36Sopenharmony_ci		return 0;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	head_room = drv_flags & MT_DRV_RX_DMA_HDR ? 0 : MT_DMA_HDR_LEN;
52862306a36Sopenharmony_ci	data_len = min_t(int, len, data_len - head_room);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	if (len == data_len &&
53162306a36Sopenharmony_ci	    dev->drv->rx_check && !dev->drv->rx_check(dev, data, data_len))
53262306a36Sopenharmony_ci		return 0;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	skb = mt76u_build_rx_skb(dev, data, data_len, buf_size);
53562306a36Sopenharmony_ci	if (!skb)
53662306a36Sopenharmony_ci		return 0;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	len -= data_len;
53962306a36Sopenharmony_ci	while (len > 0 && nsgs < urb->num_sgs) {
54062306a36Sopenharmony_ci		data_len = min_t(int, len, urb->sg[nsgs].length);
54162306a36Sopenharmony_ci		skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
54262306a36Sopenharmony_ci				sg_page(&urb->sg[nsgs]),
54362306a36Sopenharmony_ci				urb->sg[nsgs].offset, data_len,
54462306a36Sopenharmony_ci				buf_size);
54562306a36Sopenharmony_ci		len -= data_len;
54662306a36Sopenharmony_ci		nsgs++;
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	skb_mark_for_recycle(skb);
55062306a36Sopenharmony_ci	dev->drv->rx_skb(dev, MT_RXQ_MAIN, skb, NULL);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	return nsgs;
55362306a36Sopenharmony_ci}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_cistatic void mt76u_complete_rx(struct urb *urb)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci	struct mt76_dev *dev = dev_get_drvdata(&urb->dev->dev);
55862306a36Sopenharmony_ci	struct mt76_queue *q = urb->context;
55962306a36Sopenharmony_ci	unsigned long flags;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	trace_rx_urb(dev, urb);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	switch (urb->status) {
56462306a36Sopenharmony_ci	case -ECONNRESET:
56562306a36Sopenharmony_ci	case -ESHUTDOWN:
56662306a36Sopenharmony_ci	case -ENOENT:
56762306a36Sopenharmony_ci	case -EPROTO:
56862306a36Sopenharmony_ci		return;
56962306a36Sopenharmony_ci	default:
57062306a36Sopenharmony_ci		dev_err_ratelimited(dev->dev, "rx urb failed: %d\n",
57162306a36Sopenharmony_ci				    urb->status);
57262306a36Sopenharmony_ci		fallthrough;
57362306a36Sopenharmony_ci	case 0:
57462306a36Sopenharmony_ci		break;
57562306a36Sopenharmony_ci	}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	spin_lock_irqsave(&q->lock, flags);
57862306a36Sopenharmony_ci	if (WARN_ONCE(q->entry[q->head].urb != urb, "rx urb mismatch"))
57962306a36Sopenharmony_ci		goto out;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	q->head = (q->head + 1) % q->ndesc;
58262306a36Sopenharmony_ci	q->queued++;
58362306a36Sopenharmony_ci	mt76_worker_schedule(&dev->usb.rx_worker);
58462306a36Sopenharmony_ciout:
58562306a36Sopenharmony_ci	spin_unlock_irqrestore(&q->lock, flags);
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_cistatic int
58962306a36Sopenharmony_cimt76u_submit_rx_buf(struct mt76_dev *dev, enum mt76_rxq_id qid,
59062306a36Sopenharmony_ci		    struct urb *urb)
59162306a36Sopenharmony_ci{
59262306a36Sopenharmony_ci	int ep = qid == MT_RXQ_MAIN ? MT_EP_IN_PKT_RX : MT_EP_IN_CMD_RESP;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	mt76u_fill_bulk_urb(dev, USB_DIR_IN, ep, urb,
59562306a36Sopenharmony_ci			    mt76u_complete_rx, &dev->q_rx[qid]);
59662306a36Sopenharmony_ci	trace_submit_urb(dev, urb);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	return usb_submit_urb(urb, GFP_ATOMIC);
59962306a36Sopenharmony_ci}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_cistatic void
60262306a36Sopenharmony_cimt76u_process_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci	int qid = q - &dev->q_rx[MT_RXQ_MAIN];
60562306a36Sopenharmony_ci	struct urb *urb;
60662306a36Sopenharmony_ci	int err, count;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	while (true) {
60962306a36Sopenharmony_ci		urb = mt76u_get_next_rx_entry(q);
61062306a36Sopenharmony_ci		if (!urb)
61162306a36Sopenharmony_ci			break;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci		count = mt76u_process_rx_entry(dev, urb, q->buf_size);
61462306a36Sopenharmony_ci		if (count > 0) {
61562306a36Sopenharmony_ci			err = mt76u_refill_rx(dev, q, urb, count);
61662306a36Sopenharmony_ci			if (err < 0)
61762306a36Sopenharmony_ci				break;
61862306a36Sopenharmony_ci		}
61962306a36Sopenharmony_ci		mt76u_submit_rx_buf(dev, qid, urb);
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci	if (qid == MT_RXQ_MAIN) {
62262306a36Sopenharmony_ci		local_bh_disable();
62362306a36Sopenharmony_ci		mt76_rx_poll_complete(dev, MT_RXQ_MAIN, NULL);
62462306a36Sopenharmony_ci		local_bh_enable();
62562306a36Sopenharmony_ci	}
62662306a36Sopenharmony_ci}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_cistatic void mt76u_rx_worker(struct mt76_worker *w)
62962306a36Sopenharmony_ci{
63062306a36Sopenharmony_ci	struct mt76_usb *usb = container_of(w, struct mt76_usb, rx_worker);
63162306a36Sopenharmony_ci	struct mt76_dev *dev = container_of(usb, struct mt76_dev, usb);
63262306a36Sopenharmony_ci	int i;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	rcu_read_lock();
63562306a36Sopenharmony_ci	mt76_for_each_q_rx(dev, i)
63662306a36Sopenharmony_ci		mt76u_process_rx_queue(dev, &dev->q_rx[i]);
63762306a36Sopenharmony_ci	rcu_read_unlock();
63862306a36Sopenharmony_ci}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_cistatic int
64162306a36Sopenharmony_cimt76u_submit_rx_buffers(struct mt76_dev *dev, enum mt76_rxq_id qid)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	struct mt76_queue *q = &dev->q_rx[qid];
64462306a36Sopenharmony_ci	unsigned long flags;
64562306a36Sopenharmony_ci	int i, err = 0;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	spin_lock_irqsave(&q->lock, flags);
64862306a36Sopenharmony_ci	for (i = 0; i < q->ndesc; i++) {
64962306a36Sopenharmony_ci		err = mt76u_submit_rx_buf(dev, qid, q->entry[i].urb);
65062306a36Sopenharmony_ci		if (err < 0)
65162306a36Sopenharmony_ci			break;
65262306a36Sopenharmony_ci	}
65362306a36Sopenharmony_ci	q->head = q->tail = 0;
65462306a36Sopenharmony_ci	q->queued = 0;
65562306a36Sopenharmony_ci	spin_unlock_irqrestore(&q->lock, flags);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	return err;
65862306a36Sopenharmony_ci}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_cistatic int
66162306a36Sopenharmony_cimt76u_alloc_rx_queue(struct mt76_dev *dev, enum mt76_rxq_id qid)
66262306a36Sopenharmony_ci{
66362306a36Sopenharmony_ci	struct mt76_queue *q = &dev->q_rx[qid];
66462306a36Sopenharmony_ci	int i, err;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	err = mt76_create_page_pool(dev, q);
66762306a36Sopenharmony_ci	if (err)
66862306a36Sopenharmony_ci		return err;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	spin_lock_init(&q->lock);
67162306a36Sopenharmony_ci	q->entry = devm_kcalloc(dev->dev,
67262306a36Sopenharmony_ci				MT_NUM_RX_ENTRIES, sizeof(*q->entry),
67362306a36Sopenharmony_ci				GFP_KERNEL);
67462306a36Sopenharmony_ci	if (!q->entry)
67562306a36Sopenharmony_ci		return -ENOMEM;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	q->ndesc = MT_NUM_RX_ENTRIES;
67862306a36Sopenharmony_ci	q->buf_size = PAGE_SIZE;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	for (i = 0; i < q->ndesc; i++) {
68162306a36Sopenharmony_ci		err = mt76u_rx_urb_alloc(dev, q, &q->entry[i]);
68262306a36Sopenharmony_ci		if (err < 0)
68362306a36Sopenharmony_ci			return err;
68462306a36Sopenharmony_ci	}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	return mt76u_submit_rx_buffers(dev, qid);
68762306a36Sopenharmony_ci}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ciint mt76u_alloc_mcu_queue(struct mt76_dev *dev)
69062306a36Sopenharmony_ci{
69162306a36Sopenharmony_ci	return mt76u_alloc_rx_queue(dev, MT_RXQ_MCU);
69262306a36Sopenharmony_ci}
69362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76u_alloc_mcu_queue);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_cistatic void
69662306a36Sopenharmony_cimt76u_free_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
69762306a36Sopenharmony_ci{
69862306a36Sopenharmony_ci	int i;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	for (i = 0; i < q->ndesc; i++) {
70162306a36Sopenharmony_ci		if (!q->entry[i].urb)
70262306a36Sopenharmony_ci			continue;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci		mt76u_urb_free(q->entry[i].urb);
70562306a36Sopenharmony_ci		q->entry[i].urb = NULL;
70662306a36Sopenharmony_ci	}
70762306a36Sopenharmony_ci	page_pool_destroy(q->page_pool);
70862306a36Sopenharmony_ci	q->page_pool = NULL;
70962306a36Sopenharmony_ci}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_cistatic void mt76u_free_rx(struct mt76_dev *dev)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	int i;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	mt76_worker_teardown(&dev->usb.rx_worker);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	mt76_for_each_q_rx(dev, i)
71862306a36Sopenharmony_ci		mt76u_free_rx_queue(dev, &dev->q_rx[i]);
71962306a36Sopenharmony_ci}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_civoid mt76u_stop_rx(struct mt76_dev *dev)
72262306a36Sopenharmony_ci{
72362306a36Sopenharmony_ci	int i;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	mt76_worker_disable(&dev->usb.rx_worker);
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	mt76_for_each_q_rx(dev, i) {
72862306a36Sopenharmony_ci		struct mt76_queue *q = &dev->q_rx[i];
72962306a36Sopenharmony_ci		int j;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci		for (j = 0; j < q->ndesc; j++)
73262306a36Sopenharmony_ci			usb_poison_urb(q->entry[j].urb);
73362306a36Sopenharmony_ci	}
73462306a36Sopenharmony_ci}
73562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76u_stop_rx);
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ciint mt76u_resume_rx(struct mt76_dev *dev)
73862306a36Sopenharmony_ci{
73962306a36Sopenharmony_ci	int i;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	mt76_for_each_q_rx(dev, i) {
74262306a36Sopenharmony_ci		struct mt76_queue *q = &dev->q_rx[i];
74362306a36Sopenharmony_ci		int err, j;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci		for (j = 0; j < q->ndesc; j++)
74662306a36Sopenharmony_ci			usb_unpoison_urb(q->entry[j].urb);
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci		err = mt76u_submit_rx_buffers(dev, i);
74962306a36Sopenharmony_ci		if (err < 0)
75062306a36Sopenharmony_ci			return err;
75162306a36Sopenharmony_ci	}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	mt76_worker_enable(&dev->usb.rx_worker);
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	return 0;
75662306a36Sopenharmony_ci}
75762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76u_resume_rx);
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_cistatic void mt76u_status_worker(struct mt76_worker *w)
76062306a36Sopenharmony_ci{
76162306a36Sopenharmony_ci	struct mt76_usb *usb = container_of(w, struct mt76_usb, status_worker);
76262306a36Sopenharmony_ci	struct mt76_dev *dev = container_of(usb, struct mt76_dev, usb);
76362306a36Sopenharmony_ci	struct mt76_queue_entry entry;
76462306a36Sopenharmony_ci	struct mt76_queue *q;
76562306a36Sopenharmony_ci	int i;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	if (!test_bit(MT76_STATE_RUNNING, &dev->phy.state))
76862306a36Sopenharmony_ci		return;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	for (i = 0; i < IEEE80211_NUM_ACS; i++) {
77162306a36Sopenharmony_ci		q = dev->phy.q_tx[i];
77262306a36Sopenharmony_ci		if (!q)
77362306a36Sopenharmony_ci			continue;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci		while (q->queued > 0) {
77662306a36Sopenharmony_ci			if (!q->entry[q->tail].done)
77762306a36Sopenharmony_ci				break;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci			entry = q->entry[q->tail];
78062306a36Sopenharmony_ci			q->entry[q->tail].done = false;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci			mt76_queue_tx_complete(dev, q, &entry);
78362306a36Sopenharmony_ci		}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci		if (!q->queued)
78662306a36Sopenharmony_ci			wake_up(&dev->tx_wait);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci		mt76_worker_schedule(&dev->tx_worker);
78962306a36Sopenharmony_ci	}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	if (dev->drv->tx_status_data &&
79262306a36Sopenharmony_ci	    !test_and_set_bit(MT76_READING_STATS, &dev->phy.state))
79362306a36Sopenharmony_ci		queue_work(dev->wq, &dev->usb.stat_work);
79462306a36Sopenharmony_ci}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_cistatic void mt76u_tx_status_data(struct work_struct *work)
79762306a36Sopenharmony_ci{
79862306a36Sopenharmony_ci	struct mt76_usb *usb;
79962306a36Sopenharmony_ci	struct mt76_dev *dev;
80062306a36Sopenharmony_ci	u8 update = 1;
80162306a36Sopenharmony_ci	u16 count = 0;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	usb = container_of(work, struct mt76_usb, stat_work);
80462306a36Sopenharmony_ci	dev = container_of(usb, struct mt76_dev, usb);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	while (true) {
80762306a36Sopenharmony_ci		if (test_bit(MT76_REMOVED, &dev->phy.state))
80862306a36Sopenharmony_ci			break;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci		if (!dev->drv->tx_status_data(dev, &update))
81162306a36Sopenharmony_ci			break;
81262306a36Sopenharmony_ci		count++;
81362306a36Sopenharmony_ci	}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	if (count && test_bit(MT76_STATE_RUNNING, &dev->phy.state))
81662306a36Sopenharmony_ci		queue_work(dev->wq, &usb->stat_work);
81762306a36Sopenharmony_ci	else
81862306a36Sopenharmony_ci		clear_bit(MT76_READING_STATS, &dev->phy.state);
81962306a36Sopenharmony_ci}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_cistatic void mt76u_complete_tx(struct urb *urb)
82262306a36Sopenharmony_ci{
82362306a36Sopenharmony_ci	struct mt76_dev *dev = dev_get_drvdata(&urb->dev->dev);
82462306a36Sopenharmony_ci	struct mt76_queue_entry *e = urb->context;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	if (mt76u_urb_error(urb))
82762306a36Sopenharmony_ci		dev_err(dev->dev, "tx urb failed: %d\n", urb->status);
82862306a36Sopenharmony_ci	e->done = true;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	mt76_worker_schedule(&dev->usb.status_worker);
83162306a36Sopenharmony_ci}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_cistatic int
83462306a36Sopenharmony_cimt76u_tx_setup_buffers(struct mt76_dev *dev, struct sk_buff *skb,
83562306a36Sopenharmony_ci		       struct urb *urb)
83662306a36Sopenharmony_ci{
83762306a36Sopenharmony_ci	urb->transfer_buffer_length = skb->len;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	if (!dev->usb.sg_en) {
84062306a36Sopenharmony_ci		urb->transfer_buffer = skb->data;
84162306a36Sopenharmony_ci		return 0;
84262306a36Sopenharmony_ci	}
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	sg_init_table(urb->sg, MT_TX_SG_MAX_SIZE);
84562306a36Sopenharmony_ci	urb->num_sgs = skb_to_sgvec(skb, urb->sg, 0, skb->len);
84662306a36Sopenharmony_ci	if (!urb->num_sgs)
84762306a36Sopenharmony_ci		return -ENOMEM;
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	return urb->num_sgs;
85062306a36Sopenharmony_ci}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_cistatic int
85362306a36Sopenharmony_cimt76u_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q,
85462306a36Sopenharmony_ci		   enum mt76_txq_id qid, struct sk_buff *skb,
85562306a36Sopenharmony_ci		   struct mt76_wcid *wcid, struct ieee80211_sta *sta)
85662306a36Sopenharmony_ci{
85762306a36Sopenharmony_ci	struct mt76_tx_info tx_info = {
85862306a36Sopenharmony_ci		.skb = skb,
85962306a36Sopenharmony_ci	};
86062306a36Sopenharmony_ci	u16 idx = q->head;
86162306a36Sopenharmony_ci	int err;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	if (q->queued == q->ndesc)
86462306a36Sopenharmony_ci		return -ENOSPC;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	skb->prev = skb->next = NULL;
86762306a36Sopenharmony_ci	err = dev->drv->tx_prepare_skb(dev, NULL, qid, wcid, sta, &tx_info);
86862306a36Sopenharmony_ci	if (err < 0)
86962306a36Sopenharmony_ci		return err;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	err = mt76u_tx_setup_buffers(dev, tx_info.skb, q->entry[idx].urb);
87262306a36Sopenharmony_ci	if (err < 0)
87362306a36Sopenharmony_ci		return err;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	mt76u_fill_bulk_urb(dev, USB_DIR_OUT, q2ep(q->hw_idx),
87662306a36Sopenharmony_ci			    q->entry[idx].urb, mt76u_complete_tx,
87762306a36Sopenharmony_ci			    &q->entry[idx]);
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	q->head = (q->head + 1) % q->ndesc;
88062306a36Sopenharmony_ci	q->entry[idx].skb = tx_info.skb;
88162306a36Sopenharmony_ci	q->entry[idx].wcid = 0xffff;
88262306a36Sopenharmony_ci	q->queued++;
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	return idx;
88562306a36Sopenharmony_ci}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_cistatic void mt76u_tx_kick(struct mt76_dev *dev, struct mt76_queue *q)
88862306a36Sopenharmony_ci{
88962306a36Sopenharmony_ci	struct urb *urb;
89062306a36Sopenharmony_ci	int err;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	while (q->first != q->head) {
89362306a36Sopenharmony_ci		urb = q->entry[q->first].urb;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci		trace_submit_urb(dev, urb);
89662306a36Sopenharmony_ci		err = usb_submit_urb(urb, GFP_ATOMIC);
89762306a36Sopenharmony_ci		if (err < 0) {
89862306a36Sopenharmony_ci			if (err == -ENODEV)
89962306a36Sopenharmony_ci				set_bit(MT76_REMOVED, &dev->phy.state);
90062306a36Sopenharmony_ci			else
90162306a36Sopenharmony_ci				dev_err(dev->dev, "tx urb submit failed:%d\n",
90262306a36Sopenharmony_ci					err);
90362306a36Sopenharmony_ci			break;
90462306a36Sopenharmony_ci		}
90562306a36Sopenharmony_ci		q->first = (q->first + 1) % q->ndesc;
90662306a36Sopenharmony_ci	}
90762306a36Sopenharmony_ci}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_cistatic u8 mt76u_ac_to_hwq(struct mt76_dev *dev, u8 ac)
91062306a36Sopenharmony_ci{
91162306a36Sopenharmony_ci	if (mt76_chip(dev) == 0x7663) {
91262306a36Sopenharmony_ci		static const u8 lmac_queue_map[] = {
91362306a36Sopenharmony_ci			/* ac to lmac mapping */
91462306a36Sopenharmony_ci			[IEEE80211_AC_BK] = 0,
91562306a36Sopenharmony_ci			[IEEE80211_AC_BE] = 1,
91662306a36Sopenharmony_ci			[IEEE80211_AC_VI] = 2,
91762306a36Sopenharmony_ci			[IEEE80211_AC_VO] = 4,
91862306a36Sopenharmony_ci		};
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci		if (WARN_ON(ac >= ARRAY_SIZE(lmac_queue_map)))
92162306a36Sopenharmony_ci			return 1; /* BE */
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci		return lmac_queue_map[ac];
92462306a36Sopenharmony_ci	}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	return mt76_ac_to_hwq(ac);
92762306a36Sopenharmony_ci}
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_cistatic int mt76u_alloc_tx(struct mt76_dev *dev)
93062306a36Sopenharmony_ci{
93162306a36Sopenharmony_ci	struct mt76_queue *q;
93262306a36Sopenharmony_ci	int i, j, err;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	for (i = 0; i <= MT_TXQ_PSD; i++) {
93562306a36Sopenharmony_ci		if (i >= IEEE80211_NUM_ACS) {
93662306a36Sopenharmony_ci			dev->phy.q_tx[i] = dev->phy.q_tx[0];
93762306a36Sopenharmony_ci			continue;
93862306a36Sopenharmony_ci		}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci		q = devm_kzalloc(dev->dev, sizeof(*q), GFP_KERNEL);
94162306a36Sopenharmony_ci		if (!q)
94262306a36Sopenharmony_ci			return -ENOMEM;
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci		spin_lock_init(&q->lock);
94562306a36Sopenharmony_ci		q->hw_idx = mt76u_ac_to_hwq(dev, i);
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci		dev->phy.q_tx[i] = q;
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci		q->entry = devm_kcalloc(dev->dev,
95062306a36Sopenharmony_ci					MT_NUM_TX_ENTRIES, sizeof(*q->entry),
95162306a36Sopenharmony_ci					GFP_KERNEL);
95262306a36Sopenharmony_ci		if (!q->entry)
95362306a36Sopenharmony_ci			return -ENOMEM;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci		q->ndesc = MT_NUM_TX_ENTRIES;
95662306a36Sopenharmony_ci		for (j = 0; j < q->ndesc; j++) {
95762306a36Sopenharmony_ci			err = mt76u_urb_alloc(dev, &q->entry[j],
95862306a36Sopenharmony_ci					      MT_TX_SG_MAX_SIZE);
95962306a36Sopenharmony_ci			if (err < 0)
96062306a36Sopenharmony_ci				return err;
96162306a36Sopenharmony_ci		}
96262306a36Sopenharmony_ci	}
96362306a36Sopenharmony_ci	return 0;
96462306a36Sopenharmony_ci}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_cistatic void mt76u_free_tx(struct mt76_dev *dev)
96762306a36Sopenharmony_ci{
96862306a36Sopenharmony_ci	int i;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	mt76_worker_teardown(&dev->usb.status_worker);
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	for (i = 0; i < IEEE80211_NUM_ACS; i++) {
97362306a36Sopenharmony_ci		struct mt76_queue *q;
97462306a36Sopenharmony_ci		int j;
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci		q = dev->phy.q_tx[i];
97762306a36Sopenharmony_ci		if (!q)
97862306a36Sopenharmony_ci			continue;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci		for (j = 0; j < q->ndesc; j++) {
98162306a36Sopenharmony_ci			usb_free_urb(q->entry[j].urb);
98262306a36Sopenharmony_ci			q->entry[j].urb = NULL;
98362306a36Sopenharmony_ci		}
98462306a36Sopenharmony_ci	}
98562306a36Sopenharmony_ci}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_civoid mt76u_stop_tx(struct mt76_dev *dev)
98862306a36Sopenharmony_ci{
98962306a36Sopenharmony_ci	int ret;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	mt76_worker_disable(&dev->usb.status_worker);
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	ret = wait_event_timeout(dev->tx_wait, !mt76_has_tx_pending(&dev->phy),
99462306a36Sopenharmony_ci				 HZ / 5);
99562306a36Sopenharmony_ci	if (!ret) {
99662306a36Sopenharmony_ci		struct mt76_queue_entry entry;
99762306a36Sopenharmony_ci		struct mt76_queue *q;
99862306a36Sopenharmony_ci		int i, j;
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci		dev_err(dev->dev, "timed out waiting for pending tx\n");
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci		for (i = 0; i < IEEE80211_NUM_ACS; i++) {
100362306a36Sopenharmony_ci			q = dev->phy.q_tx[i];
100462306a36Sopenharmony_ci			if (!q)
100562306a36Sopenharmony_ci				continue;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci			for (j = 0; j < q->ndesc; j++)
100862306a36Sopenharmony_ci				usb_kill_urb(q->entry[j].urb);
100962306a36Sopenharmony_ci		}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci		mt76_worker_disable(&dev->tx_worker);
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci		/* On device removal we maight queue skb's, but mt76u_tx_kick()
101462306a36Sopenharmony_ci		 * will fail to submit urb, cleanup those skb's manually.
101562306a36Sopenharmony_ci		 */
101662306a36Sopenharmony_ci		for (i = 0; i < IEEE80211_NUM_ACS; i++) {
101762306a36Sopenharmony_ci			q = dev->phy.q_tx[i];
101862306a36Sopenharmony_ci			if (!q)
101962306a36Sopenharmony_ci				continue;
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci			while (q->queued > 0) {
102262306a36Sopenharmony_ci				entry = q->entry[q->tail];
102362306a36Sopenharmony_ci				q->entry[q->tail].done = false;
102462306a36Sopenharmony_ci				mt76_queue_tx_complete(dev, q, &entry);
102562306a36Sopenharmony_ci			}
102662306a36Sopenharmony_ci		}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci		mt76_worker_enable(&dev->tx_worker);
102962306a36Sopenharmony_ci	}
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	cancel_work_sync(&dev->usb.stat_work);
103262306a36Sopenharmony_ci	clear_bit(MT76_READING_STATS, &dev->phy.state);
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	mt76_worker_enable(&dev->usb.status_worker);
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	mt76_tx_status_check(dev, true);
103762306a36Sopenharmony_ci}
103862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76u_stop_tx);
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_civoid mt76u_queues_deinit(struct mt76_dev *dev)
104162306a36Sopenharmony_ci{
104262306a36Sopenharmony_ci	mt76u_stop_rx(dev);
104362306a36Sopenharmony_ci	mt76u_stop_tx(dev);
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	mt76u_free_rx(dev);
104662306a36Sopenharmony_ci	mt76u_free_tx(dev);
104762306a36Sopenharmony_ci}
104862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76u_queues_deinit);
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ciint mt76u_alloc_queues(struct mt76_dev *dev)
105162306a36Sopenharmony_ci{
105262306a36Sopenharmony_ci	int err;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	err = mt76u_alloc_rx_queue(dev, MT_RXQ_MAIN);
105562306a36Sopenharmony_ci	if (err < 0)
105662306a36Sopenharmony_ci		return err;
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	return mt76u_alloc_tx(dev);
105962306a36Sopenharmony_ci}
106062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76u_alloc_queues);
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_cistatic const struct mt76_queue_ops usb_queue_ops = {
106362306a36Sopenharmony_ci	.tx_queue_skb = mt76u_tx_queue_skb,
106462306a36Sopenharmony_ci	.kick = mt76u_tx_kick,
106562306a36Sopenharmony_ci};
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ciint __mt76u_init(struct mt76_dev *dev, struct usb_interface *intf,
106862306a36Sopenharmony_ci		 struct mt76_bus_ops *ops)
106962306a36Sopenharmony_ci{
107062306a36Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(intf);
107162306a36Sopenharmony_ci	struct mt76_usb *usb = &dev->usb;
107262306a36Sopenharmony_ci	int err;
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	INIT_WORK(&usb->stat_work, mt76u_tx_status_data);
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	usb->data_len = usb_maxpacket(udev, usb_sndctrlpipe(udev, 0));
107762306a36Sopenharmony_ci	if (usb->data_len < 32)
107862306a36Sopenharmony_ci		usb->data_len = 32;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	usb->data = devm_kmalloc(dev->dev, usb->data_len, GFP_KERNEL);
108162306a36Sopenharmony_ci	if (!usb->data)
108262306a36Sopenharmony_ci		return -ENOMEM;
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	mutex_init(&usb->usb_ctrl_mtx);
108562306a36Sopenharmony_ci	dev->bus = ops;
108662306a36Sopenharmony_ci	dev->queue_ops = &usb_queue_ops;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	dev_set_drvdata(&udev->dev, dev);
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	usb->sg_en = mt76u_check_sg(dev);
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	err = mt76u_set_endpoints(intf, usb);
109362306a36Sopenharmony_ci	if (err < 0)
109462306a36Sopenharmony_ci		return err;
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	err = mt76_worker_setup(dev->hw, &usb->rx_worker, mt76u_rx_worker,
109762306a36Sopenharmony_ci				"usb-rx");
109862306a36Sopenharmony_ci	if (err)
109962306a36Sopenharmony_ci		return err;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	err = mt76_worker_setup(dev->hw, &usb->status_worker,
110262306a36Sopenharmony_ci				mt76u_status_worker, "usb-status");
110362306a36Sopenharmony_ci	if (err)
110462306a36Sopenharmony_ci		return err;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	sched_set_fifo_low(usb->rx_worker.task);
110762306a36Sopenharmony_ci	sched_set_fifo_low(usb->status_worker.task);
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	return 0;
111062306a36Sopenharmony_ci}
111162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__mt76u_init);
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ciint mt76u_init(struct mt76_dev *dev, struct usb_interface *intf)
111462306a36Sopenharmony_ci{
111562306a36Sopenharmony_ci	static struct mt76_bus_ops bus_ops = {
111662306a36Sopenharmony_ci		.rr = mt76u_rr,
111762306a36Sopenharmony_ci		.wr = mt76u_wr,
111862306a36Sopenharmony_ci		.rmw = mt76u_rmw,
111962306a36Sopenharmony_ci		.read_copy = mt76u_read_copy,
112062306a36Sopenharmony_ci		.write_copy = mt76u_copy,
112162306a36Sopenharmony_ci		.wr_rp = mt76u_wr_rp,
112262306a36Sopenharmony_ci		.rd_rp = mt76u_rd_rp,
112362306a36Sopenharmony_ci		.type = MT76_BUS_USB,
112462306a36Sopenharmony_ci	};
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	return __mt76u_init(dev, intf, &bus_ops);
112762306a36Sopenharmony_ci}
112862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76u_init);
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ciMODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>");
113162306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
1132