162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2015 MediaTek Inc.
462306a36Sopenharmony_ci * Author:
562306a36Sopenharmony_ci *  Zhigang.Wei <zhigang.wei@mediatek.com>
662306a36Sopenharmony_ci *  Chunfeng.Yun <chunfeng.yun@mediatek.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "xhci.h"
1462306a36Sopenharmony_ci#include "xhci-mtk.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define SSP_BW_BOUNDARY	130000
1762306a36Sopenharmony_ci#define SS_BW_BOUNDARY	51000
1862306a36Sopenharmony_ci/* table 5-5. High-speed Isoc Transaction Limits in usb_20 spec */
1962306a36Sopenharmony_ci#define HS_BW_BOUNDARY	6144
2062306a36Sopenharmony_ci/* usb2 spec section11.18.1: at most 188 FS bytes per microframe */
2162306a36Sopenharmony_ci#define FS_PAYLOAD_MAX 188
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define DBG_BUF_EN	64
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/* schedule error type */
2662306a36Sopenharmony_ci#define ESCH_SS_Y6		1001
2762306a36Sopenharmony_ci#define ESCH_SS_OVERLAP		1002
2862306a36Sopenharmony_ci#define ESCH_CS_OVERFLOW	1003
2962306a36Sopenharmony_ci#define ESCH_BW_OVERFLOW	1004
3062306a36Sopenharmony_ci#define ESCH_FIXME		1005
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/* mtk scheduler bitmasks */
3362306a36Sopenharmony_ci#define EP_BPKTS(p)	((p) & 0x7f)
3462306a36Sopenharmony_ci#define EP_BCSCOUNT(p)	(((p) & 0x7) << 8)
3562306a36Sopenharmony_ci#define EP_BBM(p)	((p) << 11)
3662306a36Sopenharmony_ci#define EP_BOFFSET(p)	((p) & 0x3fff)
3762306a36Sopenharmony_ci#define EP_BREPEAT(p)	(((p) & 0x7fff) << 16)
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic char *sch_error_string(int err_num)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	switch (err_num) {
4262306a36Sopenharmony_ci	case ESCH_SS_Y6:
4362306a36Sopenharmony_ci		return "Can't schedule Start-Split in Y6";
4462306a36Sopenharmony_ci	case ESCH_SS_OVERLAP:
4562306a36Sopenharmony_ci		return "Can't find a suitable Start-Split location";
4662306a36Sopenharmony_ci	case ESCH_CS_OVERFLOW:
4762306a36Sopenharmony_ci		return "The last Complete-Split is greater than 7";
4862306a36Sopenharmony_ci	case ESCH_BW_OVERFLOW:
4962306a36Sopenharmony_ci		return "Bandwidth exceeds the maximum limit";
5062306a36Sopenharmony_ci	case ESCH_FIXME:
5162306a36Sopenharmony_ci		return "FIXME, to be resolved";
5262306a36Sopenharmony_ci	default:
5362306a36Sopenharmony_ci		return "Unknown";
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic int is_fs_or_ls(enum usb_device_speed speed)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	return speed == USB_SPEED_FULL || speed == USB_SPEED_LOW;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic const char *
6362306a36Sopenharmony_cidecode_ep(struct usb_host_endpoint *ep, enum usb_device_speed speed)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	static char buf[DBG_BUF_EN];
6662306a36Sopenharmony_ci	struct usb_endpoint_descriptor *epd = &ep->desc;
6762306a36Sopenharmony_ci	unsigned int interval;
6862306a36Sopenharmony_ci	const char *unit;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	interval = usb_decode_interval(epd, speed);
7162306a36Sopenharmony_ci	if (interval % 1000) {
7262306a36Sopenharmony_ci		unit = "us";
7362306a36Sopenharmony_ci	} else {
7462306a36Sopenharmony_ci		unit = "ms";
7562306a36Sopenharmony_ci		interval /= 1000;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	snprintf(buf, DBG_BUF_EN, "%s ep%d%s %s, mpkt:%d, interval:%d/%d%s",
7962306a36Sopenharmony_ci		 usb_speed_string(speed), usb_endpoint_num(epd),
8062306a36Sopenharmony_ci		 usb_endpoint_dir_in(epd) ? "in" : "out",
8162306a36Sopenharmony_ci		 usb_ep_type_string(usb_endpoint_type(epd)),
8262306a36Sopenharmony_ci		 usb_endpoint_maxp(epd), epd->bInterval, interval, unit);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	return buf;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic u32 get_bw_boundary(enum usb_device_speed speed)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	u32 boundary;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	switch (speed) {
9262306a36Sopenharmony_ci	case USB_SPEED_SUPER_PLUS:
9362306a36Sopenharmony_ci		boundary = SSP_BW_BOUNDARY;
9462306a36Sopenharmony_ci		break;
9562306a36Sopenharmony_ci	case USB_SPEED_SUPER:
9662306a36Sopenharmony_ci		boundary = SS_BW_BOUNDARY;
9762306a36Sopenharmony_ci		break;
9862306a36Sopenharmony_ci	default:
9962306a36Sopenharmony_ci		boundary = HS_BW_BOUNDARY;
10062306a36Sopenharmony_ci		break;
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	return boundary;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/*
10762306a36Sopenharmony_ci* get the bandwidth domain which @ep belongs to.
10862306a36Sopenharmony_ci*
10962306a36Sopenharmony_ci* the bandwidth domain array is saved to @sch_array of struct xhci_hcd_mtk,
11062306a36Sopenharmony_ci* each HS root port is treated as a single bandwidth domain,
11162306a36Sopenharmony_ci* but each SS root port is treated as two bandwidth domains, one for IN eps,
11262306a36Sopenharmony_ci* one for OUT eps.
11362306a36Sopenharmony_ci* @real_port value is defined as follow according to xHCI spec:
11462306a36Sopenharmony_ci* 1 for SSport0, ..., N+1 for SSportN, N+2 for HSport0, N+3 for HSport1, etc
11562306a36Sopenharmony_ci* so the bandwidth domain array is organized as follow for simplification:
11662306a36Sopenharmony_ci* SSport0-OUT, SSport0-IN, ..., SSportX-OUT, SSportX-IN, HSport0, ..., HSportY
11762306a36Sopenharmony_ci*/
11862306a36Sopenharmony_cistatic struct mu3h_sch_bw_info *
11962306a36Sopenharmony_ciget_bw_info(struct xhci_hcd_mtk *mtk, struct usb_device *udev,
12062306a36Sopenharmony_ci	    struct usb_host_endpoint *ep)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	struct xhci_hcd *xhci = hcd_to_xhci(mtk->hcd);
12362306a36Sopenharmony_ci	struct xhci_virt_device *virt_dev;
12462306a36Sopenharmony_ci	int bw_index;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	virt_dev = xhci->devs[udev->slot_id];
12762306a36Sopenharmony_ci	if (!virt_dev->real_port) {
12862306a36Sopenharmony_ci		WARN_ONCE(1, "%s invalid real_port\n", dev_name(&udev->dev));
12962306a36Sopenharmony_ci		return NULL;
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	if (udev->speed >= USB_SPEED_SUPER) {
13362306a36Sopenharmony_ci		if (usb_endpoint_dir_out(&ep->desc))
13462306a36Sopenharmony_ci			bw_index = (virt_dev->real_port - 1) * 2;
13562306a36Sopenharmony_ci		else
13662306a36Sopenharmony_ci			bw_index = (virt_dev->real_port - 1) * 2 + 1;
13762306a36Sopenharmony_ci	} else {
13862306a36Sopenharmony_ci		/* add one more for each SS port */
13962306a36Sopenharmony_ci		bw_index = virt_dev->real_port + xhci->usb3_rhub.num_ports - 1;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	return &mtk->sch_array[bw_index];
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic u32 get_esit(struct xhci_ep_ctx *ep_ctx)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	u32 esit;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	esit = 1 << CTX_TO_EP_INTERVAL(le32_to_cpu(ep_ctx->ep_info));
15062306a36Sopenharmony_ci	if (esit > XHCI_MTK_MAX_ESIT)
15162306a36Sopenharmony_ci		esit = XHCI_MTK_MAX_ESIT;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	return esit;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic struct mu3h_sch_tt *find_tt(struct usb_device *udev)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	struct usb_tt *utt = udev->tt;
15962306a36Sopenharmony_ci	struct mu3h_sch_tt *tt, **tt_index, **ptt;
16062306a36Sopenharmony_ci	bool allocated_index = false;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	if (!utt)
16362306a36Sopenharmony_ci		return NULL;	/* Not below a TT */
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	/*
16662306a36Sopenharmony_ci	 * Find/create our data structure.
16762306a36Sopenharmony_ci	 * For hubs with a single TT, we get it directly.
16862306a36Sopenharmony_ci	 * For hubs with multiple TTs, there's an extra level of pointers.
16962306a36Sopenharmony_ci	 */
17062306a36Sopenharmony_ci	tt_index = NULL;
17162306a36Sopenharmony_ci	if (utt->multi) {
17262306a36Sopenharmony_ci		tt_index = utt->hcpriv;
17362306a36Sopenharmony_ci		if (!tt_index) {	/* Create the index array */
17462306a36Sopenharmony_ci			tt_index = kcalloc(utt->hub->maxchild,
17562306a36Sopenharmony_ci					sizeof(*tt_index), GFP_KERNEL);
17662306a36Sopenharmony_ci			if (!tt_index)
17762306a36Sopenharmony_ci				return ERR_PTR(-ENOMEM);
17862306a36Sopenharmony_ci			utt->hcpriv = tt_index;
17962306a36Sopenharmony_ci			allocated_index = true;
18062306a36Sopenharmony_ci		}
18162306a36Sopenharmony_ci		ptt = &tt_index[udev->ttport - 1];
18262306a36Sopenharmony_ci	} else {
18362306a36Sopenharmony_ci		ptt = (struct mu3h_sch_tt **) &utt->hcpriv;
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	tt = *ptt;
18762306a36Sopenharmony_ci	if (!tt) {	/* Create the mu3h_sch_tt */
18862306a36Sopenharmony_ci		tt = kzalloc(sizeof(*tt), GFP_KERNEL);
18962306a36Sopenharmony_ci		if (!tt) {
19062306a36Sopenharmony_ci			if (allocated_index) {
19162306a36Sopenharmony_ci				utt->hcpriv = NULL;
19262306a36Sopenharmony_ci				kfree(tt_index);
19362306a36Sopenharmony_ci			}
19462306a36Sopenharmony_ci			return ERR_PTR(-ENOMEM);
19562306a36Sopenharmony_ci		}
19662306a36Sopenharmony_ci		INIT_LIST_HEAD(&tt->ep_list);
19762306a36Sopenharmony_ci		*ptt = tt;
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	return tt;
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci/* Release the TT above udev, if it's not in use */
20462306a36Sopenharmony_cistatic void drop_tt(struct usb_device *udev)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	struct usb_tt *utt = udev->tt;
20762306a36Sopenharmony_ci	struct mu3h_sch_tt *tt, **tt_index, **ptt;
20862306a36Sopenharmony_ci	int i, cnt;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	if (!utt || !utt->hcpriv)
21162306a36Sopenharmony_ci		return;		/* Not below a TT, or never allocated */
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	cnt = 0;
21462306a36Sopenharmony_ci	if (utt->multi) {
21562306a36Sopenharmony_ci		tt_index = utt->hcpriv;
21662306a36Sopenharmony_ci		ptt = &tt_index[udev->ttport - 1];
21762306a36Sopenharmony_ci		/*  How many entries are left in tt_index? */
21862306a36Sopenharmony_ci		for (i = 0; i < utt->hub->maxchild; ++i)
21962306a36Sopenharmony_ci			cnt += !!tt_index[i];
22062306a36Sopenharmony_ci	} else {
22162306a36Sopenharmony_ci		tt_index = NULL;
22262306a36Sopenharmony_ci		ptt = (struct mu3h_sch_tt **)&utt->hcpriv;
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	tt = *ptt;
22662306a36Sopenharmony_ci	if (!tt || !list_empty(&tt->ep_list))
22762306a36Sopenharmony_ci		return;		/* never allocated , or still in use*/
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	*ptt = NULL;
23062306a36Sopenharmony_ci	kfree(tt);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	if (cnt == 1) {
23362306a36Sopenharmony_ci		utt->hcpriv = NULL;
23462306a36Sopenharmony_ci		kfree(tt_index);
23562306a36Sopenharmony_ci	}
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic struct mu3h_sch_ep_info *
23962306a36Sopenharmony_cicreate_sch_ep(struct xhci_hcd_mtk *mtk, struct usb_device *udev,
24062306a36Sopenharmony_ci	      struct usb_host_endpoint *ep)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	struct mu3h_sch_ep_info *sch_ep;
24362306a36Sopenharmony_ci	struct mu3h_sch_bw_info *bw_info;
24462306a36Sopenharmony_ci	struct mu3h_sch_tt *tt = NULL;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	bw_info = get_bw_info(mtk, udev, ep);
24762306a36Sopenharmony_ci	if (!bw_info)
24862306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	sch_ep = kzalloc(sizeof(*sch_ep), GFP_KERNEL);
25162306a36Sopenharmony_ci	if (!sch_ep)
25262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	if (is_fs_or_ls(udev->speed)) {
25562306a36Sopenharmony_ci		tt = find_tt(udev);
25662306a36Sopenharmony_ci		if (IS_ERR(tt)) {
25762306a36Sopenharmony_ci			kfree(sch_ep);
25862306a36Sopenharmony_ci			return ERR_PTR(-ENOMEM);
25962306a36Sopenharmony_ci		}
26062306a36Sopenharmony_ci	}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	sch_ep->bw_info = bw_info;
26362306a36Sopenharmony_ci	sch_ep->sch_tt = tt;
26462306a36Sopenharmony_ci	sch_ep->ep = ep;
26562306a36Sopenharmony_ci	sch_ep->speed = udev->speed;
26662306a36Sopenharmony_ci	INIT_LIST_HEAD(&sch_ep->endpoint);
26762306a36Sopenharmony_ci	INIT_LIST_HEAD(&sch_ep->tt_endpoint);
26862306a36Sopenharmony_ci	INIT_HLIST_NODE(&sch_ep->hentry);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return sch_ep;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic void setup_sch_info(struct xhci_ep_ctx *ep_ctx,
27462306a36Sopenharmony_ci			   struct mu3h_sch_ep_info *sch_ep)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	u32 ep_type;
27762306a36Sopenharmony_ci	u32 maxpkt;
27862306a36Sopenharmony_ci	u32 max_burst;
27962306a36Sopenharmony_ci	u32 mult;
28062306a36Sopenharmony_ci	u32 esit_pkts;
28162306a36Sopenharmony_ci	u32 max_esit_payload;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	ep_type = CTX_TO_EP_TYPE(le32_to_cpu(ep_ctx->ep_info2));
28462306a36Sopenharmony_ci	maxpkt = MAX_PACKET_DECODED(le32_to_cpu(ep_ctx->ep_info2));
28562306a36Sopenharmony_ci	max_burst = CTX_TO_MAX_BURST(le32_to_cpu(ep_ctx->ep_info2));
28662306a36Sopenharmony_ci	mult = CTX_TO_EP_MULT(le32_to_cpu(ep_ctx->ep_info));
28762306a36Sopenharmony_ci	max_esit_payload =
28862306a36Sopenharmony_ci		(CTX_TO_MAX_ESIT_PAYLOAD_HI(
28962306a36Sopenharmony_ci			le32_to_cpu(ep_ctx->ep_info)) << 16) |
29062306a36Sopenharmony_ci		 CTX_TO_MAX_ESIT_PAYLOAD(le32_to_cpu(ep_ctx->tx_info));
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	sch_ep->esit = get_esit(ep_ctx);
29362306a36Sopenharmony_ci	sch_ep->num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit;
29462306a36Sopenharmony_ci	sch_ep->ep_type = ep_type;
29562306a36Sopenharmony_ci	sch_ep->maxpkt = maxpkt;
29662306a36Sopenharmony_ci	sch_ep->offset = 0;
29762306a36Sopenharmony_ci	sch_ep->burst_mode = 0;
29862306a36Sopenharmony_ci	sch_ep->repeat = 0;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	if (sch_ep->speed == USB_SPEED_HIGH) {
30162306a36Sopenharmony_ci		sch_ep->cs_count = 0;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci		/*
30462306a36Sopenharmony_ci		 * usb_20 spec section5.9
30562306a36Sopenharmony_ci		 * a single microframe is enough for HS synchromous endpoints
30662306a36Sopenharmony_ci		 * in a interval
30762306a36Sopenharmony_ci		 */
30862306a36Sopenharmony_ci		sch_ep->num_budget_microframes = 1;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci		/*
31162306a36Sopenharmony_ci		 * xHCI spec section6.2.3.4
31262306a36Sopenharmony_ci		 * @max_burst is the number of additional transactions
31362306a36Sopenharmony_ci		 * opportunities per microframe
31462306a36Sopenharmony_ci		 */
31562306a36Sopenharmony_ci		sch_ep->pkts = max_burst + 1;
31662306a36Sopenharmony_ci		sch_ep->bw_cost_per_microframe = maxpkt * sch_ep->pkts;
31762306a36Sopenharmony_ci	} else if (sch_ep->speed >= USB_SPEED_SUPER) {
31862306a36Sopenharmony_ci		/* usb3_r1 spec section4.4.7 & 4.4.8 */
31962306a36Sopenharmony_ci		sch_ep->cs_count = 0;
32062306a36Sopenharmony_ci		sch_ep->burst_mode = 1;
32162306a36Sopenharmony_ci		/*
32262306a36Sopenharmony_ci		 * some device's (d)wBytesPerInterval is set as 0,
32362306a36Sopenharmony_ci		 * then max_esit_payload is 0, so evaluate esit_pkts from
32462306a36Sopenharmony_ci		 * mult and burst
32562306a36Sopenharmony_ci		 */
32662306a36Sopenharmony_ci		esit_pkts = DIV_ROUND_UP(max_esit_payload, maxpkt);
32762306a36Sopenharmony_ci		if (esit_pkts == 0)
32862306a36Sopenharmony_ci			esit_pkts = (mult + 1) * (max_burst + 1);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci		if (ep_type == INT_IN_EP || ep_type == INT_OUT_EP) {
33162306a36Sopenharmony_ci			sch_ep->pkts = esit_pkts;
33262306a36Sopenharmony_ci			sch_ep->num_budget_microframes = 1;
33362306a36Sopenharmony_ci		}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci		if (ep_type == ISOC_IN_EP || ep_type == ISOC_OUT_EP) {
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci			if (sch_ep->esit == 1)
33862306a36Sopenharmony_ci				sch_ep->pkts = esit_pkts;
33962306a36Sopenharmony_ci			else if (esit_pkts <= sch_ep->esit)
34062306a36Sopenharmony_ci				sch_ep->pkts = 1;
34162306a36Sopenharmony_ci			else
34262306a36Sopenharmony_ci				sch_ep->pkts = roundup_pow_of_two(esit_pkts)
34362306a36Sopenharmony_ci					/ sch_ep->esit;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci			sch_ep->num_budget_microframes =
34662306a36Sopenharmony_ci				DIV_ROUND_UP(esit_pkts, sch_ep->pkts);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci			sch_ep->repeat = !!(sch_ep->num_budget_microframes > 1);
34962306a36Sopenharmony_ci		}
35062306a36Sopenharmony_ci		sch_ep->bw_cost_per_microframe = maxpkt * sch_ep->pkts;
35162306a36Sopenharmony_ci	} else if (is_fs_or_ls(sch_ep->speed)) {
35262306a36Sopenharmony_ci		sch_ep->pkts = 1; /* at most one packet for each microframe */
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci		/*
35562306a36Sopenharmony_ci		 * num_budget_microframes and cs_count will be updated when
35662306a36Sopenharmony_ci		 * check TT for INT_OUT_EP, ISOC/INT_IN_EP type
35762306a36Sopenharmony_ci		 */
35862306a36Sopenharmony_ci		sch_ep->cs_count = DIV_ROUND_UP(maxpkt, FS_PAYLOAD_MAX);
35962306a36Sopenharmony_ci		sch_ep->num_budget_microframes = sch_ep->cs_count;
36062306a36Sopenharmony_ci		sch_ep->bw_cost_per_microframe = min_t(u32, maxpkt, FS_PAYLOAD_MAX);
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci/* Get maximum bandwidth when we schedule at offset slot. */
36562306a36Sopenharmony_cistatic u32 get_max_bw(struct mu3h_sch_bw_info *sch_bw,
36662306a36Sopenharmony_ci	struct mu3h_sch_ep_info *sch_ep, u32 offset)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	u32 max_bw = 0;
36962306a36Sopenharmony_ci	u32 bw;
37062306a36Sopenharmony_ci	int i, j, k;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	for (i = 0; i < sch_ep->num_esit; i++) {
37362306a36Sopenharmony_ci		u32 base = offset + i * sch_ep->esit;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci		for (j = 0; j < sch_ep->num_budget_microframes; j++) {
37662306a36Sopenharmony_ci			k = XHCI_MTK_BW_INDEX(base + j);
37762306a36Sopenharmony_ci			bw = sch_bw->bus_bw[k] + sch_ep->bw_cost_per_microframe;
37862306a36Sopenharmony_ci			if (bw > max_bw)
37962306a36Sopenharmony_ci				max_bw = bw;
38062306a36Sopenharmony_ci		}
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci	return max_bw;
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic void update_bus_bw(struct mu3h_sch_bw_info *sch_bw,
38662306a36Sopenharmony_ci	struct mu3h_sch_ep_info *sch_ep, bool used)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	int bw_updated;
38962306a36Sopenharmony_ci	u32 base;
39062306a36Sopenharmony_ci	int i, j;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	bw_updated = sch_ep->bw_cost_per_microframe * (used ? 1 : -1);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	for (i = 0; i < sch_ep->num_esit; i++) {
39562306a36Sopenharmony_ci		base = sch_ep->offset + i * sch_ep->esit;
39662306a36Sopenharmony_ci		for (j = 0; j < sch_ep->num_budget_microframes; j++)
39762306a36Sopenharmony_ci			sch_bw->bus_bw[XHCI_MTK_BW_INDEX(base + j)] += bw_updated;
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic int check_fs_bus_bw(struct mu3h_sch_ep_info *sch_ep, int offset)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	struct mu3h_sch_tt *tt = sch_ep->sch_tt;
40462306a36Sopenharmony_ci	u32 tmp;
40562306a36Sopenharmony_ci	int base;
40662306a36Sopenharmony_ci	int i, j, k;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	for (i = 0; i < sch_ep->num_esit; i++) {
40962306a36Sopenharmony_ci		base = offset + i * sch_ep->esit;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci		/*
41262306a36Sopenharmony_ci		 * Compared with hs bus, no matter what ep type,
41362306a36Sopenharmony_ci		 * the hub will always delay one uframe to send data
41462306a36Sopenharmony_ci		 */
41562306a36Sopenharmony_ci		for (j = 0; j < sch_ep->num_budget_microframes; j++) {
41662306a36Sopenharmony_ci			k = XHCI_MTK_BW_INDEX(base + j);
41762306a36Sopenharmony_ci			tmp = tt->fs_bus_bw[k] + sch_ep->bw_cost_per_microframe;
41862306a36Sopenharmony_ci			if (tmp > FS_PAYLOAD_MAX)
41962306a36Sopenharmony_ci				return -ESCH_BW_OVERFLOW;
42062306a36Sopenharmony_ci		}
42162306a36Sopenharmony_ci	}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	return 0;
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic int check_sch_tt(struct mu3h_sch_ep_info *sch_ep, u32 offset)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	u32 start_ss, last_ss;
42962306a36Sopenharmony_ci	u32 start_cs, last_cs;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	if (!sch_ep->sch_tt)
43262306a36Sopenharmony_ci		return 0;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	start_ss = offset % 8;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	if (sch_ep->ep_type == ISOC_OUT_EP) {
43762306a36Sopenharmony_ci		last_ss = start_ss + sch_ep->cs_count - 1;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci		/*
44062306a36Sopenharmony_ci		 * usb_20 spec section11.18:
44162306a36Sopenharmony_ci		 * must never schedule Start-Split in Y6
44262306a36Sopenharmony_ci		 */
44362306a36Sopenharmony_ci		if (!(start_ss == 7 || last_ss < 6))
44462306a36Sopenharmony_ci			return -ESCH_SS_Y6;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	} else {
44762306a36Sopenharmony_ci		u32 cs_count = DIV_ROUND_UP(sch_ep->maxpkt, FS_PAYLOAD_MAX);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci		/*
45062306a36Sopenharmony_ci		 * usb_20 spec section11.18:
45162306a36Sopenharmony_ci		 * must never schedule Start-Split in Y6
45262306a36Sopenharmony_ci		 */
45362306a36Sopenharmony_ci		if (start_ss == 6)
45462306a36Sopenharmony_ci			return -ESCH_SS_Y6;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci		/* one uframe for ss + one uframe for idle */
45762306a36Sopenharmony_ci		start_cs = (start_ss + 2) % 8;
45862306a36Sopenharmony_ci		last_cs = start_cs + cs_count - 1;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci		if (last_cs > 7)
46162306a36Sopenharmony_ci			return -ESCH_CS_OVERFLOW;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci		if (cs_count > 7)
46462306a36Sopenharmony_ci			cs_count = 7; /* HW limit */
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci		sch_ep->cs_count = cs_count;
46762306a36Sopenharmony_ci		/* ss, idle are ignored */
46862306a36Sopenharmony_ci		sch_ep->num_budget_microframes = cs_count;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci		/*
47162306a36Sopenharmony_ci		 * if interval=1, maxp >752, num_budge_micoframe is larger
47262306a36Sopenharmony_ci		 * than sch_ep->esit, will overstep boundary
47362306a36Sopenharmony_ci		 */
47462306a36Sopenharmony_ci		if (sch_ep->num_budget_microframes > sch_ep->esit)
47562306a36Sopenharmony_ci			sch_ep->num_budget_microframes = sch_ep->esit;
47662306a36Sopenharmony_ci	}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	return check_fs_bus_bw(sch_ep, offset);
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_cistatic void update_sch_tt(struct mu3h_sch_ep_info *sch_ep, bool used)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci	struct mu3h_sch_tt *tt = sch_ep->sch_tt;
48462306a36Sopenharmony_ci	int bw_updated;
48562306a36Sopenharmony_ci	u32 base;
48662306a36Sopenharmony_ci	int i, j;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	bw_updated = sch_ep->bw_cost_per_microframe * (used ? 1 : -1);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	for (i = 0; i < sch_ep->num_esit; i++) {
49162306a36Sopenharmony_ci		base = sch_ep->offset + i * sch_ep->esit;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci		for (j = 0; j < sch_ep->num_budget_microframes; j++)
49462306a36Sopenharmony_ci			tt->fs_bus_bw[XHCI_MTK_BW_INDEX(base + j)] += bw_updated;
49562306a36Sopenharmony_ci	}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	if (used)
49862306a36Sopenharmony_ci		list_add_tail(&sch_ep->tt_endpoint, &tt->ep_list);
49962306a36Sopenharmony_ci	else
50062306a36Sopenharmony_ci		list_del(&sch_ep->tt_endpoint);
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_cistatic int load_ep_bw(struct mu3h_sch_bw_info *sch_bw,
50462306a36Sopenharmony_ci		      struct mu3h_sch_ep_info *sch_ep, bool loaded)
50562306a36Sopenharmony_ci{
50662306a36Sopenharmony_ci	if (sch_ep->sch_tt)
50762306a36Sopenharmony_ci		update_sch_tt(sch_ep, loaded);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	/* update bus bandwidth info */
51062306a36Sopenharmony_ci	update_bus_bw(sch_bw, sch_ep, loaded);
51162306a36Sopenharmony_ci	sch_ep->allocated = loaded;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	return 0;
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cistatic int check_sch_bw(struct mu3h_sch_ep_info *sch_ep)
51762306a36Sopenharmony_ci{
51862306a36Sopenharmony_ci	struct mu3h_sch_bw_info *sch_bw = sch_ep->bw_info;
51962306a36Sopenharmony_ci	const u32 bw_boundary = get_bw_boundary(sch_ep->speed);
52062306a36Sopenharmony_ci	u32 offset;
52162306a36Sopenharmony_ci	u32 worst_bw;
52262306a36Sopenharmony_ci	u32 min_bw = ~0;
52362306a36Sopenharmony_ci	int min_index = -1;
52462306a36Sopenharmony_ci	int ret = 0;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	/*
52762306a36Sopenharmony_ci	 * Search through all possible schedule microframes.
52862306a36Sopenharmony_ci	 * and find a microframe where its worst bandwidth is minimum.
52962306a36Sopenharmony_ci	 */
53062306a36Sopenharmony_ci	for (offset = 0; offset < sch_ep->esit; offset++) {
53162306a36Sopenharmony_ci		ret = check_sch_tt(sch_ep, offset);
53262306a36Sopenharmony_ci		if (ret)
53362306a36Sopenharmony_ci			continue;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci		worst_bw = get_max_bw(sch_bw, sch_ep, offset);
53662306a36Sopenharmony_ci		if (worst_bw > bw_boundary)
53762306a36Sopenharmony_ci			continue;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci		if (min_bw > worst_bw) {
54062306a36Sopenharmony_ci			min_bw = worst_bw;
54162306a36Sopenharmony_ci			min_index = offset;
54262306a36Sopenharmony_ci		}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci		/* use first-fit for LS/FS */
54562306a36Sopenharmony_ci		if (sch_ep->sch_tt && min_index >= 0)
54662306a36Sopenharmony_ci			break;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci		if (min_bw == 0)
54962306a36Sopenharmony_ci			break;
55062306a36Sopenharmony_ci	}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	if (min_index < 0)
55362306a36Sopenharmony_ci		return ret ? ret : -ESCH_BW_OVERFLOW;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	sch_ep->offset = min_index;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	return load_ep_bw(sch_bw, sch_ep, true);
55862306a36Sopenharmony_ci}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_cistatic void destroy_sch_ep(struct xhci_hcd_mtk *mtk, struct usb_device *udev,
56162306a36Sopenharmony_ci			   struct mu3h_sch_ep_info *sch_ep)
56262306a36Sopenharmony_ci{
56362306a36Sopenharmony_ci	/* only release ep bw check passed by check_sch_bw() */
56462306a36Sopenharmony_ci	if (sch_ep->allocated)
56562306a36Sopenharmony_ci		load_ep_bw(sch_ep->bw_info, sch_ep, false);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	if (sch_ep->sch_tt)
56862306a36Sopenharmony_ci		drop_tt(udev);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	list_del(&sch_ep->endpoint);
57162306a36Sopenharmony_ci	hlist_del(&sch_ep->hentry);
57262306a36Sopenharmony_ci	kfree(sch_ep);
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_cistatic bool need_bw_sch(struct usb_device *udev,
57662306a36Sopenharmony_ci			struct usb_host_endpoint *ep)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	bool has_tt = udev->tt && udev->tt->hub->parent;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	/* only for periodic endpoints */
58162306a36Sopenharmony_ci	if (usb_endpoint_xfer_control(&ep->desc)
58262306a36Sopenharmony_ci		|| usb_endpoint_xfer_bulk(&ep->desc))
58362306a36Sopenharmony_ci		return false;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	/*
58662306a36Sopenharmony_ci	 * for LS & FS periodic endpoints which its device is not behind
58762306a36Sopenharmony_ci	 * a TT are also ignored, root-hub will schedule them directly,
58862306a36Sopenharmony_ci	 * but need set @bpkts field of endpoint context to 1.
58962306a36Sopenharmony_ci	 */
59062306a36Sopenharmony_ci	if (is_fs_or_ls(udev->speed) && !has_tt)
59162306a36Sopenharmony_ci		return false;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	/* skip endpoint with zero maxpkt */
59462306a36Sopenharmony_ci	if (usb_endpoint_maxp(&ep->desc) == 0)
59562306a36Sopenharmony_ci		return false;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	return true;
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ciint xhci_mtk_sch_init(struct xhci_hcd_mtk *mtk)
60162306a36Sopenharmony_ci{
60262306a36Sopenharmony_ci	struct xhci_hcd *xhci = hcd_to_xhci(mtk->hcd);
60362306a36Sopenharmony_ci	struct mu3h_sch_bw_info *sch_array;
60462306a36Sopenharmony_ci	int num_usb_bus;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	/* ss IN and OUT are separated */
60762306a36Sopenharmony_ci	num_usb_bus = xhci->usb3_rhub.num_ports * 2 + xhci->usb2_rhub.num_ports;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	sch_array = kcalloc(num_usb_bus, sizeof(*sch_array), GFP_KERNEL);
61062306a36Sopenharmony_ci	if (sch_array == NULL)
61162306a36Sopenharmony_ci		return -ENOMEM;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	mtk->sch_array = sch_array;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	INIT_LIST_HEAD(&mtk->bw_ep_chk_list);
61662306a36Sopenharmony_ci	hash_init(mtk->sch_ep_hash);
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	return 0;
61962306a36Sopenharmony_ci}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_civoid xhci_mtk_sch_exit(struct xhci_hcd_mtk *mtk)
62262306a36Sopenharmony_ci{
62362306a36Sopenharmony_ci	kfree(mtk->sch_array);
62462306a36Sopenharmony_ci}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_cistatic int add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
62762306a36Sopenharmony_ci			struct usb_host_endpoint *ep)
62862306a36Sopenharmony_ci{
62962306a36Sopenharmony_ci	struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
63062306a36Sopenharmony_ci	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
63162306a36Sopenharmony_ci	struct xhci_ep_ctx *ep_ctx;
63262306a36Sopenharmony_ci	struct xhci_virt_device *virt_dev;
63362306a36Sopenharmony_ci	struct mu3h_sch_ep_info *sch_ep;
63462306a36Sopenharmony_ci	unsigned int ep_index;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	virt_dev = xhci->devs[udev->slot_id];
63762306a36Sopenharmony_ci	ep_index = xhci_get_endpoint_index(&ep->desc);
63862306a36Sopenharmony_ci	ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	if (!need_bw_sch(udev, ep)) {
64162306a36Sopenharmony_ci		/*
64262306a36Sopenharmony_ci		 * set @bpkts to 1 if it is LS or FS periodic endpoint, and its
64362306a36Sopenharmony_ci		 * device does not connected through an external HS hub
64462306a36Sopenharmony_ci		 */
64562306a36Sopenharmony_ci		if (usb_endpoint_xfer_int(&ep->desc)
64662306a36Sopenharmony_ci			|| usb_endpoint_xfer_isoc(&ep->desc))
64762306a36Sopenharmony_ci			ep_ctx->reserved[0] = cpu_to_le32(EP_BPKTS(1));
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci		return 0;
65062306a36Sopenharmony_ci	}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	xhci_dbg(xhci, "%s %s\n", __func__, decode_ep(ep, udev->speed));
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	sch_ep = create_sch_ep(mtk, udev, ep);
65562306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(sch_ep))
65662306a36Sopenharmony_ci		return -ENOMEM;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	setup_sch_info(ep_ctx, sch_ep);
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	list_add_tail(&sch_ep->endpoint, &mtk->bw_ep_chk_list);
66162306a36Sopenharmony_ci	hash_add(mtk->sch_ep_hash, &sch_ep->hentry, (unsigned long)ep);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	return 0;
66462306a36Sopenharmony_ci}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_cistatic void drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
66762306a36Sopenharmony_ci			  struct usb_host_endpoint *ep)
66862306a36Sopenharmony_ci{
66962306a36Sopenharmony_ci	struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
67062306a36Sopenharmony_ci	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
67162306a36Sopenharmony_ci	struct mu3h_sch_ep_info *sch_ep;
67262306a36Sopenharmony_ci	struct hlist_node *hn;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	if (!need_bw_sch(udev, ep))
67562306a36Sopenharmony_ci		return;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	xhci_dbg(xhci, "%s %s\n", __func__, decode_ep(ep, udev->speed));
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	hash_for_each_possible_safe(mtk->sch_ep_hash, sch_ep,
68062306a36Sopenharmony_ci				    hn, hentry, (unsigned long)ep) {
68162306a36Sopenharmony_ci		if (sch_ep->ep == ep) {
68262306a36Sopenharmony_ci			destroy_sch_ep(mtk, udev, sch_ep);
68362306a36Sopenharmony_ci			break;
68462306a36Sopenharmony_ci		}
68562306a36Sopenharmony_ci	}
68662306a36Sopenharmony_ci}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ciint xhci_mtk_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
68962306a36Sopenharmony_ci{
69062306a36Sopenharmony_ci	struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
69162306a36Sopenharmony_ci	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
69262306a36Sopenharmony_ci	struct xhci_virt_device *virt_dev = xhci->devs[udev->slot_id];
69362306a36Sopenharmony_ci	struct mu3h_sch_ep_info *sch_ep;
69462306a36Sopenharmony_ci	int ret;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	xhci_dbg(xhci, "%s() udev %s\n", __func__, dev_name(&udev->dev));
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	list_for_each_entry(sch_ep, &mtk->bw_ep_chk_list, endpoint) {
69962306a36Sopenharmony_ci		struct xhci_ep_ctx *ep_ctx;
70062306a36Sopenharmony_ci		struct usb_host_endpoint *ep = sch_ep->ep;
70162306a36Sopenharmony_ci		unsigned int ep_index = xhci_get_endpoint_index(&ep->desc);
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci		ret = check_sch_bw(sch_ep);
70462306a36Sopenharmony_ci		if (ret) {
70562306a36Sopenharmony_ci			xhci_err(xhci, "Not enough bandwidth! (%s)\n",
70662306a36Sopenharmony_ci				 sch_error_string(-ret));
70762306a36Sopenharmony_ci			return -ENOSPC;
70862306a36Sopenharmony_ci		}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci		ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index);
71162306a36Sopenharmony_ci		ep_ctx->reserved[0] = cpu_to_le32(EP_BPKTS(sch_ep->pkts)
71262306a36Sopenharmony_ci			| EP_BCSCOUNT(sch_ep->cs_count)
71362306a36Sopenharmony_ci			| EP_BBM(sch_ep->burst_mode));
71462306a36Sopenharmony_ci		ep_ctx->reserved[1] = cpu_to_le32(EP_BOFFSET(sch_ep->offset)
71562306a36Sopenharmony_ci			| EP_BREPEAT(sch_ep->repeat));
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci		xhci_dbg(xhci, " PKTS:%x, CSCOUNT:%x, BM:%x, OFFSET:%x, REPEAT:%x\n",
71862306a36Sopenharmony_ci			sch_ep->pkts, sch_ep->cs_count, sch_ep->burst_mode,
71962306a36Sopenharmony_ci			sch_ep->offset, sch_ep->repeat);
72062306a36Sopenharmony_ci	}
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	ret = xhci_check_bandwidth(hcd, udev);
72362306a36Sopenharmony_ci	if (!ret)
72462306a36Sopenharmony_ci		list_del_init(&mtk->bw_ep_chk_list);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	return ret;
72762306a36Sopenharmony_ci}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_civoid xhci_mtk_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
73062306a36Sopenharmony_ci{
73162306a36Sopenharmony_ci	struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
73262306a36Sopenharmony_ci	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
73362306a36Sopenharmony_ci	struct mu3h_sch_ep_info *sch_ep, *tmp;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	xhci_dbg(xhci, "%s() udev %s\n", __func__, dev_name(&udev->dev));
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	list_for_each_entry_safe(sch_ep, tmp, &mtk->bw_ep_chk_list, endpoint)
73862306a36Sopenharmony_ci		destroy_sch_ep(mtk, udev, sch_ep);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	xhci_reset_bandwidth(hcd, udev);
74162306a36Sopenharmony_ci}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ciint xhci_mtk_add_ep(struct usb_hcd *hcd, struct usb_device *udev,
74462306a36Sopenharmony_ci		    struct usb_host_endpoint *ep)
74562306a36Sopenharmony_ci{
74662306a36Sopenharmony_ci	int ret;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	ret = xhci_add_endpoint(hcd, udev, ep);
74962306a36Sopenharmony_ci	if (ret)
75062306a36Sopenharmony_ci		return ret;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	if (ep->hcpriv)
75362306a36Sopenharmony_ci		ret = add_ep_quirk(hcd, udev, ep);
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	return ret;
75662306a36Sopenharmony_ci}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ciint xhci_mtk_drop_ep(struct usb_hcd *hcd, struct usb_device *udev,
75962306a36Sopenharmony_ci		     struct usb_host_endpoint *ep)
76062306a36Sopenharmony_ci{
76162306a36Sopenharmony_ci	int ret;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	ret = xhci_drop_endpoint(hcd, udev, ep);
76462306a36Sopenharmony_ci	if (ret)
76562306a36Sopenharmony_ci		return ret;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	/* needn't check @ep->hcpriv, xhci_endpoint_disable set it NULL */
76862306a36Sopenharmony_ci	drop_ep_quirk(hcd, udev, ep);
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	return 0;
77162306a36Sopenharmony_ci}
772