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

Commit 22f3f5d7 authored by Manu Gautam's avatar Manu Gautam Committed by Ajay Agarwal
Browse files

usb: dwc3-msm: Add GSI mode support for Normal USB EPs



Allow non-GSI capable or normal USB endpoint to be used
in GSI mode. In this case, USB driver receives events from
hardware and updates GSI doorbell register with completed
TRB. USB GSI hardware wrapper isn't involved for such EPs.
In order to use this feature, GSI function driver must
choose interrupter number as '0' with such endpoints,
non-zero interrupters are reserved for USB GSI wrapper use.

Change-Id: Ief51b722bbe1aa97ff806e5a3b8b1ca806671a8f
Signed-off-by: default avatarManu Gautam <mgautam@codeaurora.org>
Signed-off-by: default avatarAjay Agarwal <ajaya@codeaurora.org>
parent ea11eaa9
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -620,6 +620,7 @@ struct dwc3_ep_events {
 * @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
 * @gsi_db_reg_addr: Address of GSI DB register mapped to this EP
 */
struct dwc3_ep {
	struct usb_ep		endpoint;
@@ -676,6 +677,7 @@ struct dwc3_ep {
	struct timespec		dbg_ep_events_ts;
	int			fifo_depth;
	struct dwc3_gadget_ep_cmd_params ep_cfg_init_params;
	void __iomem		*gsi_db_reg_addr;
};

enum dwc3_phy {
+69 −5
Original line number Diff line number Diff line
@@ -203,7 +203,7 @@ static const char * const gsi_op_strings[] = {
	"ENABLE_GSI", "UPDATE_XFER", "RING_DB",
	"END_XFER", "GET_CH_INFO", "GET_XFER_IDX", "PREPARE_TRBS",
	"FREE_TRBS", "SET_CLR_BLOCK_DBL", "CHECK_FOR_SUSP",
	"EP_DISABLE" };
	"EP_DISABLE", "EP_UPDATE_DB" };

/* Input bits to state machine (mdwc->inputs) */

@@ -898,8 +898,17 @@ static void gsi_get_channel_info(struct usb_ep *ep,
	/* Store last 16 bits of LINK TRB address as per GSI hw requirement */
	ch_info->last_trb_addr = (dwc3_trb_dma_offset(dep,
			&dep->trb_pool[last_trb_index - 1]) & 0x0000FFFF);

	/*
	 * Check if NORMAL EP is used with GSI. In that case USB driver
	 * processes events and GSI shouldn't access GEVNTCOUNT(0) register.
	 */
	if (ep->ep_intr_num) {
		ch_info->gevntcount_low_addr = (u32)(dwc->reg_phys +
			DWC3_GEVNTCOUNT(ep->ep_intr_num));
	} else {
		dev_dbg(dwc->dev, "gevntcount returned as 0 for normal EP\n");
	}
	ch_info->gevntcount_hi_addr = 0;

	dev_dbg(dwc->dev,
@@ -929,8 +938,15 @@ static int gsi_startxfer_for_ep(struct usb_ep *ep)
	}

	memset(&params, 0, sizeof(params));
	params.param0 = GSI_TRB_ADDR_BIT_53_MASK | GSI_TRB_ADDR_BIT_55_MASK;
	/*
	 * Check if NORMAL EP is used with GSI. In that case USB driver
	 * updates GSI doorbell and USB GSI wrapper h/w isn't involved.
	 */
	if (ep->ep_intr_num) {
		params.param0 = GSI_TRB_ADDR_BIT_53_MASK |
				GSI_TRB_ADDR_BIT_55_MASK;
		params.param0 |= (ep->ep_intr_num << 16);
	}
	params.param1 = lower_32_bits(dwc3_trb_dma_offset(dep,
						&dep->trb_pool[0]));
	cmd = DWC3_DEPCMD_STARTTRANSFER;
@@ -957,7 +973,18 @@ static void gsi_store_ringbase_dbl_info(struct usb_ep *ep,
	struct dwc3_ep *dep = to_dwc3_ep(ep);
	struct dwc3	*dwc = dep->dwc;
	struct dwc3_msm *mdwc = dev_get_drvdata(dwc->dev->parent);
	int n = ep->ep_intr_num - 1;
	int n;

	/*
	 * Check if NORMAL EP is used with GSI. In that case USB driver
	 * updates GSI doorbell and USB GSI wrapper h/w isn't involved.
	 */
	if (!ep->ep_intr_num) {
		dev_dbg(mdwc->dev, "%s: is no-op for normal EP\n", __func__);
		return;
	}

	n = ep->ep_intr_num - 1;

	dwc3_msm_write_reg(mdwc->base, GSI_RING_BASE_ADDR_L(n),
			dwc3_trb_dma_offset(dep, &dep->trb_pool[0]));
@@ -991,6 +1018,21 @@ static void gsi_store_ringbase_dbl_info(struct usb_ep *ep,
			dwc3_msm_read_reg(mdwc->base, GSI_DBL_ADDR_L(n)));
}

static void dwc3_msm_gsi_db_update(struct dwc3_ep *dep, dma_addr_t offset)
{
	struct dwc3 *dwc = dep->dwc;
	struct dwc3_msm *mdwc = dev_get_drvdata(dwc->dev->parent);

	if (!dep->gsi_db_reg_addr) {
		dev_err(mdwc->dev, "Failed to update GSI DBL\n");
		return;
	}

	writel_relaxed(offset, dep->gsi_db_reg_addr);
	dev_dbg(mdwc->dev, "Writing TRB addr: %pa to %pK\n",
		&offset, dep->gsi_db_reg_addr);
}

/*
* Rings Doorbell for GSI Channel
*
@@ -1016,6 +1058,8 @@ static void gsi_ring_db(struct usb_ep *ep, struct usb_gsi_request *request)
		return;
	}

	dep->gsi_db_reg_addr = gsi_dbl_address_lsb;

	gsi_dbl_address_msb = devm_ioremap_nocache(mdwc->dev,
			request->db_reg_phs_addr_msb, sizeof(u32));
	if (!gsi_dbl_address_msb) {
@@ -1399,6 +1443,15 @@ static void gsi_enable(struct usb_ep *ep)
	struct dwc3 *dwc = dep->dwc;
	struct dwc3_msm *mdwc = dev_get_drvdata(dwc->dev->parent);

	/*
	 * Check if NORMAL EP is used with GSI. In that case USB driver
	 * updates GSI doorbell and USB GSI wrapper h/w isn't involved.
	 */
	if (!ep->ep_intr_num) {
		dev_dbg(mdwc->dev, "%s: is no-op for normal EP\n", __func__);
		return;
	}

	dwc3_msm_write_reg_field(mdwc->base,
			GSI_GENERAL_CFG_REG, GSI_CLK_EN_MASK, 1);
	dwc3_msm_write_reg_field(mdwc->base,
@@ -1435,6 +1488,12 @@ static void gsi_set_clear_dbell(struct usb_ep *ep,
	else
		gsi_enable_ep_events(ep);

	/* Nothing to be done if NORMAL EP is used with GSI */
	if (!ep->ep_intr_num) {
		dev_dbg(mdwc->dev, "%s: is no-op for normal EP\n", __func__);
		return;
	}

	dwc3_msm_write_reg_field(mdwc->base,
		GSI_GENERAL_CFG_REG, BLOCK_GSI_WR_GO_MASK, block_db);
}
@@ -1490,6 +1549,7 @@ static int dwc3_msm_gsi_ep_op(struct usb_ep *ep,
	struct gsi_channel_info *ch_info;
	bool block_db;
	unsigned long flags;
	dma_addr_t offset;

	dbg_log_string("%s(%d):%s", ep->name, ep->ep_num, gsi_op_to_string(op));

@@ -1555,6 +1615,10 @@ static int dwc3_msm_gsi_ep_op(struct usb_ep *ep,
	case GSI_EP_OP_DISABLE:
		ret = ep->ops->disable(ep);
		break;
	case GSI_EP_OP_UPDATE_DB:
		offset = *(dma_addr_t *)op_data;
		dwc3_msm_gsi_db_update(dep, offset);
		break;
	default:
		dev_err(mdwc->dev, "%s: Invalid opcode GSI EP\n", __func__);
	}
+28 −0
Original line number Diff line number Diff line
@@ -2710,6 +2710,25 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
	return 1;
}

static void dwc3_gsi_ep_transfer_complete(struct dwc3 *dwc, struct dwc3_ep *dep)
{
	struct usb_ep *ep = &dep->endpoint;
	struct dwc3_trb *trb;
	dma_addr_t offset;

	trb = &dep->trb_pool[dep->trb_dequeue];
	while (trb->ctrl & DWC3_TRBCTL_LINK_TRB) {
		dwc3_ep_inc_trb(&dep->trb_dequeue);
		trb = &dep->trb_pool[dep->trb_dequeue];
	}

	if (!(trb->ctrl & DWC3_TRB_CTRL_HWO)) {
		offset = dwc3_trb_dma_offset(dep, trb);
		usb_gsi_ep_op(ep, (void *)&offset, GSI_EP_OP_UPDATE_DB);
		dwc3_ep_inc_trb(&dep->trb_dequeue);
	}
}

static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
		struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
{
@@ -2721,6 +2740,15 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,

	if (event->status & DEPEVT_STATUS_BUSERR)
		status = -ECONNRESET;
	/*
	 * Check if NORMAL EP is used with GSI.
	 * In that case dwc3 driver recevies EP events from hardware and
	 * updates GSI doorbell with completed TRB.
	 */
	if (dep->endpoint.ep_type == EP_TYPE_GSI) {
		dwc3_gsi_ep_transfer_complete(dwc, dep);
		return;
	}

	clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status);
	if (clean_busy && (!dep->endpoint.desc || is_xfer_complete ||
+1 −0
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ enum gsi_ep_op {
	GSI_EP_OP_SET_CLR_BLOCK_DBL,
	GSI_EP_OP_CHECK_FOR_SUSPEND,
	GSI_EP_OP_DISABLE,
	GSI_EP_OP_UPDATE_DB,
};

/*