Skip to content

Commit

Permalink
usb: xhci-mtk: fix unreleased bandwidth data
Browse files Browse the repository at this point in the history
xhci-mtk has hooks on add_endpoint() and drop_endpoint() from xhci
to handle its own sw bandwidth managements and stores bandwidth data
into internal table every time add_endpoint() is called,
so when one endpoint's bandwidth allocation fails, all earlier
endpoints from same interface still remain at the table.

This patch adds two more hooks from check_bandwidth() and
reset_bandwidth(), so mtk-xhci can releases all remaining
allocations in reset_bandwidth().

Fixes: 0cbd4b3 ("xhci: mediatek: support MTK xHCI host controller")
Signed-off-by: Ikjoon Jang <ikjn@chromium.org>
  • Loading branch information
ikjn-cros authored and intel-lab-lkp committed Nov 30, 2020
1 parent ab37fa8 commit 324226e
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 62 deletions.
163 changes: 101 additions & 62 deletions drivers/usb/host/xhci-mtk-sch.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,11 @@ static int is_fs_or_ls(enum usb_device_speed speed)
* so the bandwidth domain array is organized as follow for simplification:
* SSport0-OUT, SSport0-IN, ..., SSportX-OUT, SSportX-IN, HSport0, ..., HSportY
*/
static int get_bw_index(struct xhci_hcd *xhci, struct usb_device *udev,
struct usb_host_endpoint *ep)
static struct mu3h_sch_bw_info *get_bw_info(struct xhci_hcd_mtk *mtk,
struct usb_device *udev,
struct usb_host_endpoint *ep)
{
struct xhci_hcd *xhci = hcd_to_xhci(mtk->hcd);
struct xhci_virt_device *virt_dev;
int bw_index;

Expand All @@ -67,7 +69,7 @@ static int get_bw_index(struct xhci_hcd *xhci, struct usb_device *udev,
bw_index = virt_dev->real_port + xhci->usb3_rhub.num_ports - 1;
}

return bw_index;
return &mtk->sch_array[bw_index];
}

static u32 get_esit(struct xhci_ep_ctx *ep_ctx)
Expand Down Expand Up @@ -172,7 +174,6 @@ static struct mu3h_sch_ep_info *create_sch_ep(struct usb_device *udev,
struct usb_host_endpoint *ep, struct xhci_ep_ctx *ep_ctx)
{
struct mu3h_sch_ep_info *sch_ep;
struct mu3h_sch_tt *tt = NULL;
u32 len_bw_budget_table;
size_t mem_size;

Expand All @@ -190,15 +191,6 @@ static struct mu3h_sch_ep_info *create_sch_ep(struct usb_device *udev,
if (!sch_ep)
return ERR_PTR(-ENOMEM);

if (is_fs_or_ls(udev->speed)) {
tt = find_tt(udev);
if (IS_ERR(tt)) {
kfree(sch_ep);
return ERR_PTR(-ENOMEM);
}
}

sch_ep->sch_tt = tt;
sch_ep->ep = ep;

return sch_ep;
Expand Down Expand Up @@ -375,10 +367,10 @@ static void update_bus_bw(struct mu3h_sch_bw_info *sch_bw,
}
}

static int check_sch_tt(struct usb_device *udev,
struct mu3h_sch_ep_info *sch_ep, u32 offset)
static int check_sch_tt(struct mu3h_sch_tt *tt,
struct mu3h_sch_ep_info *sch_ep,
u32 offset)
{
struct mu3h_sch_tt *tt = sch_ep->sch_tt;
u32 extra_cs_count;
u32 fs_budget_start;
u32 start_ss, last_ss;
Expand Down Expand Up @@ -448,10 +440,9 @@ static int check_sch_tt(struct usb_device *udev,
return 0;
}

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

Expand All @@ -463,6 +454,7 @@ static void update_sch_tt(struct usb_device *udev,
}

list_add_tail(&sch_ep->tt_endpoint, &tt->ep_list);
sch_ep->sch_tt = tt;
}

static int check_sch_bw(struct usb_device *udev,
Expand All @@ -476,22 +468,28 @@ static int check_sch_bw(struct usb_device *udev,
u32 bw_boundary;
u32 min_num_budget;
u32 min_cs_count;
struct mu3h_sch_tt *tt = NULL;
bool tt_offset_ok = false;
int ret;

esit = sch_ep->esit;
if (is_fs_or_ls(udev->speed)) {
tt = find_tt(udev);
if (IS_ERR(tt))
return -ENOMEM;
}

/*
* Search through all possible schedule microframes.
* and find a microframe where its worst bandwidth is minimum.
*/
esit = sch_ep->esit;
min_bw = ~0;
min_index = 0;
min_cs_count = sch_ep->cs_count;
min_num_budget = sch_ep->num_budget_microframes;
for (offset = 0; offset < esit; offset++) {
if (is_fs_or_ls(udev->speed)) {
ret = check_sch_tt(udev, sch_ep, offset);
ret = check_sch_tt(tt, sch_ep, offset);
if (ret)
continue;
else
Expand Down Expand Up @@ -529,10 +527,11 @@ static int check_sch_bw(struct usb_device *udev,

if (is_fs_or_ls(udev->speed)) {
/* all offset for tt is not ok*/
if (!tt_offset_ok)
if (!tt_offset_ok) {
drop_tt(udev);
return -ERANGE;

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

/* update bus bandwidth info */
Expand Down Expand Up @@ -583,6 +582,8 @@ int xhci_mtk_sch_init(struct xhci_hcd_mtk *mtk)

mtk->sch_array = sch_array;

INIT_LIST_HEAD(&mtk->bw_ep_list_new);

return 0;
}
EXPORT_SYMBOL_GPL(xhci_mtk_sch_init);
Expand All @@ -597,18 +598,14 @@ int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
struct usb_host_endpoint *ep)
{
struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
struct xhci_hcd *xhci;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct xhci_ep_ctx *ep_ctx;
struct xhci_slot_ctx *slot_ctx;
struct xhci_virt_device *virt_dev;
struct mu3h_sch_bw_info *sch_bw;
struct mu3h_sch_ep_info *sch_ep;
struct mu3h_sch_bw_info *sch_array;
unsigned int ep_index;
int bw_index;
int ret = 0;

xhci = hcd_to_xhci(hcd);
virt_dev = xhci->devs[udev->slot_id];
ep_index = xhci_get_endpoint_index(&ep->desc);
slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx);
Expand All @@ -632,26 +629,14 @@ int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
return 0;
}

bw_index = get_bw_index(xhci, udev, ep);
sch_bw = &sch_array[bw_index];

sch_ep = create_sch_ep(udev, ep, ep_ctx);

if (IS_ERR_OR_NULL(sch_ep))
return -ENOMEM;

setup_sch_info(udev, ep_ctx, sch_ep);

ret = check_sch_bw(udev, sch_bw, sch_ep);
if (ret) {
xhci_err(xhci, "Not enough bandwidth!\n");
if (is_fs_or_ls(udev->speed))
drop_tt(udev);

kfree(sch_ep);
return -ENOSPC;
}

list_add_tail(&sch_ep->endpoint, &sch_bw->bw_ep_list);
list_add_tail(&sch_ep->endpoint, &mtk->bw_ep_list_new);

ep_ctx->reserved[0] |= cpu_to_le32(EP_BPKTS(sch_ep->pkts)
| EP_BCSCOUNT(sch_ep->cs_count) | EP_BBM(sch_ep->burst_mode));
Expand All @@ -666,22 +651,17 @@ int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
}
EXPORT_SYMBOL_GPL(xhci_mtk_add_ep_quirk);

void xhci_mtk_drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
struct usb_host_endpoint *ep)
static void xhci_mtk_drop_ep(struct xhci_hcd_mtk *mtk, struct usb_device *udev,
struct mu3h_sch_ep_info *sch_ep)
{
struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
struct xhci_hcd *xhci;
struct xhci_hcd *xhci = hcd_to_xhci(mtk->hcd);
struct xhci_slot_ctx *slot_ctx;
struct xhci_virt_device *virt_dev;
struct mu3h_sch_bw_info *sch_array;
struct mu3h_sch_bw_info *sch_bw;
struct mu3h_sch_ep_info *sch_ep;
int bw_index;
struct usb_host_endpoint *ep = sch_ep->ep;

xhci = hcd_to_xhci(hcd);
virt_dev = xhci->devs[udev->slot_id];
slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx);
sch_array = mtk->sch_array;

xhci_dbg(xhci, "%s() type:%d, speed:%d, mpks:%d, dir:%d, ep:%p\n",
__func__, usb_endpoint_type(&ep->desc), udev->speed,
Expand All @@ -691,20 +671,79 @@ void xhci_mtk_drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
if (!need_bw_sch(ep, udev->speed, slot_ctx->tt_info & TT_SLOT))
return;

bw_index = get_bw_index(xhci, udev, ep);
sch_bw = &sch_array[bw_index];
sch_bw = get_bw_info(mtk, udev, ep);

list_for_each_entry(sch_ep, &sch_bw->bw_ep_list, endpoint) {
update_bus_bw(sch_bw, sch_ep, 0);

list_del(&sch_ep->endpoint);

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

void xhci_mtk_drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
struct usb_host_endpoint *ep)
{
struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
struct mu3h_sch_bw_info *sch_bw;
struct mu3h_sch_ep_info *sch_ep, *tmp;

sch_bw = get_bw_info(mtk, udev, ep);

list_for_each_entry_safe(sch_ep, tmp, &sch_bw->bw_ep_list, endpoint) {
if (sch_ep->ep == ep) {
update_bus_bw(sch_bw, sch_ep, 0);
list_del(&sch_ep->endpoint);
if (is_fs_or_ls(udev->speed)) {
list_del(&sch_ep->tt_endpoint);
drop_tt(udev);
}
kfree(sch_ep);
xhci_mtk_drop_ep(mtk, udev, sch_ep);
break;
}
}
}
EXPORT_SYMBOL_GPL(xhci_mtk_drop_ep_quirk);

int xhci_mtk_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
{
struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct mu3h_sch_ep_info *sch_ep, *tmp;

dev_dbg(&udev->dev, "%s\n", __func__);

list_for_each_entry(sch_ep, &mtk->bw_ep_list_new, endpoint) {
int ret;
struct mu3h_sch_bw_info *sch_bw;

sch_bw = get_bw_info(mtk, udev, sch_ep->ep);

ret = check_sch_bw(udev, sch_bw, sch_ep);
if (ret) {
xhci_err(xhci, "Not enough bandwidth!\n");
return ret;
}
}

list_for_each_entry_safe(sch_ep, tmp, &mtk->bw_ep_list_new, endpoint) {
struct mu3h_sch_bw_info *sch_bw;

sch_bw = get_bw_info(mtk, udev, sch_ep->ep);

list_move_tail(&sch_ep->endpoint, &sch_bw->bw_ep_list);
}

return 0;
}
EXPORT_SYMBOL_GPL(xhci_mtk_check_bandwidth);

void xhci_mtk_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
{
struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
struct mu3h_sch_ep_info *sch_ep, *tmp;

dev_dbg(&udev->dev, "%s\n", __func__);

list_for_each_entry_safe(sch_ep, tmp, &mtk->bw_ep_list_new, endpoint) {
xhci_mtk_drop_ep(mtk, udev, sch_ep);
}
}
EXPORT_SYMBOL_GPL(xhci_mtk_reset_bandwidth);
13 changes: 13 additions & 0 deletions drivers/usb/host/xhci-mtk.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ struct mu3c_ippc_regs {
struct xhci_hcd_mtk {
struct device *dev;
struct usb_hcd *hcd;
struct list_head bw_ep_list_new;
struct mu3h_sch_bw_info *sch_array;
struct mu3c_ippc_regs __iomem *ippc_regs;
bool has_ippc;
Expand Down Expand Up @@ -166,6 +167,8 @@ int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
struct usb_host_endpoint *ep);
void xhci_mtk_drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
struct usb_host_endpoint *ep);
int xhci_mtk_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev);
void xhci_mtk_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev);

#else
static inline int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd,
Expand All @@ -179,6 +182,16 @@ static inline void xhci_mtk_drop_ep_quirk(struct usb_hcd *hcd,
{
}

static inline int xhci_mtk_check_bandwidth(struct usb_hcd *hcd,
struct usb_device *udev)
{
return 0;
}

static inline void xhci_mtk_reset_bandwidth(struct usb_hcd *hcd,
struct usb_device *udev)
{
}
#endif

#endif /* _XHCI_MTK_H_ */
9 changes: 9 additions & 0 deletions drivers/usb/host/xhci.c
Original file line number Diff line number Diff line change
Expand Up @@ -2880,6 +2880,12 @@ static int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
xhci_dbg(xhci, "%s called for udev %p\n", __func__, udev);
virt_dev = xhci->devs[udev->slot_id];

if (xhci->quirks & XHCI_MTK_HOST) {
ret = xhci_mtk_check_bandwidth(hcd, udev);
if (ret < 0)
return ret;
}

command = xhci_alloc_command(xhci, true, GFP_KERNEL);
if (!command)
return -ENOMEM;
Expand Down Expand Up @@ -2968,6 +2974,9 @@ static void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
return;
xhci = hcd_to_xhci(hcd);

if (xhci->quirks & XHCI_MTK_HOST)
xhci_mtk_reset_bandwidth(hcd, udev);

xhci_dbg(xhci, "%s called for udev %p\n", __func__, udev);
virt_dev = xhci->devs[udev->slot_id];
/* Free any rings allocated for added endpoints */
Expand Down

0 comments on commit 324226e

Please sign in to comment.