Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cc5b27ee authored by Mayank Rana's avatar Mayank Rana Committed by Ajay Agarwal
Browse files

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: default avatarMayank Rana <mrana@codeaurora.org>
parent 6093862d
Loading
Loading
Loading
Loading
+5 −3
Original line number Original line Diff line number Diff line
@@ -543,6 +543,7 @@ struct dwc3_ep_events {
 * @dbg_ep_events: different events counter for endpoint
 * @dbg_ep_events: different events counter for endpoint
 * @dbg_ep_events_diff: differential events counter for endpoint
 * @dbg_ep_events_diff: differential events counter for endpoint
 * @dbg_ep_events_ts: timestamp for previous event counters
 * @dbg_ep_events_ts: timestamp for previous event counters
 * @fifo_depth: allocated TXFIFO depth
 */
 */
struct dwc3_ep {
struct dwc3_ep {
	struct usb_ep		endpoint;
	struct usb_ep		endpoint;
@@ -585,6 +586,7 @@ struct dwc3_ep {
	struct dwc3_ep_events	dbg_ep_events;
	struct dwc3_ep_events	dbg_ep_events;
	struct dwc3_ep_events	dbg_ep_events_diff;
	struct dwc3_ep_events	dbg_ep_events_diff;
	struct timespec		dbg_ep_events_ts;
	struct timespec		dbg_ep_events_ts;
	int			fifo_depth;
};
};


enum dwc3_phy {
enum dwc3_phy {
@@ -816,7 +818,6 @@ struct dwc3_scratchpad_array {
 * @is_selfpowered: true when we are selfpowered
 * @is_selfpowered: true when we are selfpowered
 * @needs_fifo_resize: not all users might want fifo resizing, flag it
 * @needs_fifo_resize: not all users might want fifo resizing, flag it
 * @pullups_connected: true when Run/Stop bit is set
 * @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
 * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
 * @start_config_issued: true when StartConfig command has been issued
 * @start_config_issued: true when StartConfig command has been issued
 * @three_stage_setup: set if we perform a three phase setup
 * @three_stage_setup: set if we perform a three phase setup
@@ -834,6 +835,7 @@ struct dwc3_scratchpad_array {
 * @wait_linkstate: waitqueue for waiting LINK to move into required state
 * @wait_linkstate: waitqueue for waiting LINK to move into required state
 * @vbus_draw: current to be drawn from USB
 * @vbus_draw: current to be drawn from USB
 * @dwc_ipc_log_ctxt: dwc3 ipa log context
 * @dwc_ipc_log_ctxt: dwc3 ipa log context
 * @last_fifo_depth: total TXFIFO depth of all enabled USB IN/INT endpoints
 */
 */
struct dwc3 {
struct dwc3 {
	struct usb_ctrlrequest	*ctrl_req;
	struct usb_ctrlrequest	*ctrl_req;
@@ -946,7 +948,6 @@ struct dwc3 {
	unsigned		is_selfpowered:1;
	unsigned		is_selfpowered:1;
	unsigned		needs_fifo_resize:1;
	unsigned		needs_fifo_resize:1;
	unsigned		pullups_connected:1;
	unsigned		pullups_connected:1;
	unsigned		resize_fifos:1;
	unsigned		setup_packet_pending:1;
	unsigned		setup_packet_pending:1;
	unsigned		three_stage_setup:1;
	unsigned		three_stage_setup:1;
	unsigned		is_drd:1;
	unsigned		is_drd:1;
@@ -988,6 +989,7 @@ struct dwc3 {


	wait_queue_head_t	wait_linkstate;
	wait_queue_head_t	wait_linkstate;
	void			*dwc_ipc_log_ctxt;
	void			*dwc_ipc_log_ctxt;
	int			last_fifo_depth;
};
};


/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
@@ -1137,7 +1139,7 @@ struct dwc3_gadget_ep_cmd_params {


/* prototypes */
/* prototypes */
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc);
int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc, struct dwc3_ep *dep);


#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_host_init(struct dwc3 *dwc);
int dwc3_host_init(struct dwc3 *dwc);
+6 −1
Original line number Original line Diff line number Diff line
@@ -1167,6 +1167,7 @@ static void gsi_configure_ep(struct usb_ep *ep, struct usb_gsi_request *request)
	const struct usb_endpoint_descriptor *desc = ep->desc;
	const struct usb_endpoint_descriptor *desc = ep->desc;
	const struct usb_ss_ep_comp_descriptor *comp_desc = ep->comp_desc;
	const struct usb_ss_ep_comp_descriptor *comp_desc = ep->comp_desc;
	u32 reg;
	u32 reg;
	int ret;


	memset(&params, 0x00, sizeof(params));
	memset(&params, 0x00, sizeof(params));


@@ -1215,6 +1216,10 @@ static void gsi_configure_ep(struct usb_ep *ep, struct usb_gsi_request *request)


	/* Set XferRsc Index for GSI EP */
	/* Set XferRsc Index for GSI EP */
	if (!(dep->flags & DWC3_EP_ENABLED)) {
	if (!(dep->flags & DWC3_EP_ENABLED)) {
		ret = dwc3_gadget_resize_tx_fifos(dwc, dep);
		if (ret)
			return;

		memset(&params, 0x00, sizeof(params));
		memset(&params, 0x00, sizeof(params));
		params.param0 = DWC3_DEPXFERCFG_NUM_XFER_RES(1);
		params.param0 = DWC3_DEPXFERCFG_NUM_XFER_RES(1);
		dwc3_send_gadget_ep_cmd(dwc, dep->number,
		dwc3_send_gadget_ep_cmd(dwc, dep->number,
+20 −9
Original line number Original line Diff line number Diff line
@@ -586,8 +586,9 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
{
	enum usb_device_state state = dwc->gadget.state;
	enum usb_device_state state = dwc->gadget.state;
	u32 cfg;
	u32 cfg;
	int ret;
	int ret, num;
	u32 reg;
	u32 reg;
	struct dwc3_ep	*dep;


	cfg = le16_to_cpu(ctrl->wValue);
	cfg = le16_to_cpu(ctrl->wValue);


@@ -597,6 +598,24 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
		break;
		break;


	case USB_STATE_ADDRESS:
	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);
		ret = dwc3_ep0_delegate_req(dwc, ctrl);
		/* if the cfg matches and the cfg is non zero */
		/* if the cfg matches and the cfg is non zero */
		if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) {
		if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) {
@@ -621,9 +640,6 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
							DWC3_DCTL_ACCEPTU2ENA);
							DWC3_DCTL_ACCEPTU2ENA);
				dwc3_writel(dwc->regs, DWC3_DCTL, reg);
				dwc3_writel(dwc->regs, DWC3_DCTL, reg);
			}
			}

			dwc->resize_fifos = true;
			dwc3_trace(trace_dwc3_ep0, "resize FIFOs flag SET");
		}
		}
		break;
		break;


@@ -1046,11 +1062,6 @@ 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)
static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)
{
{
	int ret;
	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);
	ret = dwc3_ep0_start_control_status(dep);
	if (WARN_ON_ONCE(ret))
	if (WARN_ON_ONCE(ret))
+61 −83
Original line number Original line Diff line number Diff line
@@ -183,91 +183,64 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
 *
 *
 * Unfortunately, due to many variables that's not always the case.
 * 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		fifo_size, mdwidth, max_packet = 1024;
	int		ram1_depth;
	int		tmp, mult = 1;
	int		fifo_size;

	int		mdwidth;
	if (!dwc->needs_fifo_resize)
	int		num;
	int		num_eps;
	int		max_packet = 1024;
	struct usb_composite_dev *cdev = get_gadget_data(&dwc->gadget);

	if (!(cdev && cdev->config) || !dwc->needs_fifo_resize)
		return 0;
		return 0;


	num_eps = dwc->num_in_eps;
	/* resize IN endpoints excepts ep0 */
	ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);
	if (!usb_endpoint_dir_in(dep->endpoint.desc) ||
	mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
			dep->endpoint.ep_num == 0)
		return 0;


	/* 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 is represented in bits, we need it in bytes */
	mdwidth >>= 3;
	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 (num == high_bw_ep_in_num)
	if (dep->endpoint.ep_type == EP_TYPE_GSI || dep->endpoint.endless)
		mult = 3;
		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) &&
	if (((dep->endpoint.maxburst > 1) &&
			usb_endpoint_xfer_bulk(dep->endpoint.desc))
			usb_endpoint_xfer_bulk(dep->endpoint.desc))
			|| usb_endpoint_xfer_isoc(dep->endpoint.desc))
			|| usb_endpoint_xfer_isoc(dep->endpoint.desc))
		mult = 3;
		mult = 3;


resize_fifo:
	tmp = ((max_packet + mdwidth) * mult) + mdwidth;
		tmp *= mult;
		tmp += mdwidth;

	fifo_size = DIV_ROUND_UP(tmp, 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);


		fifo_size |= (last_fifo_depth << 16);
	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);


		dev_dbg(dwc->dev, "%s: Fifo Addr %04x Size %d\n",
	dbg_event(0xFF, "resize_fifo", dep->number);
				dep->name, last_fifo_depth, fifo_size & 0xffff);
	dbg_event(0xFF, "fifo_depth", dep->fifo_depth);

	/* Check fifo size allocation doesn't exceed available RAM size. */
		last_fifo_depth += (fifo_size & 0xffff);
	if (dwc->tx_fifo_size &&
	if (dwc->tx_fifo_size &&
				(last_fifo_depth >= 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",
			 * Fifo size allocated exceeded available RAM size.
			(dwc->last_fifo_depth * mdwidth), dwc->tx_fifo_size,
			 * Hence return error.
			dep->endpoint.name, fifo_size);
			 */
		dwc->last_fifo_depth -= (fifo_size & 0xffff);
			dev_err(dwc->dev, "Fifosize(%d) > available RAM(%d)\n",
		dep->fifo_depth = 0;
					last_fifo_depth, dwc->tx_fifo_size);
		WARN_ON(1);
		return -ENOMEM;
		return -ENOMEM;
	}
	}


		dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num), fifo_size);
	dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(dep->endpoint.ep_num),

							fifo_size);
	}

	return 0;
	return 0;
}
}


@@ -626,6 +599,17 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
	dev_vdbg(dwc->dev, "Enabling %s\n", dep->name);
	dev_vdbg(dwc->dev, "Enabling %s\n", dep->name);


	if (!(dep->flags & DWC3_EP_ENABLED)) {
	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);
		ret = dwc3_gadget_start_config(dwc, dep);
		if (ret) {
		if (ret) {
			dev_err(dwc->dev, "start_config() failed for %s\n",
			dev_err(dwc->dev, "start_config() failed for %s\n",
@@ -645,9 +629,6 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
		struct dwc3_trb	*trb_st_hw;
		struct dwc3_trb	*trb_st_hw;
		struct dwc3_trb	*trb_link;
		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;
		dep->flags |= DWC3_EP_ENABLED;


		reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
		reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
@@ -3018,9 +2999,6 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
	dwc3_stop_active_transfers(dwc);
	dwc3_stop_active_transfers(dwc);
	dwc3_clear_stall_all_ep(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 */
	/* Reset device address to zero */
	reg = dwc3_readl(dwc->regs, DWC3_DCFG);
	reg = dwc3_readl(dwc->regs, DWC3_DCFG);
	reg &= ~(DWC3_DCFG_DEVADDR_MASK);
	reg &= ~(DWC3_DCFG_DEVADDR_MASK);