Commit e19ee44a authored by Chunfeng Yun's avatar Chunfeng Yun Committed by Greg Kroah-Hartman
Browse files

usb: xhci-mtk: improve bandwidth scheduling with TT



When the USB headset is plug into an external hub, sometimes
can't set config due to not enough bandwidth, so need improve
LS/FS INT/ISOC bandwidth scheduling with TT.

Fixes: 54f6a8af ("usb: xhci-mtk: skip dropping bandwidth of unchecked endpoints")
Cc: stable <stable@vger.kernel.org>
Signed-off-by: default avatarYaqii Wu <yaqii.wu@mediatek.com>
Signed-off-by: default avatarChunfeng Yun <chunfeng.yun@mediatek.com>
Link: https://lore.kernel.org/r/2f30e81400a59afef5f8231c98149169c7520519.1615170625.git.chunfeng.yun@mediatek.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 5fa58275
Loading
Loading
Loading
Loading
+60 −14
Original line number Diff line number Diff line
@@ -378,6 +378,31 @@ static void update_bus_bw(struct mu3h_sch_bw_info *sch_bw,
	sch_ep->allocated = used;
}

static int check_fs_bus_bw(struct mu3h_sch_ep_info *sch_ep, int offset)
{
	struct mu3h_sch_tt *tt = sch_ep->sch_tt;
	u32 num_esit, tmp;
	int base;
	int i, j;

	num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit;
	for (i = 0; i < num_esit; i++) {
		base = offset + i * sch_ep->esit;

		/*
		 * Compared with hs bus, no matter what ep type,
		 * the hub will always delay one uframe to send data
		 */
		for (j = 0; j < sch_ep->cs_count; j++) {
			tmp = tt->fs_bus_bw[base + j] + sch_ep->bw_cost_per_microframe;
			if (tmp > FS_PAYLOAD_MAX)
				return -ERANGE;
		}
	}

	return 0;
}

static int check_sch_tt(struct usb_device *udev,
	struct mu3h_sch_ep_info *sch_ep, u32 offset)
{
@@ -402,7 +427,7 @@ static int check_sch_tt(struct usb_device *udev,
			return -ERANGE;

		for (i = 0; i < sch_ep->cs_count; i++)
			if (test_bit(offset + i, tt->split_bit_map))
			if (test_bit(offset + i, tt->ss_bit_map))
				return -ERANGE;

	} else {
@@ -432,7 +457,7 @@ static int check_sch_tt(struct usb_device *udev,
			cs_count = 7; /* HW limit */

		for (i = 0; i < cs_count + 2; i++) {
			if (test_bit(offset + i, tt->split_bit_map))
			if (test_bit(offset + i, tt->ss_bit_map))
				return -ERANGE;
		}

@@ -448,24 +473,44 @@ static int check_sch_tt(struct usb_device *udev,
			sch_ep->num_budget_microframes = sch_ep->esit;
	}

	return 0;
	return check_fs_bus_bw(sch_ep, offset);
}

static void update_sch_tt(struct usb_device *udev,
	struct mu3h_sch_ep_info *sch_ep)
	struct mu3h_sch_ep_info *sch_ep, bool used)
{
	struct mu3h_sch_tt *tt = sch_ep->sch_tt;
	u32 base, num_esit;
	int bw_updated;
	int bits;
	int i, j;

	num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit;
	bits = (sch_ep->ep_type == ISOC_OUT_EP) ? sch_ep->cs_count : 1;

	if (used)
		bw_updated = sch_ep->bw_cost_per_microframe;
	else
		bw_updated = -sch_ep->bw_cost_per_microframe;

	for (i = 0; i < num_esit; i++) {
		base = sch_ep->offset + i * sch_ep->esit;
		for (j = 0; j < sch_ep->num_budget_microframes; j++)
			set_bit(base + j, tt->split_bit_map);

		for (j = 0; j < bits; j++) {
			if (used)
				set_bit(base + j, tt->ss_bit_map);
			else
				clear_bit(base + j, tt->ss_bit_map);
		}

		for (j = 0; j < sch_ep->cs_count; j++)
			tt->fs_bus_bw[base + j] += bw_updated;
	}

	if (used)
		list_add_tail(&sch_ep->tt_endpoint, &tt->ep_list);
	else
		list_del(&sch_ep->tt_endpoint);
}

static int check_sch_bw(struct usb_device *udev,
@@ -535,7 +580,7 @@ static int check_sch_bw(struct usb_device *udev,
		if (!tt_offset_ok)
			return -ERANGE;

		update_sch_tt(udev, sch_ep);
		update_sch_tt(udev, sch_ep, 1);
	}

	/* update bus bandwidth info */
@@ -548,15 +593,16 @@ static void destroy_sch_ep(struct usb_device *udev,
	struct mu3h_sch_bw_info *sch_bw, struct mu3h_sch_ep_info *sch_ep)
{
	/* only release ep bw check passed by check_sch_bw() */
	if (sch_ep->allocated)
	if (sch_ep->allocated) {
		update_bus_bw(sch_bw, sch_ep, 0);
		if (sch_ep->sch_tt)
			update_sch_tt(udev, sch_ep, 0);
	}

	list_del(&sch_ep->endpoint);

	if (sch_ep->sch_tt) {
		list_del(&sch_ep->tt_endpoint);
	if (sch_ep->sch_tt)
		drop_tt(udev);
	}

	list_del(&sch_ep->endpoint);
	kfree(sch_ep);
}

+4 −2
Original line number Diff line number Diff line
@@ -20,13 +20,15 @@
#define XHCI_MTK_MAX_ESIT	64

/**
 * @split_bit_map: used to avoid split microframes overlay
 * @ss_bit_map: used to avoid start split microframes overlay
 * @fs_bus_bw: array to keep track of bandwidth already used for FS
 * @ep_list: Endpoints using this TT
 * @usb_tt: usb TT related
 * @tt_port: TT port number
 */
struct mu3h_sch_tt {
	DECLARE_BITMAP(split_bit_map, XHCI_MTK_MAX_ESIT);
	DECLARE_BITMAP(ss_bit_map, XHCI_MTK_MAX_ESIT);
	u32 fs_bus_bw[XHCI_MTK_MAX_ESIT];
	struct list_head ep_list;
	struct usb_tt *usb_tt;
	int tt_port;