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

Commit 15814608 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "USB: gadget: Fix USB EP flush issues on disconnect"

parents 546bcd43 2f66a26f
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) {
+30 −8
Original line number Diff line number Diff line
/*
 * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
 * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -20,6 +20,7 @@ static int alloc_sps_req(struct usb_ep *data_ep)
{
	struct usb_request *req = NULL;
	struct f_qdss *qdss = data_ep->driver_data;
	struct usb_gadget *gadget = qdss->gadget;
	u32 sps_params = 0;

	pr_debug("send_sps_req\n");
@@ -30,9 +31,17 @@ static int alloc_sps_req(struct usb_ep *data_ep)
		return -ENOMEM;
	}

	if (!gadget->is_chipidea) {
		req->length = 32*1024;
		sps_params = MSM_SPS_MODE | MSM_DISABLE_WB |
				qdss->bam_info.usb_bam_pipe_idx;
	} else {
		/* non DWC3 BAM requires req->length to be 0 */
		req->length = 0;
		sps_params = (MSM_SPS_MODE | qdss->bam_info.usb_bam_pipe_idx |
				MSM_VENDOR_ID) & ~MSM_IS_FINITE_TRANSFER;
	}

	req->udc_priv = sps_params;
	qdss->endless_req = req;

@@ -107,10 +116,12 @@ int set_qdss_data_connection(struct f_qdss *qdss, int enable)
				NULL, bam_info.data_fifo, NULL);

		alloc_sps_req(qdss->port.data);
		if (!gadget->is_chipidea)
			msm_data_fifo_config(qdss->port.data,
				bam_info.data_fifo->iova,
				bam_info.data_fifo->size,
				bam_info.usb_bam_pipe_idx);

		init_data(qdss->port.data);

		res = usb_bam_connect(usb_bam_type, idx,
@@ -132,8 +143,14 @@ int set_qdss_data_connection(struct f_qdss *qdss, int enable)
static int init_data(struct usb_ep *ep)
{
	struct f_qdss *qdss = ep->driver_data;
	struct usb_gadget *gadget = qdss->gadget;
	int res = 0;

	if (gadget->is_chipidea) {
		pr_debug("QDSS is used with non DWC3 core\n");
		return res;
	}

	pr_debug("init_data\n");

	res = msm_ep_config(ep, qdss->endless_req);
@@ -145,8 +162,13 @@ static int init_data(struct usb_ep *ep)

int uninit_data(struct usb_ep *ep)
{
	struct f_qdss *qdss = ep->driver_data;
	struct usb_gadget *gadget = qdss->gadget;
	int res = 0;

	if (gadget->is_chipidea)
		return res;

	pr_err("uninit_data\n");

	res = msm_ep_unconfig(ep);
+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