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