Skip to content
Permalink
Browse files
usb: xhci-mtk: fix unreleased bandwidth data
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 bandwidth allocation fails at one endpoint, all earlier
allocation from the same interface could still remain at the table.

This patch adds two more hooks from check_bandwidth() and
reset_bandwidth(), and make mtk-xhci to releases all failed endpoints
from 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 Dec 10, 2020
1 parent 2baaee2 commit a99b1fe33b427dd2ca384c78d50b6d05fb04e56d
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 35 deletions.
@@ -583,6 +583,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);
@@ -597,16 +599,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;
unsigned int ep_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);
@@ -637,42 +637,37 @@ int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,

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, &mtk->bw_ep_list_new);

list_add_tail(&sch_ep->endpoint, &sch_bw->bw_ep_list);
return 0;
}
EXPORT_SYMBOL_GPL(xhci_mtk_add_ep_quirk);

ep_ctx->reserved[0] |= cpu_to_le32(EP_BPKTS(sch_ep->pkts)
| EP_BCSCOUNT(sch_ep->cs_count) | EP_BBM(sch_ep->burst_mode));
ep_ctx->reserved[1] |= cpu_to_le32(EP_BOFFSET(sch_ep->offset)
| EP_BREPEAT(sch_ep->repeat));
static void xhci_mtk_drop_ep(struct xhci_hcd_mtk *mtk, struct usb_device *udev,
struct mu3h_sch_ep_info *sch_ep)
{
struct mu3h_sch_bw_info *sch_bw = get_bw_info(mtk, udev, sch_ep->ep);

xhci_dbg(xhci, " PKTS:%x, CSCOUNT:%x, BM:%x, OFFSET:%x, REPEAT:%x\n",
sch_ep->pkts, sch_ep->cs_count, sch_ep->burst_mode,
sch_ep->offset, sch_ep->repeat);
update_bus_bw(sch_bw, sch_ep, 0);
list_del(&sch_ep->endpoint);

return 0;
if (sch_ep->sch_tt) {
list_del(&sch_ep->tt_endpoint);
drop_tt(udev);
}
kfree(sch_ep);
}
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)
struct usb_host_endpoint *ep)
{
struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
struct xhci_hcd *xhci;
struct xhci_slot_ctx *slot_ctx;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct xhci_virt_device *virt_dev;
struct xhci_slot_ctx *slot_ctx;
struct mu3h_sch_bw_info *sch_bw;
struct mu3h_sch_ep_info *sch_ep;
struct mu3h_sch_ep_info *sch_ep, *tmp;

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

@@ -686,17 +681,72 @@ void xhci_mtk_drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,

sch_bw = get_bw_info(mtk, udev, ep);

list_for_each_entry(sch_ep, &sch_bw->bw_ep_list, endpoint) {
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 xhci_virt_device *virt_dev = xhci->devs[udev->slot_id];
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;
struct xhci_ep_ctx *ep_ctx;
struct usb_host_endpoint *ep = sch_ep->ep;
unsigned int ep_index = xhci_get_endpoint_index(&ep->desc);

sch_bw = get_bw_info(mtk, udev, ep);

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

ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index);
ep_ctx->reserved[0] |= cpu_to_le32(EP_BPKTS(sch_ep->pkts)
| EP_BCSCOUNT(sch_ep->cs_count)
| EP_BBM(sch_ep->burst_mode));
ep_ctx->reserved[1] |= cpu_to_le32(EP_BOFFSET(sch_ep->offset)
| EP_BREPEAT(sch_ep->repeat));

xhci_dbg(xhci, " PKTS:%x, CSCOUNT:%x, BM:%x, OFFSET:%x, REPEAT:%x\n",
sch_ep->pkts, sch_ep->cs_count, sch_ep->burst_mode,
sch_ep->offset, sch_ep->repeat);
}

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);
@@ -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;
@@ -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,
@@ -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_ */
@@ -3006,6 +3006,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;
@@ -3094,6 +3100,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 */

0 comments on commit a99b1fe

Please sign in to comment.