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;
|
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,
|
static int check_sch_tt(struct usb_device *udev,
|
||||||
struct mu3h_sch_ep_info *sch_ep, u32 offset)
|
struct mu3h_sch_ep_info *sch_ep, u32 offset)
|
||||||
{
|
{
|
||||||
|
@ -402,7 +427,7 @@ static int check_sch_tt(struct usb_device *udev,
|
||||||
return -ERANGE;
|
return -ERANGE;
|
||||||
|
|
||||||
for (i = 0; i < sch_ep->cs_count; i++)
|
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;
|
return -ERANGE;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -432,7 +457,7 @@ static int check_sch_tt(struct usb_device *udev,
|
||||||
cs_count = 7; /* HW limit */
|
cs_count = 7; /* HW limit */
|
||||||
|
|
||||||
for (i = 0; i < cs_count + 2; i++) {
|
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;
|
return -ERANGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,24 +473,44 @@ static int check_sch_tt(struct usb_device *udev,
|
||||||
sch_ep->num_budget_microframes = sch_ep->esit;
|
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,
|
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;
|
struct mu3h_sch_tt *tt = sch_ep->sch_tt;
|
||||||
u32 base, num_esit;
|
u32 base, num_esit;
|
||||||
|
int bw_updated;
|
||||||
|
int bits;
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit;
|
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++) {
|
for (i = 0; i < num_esit; i++) {
|
||||||
base = sch_ep->offset + i * sch_ep->esit;
|
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,
|
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)
|
if (!tt_offset_ok)
|
||||||
return -ERANGE;
|
return -ERANGE;
|
||||||
|
|
||||||
update_sch_tt(udev, sch_ep);
|
update_sch_tt(udev, sch_ep, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* update bus bandwidth info */
|
/* 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)
|
struct mu3h_sch_bw_info *sch_bw, struct mu3h_sch_ep_info *sch_ep)
|
||||||
{
|
{
|
||||||
/* only release ep bw check passed by check_sch_bw() */
|
/* 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);
|
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);
|
list_del(&sch_ep->endpoint);
|
||||||
|
|
||||||
if (sch_ep->sch_tt) {
|
|
||||||
list_del(&sch_ep->tt_endpoint);
|
|
||||||
drop_tt(udev);
|
|
||||||
}
|
|
||||||
kfree(sch_ep);
|
kfree(sch_ep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,13 +20,15 @@
|
||||||
#define XHCI_MTK_MAX_ESIT 64
|
#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
|
* @ep_list: Endpoints using this TT
|
||||||
* @usb_tt: usb TT related
|
* @usb_tt: usb TT related
|
||||||
* @tt_port: TT port number
|
* @tt_port: TT port number
|
||||||
*/
|
*/
|
||||||
struct mu3h_sch_tt {
|
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 list_head ep_list;
|
||||||
struct usb_tt *usb_tt;
|
struct usb_tt *usb_tt;
|
||||||
int tt_port;
|
int tt_port;
|
||||||
|
|
Loading…
Reference in New Issue