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

Commit 8111d698 authored by Manu Gautam's avatar Manu Gautam Committed by Ajay Agarwal
Browse files

usb: dwc3-msm: Enable EP events only after GSI doorbell is updated



USB GSI hardware wrapper monitors USB interrupters for any events
on GSI EPs and rings GSI_DBL if there is any event. Since, GSI
doorbell can be enabled/disabled for all endpoints at a time and
not per endpoint, there is always a possibility of GSI wrapper
getting enabled before DBL_ADDR is programmed for all GSI_EPs.

Consider a case when there are multiple GSI functions present in
the composition (e.g. ADPL and RNDIS/RMNET) and ADPL function's
data_path_enable happens first which enables GSI wrapper.
And if for some reason there is a delay between RNDIS/RMNET EPs'
start_xfer and doorbell address update (and GSI wrapper is
already enabled by ADPL) then hardware may try to ring GSI_DBL
with address-0 on receiving XFER_IN_PROGRESS event.

This can be avoided by deferring enable of USB GSI EP's endpoint
events until doorbell address is programmed. It requires not
enabling EP events as part of EP_CONFIG on INIT and instead
issuing MODIFY EP_CONFIG command later for GSI EPs.
Similar issue exists for function suspend/resume when dealing
with more than one GSI interfaces in a composition. In that case
when all interfaces have disabled or blocked doorbell on its
function suspend, function resume of first interface unblocks
doorbell for all endpoint and can result in issues as corresponding
xdci channel might still be in suspended state. Fix this by also
disabling EP events when blocking doorbell so that unless EP events
are enabled again, hardware shouldn't access doorbell.

Change-Id: Ibe857fd04de7c6f1b4e74b4b1e55d711d73af2cc
Signed-off-by: default avatarManu Gautam <mgautam@codeaurora.org>
Signed-off-by: default avatarAjay Agarwal <ajaya@codeaurora.org>
parent 86979a83
Loading
Loading
Loading
Loading
+15 −13
Original line number Diff line number Diff line
@@ -516,6 +516,19 @@

struct dwc3_trb;

/**
 * struct dwc3_gadget_ep_cmd_params - representation of endpoint command
 * parameters
 * @param2: third parameter
 * @param1: second parameter
 * @param0: first parameter
 */
struct dwc3_gadget_ep_cmd_params {
	u32	param2;
	u32	param1;
	u32	param0;
};

/**
 * struct dwc3_event_buffer - Software event buffer representation
 * @buf: _THE_ buffer
@@ -606,6 +619,7 @@ struct dwc3_ep_events {
 * @dbg_ep_events_diff: differential events counter for endpoint
 * @dbg_ep_events_ts: timestamp for previous event counters
 * @fifo_depth: allocated TXFIFO depth
 * @ep_cfg_init_params: Used by GSI EP to save EP_CFG init_cmd params
 */
struct dwc3_ep {
	struct usb_ep		endpoint;
@@ -661,6 +675,7 @@ struct dwc3_ep {
	struct dwc3_ep_events	dbg_ep_events_diff;
	struct timespec		dbg_ep_events_ts;
	int			fifo_depth;
	struct dwc3_gadget_ep_cmd_params ep_cfg_init_params;
};

enum dwc3_phy {
@@ -1305,19 +1320,6 @@ union dwc3_event {
	struct dwc3_event_gevt		gevt;
};

/**
 * struct dwc3_gadget_ep_cmd_params - representation of endpoint command
 * parameters
 * @param2: third parameter
 * @param1: second parameter
 * @param0: first parameter
 */
struct dwc3_gadget_ep_cmd_params {
	u32	param2;
	u32	param1;
	u32	param0;
};

/*
 * DWC3 Features to be used as Driver Data
 */
+70 −5
Original line number Diff line number Diff line
@@ -1232,6 +1232,7 @@ static void gsi_free_trbs(struct usb_ep *ep, struct usb_gsi_request *req)
	}
	sg_free_table(&req->sgt_trb_xfer_ring);
}

/*
* Configures GSI EPs. For GSI EPs we need to set interrupter numbers.
*
@@ -1274,10 +1275,8 @@ static void gsi_configure_ep(struct usb_ep *ep, struct usb_gsi_request *request)
	/* Set interrupter number for GSI endpoints */
	params.param1 |= DWC3_DEPCFG_INT_NUM(ep->ep_intr_num);

	/* Enable XferInProgress and XferComplete Interrupts */
	params.param1 |= DWC3_DEPCFG_XFER_COMPLETE_EN;
	params.param1 |= DWC3_DEPCFG_XFER_IN_PROGRESS_EN;
	params.param1 |= DWC3_DEPCFG_FIFO_ERROR_EN;
	/* EP Events are enabled later once DBL_ADDR is updated */

	/*
	 * We must use the lower 16 TX FIFOs even though
	 * HW might have more
@@ -1291,6 +1290,9 @@ static void gsi_configure_ep(struct usb_ep *ep, struct usb_gsi_request *request)
	dev_dbg(mdwc->dev, "Set EP config to params = %x %x %x, for %s\n",
		params.param0, params.param1, params.param2, dep->name);

	/* params are used later when EP_CONFIG is modified to enable events */
	dep->ep_cfg_init_params = params;

	dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, &params);

	/* Set XferRsc Index for GSI EP */
@@ -1315,6 +1317,57 @@ static void gsi_configure_ep(struct usb_ep *ep, struct usb_gsi_request *request)

}

/*
 * Enables events for GSI EPs. Modify EP_CONFIG to enable EP events
 * after GSI wrapper is initialized for the endpoint.
 *
 * @usb_ep - pointer to usb_ep instance.
 */
static void gsi_enable_ep_events(struct usb_ep *ep)
{
	struct dwc3_ep *dep = to_dwc3_ep(ep);
	struct dwc3_msm *mdwc = dev_get_drvdata(dep->dwc->dev->parent);
	struct dwc3_gadget_ep_cmd_params params;

	/* EP is already configured, just update params to enable events */
	params = dep->ep_cfg_init_params;

	/* Enable XferInProgress and XferComplete Interrupts */
	params.param1 |= DWC3_DEPCFG_XFER_COMPLETE_EN;
	params.param1 |= DWC3_DEPCFG_XFER_IN_PROGRESS_EN;
	params.param1 |= DWC3_DEPCFG_FIFO_ERROR_EN;

	params.param0 |= DWC3_DEPCFG_ACTION_MODIFY;

	dev_dbg(mdwc->dev, "Modify EP config to params = %x %x %x, for %s\n",
		params.param0, params.param1, params.param2, dep->name);

	dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, &params);
}

/*
 * Disables events for GSI EPs. Modify EP_CONFIG to disable EP events
 * to prevent USB GSI wrapper from ringing any doorbell.
 *
 * @usb_ep - pointer to usb_ep instance.
 */
static void gsi_disable_ep_events(struct usb_ep *ep)
{
	struct dwc3_ep *dep = to_dwc3_ep(ep);
	struct dwc3_msm *mdwc = dev_get_drvdata(dep->dwc->dev->parent);
	struct dwc3_gadget_ep_cmd_params params;

	/* EP is already enabled, just restore init_params to disable events */
	params = dep->ep_cfg_init_params;

	params.param0 |= DWC3_DEPCFG_ACTION_MODIFY;

	dev_dbg(mdwc->dev, "Modify EP config to params = %x %x %x, for %s\n",
		params.param0, params.param1, params.param2, dep->name);

	dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, &params);
}

/*
* Enables USB wrapper for GSI
*
@@ -1352,6 +1405,16 @@ static void gsi_set_clear_dbell(struct usb_ep *ep,
	struct dwc3 *dwc = dep->dwc;
	struct dwc3_msm *mdwc = dev_get_drvdata(dwc->dev->parent);

	/*
	 * Disable EP events if doorbell needs to be blocked to avoid issues
	 * due to another GSI interface endpoint enabling doorbell say on resume
	 * as there is no control of doorbell per endpoint.
	 */
	if (block_db)
		gsi_disable_ep_events(ep);
	else
		gsi_enable_ep_events(ep);

	dwc3_msm_write_reg_field(mdwc->base,
		GSI_GENERAL_CFG_REG, BLOCK_GSI_WR_GO_MASK, block_db);
}
@@ -1462,7 +1525,9 @@ static int dwc3_msm_gsi_ep_op(struct usb_ep *ep,
		break;
	case GSI_EP_OP_SET_CLR_BLOCK_DBL:
		block_db = *((bool *)op_data);
		spin_lock_irqsave(&dwc->lock, flags);
		gsi_set_clear_dbell(ep, block_db);
		spin_unlock_irqrestore(&dwc->lock, flags);
		break;
	case GSI_EP_OP_CHECK_FOR_SUSPEND:
		ret = gsi_check_ready_to_suspend(mdwc);
+36 −17
Original line number Diff line number Diff line
@@ -746,33 +746,34 @@ static void ipa_data_path_enable(struct gsi_data_port *d_port)
	bool block_db = false;

	log_event_dbg("IN: db_reg_phs_addr_lsb = %x",
			gsi->d_port.in_request.db_reg_phs_addr_lsb);
	usb_gsi_ep_op(gsi->d_port.in_ep,
			&gsi->d_port.in_request,
			d_port->in_request.db_reg_phs_addr_lsb);
	usb_gsi_ep_op(d_port->in_ep, &d_port->in_request,
			GSI_EP_OP_STORE_DBL_INFO);

	if (gsi->d_port.out_ep) {
	if (d_port->out_ep) {
		log_event_dbg("OUT: db_reg_phs_addr_lsb = %x",
				gsi->d_port.out_request.db_reg_phs_addr_lsb);
		usb_gsi_ep_op(gsi->d_port.out_ep,
				&gsi->d_port.out_request,
				d_port->out_request.db_reg_phs_addr_lsb);
		usb_gsi_ep_op(d_port->out_ep, &d_port->out_request,
				GSI_EP_OP_STORE_DBL_INFO);
	}

	usb_gsi_ep_op(gsi->d_port.in_ep, &gsi->d_port.in_request,
	usb_gsi_ep_op(d_port->in_ep, &d_port->in_request,
				GSI_EP_OP_ENABLE_GSI);

	/* Unblock doorbell to GSI */
	usb_gsi_ep_op(d_port->in_ep, (void *)&block_db,
				GSI_EP_OP_SET_CLR_BLOCK_DBL);

	usb_gsi_ep_op(gsi->d_port.in_ep, &gsi->d_port.in_request,
	usb_gsi_ep_op(d_port->in_ep, &d_port->in_request,
				GSI_EP_OP_RING_DB);

	if (gsi->d_port.out_ep)
		usb_gsi_ep_op(gsi->d_port.out_ep, &gsi->d_port.out_request,
	if (d_port->out_ep) {
		usb_gsi_ep_op(d_port->out_ep, (void *)&block_db,
				GSI_EP_OP_SET_CLR_BLOCK_DBL);
		usb_gsi_ep_op(d_port->out_ep, &d_port->out_request,
				GSI_EP_OP_RING_DB);
	}
}

static void ipa_disconnect_handler(struct gsi_data_port *d_port)
{
@@ -792,9 +793,12 @@ static void ipa_disconnect_handler(struct gsi_data_port *d_port)
				&gsi->d_port.in_request, GSI_EP_OP_DISABLE);
	}

	if (gsi->d_port.out_ep)
	if (gsi->d_port.out_ep) {
		usb_gsi_ep_op(d_port->out_ep, (void *)&block_db,
				GSI_EP_OP_SET_CLR_BLOCK_DBL);
		usb_gsi_ep_op(gsi->d_port.out_ep,
				&gsi->d_port.out_request, GSI_EP_OP_DISABLE);
	}

	gsi->d_port.net_ready_trigger = false;
}
@@ -846,6 +850,10 @@ static int ipa_suspend_work_handler(struct gsi_data_port *d_port)
		block_db = false;
		usb_gsi_ep_op(d_port->in_ep, (void *)&block_db,
				GSI_EP_OP_SET_CLR_BLOCK_DBL);
		if (d_port->out_ep)
			usb_gsi_ep_op(d_port->out_ep, (void *)&block_db,
				GSI_EP_OP_SET_CLR_BLOCK_DBL);

		goto done;
	}

@@ -864,6 +872,9 @@ static int ipa_suspend_work_handler(struct gsi_data_port *d_port)
		block_db = false;
		usb_gsi_ep_op(d_port->in_ep, (void *)&block_db,
				GSI_EP_OP_SET_CLR_BLOCK_DBL);
		if (d_port->out_ep)
			usb_gsi_ep_op(d_port->out_ep, (void *)&block_db,
				GSI_EP_OP_SET_CLR_BLOCK_DBL);
		gsi_wakeup_host(gsi);
	} else if (ret == -EINPROGRESS) {
		d_port->sm_state = STATE_SUSPEND_IN_PROGRESS;
@@ -895,6 +906,9 @@ static void ipa_resume_work_handler(struct gsi_data_port *d_port)
	block_db = false;
	usb_gsi_ep_op(d_port->in_ep, (void *)&block_db,
			GSI_EP_OP_SET_CLR_BLOCK_DBL);
	if (d_port->out_ep)
		usb_gsi_ep_op(d_port->out_ep, (void *)&block_db,
			GSI_EP_OP_SET_CLR_BLOCK_DBL);
}

static void ipa_work_handler(struct work_struct *w)
@@ -1059,9 +1073,11 @@ static void ipa_work_handler(struct work_struct *w)
				log_event_dbg("%s: ST_CON_HOST_NRDY\n",
								__func__);
				block_db = true;
				/* stop USB ringing doorbell to GSI(OUT_EP) */
				/* stop USB ringing doorbell to GSI(both EPs) */
				usb_gsi_ep_op(d_port->in_ep, (void *)&block_db,
						GSI_EP_OP_SET_CLR_BLOCK_DBL);
				usb_gsi_ep_op(d_port->out_ep, (void *)&block_db,
						GSI_EP_OP_SET_CLR_BLOCK_DBL);
				gsi_rndis_ipa_reset_trigger(d_port);
				usb_gsi_ep_op(d_port->in_ep, NULL,
						GSI_EP_OP_ENDXFER);
@@ -2637,6 +2653,9 @@ static void gsi_suspend(struct usb_function *f)
	block_db = true;
	usb_gsi_ep_op(gsi->d_port.in_ep, (void *)&block_db,
			GSI_EP_OP_SET_CLR_BLOCK_DBL);
	if (gsi->d_port.out_ep)
		usb_gsi_ep_op(gsi->d_port.out_ep, (void *)&block_db,
			GSI_EP_OP_SET_CLR_BLOCK_DBL);
	post_event(&gsi->d_port, EVT_SUSPEND);
	queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w);
	log_event_dbg("gsi suspended");