[PATCH 06/31] usb: usbssp: added template functions used by upper layer.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Patch adds some functionality for initialization process.
It adds to driver usbssp_reset and usbssp_start function.

Next elements added are objects usbssp_gadget_ep0_ops,
usbssp_gadget_ep_ops and usbssp_gadget_ops. These objects
constitute the interface used by gadget subsystem.
At this moment functions related to these objects are empty
and do nothing.

This patch also implements usbssp_gadget_init_endpoint and
usbssp_gadget_free_endpoint used during initialization.

Signed-off-by: Pawel Laszczak <pawell@xxxxxxxxxxx>
---
 drivers/usb/usbssp/gadget-ext-caps.h |   3 +
 drivers/usb/usbssp/gadget-if.c       | 268 ++++++++++++++++++++++++++-
 drivers/usb/usbssp/gadget.c          |  86 ++++++++-
 3 files changed, 349 insertions(+), 8 deletions(-)

diff --git a/drivers/usb/usbssp/gadget-ext-caps.h b/drivers/usb/usbssp/gadget-ext-caps.h
index 0107bc18ad7d..2cadb5e0d15f 100644
--- a/drivers/usb/usbssp/gadget-ext-caps.h
+++ b/drivers/usb/usbssp/gadget-ext-caps.h
@@ -51,3 +51,6 @@
 #define USBSSP_CMD_EWE			(1 << 10)
 
 #define USBSSP_IRQS	(USBSSP_CMD_EIE | USBSSP_CMD_HSEIE | USBSSP_CMD_EWE)
+
+/* true: Controller Not Ready to accept doorbell or op reg writes after reset */
+#define USBSSP_STS_CNR			(1 << 11)
diff --git a/drivers/usb/usbssp/gadget-if.c b/drivers/usb/usbssp/gadget-if.c
index d53e0fb65299..c3fa3be7d494 100644
--- a/drivers/usb/usbssp/gadget-if.c
+++ b/drivers/usb/usbssp/gadget-if.c
@@ -12,13 +12,277 @@
 #include <linux/usb/composite.h>
 #include "gadget.h"
 
+static int usbssp_gadget_ep_enable(struct usb_ep *ep,
+		const struct usb_endpoint_descriptor *desc)
+{
+	struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
+	int ret = 0;
+
+	if (!ep_priv)
+		return -EINVAL;
+
+	/*TODO: implements this function*/
+	return ret;
+}
+
+int usbssp_gadget_ep_disable(struct usb_ep *ep)
+{
+	struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
+	int ret = 0;
+
+	if (!ep_priv)
+		return -EINVAL;
+
+	/*TODO: implements this function*/
+	return ret;
+}
+
+static struct usb_request *usbssp_gadget_ep_alloc_request(struct usb_ep *ep,
+							  gfp_t gfp_flags)
+{
+	struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
+
+	if (!ep_priv)
+		return NULL;
+
+	/*TODO: implements this function*/
+	return NULL;
+}
+
+static void usbssp_gadget_ep_free_request(struct usb_ep *ep,
+		struct usb_request *request)
+{
+	struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
+
+	if (!ep_priv)
+		return;
+
+	/*TODO: implements this function*/
+}
+
+static int  usbssp_gadget_ep_queue(struct usb_ep *ep,
+				   struct usb_request *request,
+				   gfp_t gfp_flags)
+{
+	struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
+	int ret = 0;
+
+	if (!ep_priv)
+		return -EINVAL;
+
+	/*TODO: implements this function*/
+	return ret;
+}
+
+static int usbssp_gadget_ep_dequeue(struct usb_ep *ep,
+				    struct usb_request *request)
+{
+	struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
+	int ret = 0;
+
+	if (!ep_priv)
+		return -EINVAL;
+
+	/*TODO: implements this function*/
+	return ret;
+}
+
+static int usbssp_gadget_ep_set_halt(struct usb_ep *ep, int value)
+{
+	struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
+	int ret = 0;
+
+	if (!ep_priv)
+		return -EINVAL;
+
+	/*TODO: implements this function*/
+	return ret;
+}
+
+static int usbssp_gadget_ep_set_wedge(struct usb_ep *ep)
+{
+	struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
+	int ret = 0;
+
+	if (!ep_priv)
+		return -EINVAL;
+
+	/*TODO: implements this function*/
+	return ret;
+}
+
+static const struct usb_ep_ops usbssp_gadget_ep0_ops = {
+	.enable		= usbssp_gadget_ep_enable,
+	.disable	= usbssp_gadget_ep_disable,
+	.alloc_request	= usbssp_gadget_ep_alloc_request,
+	.free_request	= usbssp_gadget_ep_free_request,
+	.queue		= usbssp_gadget_ep_queue,
+	.dequeue	= usbssp_gadget_ep_dequeue,
+	.set_halt	= usbssp_gadget_ep_set_halt,
+	.set_wedge	= usbssp_gadget_ep_set_wedge,
+};
+
+static const struct usb_ep_ops usbssp_gadget_ep_ops = {
+	.enable		= usbssp_gadget_ep_enable,
+	.disable	= usbssp_gadget_ep_disable,
+	.alloc_request	= usbssp_gadget_ep_alloc_request,
+	.free_request	= usbssp_gadget_ep_free_request,
+	.queue		= usbssp_gadget_ep_queue,
+	.dequeue	= usbssp_gadget_ep_dequeue,
+	.set_halt	= usbssp_gadget_ep_set_halt,
+	.set_wedge	= usbssp_gadget_ep_set_wedge,
+};
+
+static struct usb_endpoint_descriptor usbssp_gadget_ep0_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bmAttributes =		USB_ENDPOINT_XFER_CONTROL,
+};
+
+static int usbssp_gadget_start(struct usb_gadget *g,
+			       struct usb_gadget_driver *driver)
+{
+	struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
+	int ret = 0;
+
+	if (usbssp_data->gadget_driver) {
+		usbssp_err(usbssp_data, "%s is already bound to %s\n",
+				usbssp_data->gadget.name,
+				usbssp_data->gadget_driver->driver.name);
+		ret = -EBUSY;
+	}
+
+	/*TODO: add implementation*/
+	return ret;
+}
+
+static int usbssp_gadget_stop(struct usb_gadget *g)
+{
+	struct usbssp_udc *usbssp_data  = gadget_to_usbssp(g);
+
+	if (!usbssp_data)
+		return -EINVAL;
+	/*TODO: add implementation*/
+	return 0;
+}
+
+static int usbssp_gadget_get_frame(struct usb_gadget *g)
+{
+	struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
+
+	if (!usbssp_data)
+		return -EINVAL;
+
+	/*TODO: add implementation*/
+	return 0;
+}
+
+static int usbssp_gadget_wakeup(struct usb_gadget *g)
+{
+	struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
+
+	if (!usbssp_data)
+		return -EINVAL;
+
+	/*TODO: add implementation*/
+	return 0;
+}
+
+static int usbssp_gadget_set_selfpowered(struct usb_gadget *g,
+		int is_selfpowered)
+{
+	struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
+
+	if (!usbssp_data)
+		return -EINVAL;
+
+	/*TODO: add implementation*/
+	return 0;
+}
+
+static const struct usb_gadget_ops usbssp_gadget_ops = {
+	.get_frame		= usbssp_gadget_get_frame,
+	.wakeup			= usbssp_gadget_wakeup,
+	.set_selfpowered	= usbssp_gadget_set_selfpowered,
+	.udc_start		= usbssp_gadget_start,
+	.udc_stop		= usbssp_gadget_stop,
+};
+
 int usbssp_gadget_init_endpoint(struct usbssp_udc *usbssp_data)
 {
-	/*TODO: it has to be implemented*/
+	int i = 0;
+	struct usbssp_ep *ep_priv;
+
+	usbssp_data->num_endpoints = USBSSP_ENDPOINTS_NUM;
+	INIT_LIST_HEAD(&usbssp_data->gadget.ep_list);
+
+	for (i = 1; i < usbssp_data->num_endpoints; i++) {
+		bool direction = i & 1;  /*start from OUT endpoint*/
+		u8 epnum = (i >>  1);
+
+		ep_priv = &usbssp_data->devs.eps[i-1];
+		ep_priv->usbssp_data = usbssp_data;
+		ep_priv->number = epnum;
+		ep_priv->direction = direction;  /*0 for OUT, 1 for IN*/
+
+		snprintf(ep_priv->name, sizeof(ep_priv->name), "ep%d%s", epnum,
+				(ep_priv->direction) ? "in"  : "out");
+
+		ep_priv->endpoint.name = ep_priv->name;
+
+		if (ep_priv->number < 2) {
+			ep_priv->endpoint.desc = &usbssp_gadget_ep0_desc;
+			ep_priv->endpoint.comp_desc = NULL;
+		}
+
+		if (epnum == 0) {  //EP0 is  bidirectional endpoint
+			usb_ep_set_maxpacket_limit(&ep_priv->endpoint, 512);
+			usbssp_dbg(usbssp_data,
+				"Initializing %s, MaxPack: %04x Type: Ctrl\n",
+				ep_priv->name, 512);
+			ep_priv->endpoint.maxburst = 1;
+			ep_priv->endpoint.ops = &usbssp_gadget_ep0_ops;
+			ep_priv->endpoint.caps.type_control = true;
+
+			usbssp_data->usb_req_ep0_in.epnum = ep_priv->number;
+			usbssp_data->usb_req_ep0_in.dep  = ep_priv;
+
+			if (!epnum)
+				usbssp_data->gadget.ep0 = &ep_priv->endpoint;
+		} else {
+			usb_ep_set_maxpacket_limit(&ep_priv->endpoint, 1024);
+			ep_priv->endpoint.maxburst = 15;
+			ep_priv->endpoint.ops = &usbssp_gadget_ep_ops;
+			list_add_tail(&ep_priv->endpoint.ep_list,
+					&usbssp_data->gadget.ep_list);
+			ep_priv->endpoint.caps.type_iso = true;
+			ep_priv->endpoint.caps.type_bulk = true;
+			ep_priv->endpoint.caps.type_int = true;
+
+		}
+
+		ep_priv->endpoint.caps.dir_in = direction;
+		ep_priv->endpoint.caps.dir_out = !direction;
+
+		usbssp_dbg(usbssp_data, "Init %s, MaxPack: %04x SupType:"
+				" INT/BULK/ISOC , SupDir %s\n",
+				ep_priv->name, 1024,
+				(ep_priv->endpoint.caps.dir_in) ? "IN" : "OUT");
+
+		INIT_LIST_HEAD(&ep_priv->pending_list);
+	}
 	return 0;
 }
 
 void usbssp_gadget_free_endpoint(struct usbssp_udc *usbssp_data)
 {
-	/*TODO: it has to be implemented*/
+	int i;
+	struct usbssp_ep *ep_priv;
+
+	for (i = 0; i < usbssp_data->num_endpoints; i++) {
+		ep_priv = &usbssp_data->devs.eps[i];
+
+		if (ep_priv->number != 0)
+			list_del(&ep_priv->endpoint.ep_list);
+	}
 }
diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
index 75272dd2447a..67845e8368bc 100644
--- a/drivers/usb/usbssp/gadget.c
+++ b/drivers/usb/usbssp/gadget.c
@@ -103,6 +103,83 @@ int usbssp_halt(struct usbssp_udc *usbssp_data)
 	return ret;
 }
 
+/*
+ * Set the run bit and wait for the device to be running.
+ */
+int usbssp_start(struct usbssp_udc *usbssp_data)
+{
+	u32 temp;
+	int ret;
+
+	temp = readl(&usbssp_data->op_regs->command);
+	temp |= (CMD_RUN | CMD_DEVEN);
+	usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+			"// Turn on USBSSP, cmd = 0x%x.", temp);
+	writel(temp, &usbssp_data->op_regs->command);
+
+	/*
+	 * Wait for the HCHalted Staus bit to be 0 to indicate the device is
+	 * running.
+	 */
+	ret = usbssp_handshake(&usbssp_data->op_regs->status,
+			STS_HALT, 0, USBSSP_MAX_HALT_USEC);
+
+	if (ret == -ETIMEDOUT)
+		usbssp_err(usbssp_data, "Device took too long to start, waited %u microseconds.\n",
+			USBSSP_MAX_HALT_USEC);
+	if (!ret)
+		/* clear state flags. Including dying, halted or removing */
+		usbssp_data->usbssp_state = 0;
+
+	return ret;
+}
+
+/*
+ * Reset a halted DC.
+ *
+ * This resets pipelines, timers, counters, state machines, etc.
+ * Transactions will be terminated immediately, and operational registers
+ * will be set to their defaults.
+ */
+int usbssp_reset(struct usbssp_udc *usbssp_data)
+{
+	u32 command;
+	u32 state;
+	int ret;
+
+	state = readl(&usbssp_data->op_regs->status);
+
+	if (state == ~(u32)0) {
+		usbssp_warn(usbssp_data, "Device not accessible, reset failed.\n");
+		return -ENODEV;
+	}
+
+	if ((state & STS_HALT) == 0) {
+		usbssp_warn(usbssp_data, "DC not halted, aborting reset.\n");
+		return 0;
+	}
+
+	usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init, "// Reset the DC");
+	command = readl(&usbssp_data->op_regs->command);
+	command |= CMD_RESET;
+	writel(command, &usbssp_data->op_regs->command);
+
+	ret = usbssp_handshake(&usbssp_data->op_regs->command,
+			CMD_RESET, 0, 10 * 1000 * 1000);
+
+	if (ret)
+		return ret;
+	usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+		"Wait for controller to be ready for doorbell rings");
+	/*
+	 * USBSSP cannot write to any doorbells or operational registers other
+	 * than status until the "Controller Not Ready" flag is cleared.
+	 */
+	ret = usbssp_handshake(&usbssp_data->op_regs->status,
+			STS_CNR, 0, 10 * 1000 * 1000);
+
+	return ret;
+}
 
 /*
  * Initialize memory for gadget driver and USBSSP (one-time init).
@@ -183,8 +260,7 @@ int usbssp_gen_setup(struct usbssp_udc *usbssp_data)
 
 	usbssp_dbg(usbssp_data, "Resetting Device Controller\n");
 	/* Reset the internal DC memory state and registers. */
-	/*TODO: add implementation of usbssp_reset function*/
-	//retval = usbssp_reset(usbssp_data);
+	retval = usbssp_reset(usbssp_data);
 	if (retval)
 		return retval;
 	usbssp_dbg(usbssp_data, "Reset complete\n");
@@ -250,8 +326,7 @@ int usbssp_gadget_init(struct usbssp_udc *usbssp_data)
 	BUILD_BUG_ON(sizeof(struct usbssp_run_regs) != (8+8*128)*32/8);
 
 	/* fill gadget fields */
-	/*TODO: implements usbssp_gadget_ops object*/
-	//usbssp_data->gadget.ops = &usbssp_gadget_ops;
+	usbssp_data->gadget.ops = &usbssp_gadget_ops;
 	usbssp_data->gadget.name = "usbssp-gadget";
 	usbssp_data->gadget.max_speed = USB_SPEED_SUPER_PLUS;
 	usbssp_data->gadget.speed = USB_SPEED_UNKNOWN;
@@ -292,8 +367,7 @@ int usbssp_gadget_init(struct usbssp_udc *usbssp_data)
 	usbssp_gadget_free_endpoint(usbssp_data);
 err1:
 	usbssp_halt(usbssp_data);
-	/*TODO add implementation of usbssp_reset function*/
-	//usbssp_reset(usbssp_data);
+	usbssp_reset(usbssp_data);
 	//TODO freeing memory
 	//usbssp_mem_cleanup(usbssp_data);
 err3:
-- 
2.17.1

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux