> > 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 | 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; 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"); 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 ��.n��������+%������w��{.n�����{���)��jg��������ݢj����G�������j:+v���w�m������w�������h�����٥