| From c560e76319a94a3b9285bc426c609903408e4826 Mon Sep 17 00:00:00 2001 |
| From: Thinh Nguyen <Thinh.Nguyen@synopsys.com> |
| Date: Mon, 19 Apr 2021 19:11:12 -0700 |
| Subject: usb: dwc3: gadget: Fix START_TRANSFER link state check |
| |
| From: Thinh Nguyen <Thinh.Nguyen@synopsys.com> |
| |
| commit c560e76319a94a3b9285bc426c609903408e4826 upstream. |
| |
| The START_TRANSFER command needs to be executed while in ON/U0 link |
| state (with an exception during register initialization). Don't use |
| dwc->link_state to check this since the driver only tracks the link |
| state when the link state change interrupt is enabled. Check the link |
| state from DSTS register instead. |
| |
| Note that often the host already brings the device out of low power |
| before it sends/requests the next transfer. So, the user won't see any |
| issue when the device starts transfer then. This issue is more |
| noticeable in cases when the device delays starting transfer, which can |
| happen during delayed control status after the host put the device in |
| low power. |
| |
| Fixes: 799e9dc82968 ("usb: dwc3: gadget: conditionally disable Link State change events") |
| Cc: <stable@vger.kernel.org> |
| Acked-by: Felipe Balbi <balbi@kernel.org> |
| Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com> |
| Link: https://lore.kernel.org/r/bcefaa9ecbc3e1936858c0baa14de6612960e909.1618884221.git.Thinh.Nguyen@synopsys.com |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/usb/dwc3/gadget.c | 13 +++++++------ |
| 1 file changed, 7 insertions(+), 6 deletions(-) |
| |
| --- a/drivers/usb/dwc3/gadget.c |
| +++ b/drivers/usb/dwc3/gadget.c |
| @@ -308,13 +308,12 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ |
| } |
| |
| if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) { |
| - int needs_wakeup; |
| + int link_state; |
| |
| - needs_wakeup = (dwc->link_state == DWC3_LINK_STATE_U1 || |
| - dwc->link_state == DWC3_LINK_STATE_U2 || |
| - dwc->link_state == DWC3_LINK_STATE_U3); |
| - |
| - if (unlikely(needs_wakeup)) { |
| + link_state = dwc3_gadget_get_link_state(dwc); |
| + if (link_state == DWC3_LINK_STATE_U1 || |
| + link_state == DWC3_LINK_STATE_U2 || |
| + link_state == DWC3_LINK_STATE_U3) { |
| ret = __dwc3_gadget_wakeup(dwc); |
| dev_WARN_ONCE(dwc->dev, ret, "wakeup failed --> %d\n", |
| ret); |
| @@ -1975,6 +1974,8 @@ static int __dwc3_gadget_wakeup(struct d |
| case DWC3_LINK_STATE_RESET: |
| case DWC3_LINK_STATE_RX_DET: /* in HS, means Early Suspend */ |
| case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */ |
| + case DWC3_LINK_STATE_U2: /* in HS, means Sleep (L1) */ |
| + case DWC3_LINK_STATE_U1: |
| case DWC3_LINK_STATE_RESUME: |
| break; |
| default: |