|
@@ -145,21 +145,29 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
|
|
|
return -ETIMEDOUT;
|
|
|
}
|
|
|
|
|
|
-static void dwc3_ep_inc_enq(struct dwc3_ep *dep)
|
|
|
+/**
|
|
|
+ * dwc3_ep_inc_trb() - Increment a TRB index.
|
|
|
+ * @index - Pointer to the TRB index to increment.
|
|
|
+ *
|
|
|
+ * The index should never point to the link TRB. After incrementing,
|
|
|
+ * if it is point to the link TRB, wrap around to the beginning. The
|
|
|
+ * link TRB is always at the last TRB entry.
|
|
|
+ */
|
|
|
+static void dwc3_ep_inc_trb(u8 *index)
|
|
|
{
|
|
|
- dep->trb_enqueue++;
|
|
|
- dep->trb_enqueue %= DWC3_TRB_NUM;
|
|
|
+ (*index)++;
|
|
|
+ if (*index == (DWC3_TRB_NUM - 1))
|
|
|
+ *index = 0;
|
|
|
}
|
|
|
|
|
|
-static void dwc3_ep_inc_deq(struct dwc3_ep *dep)
|
|
|
+static void dwc3_ep_inc_enq(struct dwc3_ep *dep)
|
|
|
{
|
|
|
- dep->trb_dequeue++;
|
|
|
- dep->trb_dequeue %= DWC3_TRB_NUM;
|
|
|
+ dwc3_ep_inc_trb(&dep->trb_enqueue);
|
|
|
}
|
|
|
|
|
|
-static int dwc3_ep_is_last_trb(unsigned int index)
|
|
|
+static void dwc3_ep_inc_deq(struct dwc3_ep *dep)
|
|
|
{
|
|
|
- return index == DWC3_TRB_NUM - 1;
|
|
|
+ dwc3_ep_inc_trb(&dep->trb_dequeue);
|
|
|
}
|
|
|
|
|
|
void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
|
@@ -172,13 +180,6 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
|
|
i = 0;
|
|
|
do {
|
|
|
dwc3_ep_inc_deq(dep);
|
|
|
- /*
|
|
|
- * Skip LINK TRB. We can't use req->trb and check for
|
|
|
- * DWC3_TRBCTL_LINK_TRB because it points the TRB we
|
|
|
- * just completed (not the LINK TRB).
|
|
|
- */
|
|
|
- if (dwc3_ep_is_last_trb(dep->trb_dequeue))
|
|
|
- dwc3_ep_inc_deq(dep);
|
|
|
} while(++i < req->request.num_mapped_sgs);
|
|
|
req->started = false;
|
|
|
}
|
|
@@ -199,57 +200,54 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
|
|
spin_unlock(&dwc->lock);
|
|
|
usb_gadget_giveback_request(&dep->endpoint, &req->request);
|
|
|
spin_lock(&dwc->lock);
|
|
|
+
|
|
|
+ if (dep->number > 1)
|
|
|
+ pm_runtime_put(dwc->dev);
|
|
|
}
|
|
|
|
|
|
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
|
|
|
{
|
|
|
u32 timeout = 500;
|
|
|
+ int status = 0;
|
|
|
+ int ret = 0;
|
|
|
u32 reg;
|
|
|
|
|
|
- trace_dwc3_gadget_generic_cmd(cmd, param);
|
|
|
-
|
|
|
dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param);
|
|
|
dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT);
|
|
|
|
|
|
do {
|
|
|
reg = dwc3_readl(dwc->regs, DWC3_DGCMD);
|
|
|
if (!(reg & DWC3_DGCMD_CMDACT)) {
|
|
|
- dwc3_trace(trace_dwc3_gadget,
|
|
|
- "Command Complete --> %d",
|
|
|
- DWC3_DGCMD_STATUS(reg));
|
|
|
- if (DWC3_DGCMD_STATUS(reg))
|
|
|
- return -EINVAL;
|
|
|
- return 0;
|
|
|
+ status = DWC3_DGCMD_STATUS(reg);
|
|
|
+ if (status)
|
|
|
+ ret = -EINVAL;
|
|
|
+ break;
|
|
|
}
|
|
|
+ } while (timeout--);
|
|
|
|
|
|
- /*
|
|
|
- * We can't sleep here, because it's also called from
|
|
|
- * interrupt context.
|
|
|
- */
|
|
|
- timeout--;
|
|
|
- if (!timeout) {
|
|
|
- dwc3_trace(trace_dwc3_gadget,
|
|
|
- "Command Timed Out");
|
|
|
- return -ETIMEDOUT;
|
|
|
- }
|
|
|
- udelay(1);
|
|
|
- } while (1);
|
|
|
+ if (!timeout) {
|
|
|
+ ret = -ETIMEDOUT;
|
|
|
+ status = -ETIMEDOUT;
|
|
|
+ }
|
|
|
+
|
|
|
+ trace_dwc3_gadget_generic_cmd(cmd, param, status);
|
|
|
+
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
static int __dwc3_gadget_wakeup(struct dwc3 *dwc);
|
|
|
|
|
|
-int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
|
|
- unsigned cmd, struct dwc3_gadget_ep_cmd_params *params)
|
|
|
+int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
|
|
|
+ struct dwc3_gadget_ep_cmd_params *params)
|
|
|
{
|
|
|
- struct dwc3_ep *dep = dwc->eps[ep];
|
|
|
+ struct dwc3 *dwc = dep->dwc;
|
|
|
u32 timeout = 500;
|
|
|
u32 reg;
|
|
|
|
|
|
+ int cmd_status = 0;
|
|
|
int susphy = false;
|
|
|
int ret = -EINVAL;
|
|
|
|
|
|
- trace_dwc3_gadget_ep_cmd(dep, cmd, params);
|
|
|
-
|
|
|
/*
|
|
|
* Synopsys Databook 2.60a states, on section 6.3.2.5.[1-8], that if
|
|
|
* we're issuing an endpoint command, we must check if
|
|
@@ -258,11 +256,13 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
|
|
* We will also set SUSPHY bit to what it was before returning as stated
|
|
|
* by the same section on Synopsys databook.
|
|
|
*/
|
|
|
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
|
|
|
- if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) {
|
|
|
- susphy = true;
|
|
|
- reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
|
|
|
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
|
|
+ if (dwc->gadget.speed <= USB_SPEED_HIGH) {
|
|
|
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
|
|
|
+ if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) {
|
|
|
+ susphy = true;
|
|
|
+ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
|
|
|
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
if (cmd == DWC3_DEPCMD_STARTTRANSFER) {
|
|
@@ -279,26 +279,21 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- dwc3_writel(dwc->regs, DWC3_DEPCMDPAR0(ep), params->param0);
|
|
|
- dwc3_writel(dwc->regs, DWC3_DEPCMDPAR1(ep), params->param1);
|
|
|
- dwc3_writel(dwc->regs, DWC3_DEPCMDPAR2(ep), params->param2);
|
|
|
+ dwc3_writel(dep->regs, DWC3_DEPCMDPAR0, params->param0);
|
|
|
+ dwc3_writel(dep->regs, DWC3_DEPCMDPAR1, params->param1);
|
|
|
+ dwc3_writel(dep->regs, DWC3_DEPCMDPAR2, params->param2);
|
|
|
|
|
|
- dwc3_writel(dwc->regs, DWC3_DEPCMD(ep), cmd | DWC3_DEPCMD_CMDACT);
|
|
|
+ dwc3_writel(dep->regs, DWC3_DEPCMD, cmd | DWC3_DEPCMD_CMDACT);
|
|
|
do {
|
|
|
- reg = dwc3_readl(dwc->regs, DWC3_DEPCMD(ep));
|
|
|
+ reg = dwc3_readl(dep->regs, DWC3_DEPCMD);
|
|
|
if (!(reg & DWC3_DEPCMD_CMDACT)) {
|
|
|
- int cmd_status = DWC3_DEPCMD_STATUS(reg);
|
|
|
-
|
|
|
- dwc3_trace(trace_dwc3_gadget,
|
|
|
- "Command Complete --> %d",
|
|
|
- cmd_status);
|
|
|
+ cmd_status = DWC3_DEPCMD_STATUS(reg);
|
|
|
|
|
|
switch (cmd_status) {
|
|
|
case 0:
|
|
|
ret = 0;
|
|
|
break;
|
|
|
case DEPEVT_TRANSFER_NO_RESOURCE:
|
|
|
- dwc3_trace(trace_dwc3_gadget, "%s: no resource available");
|
|
|
ret = -EINVAL;
|
|
|
break;
|
|
|
case DEPEVT_TRANSFER_BUS_EXPIRY:
|
|
@@ -313,7 +308,6 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
|
|
* give a hint to the gadget driver that this is
|
|
|
* the case by returning -EAGAIN.
|
|
|
*/
|
|
|
- dwc3_trace(trace_dwc3_gadget, "%s: bus expiry");
|
|
|
ret = -EAGAIN;
|
|
|
break;
|
|
|
default:
|
|
@@ -322,21 +316,14 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
|
|
|
|
|
break;
|
|
|
}
|
|
|
+ } while (--timeout);
|
|
|
|
|
|
- /*
|
|
|
- * We can't sleep here, because it is also called from
|
|
|
- * interrupt context.
|
|
|
- */
|
|
|
- timeout--;
|
|
|
- if (!timeout) {
|
|
|
- dwc3_trace(trace_dwc3_gadget,
|
|
|
- "Command Timed Out");
|
|
|
- ret = -ETIMEDOUT;
|
|
|
- break;
|
|
|
- }
|
|
|
+ if (timeout == 0) {
|
|
|
+ ret = -ETIMEDOUT;
|
|
|
+ cmd_status = -ETIMEDOUT;
|
|
|
+ }
|
|
|
|
|
|
- udelay(1);
|
|
|
- } while (1);
|
|
|
+ trace_dwc3_gadget_ep_cmd(dep, cmd, params, cmd_status);
|
|
|
|
|
|
if (unlikely(susphy)) {
|
|
|
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
|
|
@@ -366,7 +353,7 @@ static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep)
|
|
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
|
|
- return dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);
|
|
|
+ return dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
|
|
|
}
|
|
|
|
|
|
static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep,
|
|
@@ -454,7 +441,7 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
|
|
|
memset(¶ms, 0x00, sizeof(params));
|
|
|
cmd = DWC3_DEPCMD_DEPSTARTCFG;
|
|
|
|
|
|
- ret = dwc3_send_gadget_ep_cmd(dwc, 0, cmd, ¶ms);
|
|
|
+ ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
@@ -475,10 +462,14 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
|
|
|
static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
|
|
|
const struct usb_endpoint_descriptor *desc,
|
|
|
const struct usb_ss_ep_comp_descriptor *comp_desc,
|
|
|
- bool ignore, bool restore)
|
|
|
+ bool modify, bool restore)
|
|
|
{
|
|
|
struct dwc3_gadget_ep_cmd_params params;
|
|
|
|
|
|
+ if (dev_WARN_ONCE(dwc->dev, modify && restore,
|
|
|
+ "Can't modify and restore\n"))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
memset(¶ms, 0x00, sizeof(params));
|
|
|
|
|
|
params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc))
|
|
@@ -487,30 +478,22 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
|
|
|
/* Burst size is only needed in SuperSpeed mode */
|
|
|
if (dwc->gadget.speed >= USB_SPEED_SUPER) {
|
|
|
u32 burst = dep->endpoint.maxburst;
|
|
|
- u32 nump;
|
|
|
- u32 reg;
|
|
|
-
|
|
|
- /* update NumP */
|
|
|
- reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
|
|
- nump = DWC3_DCFG_NUMP(reg);
|
|
|
- nump = max(nump, burst);
|
|
|
- reg &= ~DWC3_DCFG_NUMP_MASK;
|
|
|
- reg |= nump << DWC3_DCFG_NUMP_SHIFT;
|
|
|
- dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
|
|
-
|
|
|
params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst - 1);
|
|
|
}
|
|
|
|
|
|
- if (ignore)
|
|
|
- params.param0 |= DWC3_DEPCFG_IGN_SEQ_NUM;
|
|
|
-
|
|
|
- if (restore) {
|
|
|
+ if (modify) {
|
|
|
+ params.param0 |= DWC3_DEPCFG_ACTION_MODIFY;
|
|
|
+ } else if (restore) {
|
|
|
params.param0 |= DWC3_DEPCFG_ACTION_RESTORE;
|
|
|
params.param2 |= dep->saved_state;
|
|
|
+ } else {
|
|
|
+ params.param0 |= DWC3_DEPCFG_ACTION_INIT;
|
|
|
}
|
|
|
|
|
|
- params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN
|
|
|
- | DWC3_DEPCFG_XFER_NOT_READY_EN;
|
|
|
+ params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN;
|
|
|
+
|
|
|
+ if (dep->number <= 1 || usb_endpoint_xfer_isoc(desc))
|
|
|
+ params.param1 |= DWC3_DEPCFG_XFER_NOT_READY_EN;
|
|
|
|
|
|
if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) {
|
|
|
params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE
|
|
@@ -541,8 +524,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
|
|
|
dep->interval = 1 << (desc->bInterval - 1);
|
|
|
}
|
|
|
|
|
|
- return dwc3_send_gadget_ep_cmd(dwc, dep->number,
|
|
|
- DWC3_DEPCMD_SETEPCONFIG, ¶ms);
|
|
|
+ return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, ¶ms);
|
|
|
}
|
|
|
|
|
|
static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep)
|
|
@@ -553,8 +535,8 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep)
|
|
|
|
|
|
params.param0 = DWC3_DEPXFERCFG_NUM_XFER_RES(1);
|
|
|
|
|
|
- return dwc3_send_gadget_ep_cmd(dwc, dep->number,
|
|
|
- DWC3_DEPCMD_SETTRANSFRESOURCE, ¶ms);
|
|
|
+ return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETTRANSFRESOURCE,
|
|
|
+ ¶ms);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -567,7 +549,7 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep)
|
|
|
static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
|
|
|
const struct usb_endpoint_descriptor *desc,
|
|
|
const struct usb_ss_ep_comp_descriptor *comp_desc,
|
|
|
- bool ignore, bool restore)
|
|
|
+ bool modify, bool restore)
|
|
|
{
|
|
|
struct dwc3 *dwc = dep->dwc;
|
|
|
u32 reg;
|
|
@@ -581,7 +563,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, ignore,
|
|
|
+ ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, modify,
|
|
|
restore);
|
|
|
if (ret)
|
|
|
return ret;
|
|
@@ -600,38 +582,24 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
|
|
|
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
|
|
|
|
|
|
if (usb_endpoint_xfer_control(desc))
|
|
|
- goto out;
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* Initialize the TRB ring */
|
|
|
+ dep->trb_dequeue = 0;
|
|
|
+ dep->trb_enqueue = 0;
|
|
|
+ memset(dep->trb_pool, 0,
|
|
|
+ sizeof(struct dwc3_trb) * DWC3_TRB_NUM);
|
|
|
|
|
|
/* Link TRB. The HWO bit is never reset */
|
|
|
trb_st_hw = &dep->trb_pool[0];
|
|
|
|
|
|
trb_link = &dep->trb_pool[DWC3_TRB_NUM - 1];
|
|
|
- memset(trb_link, 0, sizeof(*trb_link));
|
|
|
-
|
|
|
trb_link->bpl = lower_32_bits(dwc3_trb_dma_offset(dep, trb_st_hw));
|
|
|
trb_link->bph = upper_32_bits(dwc3_trb_dma_offset(dep, trb_st_hw));
|
|
|
trb_link->ctrl |= DWC3_TRBCTL_LINK_TRB;
|
|
|
trb_link->ctrl |= DWC3_TRB_CTRL_HWO;
|
|
|
}
|
|
|
|
|
|
-out:
|
|
|
- switch (usb_endpoint_type(desc)) {
|
|
|
- case USB_ENDPOINT_XFER_CONTROL:
|
|
|
- /* don't change name */
|
|
|
- break;
|
|
|
- case USB_ENDPOINT_XFER_ISOC:
|
|
|
- strlcat(dep->name, "-isoc", sizeof(dep->name));
|
|
|
- break;
|
|
|
- case USB_ENDPOINT_XFER_BULK:
|
|
|
- strlcat(dep->name, "-bulk", sizeof(dep->name));
|
|
|
- break;
|
|
|
- case USB_ENDPOINT_XFER_INT:
|
|
|
- strlcat(dep->name, "-int", sizeof(dep->name));
|
|
|
- break;
|
|
|
- default:
|
|
|
- dev_err(dwc->dev, "invalid endpoint transfer type\n");
|
|
|
- }
|
|
|
-
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -640,15 +608,13 @@ static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
|
|
|
{
|
|
|
struct dwc3_request *req;
|
|
|
|
|
|
- if (!list_empty(&dep->started_list)) {
|
|
|
- dwc3_stop_active_transfer(dwc, dep->number, true);
|
|
|
+ dwc3_stop_active_transfer(dwc, dep->number, true);
|
|
|
|
|
|
- /* - giveback all requests to gadget driver */
|
|
|
- while (!list_empty(&dep->started_list)) {
|
|
|
- req = next_request(&dep->started_list);
|
|
|
+ /* - giveback all requests to gadget driver */
|
|
|
+ while (!list_empty(&dep->started_list)) {
|
|
|
+ req = next_request(&dep->started_list);
|
|
|
|
|
|
- dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
|
|
|
- }
|
|
|
+ dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
|
|
|
}
|
|
|
|
|
|
while (!list_empty(&dep->pending_list)) {
|
|
@@ -689,10 +655,6 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
|
|
|
dep->type = 0;
|
|
|
dep->flags = 0;
|
|
|
|
|
|
- snprintf(dep->name, sizeof(dep->name), "ep%d%s",
|
|
|
- dep->number >> 1,
|
|
|
- (dep->number & 1) ? "in" : "out");
|
|
|
-
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -784,6 +746,8 @@ static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep,
|
|
|
req->epnum = dep->number;
|
|
|
req->dep = dep;
|
|
|
|
|
|
+ dep->allocated_requests++;
|
|
|
+
|
|
|
trace_dwc3_alloc_request(req);
|
|
|
|
|
|
return &req->request;
|
|
@@ -793,7 +757,9 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
|
|
|
struct usb_request *request)
|
|
|
{
|
|
|
struct dwc3_request *req = to_dwc3_request(request);
|
|
|
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
|
|
|
|
|
|
+ dep->allocated_requests--;
|
|
|
trace_dwc3_free_request(req);
|
|
|
kfree(req);
|
|
|
}
|
|
@@ -825,9 +791,6 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
|
|
}
|
|
|
|
|
|
dwc3_ep_inc_enq(dep);
|
|
|
- /* Skip the LINK-TRB */
|
|
|
- if (dwc3_ep_is_last_trb(dep->trb_enqueue))
|
|
|
- dwc3_ep_inc_enq(dep);
|
|
|
|
|
|
trb->size = DWC3_TRB_SIZE_LENGTH(length);
|
|
|
trb->bpl = lower_32_bits(dma);
|
|
@@ -877,137 +840,169 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
|
|
|
|
|
trb->ctrl |= DWC3_TRB_CTRL_HWO;
|
|
|
|
|
|
+ dep->queued_requests++;
|
|
|
+
|
|
|
trace_dwc3_prepare_trb(dep, trb);
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * dwc3_prepare_trbs - setup TRBs from requests
|
|
|
- * @dep: endpoint for which requests are being prepared
|
|
|
- * @starting: true if the endpoint is idle and no requests are queued.
|
|
|
+/**
|
|
|
+ * dwc3_ep_prev_trb() - Returns the previous TRB in the ring
|
|
|
+ * @dep: The endpoint with the TRB ring
|
|
|
+ * @index: The index of the current TRB in the ring
|
|
|
*
|
|
|
- * The function goes through the requests list and sets up TRBs for the
|
|
|
- * transfers. The function returns once there are no more TRBs available or
|
|
|
- * it runs out of requests.
|
|
|
+ * Returns the TRB prior to the one pointed to by the index. If the
|
|
|
+ * index is 0, we will wrap backwards, skip the link TRB, and return
|
|
|
+ * the one just before that.
|
|
|
*/
|
|
|
-static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
|
|
|
+static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u8 index)
|
|
|
{
|
|
|
- struct dwc3_request *req, *n;
|
|
|
- u32 trbs_left;
|
|
|
- unsigned int last_one = 0;
|
|
|
+ if (!index)
|
|
|
+ index = DWC3_TRB_NUM - 2;
|
|
|
+ else
|
|
|
+ index = dep->trb_enqueue - 1;
|
|
|
|
|
|
- BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM);
|
|
|
+ return &dep->trb_pool[index];
|
|
|
+}
|
|
|
|
|
|
- trbs_left = dep->trb_dequeue - dep->trb_enqueue;
|
|
|
+static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
|
|
|
+{
|
|
|
+ struct dwc3_trb *tmp;
|
|
|
+ u8 trbs_left;
|
|
|
|
|
|
/*
|
|
|
- * If enqueue & dequeue are equal than it is either full or empty. If we
|
|
|
- * are starting to process requests then we are empty. Otherwise we are
|
|
|
- * full and don't do anything
|
|
|
+ * If enqueue & dequeue are equal than it is either full or empty.
|
|
|
+ *
|
|
|
+ * One way to know for sure is if the TRB right before us has HWO bit
|
|
|
+ * set or not. If it has, then we're definitely full and can't fit any
|
|
|
+ * more transfers in our ring.
|
|
|
*/
|
|
|
- if (!trbs_left) {
|
|
|
- if (!starting)
|
|
|
- return;
|
|
|
+ if (dep->trb_enqueue == dep->trb_dequeue) {
|
|
|
+ tmp = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
|
|
|
+ if (tmp->ctrl & DWC3_TRB_CTRL_HWO)
|
|
|
+ return 0;
|
|
|
|
|
|
- trbs_left = DWC3_TRB_NUM;
|
|
|
+ return DWC3_TRB_NUM - 1;
|
|
|
}
|
|
|
|
|
|
- /* The last TRB is a link TRB, not used for xfer */
|
|
|
- if (trbs_left <= 1)
|
|
|
- return;
|
|
|
+ trbs_left = dep->trb_dequeue - dep->trb_enqueue;
|
|
|
+ trbs_left &= (DWC3_TRB_NUM - 1);
|
|
|
|
|
|
- list_for_each_entry_safe(req, n, &dep->pending_list, list) {
|
|
|
- unsigned length;
|
|
|
- dma_addr_t dma;
|
|
|
- last_one = false;
|
|
|
-
|
|
|
- if (req->request.num_mapped_sgs > 0) {
|
|
|
- struct usb_request *request = &req->request;
|
|
|
- struct scatterlist *sg = request->sg;
|
|
|
- struct scatterlist *s;
|
|
|
- int i;
|
|
|
-
|
|
|
- for_each_sg(sg, s, request->num_mapped_sgs, i) {
|
|
|
- unsigned chain = true;
|
|
|
-
|
|
|
- length = sg_dma_len(s);
|
|
|
- dma = sg_dma_address(s);
|
|
|
-
|
|
|
- if (i == (request->num_mapped_sgs - 1) ||
|
|
|
- sg_is_last(s)) {
|
|
|
- if (list_empty(&dep->pending_list))
|
|
|
- last_one = true;
|
|
|
- chain = false;
|
|
|
- }
|
|
|
+ if (dep->trb_dequeue < dep->trb_enqueue)
|
|
|
+ trbs_left--;
|
|
|
|
|
|
- trbs_left--;
|
|
|
- if (!trbs_left)
|
|
|
- last_one = true;
|
|
|
+ return trbs_left;
|
|
|
+}
|
|
|
|
|
|
- if (last_one)
|
|
|
- chain = false;
|
|
|
+static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
|
|
|
+ struct dwc3_request *req, unsigned int trbs_left,
|
|
|
+ unsigned int more_coming)
|
|
|
+{
|
|
|
+ struct usb_request *request = &req->request;
|
|
|
+ struct scatterlist *sg = request->sg;
|
|
|
+ struct scatterlist *s;
|
|
|
+ unsigned int last = false;
|
|
|
+ unsigned int length;
|
|
|
+ dma_addr_t dma;
|
|
|
+ int i;
|
|
|
|
|
|
- dwc3_prepare_one_trb(dep, req, dma, length,
|
|
|
- last_one, chain, i);
|
|
|
+ for_each_sg(sg, s, request->num_mapped_sgs, i) {
|
|
|
+ unsigned chain = true;
|
|
|
|
|
|
- if (last_one)
|
|
|
- break;
|
|
|
- }
|
|
|
+ length = sg_dma_len(s);
|
|
|
+ dma = sg_dma_address(s);
|
|
|
|
|
|
- if (last_one)
|
|
|
- break;
|
|
|
- } else {
|
|
|
- dma = req->request.dma;
|
|
|
- length = req->request.length;
|
|
|
- trbs_left--;
|
|
|
+ if (sg_is_last(s)) {
|
|
|
+ if (usb_endpoint_xfer_int(dep->endpoint.desc) ||
|
|
|
+ !more_coming)
|
|
|
+ last = true;
|
|
|
|
|
|
- if (!trbs_left)
|
|
|
- last_one = 1;
|
|
|
+ chain = false;
|
|
|
+ }
|
|
|
|
|
|
- /* Is this the last request? */
|
|
|
- if (list_is_last(&req->list, &dep->pending_list))
|
|
|
- last_one = 1;
|
|
|
+ if (!trbs_left--)
|
|
|
+ last = true;
|
|
|
|
|
|
- dwc3_prepare_one_trb(dep, req, dma, length,
|
|
|
- last_one, false, 0);
|
|
|
+ if (last)
|
|
|
+ chain = false;
|
|
|
|
|
|
- if (last_one)
|
|
|
- break;
|
|
|
- }
|
|
|
+ dwc3_prepare_one_trb(dep, req, dma, length,
|
|
|
+ last, chain, i);
|
|
|
+
|
|
|
+ if (last)
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
|
|
|
- int start_new)
|
|
|
+static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
|
|
|
+ struct dwc3_request *req, unsigned int trbs_left,
|
|
|
+ unsigned int more_coming)
|
|
|
+{
|
|
|
+ unsigned int last = false;
|
|
|
+ unsigned int length;
|
|
|
+ dma_addr_t dma;
|
|
|
+
|
|
|
+ dma = req->request.dma;
|
|
|
+ length = req->request.length;
|
|
|
+
|
|
|
+ if (!trbs_left)
|
|
|
+ last = true;
|
|
|
+
|
|
|
+ /* Is this the last request? */
|
|
|
+ if (usb_endpoint_xfer_int(dep->endpoint.desc) || !more_coming)
|
|
|
+ last = true;
|
|
|
+
|
|
|
+ dwc3_prepare_one_trb(dep, req, dma, length,
|
|
|
+ last, false, 0);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * dwc3_prepare_trbs - setup TRBs from requests
|
|
|
+ * @dep: endpoint for which requests are being prepared
|
|
|
+ *
|
|
|
+ * The function goes through the requests list and sets up TRBs for the
|
|
|
+ * transfers. The function returns once there are no more TRBs available or
|
|
|
+ * it runs out of requests.
|
|
|
+ */
|
|
|
+static void dwc3_prepare_trbs(struct dwc3_ep *dep)
|
|
|
+{
|
|
|
+ struct dwc3_request *req, *n;
|
|
|
+ unsigned int more_coming;
|
|
|
+ u32 trbs_left;
|
|
|
+
|
|
|
+ BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM);
|
|
|
+
|
|
|
+ trbs_left = dwc3_calc_trbs_left(dep);
|
|
|
+ if (!trbs_left)
|
|
|
+ return;
|
|
|
+
|
|
|
+ more_coming = dep->allocated_requests - dep->queued_requests;
|
|
|
+
|
|
|
+ list_for_each_entry_safe(req, n, &dep->pending_list, list) {
|
|
|
+ if (req->request.num_mapped_sgs > 0)
|
|
|
+ dwc3_prepare_one_trb_sg(dep, req, trbs_left--,
|
|
|
+ more_coming);
|
|
|
+ else
|
|
|
+ dwc3_prepare_one_trb_linear(dep, req, trbs_left--,
|
|
|
+ more_coming);
|
|
|
+
|
|
|
+ if (!trbs_left)
|
|
|
+ return;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param)
|
|
|
{
|
|
|
struct dwc3_gadget_ep_cmd_params params;
|
|
|
struct dwc3_request *req;
|
|
|
struct dwc3 *dwc = dep->dwc;
|
|
|
+ int starting;
|
|
|
int ret;
|
|
|
u32 cmd;
|
|
|
|
|
|
- if (start_new && (dep->flags & DWC3_EP_BUSY)) {
|
|
|
- dwc3_trace(trace_dwc3_gadget, "%s: endpoint busy", dep->name);
|
|
|
- return -EBUSY;
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * If we are getting here after a short-out-packet we don't enqueue any
|
|
|
- * new requests as we try to set the IOC bit only on the last request.
|
|
|
- */
|
|
|
- if (start_new) {
|
|
|
- if (list_empty(&dep->started_list))
|
|
|
- dwc3_prepare_trbs(dep, start_new);
|
|
|
-
|
|
|
- /* req points to the first request which will be sent */
|
|
|
- req = next_request(&dep->started_list);
|
|
|
- } else {
|
|
|
- dwc3_prepare_trbs(dep, start_new);
|
|
|
+ starting = !(dep->flags & DWC3_EP_BUSY);
|
|
|
|
|
|
- /*
|
|
|
- * req points to the first request where HWO changed from 0 to 1
|
|
|
- */
|
|
|
- req = next_request(&dep->started_list);
|
|
|
- }
|
|
|
+ dwc3_prepare_trbs(dep);
|
|
|
+ req = next_request(&dep->started_list);
|
|
|
if (!req) {
|
|
|
dep->flags |= DWC3_EP_PENDING_REQUEST;
|
|
|
return 0;
|
|
@@ -1015,16 +1010,17 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
|
|
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
|
|
- if (start_new) {
|
|
|
+ if (starting) {
|
|
|
params.param0 = upper_32_bits(req->trb_dma);
|
|
|
params.param1 = lower_32_bits(req->trb_dma);
|
|
|
- cmd = DWC3_DEPCMD_STARTTRANSFER;
|
|
|
+ cmd = DWC3_DEPCMD_STARTTRANSFER |
|
|
|
+ DWC3_DEPCMD_PARAM(cmd_param);
|
|
|
} else {
|
|
|
- cmd = DWC3_DEPCMD_UPDATETRANSFER;
|
|
|
+ cmd = DWC3_DEPCMD_UPDATETRANSFER |
|
|
|
+ DWC3_DEPCMD_PARAM(dep->resource_index);
|
|
|
}
|
|
|
|
|
|
- cmd |= DWC3_DEPCMD_PARAM(cmd_param);
|
|
|
- ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);
|
|
|
+ ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
|
|
|
if (ret < 0) {
|
|
|
/*
|
|
|
* FIXME we need to iterate over the list of requests
|
|
@@ -1039,9 +1035,8 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
|
|
|
|
|
|
dep->flags |= DWC3_EP_BUSY;
|
|
|
|
|
|
- if (start_new) {
|
|
|
- dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc,
|
|
|
- dep->number);
|
|
|
+ if (starting) {
|
|
|
+ dep->resource_index = dwc3_gadget_ep_get_transfer_index(dep);
|
|
|
WARN_ON_ONCE(!dep->resource_index);
|
|
|
}
|
|
|
|
|
@@ -1064,7 +1059,7 @@ static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
|
|
|
/* 4 micro frames in the future */
|
|
|
uf = cur_uf + dep->interval * 4;
|
|
|
|
|
|
- __dwc3_gadget_kick_transfer(dep, uf, 1);
|
|
|
+ __dwc3_gadget_kick_transfer(dep, uf);
|
|
|
}
|
|
|
|
|
|
static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
|
|
@@ -1085,18 +1080,20 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
|
|
|
|
|
if (!dep->endpoint.desc) {
|
|
|
dwc3_trace(trace_dwc3_gadget,
|
|
|
- "trying to queue request %p to disabled %s\n",
|
|
|
+ "trying to queue request %p to disabled %s",
|
|
|
&req->request, dep->endpoint.name);
|
|
|
return -ESHUTDOWN;
|
|
|
}
|
|
|
|
|
|
if (WARN(req->dep != dep, "request %p belongs to '%s'\n",
|
|
|
&req->request, req->dep->name)) {
|
|
|
- dwc3_trace(trace_dwc3_gadget, "request %p belongs to '%s'\n",
|
|
|
+ dwc3_trace(trace_dwc3_gadget, "request %p belongs to '%s'",
|
|
|
&req->request, req->dep->name);
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
+ pm_runtime_get(dwc->dev);
|
|
|
+
|
|
|
req->request.actual = 0;
|
|
|
req->request.status = -EINPROGRESS;
|
|
|
req->direction = dep->direction;
|
|
@@ -1131,9 +1128,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
|
|
* little bit faster.
|
|
|
*/
|
|
|
if (!usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
|
|
|
- !usb_endpoint_xfer_int(dep->endpoint.desc) &&
|
|
|
- !(dep->flags & DWC3_EP_BUSY)) {
|
|
|
- ret = __dwc3_gadget_kick_transfer(dep, 0, true);
|
|
|
+ !usb_endpoint_xfer_int(dep->endpoint.desc)) {
|
|
|
+ ret = __dwc3_gadget_kick_transfer(dep, 0);
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
@@ -1163,7 +1159,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
- ret = __dwc3_gadget_kick_transfer(dep, 0, true);
|
|
|
+ ret = __dwc3_gadget_kick_transfer(dep, 0);
|
|
|
if (!ret)
|
|
|
dep->flags &= ~DWC3_EP_PENDING_REQUEST;
|
|
|
|
|
@@ -1179,8 +1175,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
|
|
(dep->flags & DWC3_EP_BUSY) &&
|
|
|
!(dep->flags & DWC3_EP_MISSED_ISOC)) {
|
|
|
WARN_ON_ONCE(!dep->resource_index);
|
|
|
- ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index,
|
|
|
- false);
|
|
|
+ ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index);
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
@@ -1190,12 +1185,12 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
|
|
* handled.
|
|
|
*/
|
|
|
if (dep->stream_capable)
|
|
|
- ret = __dwc3_gadget_kick_transfer(dep, 0, true);
|
|
|
+ ret = __dwc3_gadget_kick_transfer(dep, 0);
|
|
|
|
|
|
out:
|
|
|
if (ret && ret != -EBUSY)
|
|
|
dwc3_trace(trace_dwc3_gadget,
|
|
|
- "%s: failed to kick transfers\n",
|
|
|
+ "%s: failed to kick transfers",
|
|
|
dep->name);
|
|
|
if (ret == -EBUSY)
|
|
|
ret = 0;
|
|
@@ -1215,7 +1210,7 @@ static int __dwc3_gadget_ep_queue_zlp(struct dwc3 *dwc, struct dwc3_ep *dep)
|
|
|
struct usb_request *request;
|
|
|
struct usb_ep *ep = &dep->endpoint;
|
|
|
|
|
|
- dwc3_trace(trace_dwc3_gadget, "queueing ZLP\n");
|
|
|
+ dwc3_trace(trace_dwc3_gadget, "queueing ZLP");
|
|
|
request = dwc3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
|
|
|
if (!request)
|
|
|
return -ENOMEM;
|
|
@@ -1319,23 +1314,36 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
|
|
|
memset(¶ms, 0x00, sizeof(params));
|
|
|
|
|
|
if (value) {
|
|
|
- if (!protocol && ((dep->direction && dep->flags & DWC3_EP_BUSY) ||
|
|
|
- (!list_empty(&dep->started_list) ||
|
|
|
- !list_empty(&dep->pending_list)))) {
|
|
|
+ struct dwc3_trb *trb;
|
|
|
+
|
|
|
+ unsigned transfer_in_flight;
|
|
|
+ unsigned started;
|
|
|
+
|
|
|
+ if (dep->number > 1)
|
|
|
+ trb = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
|
|
|
+ else
|
|
|
+ trb = &dwc->ep0_trb[dep->trb_enqueue];
|
|
|
+
|
|
|
+ transfer_in_flight = trb->ctrl & DWC3_TRB_CTRL_HWO;
|
|
|
+ started = !list_empty(&dep->started_list);
|
|
|
+
|
|
|
+ if (!protocol && ((dep->direction && transfer_in_flight) ||
|
|
|
+ (!dep->direction && started))) {
|
|
|
dwc3_trace(trace_dwc3_gadget,
|
|
|
"%s: pending request, cannot halt",
|
|
|
dep->name);
|
|
|
return -EAGAIN;
|
|
|
}
|
|
|
|
|
|
- ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
|
|
|
- DWC3_DEPCMD_SETSTALL, ¶ms);
|
|
|
+ ret = dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETSTALL,
|
|
|
+ ¶ms);
|
|
|
if (ret)
|
|
|
dev_err(dwc->dev, "failed to set STALL on %s\n",
|
|
|
dep->name);
|
|
|
else
|
|
|
dep->flags |= DWC3_EP_STALL;
|
|
|
} else {
|
|
|
+
|
|
|
ret = dwc3_send_clear_stall_ep_cmd(dep);
|
|
|
if (ret)
|
|
|
dev_err(dwc->dev, "failed to clear STALL on %s\n",
|
|
@@ -1444,8 +1452,8 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
|
|
|
speed = reg & DWC3_DSTS_CONNECTSPD;
|
|
|
if ((speed == DWC3_DSTS_SUPERSPEED) ||
|
|
|
(speed == DWC3_DSTS_SUPERSPEED_PLUS)) {
|
|
|
- dwc3_trace(trace_dwc3_gadget, "no wakeup on SuperSpeed\n");
|
|
|
- return -EINVAL;
|
|
|
+ dwc3_trace(trace_dwc3_gadget, "no wakeup on SuperSpeed");
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
link_state = DWC3_DSTS_USBLNKST(reg);
|
|
@@ -1456,7 +1464,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
|
|
|
break;
|
|
|
default:
|
|
|
dwc3_trace(trace_dwc3_gadget,
|
|
|
- "can't wakeup from '%s'\n",
|
|
|
+ "can't wakeup from '%s'",
|
|
|
dwc3_gadget_link_string(link_state));
|
|
|
return -EINVAL;
|
|
|
}
|
|
@@ -1525,6 +1533,9 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
|
|
|
u32 reg;
|
|
|
u32 timeout = 500;
|
|
|
|
|
|
+ if (pm_runtime_suspended(dwc->dev))
|
|
|
+ return 0;
|
|
|
+
|
|
|
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
|
|
if (is_on) {
|
|
|
if (dwc->revision <= DWC3_REVISION_187A) {
|
|
@@ -1553,18 +1564,11 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
|
|
|
|
|
|
do {
|
|
|
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
|
|
- if (is_on) {
|
|
|
- if (!(reg & DWC3_DSTS_DEVCTRLHLT))
|
|
|
- break;
|
|
|
- } else {
|
|
|
- if (reg & DWC3_DSTS_DEVCTRLHLT)
|
|
|
- break;
|
|
|
- }
|
|
|
- timeout--;
|
|
|
- if (!timeout)
|
|
|
- return -ETIMEDOUT;
|
|
|
- udelay(1);
|
|
|
- } while (1);
|
|
|
+ reg &= DWC3_DSTS_DEVCTRLHLT;
|
|
|
+ } while (--timeout && !(!is_on ^ !reg));
|
|
|
+
|
|
|
+ if (!timeout)
|
|
|
+ return -ETIMEDOUT;
|
|
|
|
|
|
dwc3_trace(trace_dwc3_gadget, "gadget %s data soft-%s",
|
|
|
dwc->gadget_driver
|
|
@@ -1616,36 +1620,52 @@ static void dwc3_gadget_disable_irq(struct dwc3 *dwc)
|
|
|
static irqreturn_t dwc3_interrupt(int irq, void *_dwc);
|
|
|
static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc);
|
|
|
|
|
|
-static int dwc3_gadget_start(struct usb_gadget *g,
|
|
|
- struct usb_gadget_driver *driver)
|
|
|
+/**
|
|
|
+ * dwc3_gadget_setup_nump - Calculate and initialize NUMP field of DCFG
|
|
|
+ * dwc: pointer to our context structure
|
|
|
+ *
|
|
|
+ * The following looks like complex but it's actually very simple. In order to
|
|
|
+ * calculate the number of packets we can burst at once on OUT transfers, we're
|
|
|
+ * gonna use RxFIFO size.
|
|
|
+ *
|
|
|
+ * To calculate RxFIFO size we need two numbers:
|
|
|
+ * MDWIDTH = size, in bits, of the internal memory bus
|
|
|
+ * RAM2_DEPTH = depth, in MDWIDTH, of internal RAM2 (where RxFIFO sits)
|
|
|
+ *
|
|
|
+ * Given these two numbers, the formula is simple:
|
|
|
+ *
|
|
|
+ * RxFIFO Size = (RAM2_DEPTH * MDWIDTH / 8) - 24 - 16;
|
|
|
+ *
|
|
|
+ * 24 bytes is for 3x SETUP packets
|
|
|
+ * 16 bytes is a clock domain crossing tolerance
|
|
|
+ *
|
|
|
+ * Given RxFIFO Size, NUMP = RxFIFOSize / 1024;
|
|
|
+ */
|
|
|
+static void dwc3_gadget_setup_nump(struct dwc3 *dwc)
|
|
|
{
|
|
|
- struct dwc3 *dwc = gadget_to_dwc(g);
|
|
|
- struct dwc3_ep *dep;
|
|
|
- unsigned long flags;
|
|
|
- int ret = 0;
|
|
|
- int irq;
|
|
|
- u32 reg;
|
|
|
+ u32 ram2_depth;
|
|
|
+ u32 mdwidth;
|
|
|
+ u32 nump;
|
|
|
+ u32 reg;
|
|
|
|
|
|
- irq = platform_get_irq(to_platform_device(dwc->dev), 0);
|
|
|
- ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
|
|
|
- IRQF_SHARED, "dwc3", dwc->ev_buf);
|
|
|
- if (ret) {
|
|
|
- dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
|
|
|
- irq, ret);
|
|
|
- goto err0;
|
|
|
- }
|
|
|
+ ram2_depth = DWC3_GHWPARAMS7_RAM2_DEPTH(dwc->hwparams.hwparams7);
|
|
|
+ mdwidth = DWC3_GHWPARAMS0_MDWIDTH(dwc->hwparams.hwparams0);
|
|
|
|
|
|
- spin_lock_irqsave(&dwc->lock, flags);
|
|
|
+ nump = ((ram2_depth * mdwidth / 8) - 24 - 16) / 1024;
|
|
|
+ nump = min_t(u32, nump, 16);
|
|
|
|
|
|
- if (dwc->gadget_driver) {
|
|
|
- dev_err(dwc->dev, "%s is already bound to %s\n",
|
|
|
- dwc->gadget.name,
|
|
|
- dwc->gadget_driver->driver.name);
|
|
|
- ret = -EBUSY;
|
|
|
- goto err1;
|
|
|
- }
|
|
|
+ /* update NumP */
|
|
|
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
|
|
+ reg &= ~DWC3_DCFG_NUMP_MASK;
|
|
|
+ reg |= nump << DWC3_DCFG_NUMP_SHIFT;
|
|
|
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
|
|
+}
|
|
|
|
|
|
- dwc->gadget_driver = driver;
|
|
|
+static int __dwc3_gadget_start(struct dwc3 *dwc)
|
|
|
+{
|
|
|
+ struct dwc3_ep *dep;
|
|
|
+ int ret = 0;
|
|
|
+ u32 reg;
|
|
|
|
|
|
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
|
|
reg &= ~(DWC3_DCFG_SPEED_MASK);
|
|
@@ -1668,16 +1688,16 @@ static int dwc3_gadget_start(struct usb_gadget *g,
|
|
|
} else {
|
|
|
switch (dwc->maximum_speed) {
|
|
|
case USB_SPEED_LOW:
|
|
|
- reg |= DWC3_DSTS_LOWSPEED;
|
|
|
+ reg |= DWC3_DCFG_LOWSPEED;
|
|
|
break;
|
|
|
case USB_SPEED_FULL:
|
|
|
- reg |= DWC3_DSTS_FULLSPEED1;
|
|
|
+ reg |= DWC3_DCFG_FULLSPEED1;
|
|
|
break;
|
|
|
case USB_SPEED_HIGH:
|
|
|
- reg |= DWC3_DSTS_HIGHSPEED;
|
|
|
+ reg |= DWC3_DCFG_HIGHSPEED;
|
|
|
break;
|
|
|
case USB_SPEED_SUPER_PLUS:
|
|
|
- reg |= DWC3_DSTS_SUPERSPEED_PLUS;
|
|
|
+ reg |= DWC3_DCFG_SUPERSPEED_PLUS;
|
|
|
break;
|
|
|
default:
|
|
|
dev_err(dwc->dev, "invalid dwc->maximum_speed (%d)\n",
|
|
@@ -1701,6 +1721,8 @@ static int dwc3_gadget_start(struct usb_gadget *g,
|
|
|
reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL;
|
|
|
dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
|
|
|
|
|
|
+ dwc3_gadget_setup_nump(dwc);
|
|
|
+
|
|
|
/* Start with SuperSpeed Default */
|
|
|
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
|
|
|
|
|
@@ -1709,7 +1731,7 @@ static int dwc3_gadget_start(struct usb_gadget *g,
|
|
|
false);
|
|
|
if (ret) {
|
|
|
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
|
|
|
- goto err2;
|
|
|
+ goto err0;
|
|
|
}
|
|
|
|
|
|
dep = dwc->eps[1];
|
|
@@ -1717,7 +1739,7 @@ static int dwc3_gadget_start(struct usb_gadget *g,
|
|
|
false);
|
|
|
if (ret) {
|
|
|
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
|
|
|
- goto err3;
|
|
|
+ goto err1;
|
|
|
}
|
|
|
|
|
|
/* begin to receive SETUP packets */
|
|
@@ -1726,43 +1748,79 @@ static int dwc3_gadget_start(struct usb_gadget *g,
|
|
|
|
|
|
dwc3_gadget_enable_irq(dwc);
|
|
|
|
|
|
- spin_unlock_irqrestore(&dwc->lock, flags);
|
|
|
-
|
|
|
return 0;
|
|
|
|
|
|
-err3:
|
|
|
- __dwc3_gadget_ep_disable(dwc->eps[0]);
|
|
|
-
|
|
|
-err2:
|
|
|
- dwc->gadget_driver = NULL;
|
|
|
-
|
|
|
err1:
|
|
|
- spin_unlock_irqrestore(&dwc->lock, flags);
|
|
|
-
|
|
|
- free_irq(irq, dwc->ev_buf);
|
|
|
+ __dwc3_gadget_ep_disable(dwc->eps[0]);
|
|
|
|
|
|
err0:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-static int dwc3_gadget_stop(struct usb_gadget *g)
|
|
|
+static int dwc3_gadget_start(struct usb_gadget *g,
|
|
|
+ struct usb_gadget_driver *driver)
|
|
|
{
|
|
|
struct dwc3 *dwc = gadget_to_dwc(g);
|
|
|
unsigned long flags;
|
|
|
+ int ret = 0;
|
|
|
int irq;
|
|
|
|
|
|
+ irq = dwc->irq_gadget;
|
|
|
+ ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
|
|
|
+ IRQF_SHARED, "dwc3", dwc->ev_buf);
|
|
|
+ if (ret) {
|
|
|
+ dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
|
|
|
+ irq, ret);
|
|
|
+ goto err0;
|
|
|
+ }
|
|
|
+
|
|
|
spin_lock_irqsave(&dwc->lock, flags);
|
|
|
+ if (dwc->gadget_driver) {
|
|
|
+ dev_err(dwc->dev, "%s is already bound to %s\n",
|
|
|
+ dwc->gadget.name,
|
|
|
+ dwc->gadget_driver->driver.name);
|
|
|
+ ret = -EBUSY;
|
|
|
+ goto err1;
|
|
|
+ }
|
|
|
+
|
|
|
+ dwc->gadget_driver = driver;
|
|
|
+
|
|
|
+ if (pm_runtime_active(dwc->dev))
|
|
|
+ __dwc3_gadget_start(dwc);
|
|
|
+
|
|
|
+ spin_unlock_irqrestore(&dwc->lock, flags);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+err1:
|
|
|
+ spin_unlock_irqrestore(&dwc->lock, flags);
|
|
|
+ free_irq(irq, dwc);
|
|
|
+
|
|
|
+err0:
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static void __dwc3_gadget_stop(struct dwc3 *dwc)
|
|
|
+{
|
|
|
+ if (pm_runtime_suspended(dwc->dev))
|
|
|
+ return;
|
|
|
|
|
|
dwc3_gadget_disable_irq(dwc);
|
|
|
__dwc3_gadget_ep_disable(dwc->eps[0]);
|
|
|
__dwc3_gadget_ep_disable(dwc->eps[1]);
|
|
|
+}
|
|
|
|
|
|
- dwc->gadget_driver = NULL;
|
|
|
+static int dwc3_gadget_stop(struct usb_gadget *g)
|
|
|
+{
|
|
|
+ struct dwc3 *dwc = gadget_to_dwc(g);
|
|
|
+ unsigned long flags;
|
|
|
|
|
|
+ spin_lock_irqsave(&dwc->lock, flags);
|
|
|
+ __dwc3_gadget_stop(dwc);
|
|
|
+ dwc->gadget_driver = NULL;
|
|
|
spin_unlock_irqrestore(&dwc->lock, flags);
|
|
|
|
|
|
- irq = platform_get_irq(to_platform_device(dwc->dev), 0);
|
|
|
- free_irq(irq, dwc->ev_buf);
|
|
|
+ free_irq(dwc->irq_gadget, dwc->ev_buf);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -1785,7 +1843,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
|
|
|
u8 i;
|
|
|
|
|
|
for (i = 0; i < num; i++) {
|
|
|
- u8 epnum = (i << 1) | (!!direction);
|
|
|
+ u8 epnum = (i << 1) | (direction ? 1 : 0);
|
|
|
|
|
|
dep = kzalloc(sizeof(*dep), GFP_KERNEL);
|
|
|
if (!dep)
|
|
@@ -1794,12 +1852,14 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
|
|
|
dep->dwc = dwc;
|
|
|
dep->number = epnum;
|
|
|
dep->direction = !!direction;
|
|
|
+ dep->regs = dwc->regs + DWC3_DEP_BASE(epnum);
|
|
|
dwc->eps[epnum] = dep;
|
|
|
|
|
|
snprintf(dep->name, sizeof(dep->name), "ep%d%s", epnum >> 1,
|
|
|
(epnum & 1) ? "in" : "out");
|
|
|
|
|
|
dep->endpoint.name = dep->name;
|
|
|
+ spin_lock_init(&dep->lock);
|
|
|
|
|
|
dwc3_trace(trace_dwc3_gadget, "initializing %s", dep->name);
|
|
|
|
|
@@ -1901,6 +1961,7 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
|
|
unsigned int s_pkt = 0;
|
|
|
unsigned int trb_status;
|
|
|
|
|
|
+ dep->queued_requests--;
|
|
|
trace_dwc3_complete_trb(dep, trb);
|
|
|
|
|
|
if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN)
|
|
@@ -1921,7 +1982,7 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
|
|
trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size);
|
|
|
if (trb_status == DWC3_TRBSTS_MISSED_ISOC) {
|
|
|
dwc3_trace(trace_dwc3_gadget,
|
|
|
- "%s: incomplete IN transfer\n",
|
|
|
+ "%s: incomplete IN transfer",
|
|
|
dep->name);
|
|
|
/*
|
|
|
* If missed isoc occurred and there is
|
|
@@ -2006,6 +2067,14 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
|
|
break;
|
|
|
} while (1);
|
|
|
|
|
|
+ /*
|
|
|
+ * Our endpoint might get disabled by another thread during
|
|
|
+ * dwc3_gadget_giveback(). If that happens, we're just gonna return 1
|
|
|
+ * early on so DWC3_EP_BUSY flag gets cleared
|
|
|
+ */
|
|
|
+ if (!dep->endpoint.desc)
|
|
|
+ return 1;
|
|
|
+
|
|
|
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
|
|
|
list_empty(&dep->started_list)) {
|
|
|
if (list_empty(&dep->pending_list)) {
|
|
@@ -2023,6 +2092,10 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc))
|
|
|
+ if ((event->status & DEPEVT_STATUS_IOC) &&
|
|
|
+ (trb->ctrl & DWC3_TRB_CTRL_IOC))
|
|
|
+ return 0;
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
@@ -2039,7 +2112,7 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
|
|
|
status = -ECONNRESET;
|
|
|
|
|
|
clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status);
|
|
|
- if (clean_busy && (is_xfer_complete ||
|
|
|
+ if (clean_busy && (!dep->endpoint.desc || is_xfer_complete ||
|
|
|
usb_endpoint_xfer_isoc(dep->endpoint.desc)))
|
|
|
dep->flags &= ~DWC3_EP_BUSY;
|
|
|
|
|
@@ -2068,10 +2141,18 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
|
|
|
dwc->u1u2 = 0;
|
|
|
}
|
|
|
|
|
|
+ /*
|
|
|
+ * Our endpoint might get disabled by another thread during
|
|
|
+ * dwc3_gadget_giveback(). If that happens, we're just gonna return 1
|
|
|
+ * early on so DWC3_EP_BUSY flag gets cleared
|
|
|
+ */
|
|
|
+ if (!dep->endpoint.desc)
|
|
|
+ return;
|
|
|
+
|
|
|
if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
|
|
int ret;
|
|
|
|
|
|
- ret = __dwc3_gadget_kick_transfer(dep, 0, is_xfer_complete);
|
|
|
+ ret = __dwc3_gadget_kick_transfer(dep, 0);
|
|
|
if (!ret || ret == -EBUSY)
|
|
|
return;
|
|
|
}
|
|
@@ -2099,7 +2180,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
|
|
|
|
|
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
|
|
dwc3_trace(trace_dwc3_gadget,
|
|
|
- "%s is an Isochronous endpoint\n",
|
|
|
+ "%s is an Isochronous endpoint",
|
|
|
dep->name);
|
|
|
return;
|
|
|
}
|
|
@@ -2122,12 +2203,12 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
|
|
dep->name, active ? "Transfer Active"
|
|
|
: "Transfer Not Active");
|
|
|
|
|
|
- ret = __dwc3_gadget_kick_transfer(dep, 0, !active);
|
|
|
+ ret = __dwc3_gadget_kick_transfer(dep, 0);
|
|
|
if (!ret || ret == -EBUSY)
|
|
|
return;
|
|
|
|
|
|
dwc3_trace(trace_dwc3_gadget,
|
|
|
- "%s: failed to kick transfers\n",
|
|
|
+ "%s: failed to kick transfers",
|
|
|
dep->name);
|
|
|
}
|
|
|
|
|
@@ -2150,11 +2231,11 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
|
|
/* FALLTHROUGH */
|
|
|
default:
|
|
|
dwc3_trace(trace_dwc3_gadget,
|
|
|
- "unable to find suitable stream\n");
|
|
|
+ "unable to find suitable stream");
|
|
|
}
|
|
|
break;
|
|
|
case DWC3_DEPEVT_RXTXFIFOEVT:
|
|
|
- dwc3_trace(trace_dwc3_gadget, "%s FIFO Overrun\n", dep->name);
|
|
|
+ dwc3_trace(trace_dwc3_gadget, "%s FIFO Overrun", dep->name);
|
|
|
break;
|
|
|
case DWC3_DEPEVT_EPCMDCMPLT:
|
|
|
dwc3_trace(trace_dwc3_gadget, "Endpoint Command Complete");
|
|
@@ -2237,7 +2318,7 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
|
|
|
cmd |= DWC3_DEPCMD_CMDIOC;
|
|
|
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
- ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);
|
|
|
+ ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
|
|
|
WARN_ON_ONCE(ret);
|
|
|
dep->resource_index = 0;
|
|
|
dep->flags &= ~DWC3_EP_BUSY;
|
|
@@ -2300,12 +2381,16 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
|
|
|
dwc->gadget.speed = USB_SPEED_UNKNOWN;
|
|
|
dwc->setup_packet_pending = false;
|
|
|
usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED);
|
|
|
+
|
|
|
+ dwc->connected = false;
|
|
|
}
|
|
|
|
|
|
static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
|
|
|
{
|
|
|
u32 reg;
|
|
|
|
|
|
+ dwc->connected = true;
|
|
|
+
|
|
|
/*
|
|
|
* WORKAROUND: DWC3 revisions <1.88a have an issue which
|
|
|
* would cause a missing Disconnect Event if there's a
|
|
@@ -2393,12 +2478,12 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
|
|
dwc3_update_ram_clk_sel(dwc, speed);
|
|
|
|
|
|
switch (speed) {
|
|
|
- case DWC3_DCFG_SUPERSPEED_PLUS:
|
|
|
+ case DWC3_DSTS_SUPERSPEED_PLUS:
|
|
|
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
|
|
|
dwc->gadget.ep0->maxpacket = 512;
|
|
|
dwc->gadget.speed = USB_SPEED_SUPER_PLUS;
|
|
|
break;
|
|
|
- case DWC3_DCFG_SUPERSPEED:
|
|
|
+ case DWC3_DSTS_SUPERSPEED:
|
|
|
/*
|
|
|
* WORKAROUND: DWC3 revisions <1.90a have an issue which
|
|
|
* would cause a missing USB3 Reset event.
|
|
@@ -2419,18 +2504,18 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
|
|
dwc->gadget.ep0->maxpacket = 512;
|
|
|
dwc->gadget.speed = USB_SPEED_SUPER;
|
|
|
break;
|
|
|
- case DWC3_DCFG_HIGHSPEED:
|
|
|
+ case DWC3_DSTS_HIGHSPEED:
|
|
|
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
|
|
|
dwc->gadget.ep0->maxpacket = 64;
|
|
|
dwc->gadget.speed = USB_SPEED_HIGH;
|
|
|
break;
|
|
|
- case DWC3_DCFG_FULLSPEED2:
|
|
|
- case DWC3_DCFG_FULLSPEED1:
|
|
|
+ case DWC3_DSTS_FULLSPEED2:
|
|
|
+ case DWC3_DSTS_FULLSPEED1:
|
|
|
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
|
|
|
dwc->gadget.ep0->maxpacket = 64;
|
|
|
dwc->gadget.speed = USB_SPEED_FULL;
|
|
|
break;
|
|
|
- case DWC3_DCFG_LOWSPEED:
|
|
|
+ case DWC3_DSTS_LOWSPEED:
|
|
|
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8);
|
|
|
dwc->gadget.ep0->maxpacket = 8;
|
|
|
dwc->gadget.speed = USB_SPEED_LOW;
|
|
@@ -2440,8 +2525,8 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
|
|
/* Enable USB2 LPM Capability */
|
|
|
|
|
|
if ((dwc->revision > DWC3_REVISION_194A) &&
|
|
|
- (speed != DWC3_DCFG_SUPERSPEED) &&
|
|
|
- (speed != DWC3_DCFG_SUPERSPEED_PLUS)) {
|
|
|
+ (speed != DWC3_DSTS_SUPERSPEED) &&
|
|
|
+ (speed != DWC3_DSTS_SUPERSPEED_PLUS)) {
|
|
|
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
|
|
reg |= DWC3_DCFG_LPM_CAP;
|
|
|
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
|
@@ -2610,6 +2695,17 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
|
|
|
dwc->link_state = next;
|
|
|
}
|
|
|
|
|
|
+static void dwc3_gadget_suspend_interrupt(struct dwc3 *dwc,
|
|
|
+ unsigned int evtinfo)
|
|
|
+{
|
|
|
+ enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK;
|
|
|
+
|
|
|
+ if (dwc->link_state != next && next == DWC3_LINK_STATE_U3)
|
|
|
+ dwc3_suspend_gadget(dwc);
|
|
|
+
|
|
|
+ dwc->link_state = next;
|
|
|
+}
|
|
|
+
|
|
|
static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc,
|
|
|
unsigned int evtinfo)
|
|
|
{
|
|
@@ -2661,7 +2757,20 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
|
|
|
dwc3_gadget_linksts_change_interrupt(dwc, event->event_info);
|
|
|
break;
|
|
|
case DWC3_DEVICE_EVENT_EOPF:
|
|
|
- dwc3_trace(trace_dwc3_gadget, "End of Periodic Frame");
|
|
|
+ /* It changed to be suspend event for version 2.30a and above */
|
|
|
+ if (dwc->revision < DWC3_REVISION_230A) {
|
|
|
+ dwc3_trace(trace_dwc3_gadget, "End of Periodic Frame");
|
|
|
+ } else {
|
|
|
+ dwc3_trace(trace_dwc3_gadget, "U3/L1-L2 Suspend Event");
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Ignore suspend event until the gadget enters into
|
|
|
+ * USB_STATE_CONFIGURED state.
|
|
|
+ */
|
|
|
+ if (dwc->gadget.state >= USB_STATE_CONFIGURED)
|
|
|
+ dwc3_gadget_suspend_interrupt(dwc,
|
|
|
+ event->event_info);
|
|
|
+ }
|
|
|
break;
|
|
|
case DWC3_DEVICE_EVENT_SOF:
|
|
|
dwc3_trace(trace_dwc3_gadget, "Start of Periodic Frame");
|
|
@@ -2767,6 +2876,13 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
|
|
|
u32 count;
|
|
|
u32 reg;
|
|
|
|
|
|
+ if (pm_runtime_suspended(dwc->dev)) {
|
|
|
+ pm_runtime_get(dwc->dev);
|
|
|
+ disable_irq_nosync(dwc->irq_gadget);
|
|
|
+ dwc->pending_events = true;
|
|
|
+ return IRQ_HANDLED;
|
|
|
+ }
|
|
|
+
|
|
|
count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
|
|
|
count &= DWC3_GEVNTCOUNT_MASK;
|
|
|
if (!count)
|
|
@@ -2798,7 +2914,33 @@ static irqreturn_t dwc3_interrupt(int irq, void *_evt)
|
|
|
*/
|
|
|
int dwc3_gadget_init(struct dwc3 *dwc)
|
|
|
{
|
|
|
- int ret;
|
|
|
+ int ret, irq;
|
|
|
+ struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
|
|
|
+
|
|
|
+ irq = platform_get_irq_byname(dwc3_pdev, "peripheral");
|
|
|
+ if (irq == -EPROBE_DEFER)
|
|
|
+ return irq;
|
|
|
+
|
|
|
+ if (irq <= 0) {
|
|
|
+ irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3");
|
|
|
+ if (irq == -EPROBE_DEFER)
|
|
|
+ return irq;
|
|
|
+
|
|
|
+ if (irq <= 0) {
|
|
|
+ irq = platform_get_irq(dwc3_pdev, 0);
|
|
|
+ if (irq <= 0) {
|
|
|
+ if (irq != -EPROBE_DEFER) {
|
|
|
+ dev_err(dwc->dev,
|
|
|
+ "missing peripheral IRQ\n");
|
|
|
+ }
|
|
|
+ if (!irq)
|
|
|
+ irq = -EINVAL;
|
|
|
+ return irq;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ dwc->irq_gadget = irq;
|
|
|
|
|
|
dwc->ctrl_req = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
|
|
|
&dwc->ctrl_req_addr, GFP_KERNEL);
|
|
@@ -2861,7 +3003,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
|
|
*/
|
|
|
if (dwc->revision < DWC3_REVISION_220A)
|
|
|
dwc3_trace(trace_dwc3_gadget,
|
|
|
- "Changing max_speed on rev %08x\n",
|
|
|
+ "Changing max_speed on rev %08x",
|
|
|
dwc->revision);
|
|
|
|
|
|
dwc->gadget.max_speed = dwc->maximum_speed;
|
|
@@ -2935,61 +3077,50 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
|
|
|
|
|
|
int dwc3_gadget_suspend(struct dwc3 *dwc)
|
|
|
{
|
|
|
+ int ret;
|
|
|
+
|
|
|
if (!dwc->gadget_driver)
|
|
|
return 0;
|
|
|
|
|
|
- if (dwc->pullups_connected) {
|
|
|
- dwc3_gadget_disable_irq(dwc);
|
|
|
- dwc3_gadget_run_stop(dwc, true, true);
|
|
|
- }
|
|
|
-
|
|
|
- __dwc3_gadget_ep_disable(dwc->eps[0]);
|
|
|
- __dwc3_gadget_ep_disable(dwc->eps[1]);
|
|
|
+ ret = dwc3_gadget_run_stop(dwc, false, false);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
|
|
|
- dwc->dcfg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
|
|
+ dwc3_disconnect_gadget(dwc);
|
|
|
+ __dwc3_gadget_stop(dwc);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int dwc3_gadget_resume(struct dwc3 *dwc)
|
|
|
{
|
|
|
- struct dwc3_ep *dep;
|
|
|
int ret;
|
|
|
|
|
|
if (!dwc->gadget_driver)
|
|
|
return 0;
|
|
|
|
|
|
- /* Start with SuperSpeed Default */
|
|
|
- dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
|
|
|
-
|
|
|
- dep = dwc->eps[0];
|
|
|
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
|
|
|
- false);
|
|
|
- if (ret)
|
|
|
+ ret = __dwc3_gadget_start(dwc);
|
|
|
+ if (ret < 0)
|
|
|
goto err0;
|
|
|
|
|
|
- dep = dwc->eps[1];
|
|
|
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
|
|
|
- false);
|
|
|
- if (ret)
|
|
|
+ ret = dwc3_gadget_run_stop(dwc, true, false);
|
|
|
+ if (ret < 0)
|
|
|
goto err1;
|
|
|
|
|
|
- /* begin to receive SETUP packets */
|
|
|
- dwc->ep0state = EP0_SETUP_PHASE;
|
|
|
- dwc3_ep0_out_start(dwc);
|
|
|
-
|
|
|
- dwc3_writel(dwc->regs, DWC3_DCFG, dwc->dcfg);
|
|
|
-
|
|
|
- if (dwc->pullups_connected) {
|
|
|
- dwc3_gadget_enable_irq(dwc);
|
|
|
- dwc3_gadget_run_stop(dwc, true, false);
|
|
|
- }
|
|
|
-
|
|
|
return 0;
|
|
|
|
|
|
err1:
|
|
|
- __dwc3_gadget_ep_disable(dwc->eps[0]);
|
|
|
+ __dwc3_gadget_stop(dwc);
|
|
|
|
|
|
err0:
|
|
|
return ret;
|
|
|
}
|
|
|
+
|
|
|
+void dwc3_gadget_process_pending_events(struct dwc3 *dwc)
|
|
|
+{
|
|
|
+ if (dwc->pending_events) {
|
|
|
+ dwc3_interrupt(dwc->irq_gadget, dwc->ev_buf);
|
|
|
+ dwc->pending_events = false;
|
|
|
+ enable_irq(dwc->irq_gadget);
|
|
|
+ }
|
|
|
+}
|