/** * This function allocates an I/O buffer to be used for a transfer * to/from the specified endpoint. * * @param usb_ep The endpoint to be used with with the request * @param bytes The desired number of bytes for the buffer * @param dma Pointer to the buffer's DMA address; must be valid * @param gfp_flags the GFP_* flags to use. * @return address of a new buffer or null is buffer could not be allocated. */ static void *dwc_otg_pcd_alloc_buffer(struct usb_ep *usb_ep, unsigned bytes, dma_addr_t * dma, gfp_t gfp_flags) { void *buf; dwc_otg_pcd_t *pcd = 0; pcd = gadget_wrapper->pcd; DWC_DEBUGPL(DBG_PCDV, "%s(%p,%d,%p,%0x)\n", __func__, usb_ep, bytes, dma, gfp_flags); /* Check dword alignment */ if ((bytes & 0x3UL) != 0) { DWC_WARN("%s() Buffer size is not a multiple of" "DWORD size (%d)", __func__, bytes); } buf = dma_alloc_coherent(NULL, bytes, dma, gfp_flags); /* Check dword alignment */ if (((int)buf & 0x3UL) != 0) { DWC_WARN("%s() Buffer is not DWORD aligned (%p)", __func__, buf); } return buf; }
/** * This function allocates an I/O buffer to be used for a transfer * to/from the specified endpoint. * * @param _ep The endpoint to be used with with the request * @param _bytes The desired number of bytes for the buffer * @param _dma Pointer to the buffer's DMA address; must be valid * @param _gfp_flags the GFP_* flags to use. * @return address of a new buffer or null is buffer could not be allocated. */ static void *dwc_otg_pcd_alloc_buffer(struct usb_ep *_ep, unsigned _bytes, dma_addr_t *_dma, unsigned _gfp_flags) { void *buf; dwc_otg_pcd_ep_t *ep; dwc_otg_pcd_t *pcd = 0; ep = container_of(_ep, dwc_otg_pcd_ep_t, ep); pcd = ep->pcd; DWC_DEBUGPL(DBG_PCDV,"%s(%p,%d,%p,%0x)\n", __func__, _ep, _bytes, _dma, _gfp_flags); /* Check dword alignment */ if ((_bytes & 0x3UL) != 0) { DWC_WARN("%s() Buffer size is not a multiple of" "DWORD size (%d)",__func__, _bytes); } if (GET_CORE_IF(pcd)->dma_enable) { buf = dma_alloc_coherent (NULL, _bytes, _dma, _gfp_flags); } else { buf = kmalloc( _bytes, _gfp_flags); } /* Check dword alignment */ if (((long)buf & 0x3UL) != 0) { DWC_WARN("%s() Buffer is not DWORD aligned (%p)", __func__, buf); } return buf; }
/** * This function is used to submit an I/O Request to an EP. * * - When the request completes the request's completion callback * is called to return the request to the driver. * - An EP, except control EPs, may have multiple requests * pending. * - Once submitted the request cannot be examined or modified. * - Each request is turned into one or more packets. * - A BULK EP can queue any amount of data; the transfer is * packetized. * - Zero length Packets are specified with the request 'zero' * flag. */ static int ep_queue(struct usb_ep *usb_ep, struct usb_request *usb_req, gfp_t gfp_flags) { dwc_otg_pcd_t *pcd; int retval = 0; //trace_printk("(%p,%p,%d)\n", // usb_ep, usb_req, gfp_flags); if (!usb_req || !usb_req->complete || (!gadget_wrapper->gadget.sg_supported && !usb_req->buf)) { DWC_WARN("bad params\n"); return -EINVAL; } if (!usb_ep) { DWC_WARN("bad ep\n"); return -EINVAL; } pcd = gadget_wrapper->pcd; if (!gadget_wrapper->driver || gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) { DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n", gadget_wrapper->gadget.speed); DWC_WARN("bogus device state\n"); return -ESHUTDOWN; } //trace_printk( "%s queue req %p, len %d buf %p\n", // usb_ep->name, usb_req, usb_req->length, usb_req->buf); usb_req->status = -EINPROGRESS; usb_req->actual = 0; retval = dwc_otg_pcd_ep_queue(pcd, usb_ep, usb_req->buf, usb_req->dma/*dma_addr*/, usb_req->length, usb_req->zero, usb_req->num_sgs, usb_req->sg, usb_req, gfp_flags == GFP_ATOMIC ? 1 : 0); if (retval) { pr_err("%s, cannot enqueue a renquest, err :%d\n", __func__, retval); pr_info( "%s queue req %p, len %d buf %p\n", usb_ep->name, usb_req, usb_req->length, usb_req->buf); return -EINVAL; } return 0; }
/** * This function is used to submit an ISOC Transfer Request to an EP. * * - Every time a sync period completes the request's completion callback * is called to provide data to the gadget driver. * - Once submitted the request cannot be modified. * - Each request is turned into periodic data packets untill ISO * Transfer is stopped.. */ static int iso_ep_start(struct usb_ep *usb_ep, struct usb_iso_request *req, gfp_t gfp_flags) { int retval = 0; if (!req || !req->process_buffer || !req->buf0 || !req->buf1) { DWC_WARN("bad params\n"); return -EINVAL; } if (!usb_ep) { DWC_PRINTF("bad params\n"); return -EINVAL; } req->status = -EINPROGRESS; retval = dwc_otg_pcd_iso_ep_start(gadget_wrapper->pcd, usb_ep, req->buf0, req->buf1, req->dma0, req->dma1, req->sync_frame, req->data_pattern_frame, req->data_per_frame, req->flags & USB_REQ_ISO_ASAP ? -1 : req-> start_frame, req->buf_proc_intrvl, req, gfp_flags == GFP_ATOMIC ? 1 : 0); if (retval) { return -EINVAL; } return retval; }
/** * This function cancels an I/O request from an EP. */ static int ep_dequeue(struct usb_ep *usb_ep, struct usb_request *usb_req) { if (!usb_ep || !usb_req) { DWC_WARN("bad argument\n"); return -EINVAL; } if (!gadget_wrapper->driver || gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) { DWC_WARN("bogus device state\n"); return -ESHUTDOWN; } if (dwc_otg_pcd_ep_dequeue(gadget_wrapper->pcd, usb_ep, usb_req)) { return -EINVAL; } return 0; }
/** * This function is used to submit an I/O Request to an EP. * * - When the request completes the request's completion callback * is called to return the request to the driver. * - An EP, except control EPs, may have multiple requests * pending. * - Once submitted the request cannot be examined or modified. * - Each request is turned into one or more packets. * - A BULK EP can queue any amount of data; the transfer is * packetized. * - Zero length Packets are specified with the request 'zero' * flag. */ static int ep_queue(struct usb_ep *usb_ep, struct usb_request *usb_req, gfp_t gfp_flags) { dwc_otg_pcd_t *pcd; int retval; DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p,%d)\n", __func__, usb_ep, usb_req, gfp_flags); if (!usb_req || !usb_req->complete || !usb_req->buf) { DWC_WARN("bad params\n"); return -EINVAL; } if (!usb_ep) { DWC_WARN("bad ep\n"); return -EINVAL; } pcd = gadget_wrapper->pcd; if (!gadget_wrapper->driver || gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) { DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n", gadget_wrapper->gadget.speed); DWC_WARN("bogus device state\n"); return -ESHUTDOWN; } DWC_DEBUGPL(DBG_PCD, "%s queue req %p, len %d buf %p\n", usb_ep->name, usb_req, usb_req->length, usb_req->buf); usb_req->status = -EINPROGRESS; usb_req->actual = 0; retval = dwc_otg_pcd_ep_queue(pcd, usb_ep, usb_req->buf, usb_req->dma, usb_req->length, usb_req->zero, usb_req, gfp_flags == GFP_ATOMIC ? 1 : 0); if (retval) { return -EINVAL; } return 0; }
/** * This function allocates a request object to use with the specified * endpoint. * * @param _ep The endpoint to be used with with the request * @param _gfp_flags the GFP_* flags to use. */ static struct usb_request *dwc_otg_pcd_alloc_request(struct usb_ep *_ep, gfp_t _gfp_flags) { dwc_otg_pcd_request_t * req; DWC_DEBUGPL(DBG_PCDV, "%s(%p,%d)\n", __func__, _ep, _gfp_flags); if (0 == _ep) { DWC_WARN("%s() %s\n", __func__, "Invalid EP!\n"); return 0; } req = kmalloc(sizeof(dwc_otg_pcd_request_t), _gfp_flags); if (0 == req) { DWC_WARN("%s() %s\n", __func__,"request allocation failed!\n"); return 0; } memset(req, 0, sizeof(dwc_otg_pcd_request_t)); req->req.dma = DMA_ADDR_INVALID; INIT_LIST_HEAD(&req->queue); return &req->req; }
static void free_wrapper(struct gadget_wrapper *d) { if (d->driver) { /* should have been done already by driver model core */ DWC_WARN("driver is still registered\n"); usb_gadget_unregister_driver(d->driver); } //device_unregister(&d->gadget.dev); dwc_free(d); }
/** * This function allocates a request object to use with the specified * endpoint. * * @param ep The endpoint to be used with with the request * @param gfp_flags the GFP_* flags to use. */ static struct usb_request *dwc_otg_pcd_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) { struct usb_request *usb_req; DWC_DEBUGPL(DBG_PCDV, "%s(%p,%d)\n", __func__, ep, gfp_flags); if (0 == ep) { DWC_WARN("%s() %s\n", __func__, "Invalid EP!\n"); return 0; } usb_req = kmalloc(sizeof(*usb_req), gfp_flags); if (0 == usb_req) { DWC_WARN("%s() %s\n", __func__, "request allocation failed!\n"); return 0; } memset(usb_req, 0, sizeof(*usb_req)); usb_req->dma = DWC_INVALID_DMA_ADDR; return usb_req; }
/** This function will log a debug message * * @param core_if Programming view of DWC_otg controller. */ int32_t dwc_otg_handle_mode_mismatch_intr(dwc_otg_core_if_t * core_if) { gintsts_data_t gintsts; DWC_WARN("Mode Mismatch Interrupt: currently in %s mode\n", dwc_otg_mode(core_if) ? "Host" : "Device"); /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.modemismatch = 1; DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32); return 1; }
/** * This function frees a request object. * * @param _ep The endpoint associated with the request * @param _req The request being freed */ static void dwc_otg_pcd_free_request(struct usb_ep *_ep, struct usb_request *_req) { dwc_otg_pcd_request_t * req; DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, _ep, _req); if (0 == _ep || 0 == _req) { DWC_WARN("%s() %s\n", __func__,"Invalid ep or req argument!\n"); return; } req = container_of(_req, dwc_otg_pcd_request_t, req); kfree(req); }
/** * This function stops ISO EP Periodic Data Transfer. */ static int iso_ep_stop(struct usb_ep *usb_ep, struct usb_iso_request *req) { int retval = 0; if (!usb_ep) { DWC_WARN("bad ep\n"); } if (!gadget_wrapper->driver || gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) { DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n", gadget_wrapper->gadget.speed); DWC_WARN("bogus device state\n"); } dwc_otg_pcd_iso_ep_stop(gadget_wrapper->pcd, usb_ep, req); if (retval) { retval = -EINVAL; } return retval; }
/** * This function frees a request object. * * @param ep The endpoint associated with the request * @param req The request being freed */ static void dwc_otg_pcd_free_request(struct usb_ep *ep, struct usb_request *req) { DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, ep, req); if (0 == ep || 0 == req) { DWC_WARN("%s() %s\n", __func__, "Invalid ep or req argument!\n"); return; } kfree(req); }
/** * This function is called by the Gadget Driver for each EP to be * configured for the current configuration (SET_CONFIGURATION). * * This function initializes the dwc_otg_ep_t data structure, and then * calls dwc_otg_ep_activate. */ static int ep_enable(struct usb_ep *usb_ep, const struct usb_endpoint_descriptor *ep_desc) { int retval; DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, usb_ep, ep_desc); if (!usb_ep || !ep_desc || ep_desc->bDescriptorType != USB_DT_ENDPOINT) { DWC_WARN("%s, bad ep or descriptor\n", __func__); return -EINVAL; } if (usb_ep == &gadget_wrapper->ep0) { DWC_WARN("%s, bad ep(0)\n", __func__); return -EINVAL; } /* Check FIFO size? */ if (!ep_desc->wMaxPacketSize) { DWC_WARN("%s, bad %s maxpacket\n", __func__, usb_ep->name); return -ERANGE; } if (!gadget_wrapper->driver || gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) { DWC_WARN("%s, bogus device state\n", __func__); return -ESHUTDOWN; } retval = dwc_otg_pcd_ep_enable(gadget_wrapper->pcd, (const uint8_t *)ep_desc, (void *)usb_ep); if (retval) { DWC_WARN("dwc_otg_pcd_ep_enable failed\n"); return -EINVAL; } usb_ep->maxpacket = le16_to_cpu(ep_desc->wMaxPacketSize); return 0; }
/** * usb_ep_set_halt stalls an endpoint. * * usb_ep_clear_halt clears an endpoint halt and resets its data * toggle. * * Both of these functions are implemented with the same underlying * function. The behavior depends on the value argument. * * @param[in] usb_ep the Endpoint to halt or clear halt. * @param[in] value * - 0 means clear_halt. * - 1 means set_halt, * - 2 means clear stall lock flag. * - 3 means set stall lock flag. */ static int ep_halt(struct usb_ep *usb_ep, int value) { int retval = 0; DWC_DEBUGPL(DBG_PCD, "HALT %s %d\n", usb_ep->name, value); if (!usb_ep) { DWC_WARN("bad ep\n"); return -EINVAL; } retval = dwc_otg_pcd_ep_halt(gadget_wrapper->pcd, usb_ep, value); if (retval == -DWC_E_AGAIN) { return -EAGAIN; } else if (retval) { retval = -EINVAL; } return retval; }
/** * ep_wedge: sets the halt feature and ignores clear requests * * @usb_ep: the endpoint being wedged * * Use this to stall an endpoint and ignore CLEAR_FEATURE(HALT_ENDPOINT) * requests. If the gadget driver clears the halt status, it will * automatically unwedge the endpoint. * * Returns zero on success, else negative errno. * * Check usb_ep_set_wedge() at "usb_gadget.h" for details */ static int ep_wedge(struct usb_ep *usb_ep) { int retval = 0; DWC_DEBUGPL(DBG_PCD, "WEDGE %s\n", usb_ep->name); if (!usb_ep) { DWC_WARN("bad ep\n"); return -EINVAL; } retval = dwc_otg_pcd_ep_wedge(gadget_wrapper->pcd, usb_ep); if (retval == -DWC_E_AGAIN) { retval = -EAGAIN; } else if (retval) { retval = -EINVAL; } return retval; }
static struct usb_iso_request *alloc_iso_request(struct usb_ep *ep, int packets, gfp_t gfp_flags) { struct usb_iso_request *pReq = NULL; uint32_t req_size; req_size = sizeof(struct usb_iso_request); req_size += (2 * packets * (sizeof(struct usb_gadget_iso_packet_descriptor))); pReq = kmalloc(req_size, gfp_flags); if (!pReq) { DWC_WARN("Can't allocate Iso Request\n"); return 0; } pReq->iso_packet_desc0 = (void *)(pReq + 1); pReq->iso_packet_desc1 = pReq->iso_packet_desc0 + packets; return pReq; }
/** * This function is called by the Gadget Driver for each EP to be * configured for the current configuration (SET_CONFIGURATION). * * This function initializes the dwc_otg_ep_t data structure, and then * calls dwc_otg_ep_activate. */ static int dwc_otg_pcd_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *_desc) { dwc_otg_pcd_ep_t *ep = 0; dwc_otg_pcd_t *pcd = 0; unsigned long flags; DWC_DEBUGPL(DBG_PCDV,"%s(%p,%p)\n", __func__, _ep, _desc ); ep = container_of(_ep, dwc_otg_pcd_ep_t, ep); if (!_ep || !_desc || ep->desc || _desc->bDescriptorType != USB_DT_ENDPOINT) { DWC_WARN( "%s, bad ep or descriptor\n", __func__); return -EINVAL; } if (ep == &ep->pcd->ep[0]){ DWC_WARN("%s, bad ep(0)\n", __func__); return -EINVAL; } /* Check FIFO size? */ if (!_desc->wMaxPacketSize) { DWC_WARN("%s, bad %s maxpacket\n", __func__, _ep->name); return -ERANGE; } pcd = ep->pcd; if (!pcd->driver || pcd->gadget.speed == USB_SPEED_UNKNOWN) { DWC_WARN("%s, bogus device state\n", __func__); return -ESHUTDOWN; } spin_lock_irqsave(&pcd->lock, flags); ep->desc = _desc; ep->ep.maxpacket = le16_to_cpu (_desc->wMaxPacketSize); /* * Activate the EP */ ep->stopped = 0; ep->dwc_ep.is_in = (USB_DIR_IN & _desc->bEndpointAddress) != 0; ep->dwc_ep.maxpacket = ep->ep.maxpacket; ep->dwc_ep.tx_fifo_num = 0; ep->dwc_ep.type = _desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; if ((_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_ISOC ) { /* * if ISOC EP then assign a Periodic Tx FIFO. */ /** @todo NGS Determine Tx FIFO for periodic EP. * Currently using 1. */ ep->dwc_ep.tx_fifo_num = 1; } /* Set initial data PID. */ if ((_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK ) { ep->dwc_ep.data_pid_start = 0; } DWC_DEBUGPL(DBG_PCD, "Activate %s-%s: type=%d, mps=%d desc=%p\n", ep->ep.name, (ep->dwc_ep.is_in ?"IN":"OUT"), ep->dwc_ep.type, ep->dwc_ep.maxpacket, ep->desc ); dwc_otg_ep_activate( GET_CORE_IF(pcd), &ep->dwc_ep ); spin_unlock_irqrestore(&pcd->lock, flags); return 0; }
/** * This function is called when the ADP vbus timer expires. Timeout is 1.1s. */ static void adp_vbuson_timeout(void *ptr) { gpwrdn_data_t gpwrdn; dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) ptr; hprt0_data_t hprt0 = {.d32 = 0 }; pcgcctl_data_t pcgcctl = {.d32 = 0 }; DWC_PRINTF("%s: 1.1 seconds expire after turning on VBUS\n",__FUNCTION__); if (core_if) { core_if->adp.vbuson_timer_started = 0; /* Turn off vbus */ hprt0.b.prtpwr = 1; DWC_MODIFY_REG32(core_if->host_if->hprt0, hprt0.d32, 0); gpwrdn.d32 = 0; /* Power off the core */ if (core_if->power_down == 2) { /* Enable Wakeup Logic */ /* gpwrdn.b.wkupactiv = 1; */ gpwrdn.b.pmuactv = 0; gpwrdn.b.pwrdnrstn = 1; gpwrdn.b.pwrdnclmp = 1; DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32); /* Suspend the Phy Clock */ pcgcctl.b.stoppclk = 1; DWC_MODIFY_REG32(core_if->pcgcctl, 0, pcgcctl.d32); /* Switch on VDD */ /* gpwrdn.b.wkupactiv = 1;*/ gpwrdn.b.pmuactv = 1; gpwrdn.b.pwrdnrstn = 1; gpwrdn.b.pwrdnclmp = 1; DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32); } else { /* Enable Power Down Logic */ gpwrdn.b.pmuintsel = 1; gpwrdn.b.pmuactv = 1; DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32); } /* Power off the core */ if (core_if->power_down == 2) { gpwrdn.d32 = 0; gpwrdn.b.pwrdnswtch = 1; DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); } /* Unmask SRP detected interrupt from Power Down Logic */ gpwrdn.d32 = 0; gpwrdn.b.srp_det_msk = 1; DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32); dwc_otg_adp_probe_start(core_if); dwc_otg_dump_global_registers(core_if); dwc_otg_dump_host_registers(core_if); } } /** * Start the ADP Initial Probe timer to detect if Port Connected interrupt is * not asserted within 1.1 seconds. * * @param core_if the pointer to core_if strucure. */ void dwc_otg_adp_vbuson_timer_start(dwc_otg_core_if_t * core_if) { core_if->adp.vbuson_timer_started = 1; if (core_if->adp.vbuson_timer) { DWC_PRINTF("SCHEDULING VBUSON TIMER\n"); /* 1.1 secs + 60ms necessary for cil_hcd_start*/ DWC_TIMER_SCHEDULE(core_if->adp.vbuson_timer, 1160); } else { DWC_WARN("VBUSON_TIMER = %p\n",core_if->adp.vbuson_timer); } } #if 0 /** * Masks all DWC OTG core interrupts * */ static void mask_all_interrupts(dwc_otg_core_if_t * core_if) { int i; gahbcfg_data_t ahbcfg = {.d32 = 0 }; /* Mask Host Interrupts */ /* Clear and disable HCINTs */ for (i = 0; i < core_if->core_params->host_channels; i++) { DWC_WRITE_REG32(&core_if->host_if->hc_regs[i]->hcintmsk, 0); DWC_WRITE_REG32(&core_if->host_if->hc_regs[i]->hcint, 0xFFFFFFFF); } /* Clear and disable HAINT */ DWC_WRITE_REG32(&core_if->host_if->host_global_regs->haintmsk, 0x0000); DWC_WRITE_REG32(&core_if->host_if->host_global_regs->haint, 0xFFFFFFFF); /* Mask Device Interrupts */ if (!core_if->multiproc_int_enable) { /* Clear and disable IN Endpoint interrupts */ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->diepmsk, 0); for (i = 0; i <= core_if->dev_if->num_in_eps; i++) { DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[i]-> diepint, 0xFFFFFFFF); } /* Clear and disable OUT Endpoint interrupts */ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->doepmsk, 0); for (i = 0; i <= core_if->dev_if->num_out_eps; i++) { DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[i]-> doepint, 0xFFFFFFFF); } /* Clear and disable DAINT */ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->daint, 0xFFFFFFFF); DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->daintmsk, 0); } else { for (i = 0; i < core_if->dev_if->num_in_eps; ++i) { DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs-> diepeachintmsk[i], 0); DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[i]-> diepint, 0xFFFFFFFF); } for (i = 0; i < core_if->dev_if->num_out_eps; ++i) { DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs-> doepeachintmsk[i], 0); DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[i]-> doepint, 0xFFFFFFFF); } DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->deachintmsk, 0); DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->deachint, 0xFFFFFFFF); } /* Disable interrupts */ ahbcfg.b.glblintrmsk = 1; DWC_MODIFY_REG32(&core_if->core_global_regs->gahbcfg, ahbcfg.d32, 0); /* Disable all interrupts. */ DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, 0); /* Clear any pending interrupts */ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF); /* Clear any pending OTG Interrupts */ DWC_WRITE_REG32(&core_if->core_global_regs->gotgint, 0xFFFFFFFF); } /** * Unmask Port Connection Detected interrupt * */ static void unmask_conn_det_intr(dwc_otg_core_if_t * core_if) { gintmsk_data_t gintmsk = {.d32 = 0,.b.portintr = 1 }; DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gintmsk.d32); } #endif /** * Starts the ADP Probing * * @param core_if the pointer to core_if structure. */ uint32_t dwc_otg_adp_probe_start(dwc_otg_core_if_t * core_if) { adpctl_data_t adpctl = {.d32 = 0}; gpwrdn_data_t gpwrdn; #if 0 adpctl_data_t adpctl_int = {.d32 = 0, .b.adp_prb_int = 1, .b.adp_sns_int = 1, b.adp_tmout_int}; #endif dwc_otg_disable_global_interrupts(core_if); DWC_PRINTF("ADP Probe Start\n"); core_if->adp.probe_enabled = 1; adpctl.b.adpres = 1; dwc_otg_adp_write_reg(core_if, adpctl.d32); while (adpctl.b.adpres) { adpctl.d32 = dwc_otg_adp_read_reg(core_if); } adpctl.d32 = 0; gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn); /* In Host mode unmask SRP detected interrupt */ gpwrdn.d32 = 0; gpwrdn.b.sts_chngint_msk = 1; if (!gpwrdn.b.idsts) { gpwrdn.b.srp_det_msk = 1; } DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32); adpctl.b.adp_tmout_int_msk = 1; adpctl.b.adp_prb_int_msk = 1; adpctl.b.prb_dschg = 1; adpctl.b.prb_delta = 1; adpctl.b.prb_per = 1; adpctl.b.adpen = 1; adpctl.b.enaprb = 1; dwc_otg_adp_write_reg(core_if, adpctl.d32); DWC_PRINTF("ADP Probe Finish\n"); return 0; } /** * Starts the ADP Sense timer to detect if ADP Sense interrupt is not asserted * within 3 seconds. * * @param core_if the pointer to core_if strucure. */ void dwc_otg_adp_sense_timer_start(dwc_otg_core_if_t * core_if) { core_if->adp.sense_timer_started = 1; DWC_TIMER_SCHEDULE(core_if->adp.sense_timer, 3000 /* 3 secs */ ); } /** * Starts the ADP Sense * * @param core_if the pointer to core_if strucure. */ uint32_t dwc_otg_adp_sense_start(dwc_otg_core_if_t * core_if) { adpctl_data_t adpctl; DWC_PRINTF("ADP Sense Start\n"); /* Unmask ADP sense interrupt and mask all other from the core */ adpctl.d32 = dwc_otg_adp_read_reg_filter(core_if); adpctl.b.adp_sns_int_msk = 1; dwc_otg_adp_write_reg(core_if, adpctl.d32); dwc_otg_disable_global_interrupts(core_if); // vahrama /* Set ADP reset bit*/ adpctl.d32 = dwc_otg_adp_read_reg_filter(core_if); adpctl.b.adpres = 1; dwc_otg_adp_write_reg(core_if, adpctl.d32); while (adpctl.b.adpres) { adpctl.d32 = dwc_otg_adp_read_reg(core_if); } adpctl.b.adpres = 0; adpctl.b.adpen = 1; adpctl.b.enasns = 1; dwc_otg_adp_write_reg(core_if, adpctl.d32); dwc_otg_adp_sense_timer_start(core_if); return 0; }
/** * This function is used to submit an I/O Request to an EP. * * - When the request completes the request's completion callback * is called to return the request to the driver. * - An EP, except control EPs, may have multiple requests * pending. * - Once submitted the request cannot be examined or modified. * - Each request is turned into one or more packets. * - A BULK EP can queue any amount of data; the transfer is * packetized. * - Zero length Packets are specified with the request 'zero' * flag. */ static int dwc_otg_pcd_ep_queue(struct usb_ep *_ep, struct usb_request *_req, int _gfp_flags) { int prevented = 0; dwc_otg_pcd_request_t *req; dwc_otg_pcd_ep_t *ep; dwc_otg_pcd_t *pcd; unsigned long flags = 0; DWC_DEBUGPL(DBG_PCDV,"%s(%p,%p,%d)\n", __func__, _ep, _req, _gfp_flags); req = container_of(_req, dwc_otg_pcd_request_t, req); if (!_req || !_req->complete || !_req->buf || !list_empty(&req->queue)) { if( !_req ) printk("bad _req\n"); if( !_req->complete ) printk("bad _req->complete\n"); if( !_req->buf ) printk("bad _req->buf\n"); if( !list_empty(&req->queue) ) printk("bad list_empty\n"); DWC_WARN("%s, bad params\n", __func__); return -EINVAL; } ep = container_of(_ep, dwc_otg_pcd_ep_t, ep); if (!_ep || (!ep->desc && ep->dwc_ep.num != 0)) { DWC_WARN("%s, bad ep\n", __func__); return -EINVAL; } pcd = ep->pcd; //cathy, if suspended, drop request if ( (GET_CORE_IF(pcd)->dev_if->suspended == 1) && (ep->dwc_ep.num != 0) ) { DWC_DEBUGPL(DBG_PCDV,"%s, epnum = %d, drop request\n", __func__, ep->dwc_ep.num); return -ESHUTDOWN; } if (!pcd->driver || pcd->gadget.speed == USB_SPEED_UNKNOWN) { DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n", pcd->gadget.speed); DWC_WARN("%s, bogus device state\n", __func__); return -ESHUTDOWN; } DWC_DEBUGPL(DBG_PCD, "%s queue req %p, len %d buf %p\n", _ep->name, _req, _req->length, _req->buf); if (!GET_CORE_IF(pcd)->core_params->opt) { if (ep->dwc_ep.num != 0) { DWC_ERROR("%s queue req %p, len %d buf %p\n", _ep->name, _req, _req->length, _req->buf); } } SPIN_LOCK_IRQSAVE(&ep->pcd->lock, flags); #if defined(DEBUG) & defined(VERBOSE) dump_msg(_req->buf, _req->length); #endif _req->status = -EINPROGRESS; _req->actual = 0; /* * For EP0 IN without premature status, zlp is required? */ if (ep->dwc_ep.num == 0 && ep->dwc_ep.is_in) { DWC_DEBUGPL(DBG_PCDV, "%s-OUT ZLP\n", _ep->name); //_req->zero = 1; } /* Start the transfer */ if (list_empty(&ep->queue) && !ep->stopped) { /* EP0 Transfer? */ if (ep->dwc_ep.num == 0) { switch (pcd->ep0state) { case EP0_IN_DATA_PHASE: DWC_DEBUGPL(DBG_PCD, "%s ep0: EP0_IN_DATA_PHASE\n", __func__); break; case EP0_OUT_DATA_PHASE: DWC_DEBUGPL(DBG_PCD, "%s ep0: EP0_OUT_DATA_PHASE\n", __func__); if (pcd->request_config) { /* Complete STATUS PHASE */ ep->dwc_ep.is_in = 1; pcd->ep0state = EP0_STATUS; } break; default: DWC_DEBUGPL(DBG_ANY, "ep0: odd state %d\n", pcd->ep0state); SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags); return -EL2HLT; } ep->dwc_ep.dma_addr = (u32)_req->buf & ~Uncache_Mask; //_req->dma; //cathy ep->dwc_ep.start_xfer_buff = _req->buf; ep->dwc_ep.xfer_buff = _req->buf; ep->dwc_ep.xfer_len = _req->length; ep->dwc_ep.xfer_count = 0; ep->dwc_ep.sent_zlp = 0; ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; dwc_otg_ep0_start_transfer( GET_CORE_IF(pcd), &ep->dwc_ep ); } else { /* Setup and start the Transfer */ ep->dwc_ep.dma_addr = (u32)_req->buf & ~Uncache_Mask; //_req->dma; //cathy //ep->dwc_ep.dma_addr = _req->dma; ep->dwc_ep.start_xfer_buff = _req->buf; ep->dwc_ep.xfer_buff = _req->buf; ep->dwc_ep.xfer_len = _req->length; ep->dwc_ep.xfer_count = 0; ep->dwc_ep.sent_zlp = 0; ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; dwc_otg_ep_start_transfer( GET_CORE_IF(pcd), &ep->dwc_ep ); } } if ((req != 0) || prevented) { ++pcd->request_pending; list_add_tail(&req->queue, &ep->queue); //cathy #if 0 if (ep->dwc_ep.is_in && ep->stopped && !(GET_CORE_IF(pcd)->dma_enable)) { /** @todo NGS Create a function for this. */ diepmsk_data_t diepmsk = { .d32 = 0}; diepmsk.b.intktxfemp = 1; dwc_modify_reg32( &GET_CORE_IF(pcd)->dev_if->dev_global_regs->diepmsk, 0, diepmsk.d32 ); } #endif }
/** * This function is called by the Gadget Driver for each EP to be * configured for the current configuration (SET_CONFIGURATION). * * This function initializes the dwc_otg_ep_t data structure, and then * calls dwc_otg_ep_activate. */ static int dwc_otg_pcd_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *_desc) { dwc_otg_pcd_ep_t *ep = 0; dwc_otg_pcd_t *pcd = 0; unsigned long flags; DWC_DEBUGPL(DBG_PCDV,"%s(%p,%p)\n", __func__, _ep, _desc ); ep = container_of(_ep, dwc_otg_pcd_ep_t, ep); if (!_ep || !_desc || ep->desc || _desc->bDescriptorType != USB_DT_ENDPOINT) { DWC_WARN( "%s, bad ep or descriptor\n", __func__); return -EINVAL; } if (ep == &ep->pcd->ep0) { DWC_WARN("%s, bad ep(0)\n", __func__); return -EINVAL; } /* Check FIFO size? */ if (!_desc->wMaxPacketSize) { DWC_WARN("%s, bad %s maxpacket\n", __func__, _ep->name); return -ERANGE; } pcd = ep->pcd; if (!pcd->driver || pcd->gadget.speed == USB_SPEED_UNKNOWN) { if (!pcd->driver) { bh_otg_dbg2("[%s:%d] gadget[%s] !pcd->driver\n", __FUNCTION__, __LINE__, pcd->gadget.name); } if (pcd->gadget.speed == USB_SPEED_UNKNOWN) { bh_otg_dbg2("[%s:%d] gadget[%s] USB_SPEED_UNKNOWN\n", __FUNCTION__, __LINE__, pcd->gadget.name); } DWC_WARN("%s, bogus device state\n", __func__); return -ESHUTDOWN; } SPIN_LOCK_IRQSAVE(&pcd->lock, flags); ep->desc = _desc; ep->ep.maxpacket = le16_to_cpu (_desc->wMaxPacketSize); /* * Activate the EP */ ep->stopped = 0; ep->dwc_ep.is_in = (USB_DIR_IN & _desc->bEndpointAddress) != 0; ep->dwc_ep.maxpacket = ep->ep.maxpacket; ep->dwc_ep.type = _desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; if(ep->dwc_ep.is_in) { if(!pcd->otg_dev->core_if->en_multiple_tx_fifo) { ep->dwc_ep.tx_fifo_num = 0; if ((_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == //cathy USB_ENDPOINT_XFER_ISOC ) USB_ENDPOINT_XFER_INT ) //assign interrupt in ep (ep3) to periodic tx fifo { /* * if ISOC EP then assign a Periodic Tx FIFO. */ ep->dwc_ep.tx_fifo_num = assign_perio_tx_fifo(pcd->otg_dev->core_if); } } else { /* * if Dedicated FIFOs mode is on then assign a Tx FIFO. */ ep->dwc_ep.tx_fifo_num = assign_tx_fifo(pcd->otg_dev->core_if); } } /* Set initial data PID. */ if ((_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK ) { ep->dwc_ep.data_pid_start = 0; } DWC_DEBUGPL(DBG_PCD, "Activate %s-%s: type=%d, mps=%d desc=%p\n", ep->ep.name, (ep->dwc_ep.is_in ?"IN":"OUT"), ep->dwc_ep.type, ep->dwc_ep.maxpacket, ep->desc ); dwc_otg_ep_activate( GET_CORE_IF(pcd), &ep->dwc_ep ); SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags); return 0; }
/** * Sets the final status of an URB and returns it to the device driver. Any * required cleanup of the URB is performed. */ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle, dwc_otg_hcd_urb_t * dwc_otg_urb, uint32_t status) { struct urb *urb = (struct urb *)urb_handle; #ifdef DEBUG if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { DWC_PRINTF("%s: urb %p, device %d, ep %d %s, status=%d\n", __func__, urb, usb_pipedevice(urb->pipe), usb_pipeendpoint(urb->pipe), usb_pipein(urb->pipe) ? "IN" : "OUT", status); if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { int i; for (i = 0; i < urb->number_of_packets; i++) { DWC_PRINTF(" ISO Desc %d status: %d\n", i, urb->iso_frame_desc[i].status); } } } #endif urb->actual_length = dwc_otg_hcd_urb_get_actual_length(dwc_otg_urb); /* Convert status value. */ switch (status) { case -DWC_E_PROTOCOL: status = -EPROTO; break; case -DWC_E_IN_PROGRESS: status = -EINPROGRESS; break; case -DWC_E_PIPE: status = -EPIPE; break; case -DWC_E_IO: status = -EIO; break; case -DWC_E_TIMEOUT: status = -ETIMEDOUT; break; default: if (status) { /* alan.K * DWC_OTG IP don't know this status, so assumed to be a DWC_E_PROTOCOL. */ DWC_WARN("Unknown urb status %d, but assumed to be an EPROTO\n", status); status = -EPROTO; } } if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { int i; urb->error_count = dwc_otg_hcd_urb_get_error_count(dwc_otg_urb); for (i = 0; i < urb->number_of_packets; ++i) { urb->iso_frame_desc[i].actual_length = dwc_otg_hcd_urb_get_iso_desc_actual_length (dwc_otg_urb, i); urb->iso_frame_desc[i].status = dwc_otg_hcd_urb_get_iso_desc_actual_length (dwc_otg_urb, i); } } urb->status = status; urb->hcpriv = NULL; if (!status) { if ((urb->transfer_flags & URB_SHORT_NOT_OK) && (urb->actual_length < urb->transfer_buffer_length)) { urb->status = -EREMOTEIO; } } if ((usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) || (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) { struct usb_host_endpoint *ep = dwc_urb_to_endpoint(urb); if (ep) { free_bus_bandwidth(dwc_otg_hcd_to_hcd(hcd), dwc_otg_hcd_get_ep_bandwidth(hcd, ep->hcpriv), urb); } } dwc_free(dwc_otg_urb); usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, status); return 0; }