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

Commit 8c2f6ab6 authored by Devdutt Patnaik's avatar Devdutt Patnaik Committed by Hemant Kumar
Browse files

usb: f_gsi: Add generic function driver for GSI transport



Adds a generic function driver that will handle all functions
that support h/w acceleration to IPA over GSI.
This function driver leverages common data path and implements
protocol specific control path.

Change-Id: I414478c76d950a568155ac8b0c2d180170cf7183
Signed-off-by: default avatarDevdutt Patnaik <dpatnaik@codeaurora.org>
Signed-off-by: default avatarHemant Kumar <hemantk@codeaurora.org>
parent 031e28bb
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@
			interrupt-parent = <&intc>;
			interrupts = <0 131 0>;
			usb-phy = <&qusb_phy>, <&ssphy>;
			tx-fifo-resize;
			snps,nominal-elastic-buffer;
			snps,is-utmi-l1-suspend;
			snps,hird-threshold = /bits/ 8 <0x0>;
@@ -105,6 +106,9 @@
		compatible = "qcom,android-usb";
		reg = <0x086000c8 0xc8>;
		qcom,pm-qos-latency = <101 2001 30001>;
		qcom,supported-func = "ffs","audio","diag","serial",
			"mass_storage","rndis_gsi","ecm_gsi","rmnet_gsi",
			"mbim_gsi","dpl_gsi","gps";
	};

	qusb_phy: qusb@79000 {
+66 −7
Original line number Diff line number Diff line
@@ -115,6 +115,7 @@ MODULE_PARM_DESC(dcp_max_current, "max current drawn for DCP charger");
#define	GSI_GENERAL_CFG_REG		(QSCRATCH_REG_OFFSET + 0xFC)
#define	GSI_RESTART_DBL_PNTR_MASK	BIT(20)
#define	GSI_CLK_EN_MASK			BIT(12)
#define	BLOCK_GSI_WR_GO_MASK		BIT(1)
#define	GSI_EN_MASK			BIT(0)

#define GSI_DBL_ADDR_L(n)	((QSCRATCH_REG_OFFSET + 0x110) + (n*4))
@@ -122,6 +123,9 @@ MODULE_PARM_DESC(dcp_max_current, "max current drawn for DCP charger");
#define GSI_RING_BASE_ADDR_L(n)	((QSCRATCH_REG_OFFSET + 0x130) + (n*4))
#define GSI_RING_BASE_ADDR_H(n)	((QSCRATCH_REG_OFFSET + 0x140) + (n*4))

#define	GSI_IF_STS	(QSCRATCH_REG_OFFSET + 0x1A4)
#define	GSI_WR_CTRL_STATE_MASK	BIT(15)

struct dwc3_msm_req_complete {
	struct list_head list_item;
	struct usb_request *req;
@@ -1192,6 +1196,57 @@ static void gsi_enable(struct usb_ep *ep)
			GSI_GENERAL_CFG_REG, GSI_EN_MASK, 1);
}

/*
* Block or allow doorbell towards GSI
*
* @usb_ep - pointer to usb_ep instance.
* @request - pointer to GSI request. In this case num_bufs is used as a bool
* to set or clear the doorbell bit
*/
static void gsi_set_clear_dbell(struct usb_ep *ep,
					bool block_db)
{

	struct dwc3_ep *dep = to_dwc3_ep(ep);
	struct dwc3 *dwc = dep->dwc;
	struct dwc3_msm *mdwc = dev_get_drvdata(dwc->dev->parent);

	dwc3_msm_write_reg_field(mdwc->base,
		GSI_GENERAL_CFG_REG, BLOCK_GSI_WR_GO_MASK, block_db);
}

/*
* Performs necessary checks before stopping GSI channels
*
* @usb_ep - pointer to usb_ep instance to access DWC3 regs
*/
static bool gsi_check_ready_to_suspend(struct usb_ep *ep)
{
	u32	timeout = 1500;
	u32	reg = 0;
	struct dwc3_ep *dep = to_dwc3_ep(ep);
	struct dwc3 *dwc = dep->dwc;
	struct dwc3_msm *mdwc = dev_get_drvdata(dwc->dev->parent);

	while (dwc3_msm_read_reg_field(mdwc->base,
		GSI_IF_STS, GSI_WR_CTRL_STATE_MASK)) {
		if (!timeout--) {
			dev_err(mdwc->dev,
			"Unable to suspend GSI ch. WR_CTRL_STATE != 0\n");
			return false;
		}
	}

	reg = dwc3_readl(dwc->regs, DWC3_DSTS);
	if (DWC3_DSTS_USBLNKST(reg) != DWC3_LINK_STATE_U3) {
		dev_err(mdwc->dev, "Unable to suspend GSI ch\n");
		return false;
	}

	return true;
}


/**
* Performs GSI operations or GSI EP related operations.
*
@@ -1211,13 +1266,7 @@ static int dwc3_msm_gsi_ep_op(struct usb_ep *ep,
	struct dwc3_msm *mdwc = dev_get_drvdata(dwc->dev->parent);
	struct usb_gsi_request *request;
	struct gsi_channel_info *ch_info;

	if (!dep->endpoint.desc) {
		dev_err(mdwc->dev,
			"%s: trying to queue request %p to disabled ep %s\n",
			__func__, request, ep->name);
		return -EPERM;
	}
	bool block_db;

	switch (op) {
	case GSI_EP_OP_PREPARE_TRBS:
@@ -1269,6 +1318,16 @@ static int dwc3_msm_gsi_ep_op(struct usb_ep *ep,
		dev_dbg(mdwc->dev, "EP_OP_ENDXFER for %s\n", ep->name);
		gsi_endxfer_for_ep(ep);
		break;
	case GSI_EP_OP_SET_CLR_BLOCK_DBL:
		block_db = *((bool *)op_data);
		dev_dbg(mdwc->dev, "EP_OP_SET_CLR_BLOCK_DBL %d\n",
						block_db);
		gsi_set_clear_dbell(ep, block_db);
		break;
	case GSI_EP_OP_CHECK_FOR_SUSPEND:
		dev_dbg(mdwc->dev, "EP_OP_CHECK_FOR_SUSPEND\n");
		ret = gsi_check_ready_to_suspend(ep);
		break;
	default:
		dev_err(mdwc->dev, "%s: Invalid opcode GSI EP\n", __func__);
	}
+148 −0
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@
#include "u_data_ipa.c"
#include "u_ether.c"
#include "u_qc_ether.c"
#include "f_gsi.c"
#include "f_mass_storage.h"

USB_ETHERNET_MODULE_PARAMETERS();
@@ -2855,6 +2856,148 @@ static struct android_usb_function midi_function = {
	.attributes	= midi_function_attributes,
};
#endif

static int rndis_gsi_function_init(struct android_usb_function *f,
					struct usb_composite_dev *cdev)
{

	/* "Wireless" RNDIS; auto-detected by Windows */
	rndis_gsi_iad_descriptor.bFunctionClass =
					USB_CLASS_WIRELESS_CONTROLLER;
	rndis_gsi_iad_descriptor.bFunctionSubClass = 0x01;
	rndis_gsi_iad_descriptor.bFunctionProtocol = 0x03;
	rndis_gsi_control_intf.bInterfaceClass =
					USB_CLASS_WIRELESS_CONTROLLER;
	rndis_gsi_control_intf.bInterfaceSubClass =	 0x01;
	rndis_gsi_control_intf.bInterfaceProtocol =	 0x03;

	return gsi_function_init(IPA_USB_RNDIS);
}

static void rndis_gsi_function_cleanup(struct android_usb_function *f)
{
	gsi_function_cleanup(IPA_USB_RNDIS);
}

static int rndis_gsi_function_bind_config(struct android_usb_function *f,
					struct usb_configuration *c)
{

	return gsi_bind_config(c, IPA_USB_RNDIS);
}

static struct android_usb_function rndis_gsi_function = {
	.name		= "rndis_gsi",
	.init		= rndis_gsi_function_init,
	.cleanup	= rndis_gsi_function_cleanup,
	.bind_config	= rndis_gsi_function_bind_config,
};

static int rmnet_gsi_function_init(struct android_usb_function *f,
					struct usb_composite_dev *cdev)
{
	return gsi_function_init(IPA_USB_RMNET);
}

static void rmnet_gsi_function_cleanup(struct android_usb_function *f)
{
	gsi_function_cleanup(IPA_USB_RMNET);
}

static int rmnet_gsi_function_bind_config(struct android_usb_function *f,
					 struct usb_configuration *c)
{
	return gsi_bind_config(c, IPA_USB_RMNET);
}

static struct android_usb_function rmnet_gsi_function = {
	.name		= "rmnet_gsi",
	.init		= rmnet_gsi_function_init,
	.cleanup	= rmnet_gsi_function_cleanup,
	.bind_config	= rmnet_gsi_function_bind_config,
};

static int ecm_gsi_function_init(struct android_usb_function *f,
				struct usb_composite_dev *cdev)
{
	return gsi_function_init(IPA_USB_ECM);
}

static void ecm_gsi_function_cleanup(struct android_usb_function *f)
{
	return gsi_function_cleanup(IPA_USB_ECM);
}

static int ecm_gsi_function_bind_config(struct android_usb_function *f,
					struct usb_configuration *c)
{
	return gsi_bind_config(c, IPA_USB_ECM);
}

static struct android_usb_function ecm_gsi_function = {
	.name		= "ecm_gsi",
	.init		= ecm_gsi_function_init,
	.cleanup	= ecm_gsi_function_cleanup,
	.bind_config	= ecm_gsi_function_bind_config,
};

static int mbim_gsi_function_init(struct android_usb_function *f,
					 struct usb_composite_dev *cdev)
{
	return gsi_function_init(IPA_USB_MBIM);
}

static void mbim_gsi_function_cleanup(struct android_usb_function *f)
{
	gsi_function_cleanup(IPA_USB_MBIM);
}

static int mbim_gsi_function_bind_config(struct android_usb_function *f,
					  struct usb_configuration *c)
{
	return gsi_bind_config(c, IPA_USB_MBIM);
}

static int mbim_gsi_function_ctrlrequest(struct android_usb_function *f,
					struct usb_composite_dev *cdev,
					const struct usb_ctrlrequest *c)
{
	return gsi_os_desc_ctrlrequest(cdev, c);
}

static struct android_usb_function mbim_gsi_function = {
	.name		= "mbim_gsi",
	.cleanup	= mbim_gsi_function_cleanup,
	.bind_config	= mbim_gsi_function_bind_config,
	.init		= mbim_gsi_function_init,
	.ctrlrequest	= mbim_gsi_function_ctrlrequest,
};

static int dpl_gsi_function_init(struct android_usb_function *f,
	struct usb_composite_dev *cdev)
{
	return gsi_function_init(IPA_USB_DIAG);
}

static void dpl_gsi_function_cleanup(struct android_usb_function *f)
{
	gsi_function_cleanup(IPA_USB_DIAG);
}

static int dpl_gsi_function_bind_config(struct android_usb_function *f,
					struct usb_configuration *c)
{
	return gsi_bind_config(c, IPA_USB_DIAG);

}

static struct android_usb_function dpl_gsi_function = {
	.name		= "dpl_gsi",
	.init		= dpl_gsi_function_init,
	.cleanup	= dpl_gsi_function_cleanup,
	.bind_config	= dpl_gsi_function_bind_config,
};

static struct android_usb_function *supported_functions[] = {
	[ANDROID_FFS] = &ffs_function,
	[ANDROID_MBIM_BAM] = &mbim_function,
@@ -2882,6 +3025,11 @@ static struct android_usb_function *supported_functions[] = {
#ifdef CONFIG_SND_RAWMIDI
	[ANDROID_MIDI] = &midi_function,
#endif
	[ANDROID_RNDIS_GSI] = &rndis_gsi_function,
	[ANDROID_ECM_GSI] = &ecm_gsi_function,
	[ANDROID_RMNET_GSI] = &rmnet_gsi_function,
	[ANDROID_MBIM_GSI] = &mbim_gsi_function,
	[ANDROID_DPL_GSI] = &dpl_gsi_function,
	NULL
};

+2778 −0

File added.

Preview size limit exceeded, changes collapsed.

+15 −0
Original line number Diff line number Diff line
@@ -43,6 +43,11 @@ enum android_function_index {
	ANDROID_AUDIO_SRC,
	ANDROID_CHARGER,
	ANDROID_MIDI,
	ANDROID_RNDIS_GSI,
	ANDROID_ECM_GSI,
	ANDROID_RMNET_GSI,
	ANDROID_MBIM_GSI,
	ANDROID_DPL_GSI,
	ANDROID_MAX_FUNC_CNT,
	ANDROID_INVALID_FUNC,
};
@@ -96,6 +101,16 @@ static enum android_function_index name_to_func_idx(const char *name)
		return ANDROID_AUDIO_SRC;
	if (!strncasecmp("MIDI", name, FUNC_NAME_LEN))
		return ANDROID_MIDI;
	if (!strncasecmp("RNDIS_GSI", name, FUNC_NAME_LEN))
		return ANDROID_RNDIS_GSI;
	if (!strncasecmp("ECM_GSI", name, FUNC_NAME_LEN))
		return ANDROID_ECM_GSI;
	if (!strncasecmp("RMNET_GSI", name, FUNC_NAME_LEN))
		return ANDROID_RMNET_GSI;
	if (!strncasecmp("MBIM_GSI", name, FUNC_NAME_LEN))
		return ANDROID_MBIM_GSI;
	if (!strncasecmp("DPL_GSI", name, FUNC_NAME_LEN))
		return ANDROID_DPL_GSI;

	return ANDROID_INVALID_FUNC;
}
Loading