/**
 * 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;
}
Beispiel #2
0
/**
 * 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;
}
Beispiel #3
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 = 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;
}
Beispiel #7
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;
}
Beispiel #8
0
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;
}
Beispiel #11
0
/**
 * 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;
}
Beispiel #18
0
/**
 * 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;
}
Beispiel #19
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;
}
Beispiel #20
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		
	}
Beispiel #21
0
/**
 * 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;
}