usb: xhci-mtk: improve bandwidth scheduling with TT

commit e19ee44a3d07c232f9241024dab1ebd0748cdf5f upstream.

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: 54f6a8af3722 ("usb: xhci-mtk: skip dropping bandwidth of unchecked endpoints")
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Yaqii Wu <yaqii.wu@mediatek.com>
Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
Link: https://lore.kernel.org/r/2f30e81400a59afef5f8231c98149169c7520519.1615170625.git.chunfeng.yun@mediatek.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Chunfeng Yun 2021-03-08 10:51:51 +08:00 committed by Greg Kroah-Hartman
parent 9f0d3e676a
commit 2b8b8cc94f
2 changed files with 64 additions and 16 deletions

View File

@ -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;
}
list_add_tail(&sch_ep->tt_endpoint, &tt->ep_list);
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);
}
if (sch_ep->sch_tt)
drop_tt(udev);
list_del(&sch_ep->endpoint);
if (sch_ep->sch_tt) {
list_del(&sch_ep->tt_endpoint);
drop_tt(udev);
}
kfree(sch_ep);
}

View File

@ -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;