| From e77a6858bd041ddd0dbf07a72b0b163426383f99 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Wed, 22 Jul 2020 13:07:42 -0400 |
| Subject: bdc: Fix bug causing crash after multiple disconnects |
| |
| From: Sasi Kumar <sasi.kumar@broadcom.com> |
| |
| [ Upstream commit a95bdfd22076497288868c028619bc5995f5cc7f ] |
| |
| Multiple connects/disconnects can cause a crash on the second |
| disconnect. The driver had a problem where it would try to send |
| endpoint commands after it was disconnected which is not allowed |
| by the hardware. The fix is to only allow the endpoint commands |
| when the endpoint is connected. This will also fix issues that |
| showed up when using configfs to create gadgets. |
| |
| Signed-off-by: Sasi Kumar <sasi.kumar@broadcom.com> |
| Signed-off-by: Al Cooper <alcooperx@gmail.com> |
| Acked-by: Florian Fainelli <f.fainelli@gmail.com> |
| Signed-off-by: Felipe Balbi <balbi@kernel.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/usb/gadget/udc/bdc/bdc_core.c | 4 ++++ |
| drivers/usb/gadget/udc/bdc/bdc_ep.c | 16 ++++++++++------ |
| 2 files changed, 14 insertions(+), 6 deletions(-) |
| |
| diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c |
| index 02a3a774670b1..5fde5a8b065c1 100644 |
| --- a/drivers/usb/gadget/udc/bdc/bdc_core.c |
| +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c |
| @@ -282,6 +282,7 @@ static void bdc_mem_init(struct bdc *bdc, bool reinit) |
| * in that case reinit is passed as 1 |
| */ |
| if (reinit) { |
| + int i; |
| /* Enable interrupts */ |
| temp = bdc_readl(bdc->regs, BDC_BDCSC); |
| temp |= BDC_GIE; |
| @@ -291,6 +292,9 @@ static void bdc_mem_init(struct bdc *bdc, bool reinit) |
| /* Initialize SRR to 0 */ |
| memset(bdc->srr.sr_bds, 0, |
| NUM_SR_ENTRIES * sizeof(struct bdc_bd)); |
| + /* clear ep flags to avoid post disconnect stops/deconfigs */ |
| + for (i = 1; i < bdc->num_eps; ++i) |
| + bdc->bdc_ep_array[i]->flags = 0; |
| } else { |
| /* One time initiaization only */ |
| /* Enable status report function pointers */ |
| diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c |
| index d49c6dc1082dc..9ddc0b4e92c9c 100644 |
| --- a/drivers/usb/gadget/udc/bdc/bdc_ep.c |
| +++ b/drivers/usb/gadget/udc/bdc/bdc_ep.c |
| @@ -615,7 +615,6 @@ int bdc_ep_enable(struct bdc_ep *ep) |
| } |
| bdc_dbg_bd_list(bdc, ep); |
| /* only for ep0: config ep is called for ep0 from connect event */ |
| - ep->flags |= BDC_EP_ENABLED; |
| if (ep->ep_num == 1) |
| return ret; |
| |
| @@ -759,10 +758,13 @@ static int ep_dequeue(struct bdc_ep *ep, struct bdc_req *req) |
| __func__, ep->name, start_bdi, end_bdi); |
| dev_dbg(bdc->dev, "ep_dequeue ep=%p ep->desc=%p\n", |
| ep, (void *)ep->usb_ep.desc); |
| - /* Stop the ep to see where the HW is ? */ |
| - ret = bdc_stop_ep(bdc, ep->ep_num); |
| - /* if there is an issue with stopping ep, then no need to go further */ |
| - if (ret) |
| + /* if still connected, stop the ep to see where the HW is ? */ |
| + if (!(bdc_readl(bdc->regs, BDC_USPC) & BDC_PST_MASK)) { |
| + ret = bdc_stop_ep(bdc, ep->ep_num); |
| + /* if there is an issue, then no need to go further */ |
| + if (ret) |
| + return 0; |
| + } else |
| return 0; |
| |
| /* |
| @@ -1911,7 +1913,9 @@ static int bdc_gadget_ep_disable(struct usb_ep *_ep) |
| __func__, ep->name, ep->flags); |
| |
| if (!(ep->flags & BDC_EP_ENABLED)) { |
| - dev_warn(bdc->dev, "%s is already disabled\n", ep->name); |
| + if (bdc->gadget.speed != USB_SPEED_UNKNOWN) |
| + dev_warn(bdc->dev, "%s is already disabled\n", |
| + ep->name); |
| return 0; |
| } |
| spin_lock_irqsave(&bdc->lock, flags); |
| -- |
| 2.25.1 |
| |