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: 54f6a8af37
("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:
parent
5fa5827566
commit
e19ee44a3d
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue