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

Commit 36a9a871 authored by Vijayavardhan Vennapusa's avatar Vijayavardhan Vennapusa
Browse files

USB: gadget: Fix USB EP flush issues on disconnect



Currently if cable is disconnected or composition switch happens during
BAM2BAM data transfers, USB driver tries to flush USB endpoint as part of
disconnect, which is failing. This eventually leads to NOC error crash.
Fix the issue by making sure pipes are empty and IPA endpoints are disabled
and perform USB bam reset before disabling USB endpoints as part of new
sequence implementation recommended by HW designers.

Change-Id: I17000fd380d95497629372a9f6371902f04ca2d9
Signed-off-by: default avatarVijayavardhan Vennapusa <vvreddy@codeaurora.org>
parent 403033bb
Loading
Loading
Loading
Loading
+127 −5
Original line number Diff line number Diff line
@@ -2644,12 +2644,12 @@ int usb_bam_disconnect_pipe(enum usb_ctrl bam_type, u8 idx)

		if (bam_type == CI_CTRL)
			msm_hw_bam_disable(0);
	}
	/* Enable usb irq here which is disabled in function drivers
	 * during disconnect after BAM reset.
	 */
		if (bam_type == CI_CTRL)
	if (!ctx->pipes_enabled_per_bam && (bam_type == CI_CTRL))
		msm_usb_irq_disable(false);
	}
	/* This function is directly called by USB Transport drivers
	 * to disconnect pipes. Drop runtime usage count here. For
	 * IPA, caller takes care of it
@@ -3053,6 +3053,7 @@ static int usb_bam_init(struct platform_device *pdev)
	if (pdata->enable_hsusb_bam_on_boot && bam_type == CI_CTRL) {
		pr_debug("Register and enable HSUSB BAM\n");
		props.options |= SPS_BAM_OPT_ENABLE_AT_BOOT;
		props.options |= SPS_BAM_FORCE_RESET;
	}
	ret = sps_register_bam_device(&props, &ctx->h_bam);

@@ -3395,6 +3396,127 @@ int usb_bam_get_bam_type(const char *core_name)
}
EXPORT_SYMBOL(usb_bam_get_bam_type);

/*
 * This function makes sure ipa endpoints are disabled for both USB->IPA
 * and IPA->USB pipes before USB bam reset. USB BAM reset is required to
 * to avoid EP flush issues while disabling USB endpoints on disconnect.
 */
int msm_do_bam_disable_enable(enum usb_ctrl bam)
{
	struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam];
	struct sps_pipe *pipe;
	u32 timeout = 10, pipe_empty;
	int ret = 0, i;
	struct sps_connect *sps_connection;
	struct usb_bam_sps_type usb_bam_sps = ctx->usb_bam_sps;
	struct usb_bam_pipe_connect *pipe_connect;
	int qdss_idx;
	struct msm_usb_bam_platform_data *pdata;

	if (!ctx->usb_bam_pdev)
		return 0;

	pdata = ctx->usb_bam_pdev->dev.platform_data;
	if ((bam != CI_CTRL) || !pdata->enable_hsusb_bam_on_boot)
		return 0;

	if (!ctx->pipes_enabled_per_bam || info[bam].pipes_suspended)
		return 0;

	if (in_interrupt()) {
		pr_err("%s:API called in interrupt context\n", __func__);
		return 0;
	}

	mutex_lock(&info[bam].suspend_resume_mutex);
	log_event_dbg("%s: Perform USB BAM reset\n", __func__);
	/* Get QDSS pipe index to avoid pipe reset */
	qdss_idx = usb_bam_get_connection_idx(qdss_usb_bam_type, QDSS_P_BAM,
		PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE, 0);

	for (i = 0; i < ctx->max_connections; i++) {
		pipe_connect = &ctx->usb_bam_connections[i];
		if (pipe_connect->enabled &&
				(pipe_connect->dir == PEER_PERIPHERAL_TO_USB) &&
							(qdss_idx != i)) {
			/* Call to disable IPA producer endpoint */
			ipa_disable_endpoint(pipe_connect->ipa_clnt_hdl);
			sps_pipe_reset(ctx->h_bam,
						pipe_connect->dst_pipe_index);
		}
	}

	for (i = 0; i < ctx->max_connections; i++) {
		pipe_connect = &ctx->usb_bam_connections[i];
		if (pipe_connect->enabled &&
				(pipe_connect->dir == USB_TO_PEER_PERIPHERAL) &&
							(qdss_idx != i)) {
			pipe = ctx->usb_bam_sps.sps_pipes[i];
			sps_connection = &usb_bam_sps.sps_connections[i];
			timeout = 10;
			/*
			 * On some platforms, there is a chance that flow
			 * control is disabled from IPA side, due to this IPA
			 * core may not consume data from USB. Hence notify IPA
			 * to enable flow control and then check sps pipe is
			 * empty or not before processing USB->IPA disconnect.
			 */
			ipa_clear_endpoint_delay(pipe_connect->ipa_clnt_hdl);

			/* Make sure pipes are empty before disconnecting it */
			while (1) {
				ret = sps_is_pipe_empty(pipe, &pipe_empty);
				if (ret) {
					log_event_err("%s: pipeempty fail %d\n",
								__func__, ret);
					goto err;
				}
				if (pipe_empty || !--timeout)
					break;

				/* Check again */
				usleep_range(1000, 2000);
			}
			if (!pipe_empty) {
				log_event_dbg("%s: Inject ZLT\n", __func__);
				sps_pipe_inject_zlt(sps_connection->destination,
					sps_connection->dest_pipe_index);

				timeout = 0;
				while (1) {
					ret = sps_is_pipe_empty(pipe,
								&pipe_empty);
					if (ret)
						goto err;

					if (pipe_empty)
						break;

					timeout++;
					/* Check again */
					usleep_range(1000, 2000);
				}
			}
			/* Call to disable IPA consumer endpoint */
			ipa_disable_endpoint(pipe_connect->ipa_clnt_hdl);
			sps_pipe_reset(ctx->h_bam,
						pipe_connect->src_pipe_index);
		}
	}

	/* Perform USB BAM reset */
	msm_hw_bam_disable(1);
	sps_device_reset(ctx->h_bam);
	msm_hw_bam_disable(0);
	log_event_dbg("%s: USB BAM reset done\n", __func__);
	ret = 0;

err:
	mutex_unlock(&info[bam].suspend_resume_mutex);
	return ret;
}
EXPORT_SYMBOL(msm_do_bam_disable_enable);

bool msm_usb_bam_enable(enum usb_ctrl bam, bool bam_enable)
{
	struct msm_usb_bam_platform_data *pdata;
+21 −2
Original line number Diff line number Diff line
@@ -18,7 +18,9 @@
#include <linux/device.h>
#include <linux/utsname.h>

#include "gadget_chips.h"
#include <linux/usb/composite.h>
#include <linux/usb/msm_hsusb.h>
#include <asm/unaligned.h>

#include "u_os_desc.h"
@@ -722,6 +724,11 @@ static void reset_config(struct usb_composite_dev *cdev)

	DBG(cdev, "reset config\n");

	if (!cdev->config) {
		pr_err("%s:cdev->config is already NULL\n", __func__);
		return;
	}

	list_for_each_entry(f, &cdev->config->functions, list) {
		if (f->disable)
			f->disable(f);
@@ -1023,8 +1030,14 @@ void usb_remove_config(struct usb_composite_dev *cdev,
		return;
	}

	if (cdev->config == config)
	if (cdev->config == config) {
		if (!gadget_is_dwc3(cdev->gadget) && !cdev->suspended) {
			spin_unlock_irqrestore(&cdev->lock, flags);
			msm_do_bam_disable_enable(CI_CTRL);
			spin_lock_irqsave(&cdev->lock, flags);
		}
		reset_config(cdev);
	}

	list_del(&config->list);

@@ -2015,8 +2028,14 @@ void composite_disconnect(struct usb_gadget *gadget)
	 * disconnect callbacks?
	 */
	spin_lock_irqsave(&cdev->lock, flags);
	if (cdev->config)
	if (cdev->config) {
		if (!gadget_is_dwc3(gadget) && !cdev->suspended) {
			spin_unlock_irqrestore(&cdev->lock, flags);
			msm_do_bam_disable_enable(CI_CTRL);
			spin_lock_irqsave(&cdev->lock, flags);
		}
		reset_config(cdev);
	}
	if (cdev->driver->disconnect)
		cdev->driver->disconnect(cdev);
	if (cdev->delayed_status != 0) {
+9 −9
Original line number Diff line number Diff line
@@ -2091,6 +2091,15 @@ void gbam_disconnect(struct grmnet *gr, u8 port_num, enum transport_type trans)
	spin_unlock(&port->port_lock_dl);
	spin_unlock_irqrestore(&port->port_lock_ul, flags_ul);

	usb_ep_disable(gr->in);
	if (trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
		spin_lock_irqsave(&port->port_lock_dl, flags_dl);
		if (d->tx_req) {
			usb_ep_free_request(gr->in, d->tx_req);
			d->tx_req = NULL;
		}
		spin_unlock_irqrestore(&port->port_lock_dl, flags_dl);
	}
	/* disable endpoints */
	if (gr->out) {
		usb_ep_disable(gr->out);
@@ -2103,15 +2112,6 @@ void gbam_disconnect(struct grmnet *gr, u8 port_num, enum transport_type trans)
			spin_unlock_irqrestore(&port->port_lock_ul, flags_ul);
		}
	}
	usb_ep_disable(gr->in);
	if (trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
		spin_lock_irqsave(&port->port_lock_dl, flags_dl);
		if (d->tx_req) {
			usb_ep_free_request(gr->in, d->tx_req);
			d->tx_req = NULL;
		}
		spin_unlock_irqrestore(&port->port_lock_dl, flags_dl);
	}

	/*
	 * Set endless flag to false as USB Endpoint is already
+7 −6
Original line number Diff line number Diff line
@@ -1423,6 +1423,13 @@ void bam_data_disconnect(struct data_port *gr, enum function_type func,
			 * to obtain the spinlock as well.
			 */
			spin_unlock_irqrestore(&port->port_lock, flags);
			usb_ep_disable(port->port_usb->in);
			if (d->tx_req) {
				usb_ep_free_request(port->port_usb->in,
								d->tx_req);
				d->tx_req = NULL;
			}

			usb_ep_disable(port->port_usb->out);
			if (d->rx_req) {
				usb_ep_free_request(port->port_usb->out,
@@ -1430,12 +1437,6 @@ void bam_data_disconnect(struct data_port *gr, enum function_type func,
				d->rx_req = NULL;
			}

			usb_ep_disable(port->port_usb->in);
			if (d->tx_req) {
				usb_ep_free_request(port->port_usb->in,
								d->tx_req);
				d->tx_req = NULL;
			}
			spin_lock_irqsave(&port->port_lock, flags);

			/* Only for SYS2BAM mode related UL workaround */
+2 −0
Original line number Diff line number Diff line
@@ -637,6 +637,7 @@ void msm_bam_usb_host_notify_on_resume(void);
void msm_bam_hsic_host_notify_on_resume(void);
bool msm_bam_hsic_host_pipe_empty(void);
bool msm_usb_bam_enable(enum usb_ctrl ctrl, bool bam_enable);
int msm_do_bam_disable_enable(enum usb_ctrl ctrl);
#else
static inline void msm_bam_set_usb_host_dev(struct device *dev) {}
static inline void msm_bam_set_hsic_host_dev(struct device *dev) {}
@@ -650,6 +651,7 @@ static inline bool msm_usb_bam_enable(enum usb_ctrl ctrl, bool bam_enable)
{
	return true;
}
int msm_do_bam_disable_enable(enum usb_ctrl ctrl) { return true; }
#endif
#ifdef CONFIG_USB_CI13XXX_MSM
void msm_hw_bam_disable(bool bam_disable);