summaryrefslogtreecommitdiff
path: root/drivers/usb/dwc3/ep0.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/dwc3/ep0.c')
-rw-r--r--drivers/usb/dwc3/ep0.c120
1 files changed, 94 insertions, 26 deletions
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index b13912d5fa99..4ee0faa87cff 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -16,6 +16,7 @@
* GNU General Public License for more details.
*/
+#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
@@ -34,6 +35,12 @@
#include "debug.h"
#include "gadget.h"
#include "io.h"
+#include "debug.h"
+
+
+static bool enable_dwc3_u1u2;
+module_param(enable_dwc3_u1u2, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(enable_dwc3_u1u2, "Enable support for U1U2 low power modes");
static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep);
static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
@@ -288,6 +295,7 @@ int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
struct dwc3_ep *dep = to_dwc3_ep(ep);
struct dwc3 *dwc = dep->dwc;
+ dbg_event(dep->number, "EP0STAL", value);
dwc3_ep0_stall_and_restart(dwc);
return 0;
@@ -313,7 +321,9 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
ret = dwc3_ep0_start_trans(dwc, 0, dwc->ctrl_req_addr, 8,
DWC3_TRBCTL_CONTROL_SETUP, false);
- WARN_ON(ret < 0);
+
+ if (WARN_ON_ONCE(ret < 0))
+ dbg_event(dwc->eps[0]->number, "EOUTSTART", ret);
}
static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le)
@@ -336,12 +346,24 @@ static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le)
static void dwc3_ep0_status_cmpl(struct usb_ep *ep, struct usb_request *req)
{
}
+
+static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
+{
+ int ret;
+
+ spin_unlock(&dwc->lock);
+ ret = dwc->gadget_driver->setup(&dwc->gadget, ctrl);
+ spin_lock(&dwc->lock);
+ return ret;
+}
+
/*
* ch 9.4.5
*/
static int dwc3_ep0_handle_status(struct dwc3 *dwc,
struct usb_ctrlrequest *ctrl)
{
+ int ret;
struct dwc3_ep *dep;
u32 recip;
u32 reg;
@@ -362,6 +384,9 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
usb_status |= 1 << USB_DEV_STAT_U1_ENABLED;
if (reg & DWC3_DCTL_INITU2ENA)
usb_status |= 1 << USB_DEV_STAT_U2_ENABLED;
+ } else {
+ usb_status |= dwc->gadget.remote_wakeup <<
+ USB_DEVICE_REMOTE_WAKEUP;
}
break;
@@ -371,6 +396,10 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
* Function Remote Wake Capable D0
* Function Remote Wakeup D1
*/
+
+ ret = dwc3_ep0_delegate_req(dwc, ctrl);
+ if (ret)
+ return ret;
break;
case USB_RECIP_ENDPOINT:
@@ -418,6 +447,9 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
switch (wValue) {
case USB_DEVICE_REMOTE_WAKEUP:
+ pr_debug("%s(): remote wakeup :%s\n", __func__,
+ (set ? "enabled" : "disabled"));
+ dwc->gadget.remote_wakeup = set;
break;
/*
* 9.4.1 says only only for SS, in AddressState only for
@@ -429,6 +461,9 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
if (dwc->speed != DWC3_DSTS_SUPERSPEED)
return -EINVAL;
+ if (dwc->usb3_u1u2_disable && !enable_dwc3_u1u2)
+ return -EINVAL;
+
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (set)
reg |= DWC3_DCTL_INITU1ENA;
@@ -443,6 +478,9 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
if (dwc->speed != DWC3_DSTS_SUPERSPEED)
return -EINVAL;
+ if (dwc->usb3_u1u2_disable && !enable_dwc3_u1u2)
+ return -EINVAL;
+
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (set)
reg |= DWC3_DCTL_INITU2ENA;
@@ -477,6 +515,9 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
if (wIndex & USB_INTRF_FUNC_SUSPEND_RW)
/* XXX enable remote wakeup */
;
+ ret = dwc3_ep0_delegate_req(dwc, ctrl);
+ if (ret)
+ return ret;
break;
default:
return -EINVAL;
@@ -538,16 +579,6 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
return 0;
}
-static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
-{
- int ret;
-
- spin_unlock(&dwc->lock);
- ret = dwc->gadget_driver->setup(&dwc->gadget, ctrl);
- spin_lock(&dwc->lock);
- return ret;
-}
-
static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
enum usb_device_state state = dwc->gadget.state;
@@ -576,13 +607,16 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
usb_gadget_set_state(&dwc->gadget,
USB_STATE_CONFIGURED);
- /*
- * Enable transition to U1/U2 state when
- * nothing is pending from application.
- */
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
- reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA);
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ if (!dwc->usb3_u1u2_disable || enable_dwc3_u1u2) {
+ /*
+ * Enable transition to U1/U2 state when
+ * nothing is pending from application.
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg |= (DWC3_DCTL_ACCEPTU1ENA |
+ DWC3_DCTL_ACCEPTU2ENA);
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ }
dwc->resize_fifos = true;
dwc3_trace(trace_dwc3_ep0, "resize FIFOs flag SET");
@@ -642,7 +676,8 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req)
/* now that we have the time, issue DGCMD Set Sel */
ret = dwc3_send_gadget_generic_command(dwc,
DWC3_DGCMD_SET_PERIODIC_PAR, param);
- WARN_ON(ret < 0);
+ if (WARN_ON_ONCE(ret < 0))
+ dbg_event(dep->number, "ESET_SELCMPL", ret);
}
static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
@@ -768,6 +803,7 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
dwc->ep0_next_event = DWC3_EP0_NRDY_DATA;
}
+ dbg_setup(0x00, ctrl);
if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
ret = dwc3_ep0_std_request(dwc, ctrl);
else
@@ -777,8 +813,10 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
dwc->delayed_status = true;
out:
- if (ret < 0)
+ if (ret < 0) {
+ dbg_event(0x0, "ERRSTAL", ret);
dwc3_ep0_stall_and_restart(dwc);
+ }
}
static void dwc3_ep0_complete_data(struct dwc3 *dwc,
@@ -860,7 +898,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
if ((epnum & 1) && ur->actual < ur->length) {
/* for some reason we did not get everything out */
-
+ dbg_event(epnum, "INDATSTAL", 0);
dwc3_ep0_stall_and_restart(dwc);
} else {
dwc3_gadget_giveback(ep0, r, 0);
@@ -874,7 +912,8 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
ret = dwc3_ep0_start_trans(dwc, epnum,
dwc->ctrl_req_addr, 0,
DWC3_TRBCTL_CONTROL_DATA, false);
- WARN_ON(ret < 0);
+ if (WARN_ON_ONCE(ret < 0))
+ dbg_event(epnum, "ECTRL_DATA", ret);
}
}
}
@@ -905,6 +944,7 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
if (ret < 0) {
dwc3_trace(trace_dwc3_ep0, "Invalid Test #%d",
dwc->test_mode_nr);
+ dbg_event(0x00, "INVALTEST", ret);
dwc3_ep0_stall_and_restart(dwc);
return;
}
@@ -914,6 +954,7 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
if (status == DWC3_TRBSTS_SETUP_PENDING)
dwc3_trace(trace_dwc3_ep0, "Setup Pending received");
+ dbg_print(dep->number, "DONE", status, "STATUS");
dwc->ep0state = EP0_SETUP_PHASE;
dwc3_ep0_out_start(dwc);
}
@@ -1003,7 +1044,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
false);
}
- WARN_ON(ret < 0);
+ dbg_queue(dep->number, &req->request, ret);
}
static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
@@ -1020,13 +1061,16 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)
{
+ int ret;
if (dwc->resize_fifos) {
dwc3_trace(trace_dwc3_ep0, "Resizing FIFOs");
dwc3_gadget_resize_tx_fifos(dwc);
dwc->resize_fifos = 0;
}
- WARN_ON(dwc3_ep0_start_control_status(dep));
+ ret = dwc3_ep0_start_control_status(dep);
+ if (WARN_ON_ONCE(ret))
+ dbg_event(dep->number, "ECTRLSTATUS", ret);
}
static void dwc3_ep0_do_control_status(struct dwc3 *dwc,
@@ -1051,18 +1095,28 @@ static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
memset(&params, 0, sizeof(params));
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, &params);
- WARN_ON_ONCE(ret);
+ if (ret) {
+ dev_dbg(dwc->dev, "%s: send ep cmd ENDTRANSFER failed",
+ dep->name);
+ dbg_event(dep->number, "EENDXFER", ret);
+ }
dep->resource_index = 0;
}
static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
+ u8 epnum;
+ struct dwc3_ep *dep;
+
dwc->setup_packet_pending = true;
+ epnum = event->endpoint_number;
+ dep = dwc->eps[epnum];
switch (event->status) {
case DEPEVT_STATUS_CONTROL_DATA:
dwc3_trace(trace_dwc3_ep0, "Control Data");
+ dep->dbg_ep_events.control_data++;
/*
* We already have a DATA transfer in the controller's cache,
@@ -1079,6 +1133,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
dwc3_trace(trace_dwc3_ep0,
"Wrong direction for Data phase");
dwc3_ep0_end_control_data(dwc, dep);
+ dbg_event(epnum, "WRONGDR", 0);
dwc3_ep0_stall_and_restart(dwc);
return;
}
@@ -1086,6 +1141,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
break;
case DEPEVT_STATUS_CONTROL_STATUS:
+ dep->dbg_ep_events.control_status++;
if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS)
return;
@@ -1094,7 +1150,8 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
dwc->ep0state = EP0_STATUS_PHASE;
if (dwc->delayed_status) {
- WARN_ON_ONCE(event->endpoint_number != 1);
+ if (event->endpoint_number != 1)
+ dbg_event(epnum, "EEPNUM", event->status);
dwc3_trace(trace_dwc3_ep0, "Delayed Status");
return;
}
@@ -1107,25 +1164,36 @@ void dwc3_ep0_interrupt(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
u8 epnum = event->endpoint_number;
+ struct dwc3_ep *dep;
dwc3_trace(trace_dwc3_ep0, "%s while ep%d%s in state '%s'",
dwc3_ep_event_string(event->endpoint_event),
epnum >> 1, (epnum & 1) ? "in" : "out",
dwc3_ep0_state_string(dwc->ep0state));
+ dep = dwc->eps[epnum];
switch (event->endpoint_event) {
case DWC3_DEPEVT_XFERCOMPLETE:
dwc3_ep0_xfer_complete(dwc, event);
+ dep->dbg_ep_events.xfercomplete++;
break;
case DWC3_DEPEVT_XFERNOTREADY:
dwc3_ep0_xfernotready(dwc, event);
+ dep->dbg_ep_events.xfernotready++;
break;
case DWC3_DEPEVT_XFERINPROGRESS:
+ dep->dbg_ep_events.xferinprogress++;
+ break;
case DWC3_DEPEVT_RXTXFIFOEVT:
+ dep->dbg_ep_events.rxtxfifoevent++;
+ break;
case DWC3_DEPEVT_STREAMEVT:
+ dep->dbg_ep_events.streamevent++;
+ break;
case DWC3_DEPEVT_EPCMDCMPLT:
+ dep->dbg_ep_events.epcmdcomplete++;
break;
}
}