dwc3: resize txfifo of IN/INT endpoint before enabling it
USB IN/INT endpoint stalls when performing TX FIFO resize functionality
when IN/INT endpoint is already active i.e. usb endpoint is enabled and
usb request is pending with it. Fix this issue by making sure that TX
FIFO resize is performed before enabling endpoint which shall happen
after set_alt(1) and before any function queues request with its allocated
USB endpoint.
CRs-Fixed: 2039310
Change-Id: I13a590f87ab8492f7c95a15b2da9f00c9c63c4f9
Signed-off-by: Mayank Rana <mrana@codeaurora.org>
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 009193c..b042152 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -590,6 +590,7 @@
* @dbg_ep_events: different events counter for endpoint
* @dbg_ep_events_diff: differential events counter for endpoint
* @dbg_ep_events_ts: timestamp for previous event counters
+ * @fifo_depth: allocated TXFIFO depth
*/
struct dwc3_ep {
struct usb_ep endpoint;
@@ -644,6 +645,7 @@
struct dwc3_ep_events dbg_ep_events;
struct dwc3_ep_events dbg_ep_events_diff;
struct timespec dbg_ep_events_ts;
+ int fifo_depth;
};
enum dwc3_phy {
@@ -905,7 +907,6 @@
* @pending_events: true when we have pending IRQs to be handled
* @needs_fifo_resize: not all users might want fifo resizing, flag it
* @pullups_connected: true when Run/Stop bit is set
- * @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes.
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
* @start_config_issued: true when StartConfig command has been issued
* @three_stage_setup: set if we perform a three phase setup
@@ -947,6 +948,7 @@
* @vbus_draw: current to be drawn from USB
* @index: dwc3 instance's number
* @dwc_ipc_log_ctxt: dwc3 ipc log context
+ * @last_fifo_depth: total TXFIFO depth of all enabled USB IN/INT endpoints
* @imod_interval: set the interrupt moderation interval in 250ns
* increments or 0 to disable.
*/
@@ -1082,7 +1084,6 @@
unsigned pending_events:1;
unsigned needs_fifo_resize:1;
unsigned pullups_connected:1;
- unsigned resize_fifos:1;
unsigned setup_packet_pending:1;
unsigned three_stage_setup:1;
unsigned usb3_lpm_capable:1;
@@ -1142,6 +1143,7 @@
wait_queue_head_t wait_linkstate;
unsigned int index;
void *dwc_ipc_log_ctxt;
+ int last_fifo_depth;
struct dwc3_gadget_events dbg_gadget_events;
};
@@ -1297,7 +1299,7 @@
/* prototypes */
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);
-int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc);
+int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc, struct dwc3_ep *dep);
/* check whether we are on the DWC_usb3 core */
static inline bool dwc3_is_usb3(struct dwc3 *dwc)
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 228d8af..d92df58 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -1127,7 +1127,8 @@
struct dwc3_gadget_ep_cmd_params params;
const struct usb_endpoint_descriptor *desc = ep->desc;
const struct usb_ss_ep_comp_descriptor *comp_desc = ep->comp_desc;
- u32 reg;
+ u32 reg;
+ int ret;
memset(¶ms, 0x00, sizeof(params));
@@ -1175,6 +1176,10 @@
/* Set XferRsc Index for GSI EP */
if (!(dep->flags & DWC3_EP_ENABLED)) {
+ ret = dwc3_gadget_resize_tx_fifos(dwc, dep);
+ if (ret)
+ return;
+
memset(¶ms, 0x00, sizeof(params));
params.param0 = DWC3_DEPXFERCFG_NUM_XFER_RES(1);
dwc3_send_gadget_ep_cmd(dep,
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index b062d58..ec9ffc1 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -588,8 +588,9 @@
{
enum usb_device_state state = dwc->gadget.state;
u32 cfg;
- int ret;
+ int ret, num;
u32 reg;
+ struct dwc3_ep *dep;
cfg = le16_to_cpu(ctrl->wValue);
@@ -598,6 +599,24 @@
return -EINVAL;
case USB_STATE_ADDRESS:
+ /* Read ep0IN related TXFIFO size */
+ dwc->last_fifo_depth = (dwc3_readl(dwc->regs,
+ DWC3_GTXFIFOSIZ(0)) & 0xFFFF);
+ /* Clear existing allocated TXFIFO for all IN eps except ep0 */
+ for (num = 0; num < dwc->num_in_eps; num++) {
+ dep = dwc->eps[(num << 1) | 1];
+ if (num) {
+ dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num), 0);
+ dep->fifo_depth = 0;
+ } else {
+ dep->fifo_depth = dwc->last_fifo_depth;
+ }
+
+ dev_dbg(dwc->dev, "%s(): %s dep->fifo_depth:%x\n",
+ __func__, dep->name, dep->fifo_depth);
+ dbg_event(0xFF, "fifo_reset", dep->number);
+ }
+
ret = dwc3_ep0_delegate_req(dwc, ctrl);
/* if the cfg matches and the cfg is non zero */
if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) {
@@ -619,9 +638,6 @@
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");
}
break;
@@ -1080,12 +1096,6 @@
{
int ret;
- if (dwc->resize_fifos) {
- dwc3_trace(trace_dwc3_ep0, "Resizing FIFOs");
- dwc3_gadget_resize_tx_fifos(dwc);
- dwc->resize_fifos = 0;
- }
-
ret = dwc3_ep0_start_control_status(dep);
if (WARN_ON_ONCE(ret))
dbg_event(dep->number, "ECTRLSTATUS", ret);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index df0427c..cf53d06 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -195,88 +195,64 @@
*
* Unfortunately, due to many variables that's not always the case.
*/
-int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc)
+int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc, struct dwc3_ep *dep)
{
- int last_fifo_depth = 0;
- int ram1_depth;
- int fifo_size;
- int mdwidth;
- int num;
- int num_eps;
- int max_packet = 1024;
- struct usb_composite_dev *cdev = get_gadget_data(&dwc->gadget);
+ int fifo_size, mdwidth, max_packet = 1024;
+ int tmp, mult = 1;
- if (!(cdev && cdev->config) || !dwc->needs_fifo_resize)
+ if (!dwc->needs_fifo_resize)
return 0;
- num_eps = dwc->num_in_eps;
- ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);
- mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
+ /* resize IN endpoints excepts ep0 */
+ if (!usb_endpoint_dir_in(dep->endpoint.desc) ||
+ dep->endpoint.ep_num == 0)
+ return 0;
- /* MDWIDTH is represented in bits, we need it in bytes */
- mdwidth >>= 3;
- last_fifo_depth = (dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0)) & 0xFFFF);
- dev_dbg(dwc->dev, "%s: num eps:%d max_packet:%d last_fifo_depth:%04x\n",
- __func__, num_eps, max_packet, last_fifo_depth);
-
- /* Don't resize ep0IN TxFIFO, start with ep1IN only. */
- for (num = 1; num < num_eps; num++) {
- /* bit0 indicates direction; 1 means IN ep */
- struct dwc3_ep *dep = dwc->eps[(num << 1) | 1];
- int mult = 1;
- int tmp;
-
- tmp = max_packet + mdwidth;
- /*
- * Interfaces like MBIM or ECM is having multiple data
- * interfaces. SET_CONFIG() happens before set_alt with
- * data interface 1 which results into calling this API
- * before GSI endpoint enabled. This results no txfifo
- * resize with GSI endpoint causing low throughput. Hence
- * use mult as 3 for GSI IN endpoint always irrespective
- * USB speed.
- */
- if (dep->endpoint.ep_type == EP_TYPE_GSI ||
- dep->endpoint.endless)
- mult = 3;
-
- if (!(dep->flags & DWC3_EP_ENABLED)) {
- dev_dbg(dwc->dev, "ep%dIn not enabled", num);
- goto resize_fifo;
- }
-
- if (((dep->endpoint.maxburst > 1) &&
- usb_endpoint_xfer_bulk(dep->endpoint.desc))
- || usb_endpoint_xfer_isoc(dep->endpoint.desc))
- mult = 3;
-
-resize_fifo:
- tmp *= mult;
- tmp += mdwidth;
-
- fifo_size = DIV_ROUND_UP(tmp, mdwidth);
-
- fifo_size |= (last_fifo_depth << 16);
-
- dev_dbg(dwc->dev, "%s: Fifo Addr %04x Size %d",
- dep->name, last_fifo_depth, fifo_size & 0xffff);
-
- last_fifo_depth += (fifo_size & 0xffff);
- if (dwc->tx_fifo_size &&
- (last_fifo_depth >= dwc->tx_fifo_size)) {
- /*
- * Fifo size allocated exceeded available RAM size.
- * Hence return error.
- */
- dev_err(dwc->dev, "Fifosize(%d) > available RAM(%d)\n",
- last_fifo_depth, dwc->tx_fifo_size);
- return -ENOMEM;
- }
-
- dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num), fifo_size);
-
+ /* Don't resize already resized IN endpoint */
+ if (dep->fifo_depth) {
+ dev_dbg(dwc->dev, "%s fifo_depth:%d is already set\n",
+ dep->endpoint.name, dep->fifo_depth);
+ return 0;
}
+ mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
+ /* MDWIDTH is represented in bits, we need it in bytes */
+ mdwidth >>= 3;
+
+ if (dep->endpoint.ep_type == EP_TYPE_GSI || dep->endpoint.endless)
+ mult = 3;
+
+ if (((dep->endpoint.maxburst > 1) &&
+ usb_endpoint_xfer_bulk(dep->endpoint.desc))
+ || usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ mult = 3;
+
+ tmp = ((max_packet + mdwidth) * mult) + mdwidth;
+ fifo_size = DIV_ROUND_UP(tmp, mdwidth);
+ dep->fifo_depth = fifo_size;
+ fifo_size |= (dwc->last_fifo_depth << 16);
+ dwc->last_fifo_depth += (fifo_size & 0xffff);
+
+ dev_dbg(dwc->dev, "%s ep_num:%d last_fifo_depth:%04x fifo_depth:%d\n",
+ dep->endpoint.name, dep->endpoint.ep_num, dwc->last_fifo_depth,
+ dep->fifo_depth);
+
+ dbg_event(0xFF, "resize_fifo", dep->number);
+ dbg_event(0xFF, "fifo_depth", dep->fifo_depth);
+ /* Check fifo size allocation doesn't exceed available RAM size. */
+ if (dwc->tx_fifo_size &&
+ ((dwc->last_fifo_depth * mdwidth) >= dwc->tx_fifo_size)) {
+ dev_err(dwc->dev, "Fifosize(%d) > RAM size(%d) %s depth:%d\n",
+ (dwc->last_fifo_depth * mdwidth), dwc->tx_fifo_size,
+ dep->endpoint.name, fifo_size);
+ dwc->last_fifo_depth -= (fifo_size & 0xffff);
+ dep->fifo_depth = 0;
+ WARN_ON(1);
+ return -ENOMEM;
+ }
+
+ dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(dep->endpoint.ep_num),
+ fifo_size);
return 0;
}
@@ -691,6 +667,17 @@
dwc3_trace(trace_dwc3_gadget, "Enabling %s", dep->name);
if (!(dep->flags & DWC3_EP_ENABLED)) {
+ dep->endpoint.desc = desc;
+ dep->comp_desc = comp_desc;
+ dep->type = usb_endpoint_type(desc);
+ ret = dwc3_gadget_resize_tx_fifos(dwc, dep);
+ if (ret) {
+ dep->endpoint.desc = NULL;
+ dep->comp_desc = NULL;
+ dep->type = 0;
+ return ret;
+ }
+
ret = dwc3_gadget_start_config(dwc, dep);
if (ret) {
dev_err(dwc->dev, "start_config() failed for %s\n",
@@ -710,9 +697,6 @@
struct dwc3_trb *trb_st_hw;
struct dwc3_trb *trb_link;
- dep->endpoint.desc = desc;
- dep->comp_desc = comp_desc;
- dep->type = usb_endpoint_type(desc);
dep->flags |= DWC3_EP_ENABLED;
reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
@@ -2986,9 +2970,6 @@
dwc3_stop_active_transfers(dwc);
dwc3_clear_stall_all_ep(dwc);
- /* bus reset issued due to missing status stage of a control transfer */
- dwc->resize_fifos = 0;
-
/* Reset device address to zero */
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
reg &= ~(DWC3_DCFG_DEVADDR_MASK);