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

Commit 2f66a26f authored by Vijayavardhan Vennapusa's avatar Vijayavardhan Vennapusa Committed by Gerrit - the friendly Code Review server
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. 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>
Signed-off-by: default avatarAjay Agarwal <ajaya@codeaurora.org>
parent 44b79ab7
Loading
Loading
Loading
Loading
+127 −5
Original line number Diff line number Diff line
@@ -2729,12 +2729,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
@@ -3136,6 +3136,7 @@ static int usb_bam_init(struct platform_device *pdev)
					&& 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;
	}

	dev = &ctx->usb_bam_pdev->dev;
@@ -3406,6 +3407,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_data *usb_bam_data;

	if (!ctx->usb_bam_pdev)
		return 0;

	usb_bam_data = ctx->usb_bam_data;
	if ((bam != CI_CTRL) || !usb_bam_data->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_data *usb_bam_data;
+20 −2
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@

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

#include "u_os_desc.h"
@@ -856,6 +857,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);
@@ -1120,8 +1126,14 @@ void usb_remove_config(struct usb_composite_dev *cdev,

	spin_lock_irqsave(&cdev->lock, flags);

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

	spin_unlock_irqrestore(&cdev->lock, flags);

@@ -2161,8 +2173,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_chipidea && !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) {
+2 −0
Original line number Diff line number Diff line
@@ -438,6 +438,7 @@ int get_qdss_bam_info(enum usb_ctrl cur_bam, u8 idx,
bool msm_bam_hsic_lpm_ok(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 int usb_bam_connect(enum usb_ctrl bam, u8 idx, u32 *bam_pipe_idx,
							unsigned long iova)
@@ -543,6 +544,7 @@ static inline bool msm_bam_hsic_lpm_ok(void) { return true; }
static inline bool msm_bam_hsic_host_pipe_empty(void) { return true; }
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