Message ID | 1532023084-28083-7-git-send-email-pawell@cadence.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 19/07/18 20:57, Pawel Laszczak wrote: > 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@cadence.com> > --- > drivers/usb/usbssp/gadget-ext-caps.h | 3 + > drivers/usb/usbssp/gadget-if.c | 269 ++++++++++++++++++++++++++- > drivers/usb/usbssp/gadget.c | 84 ++++++++- > 3 files changed, 350 insertions(+), 6 deletions(-) > > diff --git a/drivers/usb/usbssp/gadget-ext-caps.h b/drivers/usb/usbssp/gadget-ext-caps.h > index 2bf327046376..86c0ce331037 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 BIT(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 BIT(11) > diff --git a/drivers/usb/usbssp/gadget-if.c b/drivers/usb/usbssp/gadget-if.c > index d53e0fb65299..70def978b085 100644 > --- a/drivers/usb/usbssp/gadget-if.c > +++ b/drivers/usb/usbssp/gadget-if.c > @@ -12,13 +12,278 @@ > #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) { > + dev_err(usbssp_data->dev, "%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) Since we're initializing all endpoints this function should be named usbssp_gadget_init_endpoints(). > { > - /*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); > + dev_dbg(usbssp_data->dev, > + "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; Since you start from 1, ep0 will only be initialized as dir_in. Is it better to represent EP0in and EP0out separately? If so you can start i from 0 and use ep_priv = &usbssp_data->devs.eps[i]; This should fix the direction. eps[0] will be ep0out and eps[1] will be ep0in. > + > + dev_dbg(usbssp_data->dev, "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 you start i from 1 then you can skip the if(). > + 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 338ec2ec18b1..195f5777cf8a 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) > + dev_err(usbssp_data->dev, "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) { > + dev_warn(usbssp_data->dev, "Device not accessible, reset failed.\n"); > + return -ENODEV; > + } > + > + if ((state & STS_HALT) == 0) { > + dev_warn(usbssp_data->dev, "DC not halted, aborting reset.\n"); DC is not a familiar abbreviation. Mabe just use Controller? or Device controller. > + 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). > @@ -179,8 +256,7 @@ int usbssp_gen_setup(struct usbssp_udc *usbssp_data) > > dev_dbg(usbssp_data->dev, "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; > dev_dbg(usbssp_data->dev, "Reset complete\n"); > @@ -244,8 +320,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; > @@ -288,6 +363,7 @@ int usbssp_gadget_init(struct usbssp_udc *usbssp_data) > usbssp_halt(usbssp_data); > /*TODO add implementation of usbssp_reset function*/ > //usbssp_reset(usbssp_data); > + usbssp_reset(usbssp_data); > /*TODO add implementation of freeing memory*/ > //usbssp_mem_cleanup(usbssp_data); > err3: >
On 04/08/18 09:37, Pawel Laszczak wrote: >>> 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@cadence.com> >>> --- >>> drivers/usb/usbssp/gadget-ext-caps.h | 3 + >>> drivers/usb/usbssp/gadget-if.c | 269 ++++++++++++++++++++++++++- >>> drivers/usb/usbssp/gadget.c | 84 ++++++++- >>> 3 files changed, 350 insertions(+), 6 deletions(-) >>> >>> diff --git a/drivers/usb/usbssp/gadget-ext-caps.h b/drivers/usb/usbssp/gadget-ext-caps.h >>> index 2bf327046376..86c0ce331037 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 BIT(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 BIT(11) >>> diff --git a/drivers/usb/usbssp/gadget-if.c b/drivers/usb/usbssp/gadget-if.c >>> index d53e0fb65299..70def978b085 100644 >>> --- a/drivers/usb/usbssp/gadget-if.c >>> +++ b/drivers/usb/usbssp/gadget-if.c >>> @@ -12,13 +12,278 @@ >>> #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) { >>> + dev_err(usbssp_data->dev, "%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) >> >> Since we're initializing all endpoints this function should be named usbssp_gadget_init_endpoints(). >> >>> { >>> - /*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); >>> + dev_dbg(usbssp_data->dev, >>> + "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; >> >> Since you start from 1, ep0 will only be initialized as dir_in. >> Is it better to represent EP0in and EP0out separately? >> If so you can start i from 0 and use >> ep_priv = &usbssp_data->devs.eps[i]; >> >> This should fix the direction. eps[0] will be ep0out and eps[1] will be ep0in. > > I wanted to be consistent with Device Context Data Structure from > XHCI specification (EP Context 0 BiDir, EP Context 1 OUT, EP Context 1 IN ...). > > I little change this function, and now it look like this: > int usbssp_gadget_init_endpoints(struct usbssp_udc *usbssp_data) > { > 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 = 0; i < usbssp_data->num_endpoints; i++) { > bool direction = i & 1; /*start from OUT endpoint*/ > u8 epnum = ((i + 1) >> 1); > > ep_priv = &usbssp_data->devs.eps[i]; > ep_priv->usbssp_data = usbssp_data; > ep_priv->number = epnum; > ep_priv->direction = direction; /*0 for OUT, 1 for IN*/ > > /* > *Ep0 is bidirectional so Ep0In and Ep0Out is represented by > * usbssp_data->devs.eps[0] > */ > if (epnum == 0) { > snprintf(ep_priv->name, sizeof(ep_priv->name), "ep%d%s", > epnum, "BiDir"); > > usb_ep_set_maxpacket_limit(&ep_priv->endpoint, 512); > ep_priv->endpoint.maxburst = 1; > ep_priv->endpoint.ops = &usbssp_gadget_ep0_ops; > ep_priv->endpoint.desc = &usbssp_gadget_ep0_desc; > > ep_priv->endpoint.comp_desc = NULL; > ep_priv->endpoint.caps.type_control = true; > ep_priv->endpoint.caps.dir_in = true; > ep_priv->endpoint.caps.dir_out = true; > > usbssp_data->usb_req_ep0_in.epnum = ep_priv->number; > usbssp_data->usb_req_ep0_in.dep = ep_priv; > usbssp_data->gadget.ep0 = &ep_priv->endpoint; > > } else { > snprintf(ep_priv->name, sizeof(ep_priv->name), "ep%d%s", > epnum, (ep_priv->direction) ? "in" : "out"); > > 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; > } > > ep_priv->endpoint.name = ep_priv->name; I think this is much better now. > > dev_dbg(usbssp_data->dev, "Init %s, MaxPack: %04x SupType: " > "CTRL: %s, INT: %s, BULK: %s, ISOC %s, " > "SupDir IN: %s, OUT: %s\n", > ep_priv->name, 1024, > (ep_priv->endpoint.caps.type_control) ? "yes" : "no", > (ep_priv->endpoint.caps.type_int) ? "yes" : "no", > (ep_priv->endpoint.caps.type_bulk) ? "yes" : "no", > (ep_priv->endpoint.caps.type_iso) ? "yes" : "no", > (ep_priv->endpoint.caps.dir_in) ? "yes" : "no", > (ep_priv->endpoint.caps.dir_out) ? "yes" : "no"); > In some places you use usbssp_dbg_trace() and at others you use dev_dbg(). Is it better to stick with one throughout? > INIT_LIST_HEAD(&ep_priv->pending_list); > } > > return 0; > } > >>> + >>> + dev_dbg(usbssp_data->dev, "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 you start i from 1 then you can skip the if(). >> >>> + 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 338ec2ec18b1..195f5777cf8a 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) >>> + dev_err(usbssp_data->dev, "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) { >>> + dev_warn(usbssp_data->dev, "Device not accessible, reset failed.\n"); >>> + return -ENODEV; >>> + } >>> + >>> + if ((state & STS_HALT) == 0) { >>> + dev_warn(usbssp_data->dev, "DC not halted, aborting reset.\n"); >> >> DC is not a familiar abbreviation. Mabe just use Controller? or Device controller. > > In xhci driver is often used HC for host controller. In device driver I change this abbreviation > to DC. I will review the patches and change this to Controller. > >> >>> + 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). >>> @@ -179,8 +256,7 @@ int usbssp_gen_setup(struct usbssp_udc *usbssp_data) >>> >>> dev_dbg(usbssp_data->dev, "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; >>> dev_dbg(usbssp_data->dev, "Reset complete\n"); >>> @@ -244,8 +320,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; >>> @@ -288,6 +363,7 @@ int usbssp_gadget_init(struct usbssp_udc *usbssp_data) >>> usbssp_halt(usbssp_data); >>> /*TODO add implementation of usbssp_reset function*/ >>> //usbssp_reset(usbssp_data); >>> + usbssp_reset(usbssp_data); >>> /*TODO add implementation of freeing memory*/ >>> //usbssp_mem_cleanup(usbssp_data); >>> err3: >>> >> >> -- >> cheers, >> -roger >> >> Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki. >> Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki > > Cheers > Pawel >
> > On 04/08/18 09:37, Pawel Laszczak wrote: > >>> 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@cadence.com> > >>> --- > >>> drivers/usb/usbssp/gadget-ext-caps.h | 3 + > >>> drivers/usb/usbssp/gadget-if.c | 269 ++++++++++++++++++++++++++- > >>> drivers/usb/usbssp/gadget.c | 84 ++++++++- > >>> 3 files changed, 350 insertions(+), 6 deletions(-) > >>> > >>> diff --git a/drivers/usb/usbssp/gadget-ext-caps.h b/drivers/usb/usbssp/gadget-ext-caps.h > >>> index 2bf327046376..86c0ce331037 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 BIT(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 BIT(11) > >>> diff --git a/drivers/usb/usbssp/gadget-if.c b/drivers/usb/usbssp/gadget-if.c > >>> index d53e0fb65299..70def978b085 100644 > >>> --- a/drivers/usb/usbssp/gadget-if.c > >>> +++ b/drivers/usb/usbssp/gadget-if.c > >>> @@ -12,13 +12,278 @@ > >>> #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) { > >>> + dev_err(usbssp_data->dev, "%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) > >> > >> Since we're initializing all endpoints this function should be named usbssp_gadget_init_endpoints(). > >> > >>> { > >>> - /*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); > >>> + dev_dbg(usbssp_data->dev, > >>> + "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; > >> > >> Since you start from 1, ep0 will only be initialized as dir_in. > >> Is it better to represent EP0in and EP0out separately? > >> If so you can start i from 0 and use > >> ep_priv = &usbssp_data->devs.eps[i]; > >> > >> This should fix the direction. eps[0] will be ep0out and eps[1] will be ep0in. > > > > I wanted to be consistent with Device Context Data Structure from > > XHCI specification (EP Context 0 BiDir, EP Context 1 OUT, EP Context 1 IN ...). > > > > I little change this function, and now it look like this: > > int usbssp_gadget_init_endpoints(struct usbssp_udc *usbssp_data) > > { > > 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 = 0; i < usbssp_data->num_endpoints; i++) { > > bool direction = i & 1; /*start from OUT endpoint*/ > > u8 epnum = ((i + 1) >> 1); > > > > ep_priv = &usbssp_data->devs.eps[i]; > > ep_priv->usbssp_data = usbssp_data; > > ep_priv->number = epnum; > > ep_priv->direction = direction; /*0 for OUT, 1 for IN*/ > > > > /* > > *Ep0 is bidirectional so Ep0In and Ep0Out is represented by > > * usbssp_data->devs.eps[0] > > */ > > if (epnum == 0) { > > snprintf(ep_priv->name, sizeof(ep_priv->name), "ep%d%s", > > epnum, "BiDir"); > > > > usb_ep_set_maxpacket_limit(&ep_priv->endpoint, 512); > > ep_priv->endpoint.maxburst = 1; > > ep_priv->endpoint.ops = &usbssp_gadget_ep0_ops; > > ep_priv->endpoint.desc = &usbssp_gadget_ep0_desc; > > > > ep_priv->endpoint.comp_desc = NULL; > > ep_priv->endpoint.caps.type_control = true; > > ep_priv->endpoint.caps.dir_in = true; > > ep_priv->endpoint.caps.dir_out = true; > > > > usbssp_data->usb_req_ep0_in.epnum = ep_priv->number; > > usbssp_data->usb_req_ep0_in.dep = ep_priv; > > usbssp_data->gadget.ep0 = &ep_priv->endpoint; > > > > } else { > > snprintf(ep_priv->name, sizeof(ep_priv->name), "ep%d%s", > > epnum, (ep_priv->direction) ? "in" : "out"); > > > > 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; > > } > > > > ep_priv->endpoint.name = ep_priv->name; > > I think this is much better now. > > > > > dev_dbg(usbssp_data->dev, "Init %s, MaxPack: %04x SupType: " > > "CTRL: %s, INT: %s, BULK: %s, ISOC %s, " > > "SupDir IN: %s, OUT: %s\n", > > ep_priv->name, 1024, > > (ep_priv->endpoint.caps.type_control) ? "yes" : "no", > > (ep_priv->endpoint.caps.type_int) ? "yes" : "no", > > (ep_priv->endpoint.caps.type_bulk) ? "yes" : "no", > > (ep_priv->endpoint.caps.type_iso) ? "yes" : "no", > > (ep_priv->endpoint.caps.dir_in) ? "yes" : "no", > > (ep_priv->endpoint.caps.dir_out) ? "yes" : "no"); > > > > In some places you use usbssp_dbg_trace() and at others you use dev_dbg(). > Is it better to stick with one throughout? Yes, it true, It would be better. I will think about that. > > INIT_LIST_HEAD(&ep_priv->pending_list); > > } > > > > return 0; > > } > > > >>> + > >>> + dev_dbg(usbssp_data->dev, "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 you start i from 1 then you can skip the if(). > >> > >>> + 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 338ec2ec18b1..195f5777cf8a 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) > >>> + dev_err(usbssp_data->dev, "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) { > >>> + dev_warn(usbssp_data->dev, "Device not accessible, reset failed.\n"); > >>> + return -ENODEV; > >>> + } > >>> + > >>> + if ((state & STS_HALT) == 0) { > >>> + dev_warn(usbssp_data->dev, "DC not halted, aborting reset.\n"); > >> > >> DC is not a familiar abbreviation. Mabe just use Controller? or Device controller. > > > > In xhci driver is often used HC for host controller. In device driver I change this abbreviation > > to DC. I will review the patches and change this to Controller. > > > >> > >>> + 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). > >>> @@ -179,8 +256,7 @@ int usbssp_gen_setup(struct usbssp_udc *usbssp_data) > >>> > >>> dev_dbg(usbssp_data->dev, "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; > >>> dev_dbg(usbssp_data->dev, "Reset complete\n"); > >>> @@ -244,8 +320,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; > >>> @@ -288,6 +363,7 @@ int usbssp_gadget_init(struct usbssp_udc *usbssp_data) > >>> usbssp_halt(usbssp_data); > >>> /*TODO add implementation of usbssp_reset function*/ > >>> //usbssp_reset(usbssp_data); > >>> + usbssp_reset(usbssp_data); > >>> /*TODO add implementation of freeing memory*/ > >>> //usbssp_mem_cleanup(usbssp_data); > >>> err3: > >>> > >> > >> -- > >> cheers, > >> -roger > >> > >> Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki. > >> Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki > > > > Cheers > > Pawel > > > > -- > cheers, > -roger > > Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki. > Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki cheers Pawel
diff --git a/drivers/usb/usbssp/gadget-ext-caps.h b/drivers/usb/usbssp/gadget-ext-caps.h index 2bf327046376..86c0ce331037 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 BIT(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 BIT(11) diff --git a/drivers/usb/usbssp/gadget-if.c b/drivers/usb/usbssp/gadget-if.c index d53e0fb65299..70def978b085 100644 --- a/drivers/usb/usbssp/gadget-if.c +++ b/drivers/usb/usbssp/gadget-if.c @@ -12,13 +12,278 @@ #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) { + dev_err(usbssp_data->dev, "%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); + dev_dbg(usbssp_data->dev, + "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; + + dev_dbg(usbssp_data->dev, "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 338ec2ec18b1..195f5777cf8a 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) + dev_err(usbssp_data->dev, "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) { + dev_warn(usbssp_data->dev, "Device not accessible, reset failed.\n"); + return -ENODEV; + } + + if ((state & STS_HALT) == 0) { + dev_warn(usbssp_data->dev, "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). @@ -179,8 +256,7 @@ int usbssp_gen_setup(struct usbssp_udc *usbssp_data) dev_dbg(usbssp_data->dev, "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; dev_dbg(usbssp_data->dev, "Reset complete\n"); @@ -244,8 +320,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; @@ -288,6 +363,7 @@ int usbssp_gadget_init(struct usbssp_udc *usbssp_data) usbssp_halt(usbssp_data); /*TODO add implementation of usbssp_reset function*/ //usbssp_reset(usbssp_data); + usbssp_reset(usbssp_data); /*TODO add implementation of freeing memory*/ //usbssp_mem_cleanup(usbssp_data); err3:
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@cadence.com> --- drivers/usb/usbssp/gadget-ext-caps.h | 3 + drivers/usb/usbssp/gadget-if.c | 269 ++++++++++++++++++++++++++- drivers/usb/usbssp/gadget.c | 84 ++++++++- 3 files changed, 350 insertions(+), 6 deletions(-)