コード例 #1
0
/**
 * This function initialized the PCD portion of the driver.
 *
 */
int pcd_init(
	struct dwc_otg_device *_dev
	)
{
       //dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev);
       dwc_otg_device_t *otg_dev = _dev;
	int retval = 0;
	int irq;

	DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _dev);

	otg_dev->pcd = dwc_otg_pcd_init(otg_dev->core_if);

	if (!otg_dev->pcd) {
		DWC_ERROR("dwc_otg_pcd_init failed\n");
		return -ENOMEM;
	}

	gadget_wrapper = alloc_wrapper(_dev);

	/*
	 * Initialize EP structures
	 */
	gadget_add_eps(gadget_wrapper);

	/*
	 * Setup interupt handler
	 */
	 
	#if 0
	irq = platform_get_irq(_dev, 0);
	DWC_DEBUGPL(DBG_ANY, "registering handler for irq%d\n", irq);
	retval = request_irq(irq, dwc_otg_pcd_irq,
			     0, gadget_wrapper->gadget.name,
			     otg_dev->pcd);
			     //SA_SHIRQ, gadget_wrapper->gadget.name,
	if (retval != 0) {
		DWC_ERROR("request of irq%d failed\n", irq);
		free_wrapper(gadget_wrapper);
		return -EBUSY;
	}
	#endif
	sprd_pcd = otg_dev->pcd;
	
	dwc_otg_pcd_start(gadget_wrapper->pcd, &fops);

	return retval;
}
コード例 #2
0
/**
 * This function initialized the PCD portion of the driver.
 *
 */
int pcd_init(struct platform_device *_dev)
{
	dwc_otg_device_t *otg_dev = platform_get_otgdata(_dev);
	int irq;
	int retval = 0;

	DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _dev);

	otg_dev->pcd = dwc_otg_pcd_init(otg_dev->core_if);
	pdev = otg_dev->core_if;

	if (!otg_dev->pcd) {
		DWC_ERROR("dwc_otg_pcd_init failed\n");
		return -ENOMEM;
	}

	gadget_wrapper = alloc_wrapper(_dev);
	gadget_wrapper->p = otg_dev;
	/*
	 * Initialize EP structures
	 */
	gadget_add_eps(gadget_wrapper);

	/*
	 * Setup interupt handler
	 */
	irq = platform_get_irq(_dev, 0);
	if(irq < 0) {
		DWC_ERROR("no irq? (irq=%d)\n", irq);
		kfree(otg_dev->pcd);
		return -ENODEV;
	}
	DWC_DEBUGPL(DBG_ANY, "registering handler for irq%d\n", irq);
	retval = request_irq(irq, dwc_otg_pcd_irq,
			     IRQF_SHARED, gadget_wrapper->gadget.name,
			     otg_dev->pcd);
	if (retval != 0) {
		DWC_ERROR("request of irq%d failed\n", irq);
		free_wrapper(gadget_wrapper);
		return -EBUSY;
	}

	dwc_otg_pcd_start(gadget_wrapper->pcd, &fops);

	return retval;
}
コード例 #3
0
/**
 * dwc_otg_device_init
 *
 * Initialise the dwc_otg_device structure, and the hardware
 * so that it can run in device mode.
 */
int dwc_otg_device_init(dwc_otg_device_t *_dev, dwc_otg_core_t *_core)
{
	int ret = 0;

	if(!_dev)
	{
		DWC_ERROR("%s passed a null device structure pointer!\n", __func__);
		return -EINVAL;
	}

	// Clear device structure.
	// (This should have been by the driver,
	//  but better safe than sorry.)
	memset(_dev, 0, sizeof(dwc_otg_device_t));
	INIT_WORK(&_dev->reset_work, &dwc_otg_device_usb_reset_work);
	INIT_WORK(&_dev->suspend_work, &dwc_otg_device_usb_suspend_work);
	INIT_WORK(&_dev->disconnect_work, &dwc_otg_device_disconnect_work);

	// Initialise members
	_dev->remote_wakeup = 1;

	if(!_core)
	{
		DWC_ERROR("%s passed a null core structure pointer!\n", __func__);
		return -EINVAL;
	}
	else
		_dev->core = _core;

	// Register IRQ.
	ret = request_irq(_core->irq, dwc_otg_device_irq, IRQF_SHARED, DWC_OTG_DRIVER_NAME, _dev);
	if(ret)
	{
		DWC_ERROR("Failed to register IRQ (%#x).\n", ret);
		return ret;
	}
	else
		_dev->irq = _core->irq;

	
	return 0;
}
コード例 #4
0
ファイル: dwc_os.c プロジェクト: pafcndg/ndgIqSoftwareKit
dwc_timer_t *DWC_TIMER_ALLOC(char *name, dwc_timer_callback_t cb, void *data)
{

	dwc_timer_t *t = DWC_ALLOC(sizeof(*t));
	/*OS_ERR_TYPE err;*/

	if (t == NULL) {
		DWC_ERROR("Cannot allocate memory for timer");
		return NULL;
	}

	t->lock = DWC_SPINLOCK_ALLOC();
	if (!t->lock) {
		DWC_ERROR("Cannot allocate memory for lock");
		goto no_lock;
	}
	t->scheduled = 0;
	t->cb = cb;
	t->data = data;
	/*t->t = timer_create(timer_callback, (void *)t, 0, 0, 0, &err);
	if (err == E_OS_ERR) {
		DWC_ERROR("Cannot allocate memory for timer->t");
		goto no_timer;
	}*/



	return t;

/* no_timer: */
	DWC_SPINLOCK_FREE(t->lock);
no_lock:
	DWC_FREE(t);
	return NULL;

}
コード例 #5
0
/**
 * This function registers a gadget driver with the PCD.
 *
 * When a driver is successfully registered, it will receive control
 * requests including set_configuration(), which enables non-control
 * requests.  then usb traffic follows until a disconnect is reported.
 * then a host may connect again, or the driver might get unbound.
 *
 * @param driver The driver being registered
 */
int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
	int (*bind)(struct usb_gadget *))
{
	int retval;

	pr_info("%s\n", __func__);
	if (!driver || driver->speed == USB_SPEED_UNKNOWN ||
			!driver->disconnect || !driver->setup) {
		//!driver->unbind || !driver->disconnect || !driver->setup) {
		DWC_DEBUGPL(DBG_PCDV, "EINVAL\n");
		return -EINVAL;
	}

	DWC_DEBUGPL(DBG_PCD, "registering gadget driver '%s'\n",
			driver->driver.name);
	
	if (gadget_wrapper == 0) {
		DWC_DEBUGPL(DBG_PCDV, "ENODEV\n");
		return -ENODEV;
	}
	if (gadget_wrapper->driver != 0) {
		DWC_DEBUGPL(DBG_PCDV, "EBUSY (%p)\n", gadget_wrapper->driver);
		return -EBUSY;
	}

	/* hook up the driver */
	gadget_wrapper->driver = driver;
	gadget_wrapper->gadget.dev.driver = &driver->driver;

	DWC_DEBUGPL(DBG_PCD, "bind to driver %s\n", driver->driver.name);
	retval = bind(&gadget_wrapper->gadget);
	gadget_wrapper->enabled = 1;
	if (retval) {
		DWC_ERROR("bind to driver %s --> error %d\n",
				driver->driver.name, retval);
		gadget_wrapper->driver = 0;
		gadget_wrapper->gadget.dev.driver = 0;
		gadget_wrapper->enabled = 0;
		return retval;
	}
	DWC_DEBUGPL(DBG_ANY, "registered gadget driver '%s'\n",
			driver->driver.name);

	return 0;
}
コード例 #6
0
static struct gadget_wrapper *alloc_wrapper(
	struct dwc_otg_device *_dev
	)
{
	static char pcd_name[] = "dwc_otg";
	//dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev);
	dwc_otg_device_t *otg_dev = _dev;
	struct gadget_wrapper *d;
	int retval;

	d = dwc_alloc(sizeof(*d));
	if (d == NULL) {
		return NULL;
	}

	memset(d, 0, sizeof(*d));

	d->gadget.name = pcd_name;
	d->pcd = otg_dev->pcd;
	//sword
	/*
	strcpy(d->gadget.dev.bus_id, "gadget");
	*/
	#if 0
	dev_set_name(&d->gadget.dev, "gadget");
	d->gadget.dev.parent = &_dev->dev;
	d->gadget.dev.release = dwc_otg_pcd_gadget_release;
	#endif
	d->gadget.ops = &dwc_otg_pcd_ops;
	d->gadget.is_dualspeed = dwc_otg_pcd_is_dualspeed(otg_dev->pcd);
	d->gadget.is_otg = dwc_otg_pcd_is_otg(otg_dev->pcd);

	d->driver = 0;
	/* Register the gadget device */
	#if 0
	retval = device_register(&d->gadget.dev);
	if (retval != 0) {
		DWC_ERROR("device_register failed\n");
		dwc_free(d);
		return NULL;
	}
	#endif
	
	return d;
}
コード例 #7
0
static struct gadget_wrapper *alloc_wrapper(
	struct platform_device *_dev
	)
{
	static char pcd_name[] = "dwc_otg";
	dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev);
	struct gadget_wrapper *d;
	int retval;

	d = dwc_alloc(sizeof(*d));
	if (d == NULL) {
		return NULL;
	}

	memset(d, 0, sizeof(*d));

	d->gadget.name = pcd_name;
	d->pcd = otg_dev->pcd;
	//sword
	/*
	strcpy(d->gadget.dev.bus_id, "gadget");
	*/
	dev_set_name(&d->gadget.dev, "gadget");
	d->gadget.dev.parent = &_dev->dev;
	d->gadget.dev.release = dwc_otg_pcd_gadget_release;
	d->gadget.ops = &dwc_otg_pcd_ops;
	d->gadget.max_speed = USB_SPEED_HIGH;
	d->gadget.is_otg = dwc_otg_pcd_is_otg(otg_dev->pcd);
	if (d->pcd->core_if->dma_enable &&
		d->pcd->core_if->dma_desc_enable)
		d->gadget.sg_supported = 1;

	d->driver = 0;
	/* Register the gadget device */
#if 0
	retval = device_register(&d->gadget.dev);
	if (retval != 0) {
		DWC_ERROR("device_register failed\n");
		dwc_free(d);
		return NULL;
	}
#endif

	return d;
}
コード例 #8
0
/**
 * This function registers a gadget driver with the PCD.
 *
 * When a driver is successfully registered, it will receive control
 * requests including set_configuration(), which enables non-control
 * requests.  then usb traffic follows until a disconnect is reported.
 * then a host may connect again, or the driver might get unbound.
 *
 * @param driver The driver being registered
 */
int usb_gadget_register_driver(struct usb_gadget_driver *driver)
{
	int retval;

	DWC_DEBUGPL(DBG_PCD, "registering gadget driver \n");

	if (!driver || driver->speed == USB_SPEED_UNKNOWN ||
	    !driver->bind ||
	    !driver->disconnect || !driver->setup) {
	    //!driver->unbind || !driver->disconnect || !driver->setup) {
		DWC_DEBUGPL(DBG_PCDV, "EINVAL\n");
		return -EINVAL;
	}
	if (gadget_wrapper == 0) {
		DWC_DEBUGPL(DBG_PCDV, "ENODEV\n");
		return -ENODEV;
	}
	if (gadget_wrapper->driver != 0) {
		DWC_DEBUGPL(DBG_PCDV, "EBUSY (%p)\n", gadget_wrapper->driver);
		return -EBUSY;
	}

	/* hook up the driver */
	gadget_wrapper->driver = driver;
	//gadget_wrapper->gadget.dev.driver = &driver->driver;

	DWC_DEBUGPL(DBG_PCD, "bind to driver \n");
	retval = driver->bind(&gadget_wrapper->gadget);
	if (retval) {
		DWC_ERROR("bind to driver --> error %d\n",
			  retval);
		gadget_wrapper->driver = 0;
		//gadget_wrapper->gadget.dev.driver = 0;
		return retval;
	}
	/*
	DWC_DEBUGPL(DBG_ANY, "registered gadget driver '%s'\n",
		    driver->driver.name);
	*/
	return 0;
}
コード例 #9
0
ファイル: dwc_otg_pcd.c プロジェクト: spfy/040005
/**
 * 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		
	}
コード例 #10
0
/**
 * This function initialized the PCD portion of the driver.
 *
 */
int pcd_init(
	struct platform_device *_dev
	)
{
	dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev);
	int retval = 0;
	int irq;

	DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _dev);

	wake_lock_init(&usb_wake_lock, WAKE_LOCK_SUSPEND, "usb_work");
	otg_dev->pcd = dwc_otg_pcd_init(otg_dev->core_if);

	if (!otg_dev->pcd) {
		DWC_ERROR("dwc_otg_pcd_init failed\n");
		return -ENOMEM;
	}

	gadget_wrapper = alloc_wrapper(_dev);

	/*
	 * Initialize EP structures
	 */
	gadget_add_eps(gadget_wrapper);

	/*
	 * Setup interupt handler
	 */
	irq = platform_get_irq(_dev, 0);
	DWC_DEBUGPL(DBG_ANY, "registering handler for irq%d\n", irq);
	retval = request_irq(irq, dwc_otg_pcd_irq,
			0, gadget_wrapper->gadget.name,
			otg_dev->pcd);
	//SA_SHIRQ, gadget_wrapper->gadget.name,
	if (retval != 0) {
		DWC_ERROR("request of irq%d failed\n", irq);
		free_wrapper(gadget_wrapper);
		return -EBUSY;
	}
	/*
	 * initialize a timer for checking cable type.
	 */
	{
		setup_timer(&setup_transfer_timer,setup_transfer_timer_fun,(unsigned long)gadget_wrapper);
		setup_transfer_timer_start = 0;
	}
	setup_timer(&gadget_wrapper->cable_timer, cable_detect_handler,
			(unsigned long)gadget_wrapper);
	/*
	 * setup usb cable detect interupt
	 */
	{
		int plug_irq;
		plug_irq = usb_alloc_vbus_irq();
		if (plug_irq < 0) {
			pr_warning("cannot alloc vbus irq\n");
			return -EBUSY;
		}
		usb_set_vbus_irq_type(plug_irq, VBUS_PLUG_IN);
		gadget_wrapper->vbus = usb_get_vbus_state();
		pr_info("now usb vbus is :%d\n", gadget_wrapper->vbus);
		retval = request_irq(plug_irq, usb_detect_handler, IRQF_SHARED,
				"usb detect", otg_dev->pcd);
	}
	spin_lock_init(&gadget_wrapper->lock);

	INIT_WORK(&gadget_wrapper->detect_work, usb_detect_works);
	gadget_wrapper->detect_wq = create_singlethread_workqueue("usb detect wq");
	/*
	 * register a switch device for sending pnp message,
	 * for the user app need be notified immediately
	 * when plug in & plug out happen;
	 */
	gadget_wrapper->sdev.name = "charger_cable";
	retval = switch_dev_register(&gadget_wrapper->sdev);
	if (retval){
		pr_warning("register switch dev error:%s\n", __func__);
	}

	dwc_otg_pcd_start(gadget_wrapper->pcd, &fops);

	/*
	 * dwc driver is ok, check if the cable is insert, if no,
	 * shutdown udc for saving power.
	 */
	if (!gadget_wrapper->vbus){
		pr_debug("vbus is not power now \n");
		gadget_wrapper->udc_startup = 1;
		__udc_shutdown();
	}
	gadget_wrapper->udc_startup = gadget_wrapper->vbus;
	gadget_wrapper->enabled = 0;

	return retval;
}
コード例 #11
0
/**
 * This function is called during module intialization to verify that
 * the module parameters are in a valid state.
 */
static int check_parameters(dwc_otg_core_if_t *core_if)
{
	int i;
	int retval = 0;
	
/* Checks if the parameter is outside of its valid range of values */
#define DWC_OTG_PARAM_TEST(_param_, _low_, _high_) \
		((dwc_otg_module_params._param_ < (_low_)) || \
		(dwc_otg_module_params._param_ > (_high_)))

/* If the parameter has been set by the user, check that the parameter value is
 * within the value range of values.  If not, report a module error. */
#define DWC_OTG_PARAM_ERR(_param_, _low_, _high_, _string_) \
		do { \
			if (dwc_otg_module_params._param_ != -1) { \
				if (DWC_OTG_PARAM_TEST(_param_, (_low_), (_high_))) { \
					DWC_ERROR("`%d' invalid for parameter `%s'\n", \
						  dwc_otg_module_params._param_, _string_); \
					dwc_otg_module_params._param_ = dwc_param_##_param_##_default; \
					retval++; \
				} \
			} \
		} while (0)

	DWC_OTG_PARAM_ERR(opt,0,1,"opt");
	DWC_OTG_PARAM_ERR(otg_cap,0,2,"otg_cap");
	DWC_OTG_PARAM_ERR(dma_enable,0,1,"dma_enable");
	DWC_OTG_PARAM_ERR(dma_desc_enable,0,1,"dma_desc_enable");
	DWC_OTG_PARAM_ERR(speed,0,1,"speed");
	DWC_OTG_PARAM_ERR(host_support_fs_ls_low_power,0,1,"host_support_fs_ls_low_power");
	DWC_OTG_PARAM_ERR(host_ls_low_power_phy_clk,0,1,"host_ls_low_power_phy_clk");
	DWC_OTG_PARAM_ERR(enable_dynamic_fifo,0,1,"enable_dynamic_fifo");
	DWC_OTG_PARAM_ERR(data_fifo_size,32,32768,"data_fifo_size");
	DWC_OTG_PARAM_ERR(dev_rx_fifo_size,16,32768,"dev_rx_fifo_size");
	DWC_OTG_PARAM_ERR(dev_nperio_tx_fifo_size,16,32768,"dev_nperio_tx_fifo_size");
	DWC_OTG_PARAM_ERR(host_rx_fifo_size,16,32768,"host_rx_fifo_size");
	DWC_OTG_PARAM_ERR(host_nperio_tx_fifo_size,16,32768,"host_nperio_tx_fifo_size");
	DWC_OTG_PARAM_ERR(host_perio_tx_fifo_size,16,32768,"host_perio_tx_fifo_size");
	DWC_OTG_PARAM_ERR(max_transfer_size,2047,524288,"max_transfer_size");
	DWC_OTG_PARAM_ERR(max_packet_count,15,511,"max_packet_count");
	DWC_OTG_PARAM_ERR(host_channels,1,16,"host_channels");
	DWC_OTG_PARAM_ERR(dev_endpoints,1,15,"dev_endpoints");
	DWC_OTG_PARAM_ERR(phy_type,0,2,"phy_type");
	DWC_OTG_PARAM_ERR(phy_ulpi_ddr,0,1,"phy_ulpi_ddr");
	DWC_OTG_PARAM_ERR(phy_ulpi_ext_vbus,0,1,"phy_ulpi_ext_vbus");
	DWC_OTG_PARAM_ERR(i2c_enable,0,1,"i2c_enable");
	DWC_OTG_PARAM_ERR(ulpi_fs_ls,0,1,"ulpi_fs_ls");
	DWC_OTG_PARAM_ERR(ts_dline,0,1,"ts_dline");

	if (dwc_otg_module_params.dma_burst_size != -1) {
		if (DWC_OTG_PARAM_TEST(dma_burst_size,1,1) &&
		    DWC_OTG_PARAM_TEST(dma_burst_size,4,4) &&
		    DWC_OTG_PARAM_TEST(dma_burst_size,8,8) &&
		    DWC_OTG_PARAM_TEST(dma_burst_size,16,16) &&
		    DWC_OTG_PARAM_TEST(dma_burst_size,32,32) &&
		    DWC_OTG_PARAM_TEST(dma_burst_size,64,64) &&
		    DWC_OTG_PARAM_TEST(dma_burst_size,128,128) &&
		    DWC_OTG_PARAM_TEST(dma_burst_size,256,256)) {
			DWC_ERROR("`%d' invalid for parameter `dma_burst_size'\n",
				  dwc_otg_module_params.dma_burst_size);
			dwc_otg_module_params.dma_burst_size = 32;
			retval++;
		}

		{
			uint8_t brst_sz = 0;
			while(dwc_otg_module_params.dma_burst_size > 1) {
				brst_sz ++;
				dwc_otg_module_params.dma_burst_size >>= 1;
			}
			dwc_otg_module_params.dma_burst_size = brst_sz;
		}
	}

	if (dwc_otg_module_params.phy_utmi_width != -1) {
		if (DWC_OTG_PARAM_TEST(phy_utmi_width, 8, 8) &&
		    DWC_OTG_PARAM_TEST(phy_utmi_width, 16, 16)) {
			DWC_ERROR("`%d' invalid for parameter `phy_utmi_width'\n",
				  dwc_otg_module_params.phy_utmi_width);
			dwc_otg_module_params.phy_utmi_width = 16;
			retval++;
		}
	}

	for (i = 0; i < 15; i++) {
		/** @todo should be like above */
		//DWC_OTG_PARAM_ERR(dev_perio_tx_fifo_size[i], 4, 768, "dev_perio_tx_fifo_size");
		if (dwc_otg_module_params.dev_perio_tx_fifo_size[i] != -1) {
			if (DWC_OTG_PARAM_TEST(dev_perio_tx_fifo_size[i], 4, 768)) {
				DWC_ERROR("`%d' invalid for parameter `%s_%d'\n",
					  dwc_otg_module_params.dev_perio_tx_fifo_size[i], "dev_perio_tx_fifo_size", i);
				dwc_otg_module_params.dev_perio_tx_fifo_size[i] = dwc_param_dev_perio_tx_fifo_size_default;
				retval++;
			}
		}
	}

	DWC_OTG_PARAM_ERR(en_multiple_tx_fifo, 0, 1, "en_multiple_tx_fifo");

	for (i = 0; i < 15; i++) {
		/** @todo should be like above */
		//DWC_OTG_PARAM_ERR(dev_tx_fifo_size[i], 4, 768, "dev_tx_fifo_size");
		if (dwc_otg_module_params.dev_tx_fifo_size[i] != -1) {
			if (DWC_OTG_PARAM_TEST(dev_tx_fifo_size[i], 4, 768)) {
				DWC_ERROR("`%d' invalid for parameter `%s_%d'\n",
					  dwc_otg_module_params.dev_tx_fifo_size[i], "dev_tx_fifo_size", i);
				dwc_otg_module_params.dev_tx_fifo_size[i] = dwc_param_dev_tx_fifo_size_default;
				retval++;
			}
		}
	}

	DWC_OTG_PARAM_ERR(thr_ctl, 0, 7, "thr_ctl");
	DWC_OTG_PARAM_ERR(tx_thr_length, 8, 128, "tx_thr_length");
	DWC_OTG_PARAM_ERR(rx_thr_length, 8, 128, "rx_thr_length");

	DWC_OTG_PARAM_ERR(pti_enable,0,1,"pti_enable");
	DWC_OTG_PARAM_ERR(mpi_enable,0,1,"mpi_enable");

	/* At this point, all module parameters that have been set by the user
	 * are valid, and those that have not are left unset.  Now set their
	 * default values and/or check the parameters against the hardware
	 * configurations of the OTG core. */

/* This sets the parameter to the default value if it has not been set by the
 * user */
#define DWC_OTG_PARAM_SET_DEFAULT(_param_) \
	({ \
		int changed = 1; \
		if (dwc_otg_module_params._param_ == -1) { \
			changed = 0; \
			dwc_otg_module_params._param_ = dwc_param_##_param_##_default; \
		} \
		changed; \
	})

/* This checks the macro agains the hardware configuration to see if it is
 * valid.  It is possible that the default value could be invalid. In this
 * case, it will report a module error if the user touched the parameter.
 * Otherwise it will adjust the value without any error. */
#define DWC_OTG_PARAM_CHECK_VALID(_param_, _str_, _is_valid_, _set_valid_) \
	({ \
		int changed = DWC_OTG_PARAM_SET_DEFAULT(_param_); \
		int error = 0; \
		if (!(_is_valid_)) { \
			if (changed) { \
				DWC_ERROR("`%d' invalid for parameter `%s'. Check HW configuration.\n", dwc_otg_module_params._param_, _str_); \
				error = 1; \
			} \
			dwc_otg_module_params._param_ = (_set_valid_); \
		} \
		error; \
	})

	/* OTG Cap */
	retval += DWC_OTG_PARAM_CHECK_VALID(otg_cap, "otg_cap",
				({
					int valid;
					valid = 1;
					switch (dwc_otg_module_params.otg_cap) {
					case DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE:
						if (core_if->hwcfg2.b.op_mode != DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG)
							valid = 0;
						break;
					case DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE:
						if ((core_if->hwcfg2.b.op_mode != DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG) &&
						    (core_if->hwcfg2.b.op_mode != DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG) &&
						    (core_if->hwcfg2.b.op_mode != DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) &&
						    (core_if->hwcfg2.b.op_mode != DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST)) {
							valid = 0;
						}
						break;
					case DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE:
						/* always valid */
						break;
					}
					valid;
				}),
コード例 #12
0
int hcd_init(struct platform_device *_dev)
{
	struct usb_hcd *hcd = NULL;
	dwc_otg_hcd_t *dwc_otg_hcd = NULL;
	dwc_otg_device_t *otg_dev = platform_get_otgdata(_dev);
	int irq;
	int retval = 0;

	DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD INIT\n");

	/* Set device flags indicating whether the HCD supports DMA. */
	if (dwc_otg_is_dma_enable(otg_dev->core_if)) {
		_dev->dev.dma_mask = &dwc_otg_dmamask/*(void *)~0*/;
		_dev->dev.coherent_dma_mask = ~0;
	} else {
		_dev->dev.dma_mask = (void *)0;
		_dev->dev.coherent_dma_mask = 0;
	}

	/*
	 * Allocate memory for the base HCD plus the DWC OTG HCD.
	 * Initialize the base HCD.
	 */
	hcd = usb_create_hcd(&dwc_otg_hc_driver, &_dev->dev, dev_name(&_dev->dev));
	if (!hcd) {
		retval = -ENOMEM;
		goto error1;
	}

	hcd->regs = otg_dev->base;
    	hcd->has_tt = 1;

	/* Initialize the DWC OTG HCD. */
	dwc_otg_hcd = dwc_otg_hcd_alloc_hcd();
	if (!dwc_otg_hcd) {
		goto error2;
	}
	((struct wrapper_priv_data *)(hcd->hcd_priv))->dwc_otg_hcd =
	    dwc_otg_hcd;
	otg_dev->hcd = dwc_otg_hcd;

	if (dwc_otg_hcd_init(dwc_otg_hcd, otg_dev->core_if)) {
		goto error2;
	}

	hcd->self.otg_port = dwc_otg_hcd_otg_port(dwc_otg_hcd);

	/*
	 * Finish generic HCD initialization and start the HCD. This function
	 * allocates the DMA buffer pool, registers the USB bus, requests the
	 * IRQ line, and calls hcd_start method.
	 */
	irq = platform_get_irq(_dev, 0);
	if(irq < 0) {
		DWC_ERROR("no irq? (irq=%d)\n", irq);
		retval = -ENODEV;
		goto error2;
	}
	retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
	if (retval < 0) {
		goto error2;
	}

	dwc_otg_hcd_set_priv_data(dwc_otg_hcd, hcd);
	return 0;

      error2:
	usb_put_hcd(hcd);
      error1:
	return retval;
}
コード例 #13
0
/**
 * This function handles the OTG Interrupts. It reads the OTG
 * Interrupt Register (GOTGINT) to determine what interrupt has
 * occurred.
 *
 * @param _core_if Programming view of DWC_otg controller.
 */ 
int32_t dwc_otg_handle_otg_intr(dwc_otg_core_if_t * _core_if) 
{
	dwc_otg_core_global_regs_t * global_regs = _core_if->core_global_regs;
	gotgint_data_t gotgint;
	gotgctl_data_t gotgctl;
	gintmsk_data_t gintmsk;
	gotgint.d32 = dwc_read_reg32(&global_regs->gotgint);
	gotgctl.d32 = dwc_read_reg32(&global_regs->gotgctl);
	DWC_DEBUGPL(DBG_CIL, "++OTG Interrupt gotgint=%0x [%s]\n", gotgint.d32,
		     op_state_str(_core_if));
	
	//DWC_DEBUGPL(DBG_CIL, "gotgctl=%08x\n", gotgctl.d32 );
	if (gotgint.b.sesenddet) {
		DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " 
			     "Session End Detected++ (%s)\n",
			     op_state_str(_core_if));
		gotgctl.d32 = dwc_read_reg32(&global_regs->gotgctl);
		if (_core_if->op_state == B_HOST) {
			pcd_start(_core_if);
			_core_if->op_state = B_PERIPHERAL;
		} else {
			/* If not B_HOST and Device HNP still set. HNP
			 * Did not succeed!*/ 
			if (gotgctl.b.devhnpen) {
				DWC_DEBUGPL(DBG_ANY, "Session End Detected\n");
				DWC_ERROR("Device Not Connected/Responding!\n");
			}
			/* If Session End Detected the B-Cable has
			 * been disconnected. */ 
			/* Reset PCD and Gadget driver to a
			 * clean state. */ 
			pcd_stop(_core_if);
		}
		gotgctl.d32 = 0;
		gotgctl.b.devhnpen = 1;
		dwc_modify_reg32(&global_regs->gotgctl, gotgctl.d32, 0);
	}
	if (gotgint.b.sesreqsucstschng) {
		DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " 
			     "Session Reqeust Success Status Change++\n");
		gotgctl.d32 = dwc_read_reg32(&global_regs->gotgctl);
		if (gotgctl.b.sesreqscs) {
			if ((_core_if->core_params->phy_type ==
			      DWC_PHY_TYPE_PARAM_FS)
			     && (_core_if->core_params->i2c_enable)) {
				_core_if->srp_success = 1;
			} else {
				pcd_resume(_core_if);
				
				/* Clear Session Request */ 
				gotgctl.d32 = 0;
				gotgctl.b.sesreq = 1;
				dwc_modify_reg32(&global_regs->gotgctl,
						  gotgctl.d32, 0);
			}
		}
	}
	if (gotgint.b.hstnegsucstschng) {
		/* Print statements during the HNP interrupt handling
		 * can cause it to fail.*/ 
		gotgctl.d32 = dwc_read_reg32(&global_regs->gotgctl);
		if (gotgctl.b.hstnegscs) {
			if (dwc_otg_is_host_mode(_core_if)) {
				_core_if->op_state = B_HOST;
				
				/*
				 * Need to disable SOF interrupt immediately.
				 * When switching from device to host, the PCD
				 * interrupt handler won't handle the
				 * interrupt if host mode is already set. The
				 * HCD interrupt handler won't get called if
				 * the HCD state is HALT. This means that the
				 * interrupt does not get handled and Linux
				 * complains loudly.
				 */ 
				gintmsk.d32 = 0;
				gintmsk.b.sofintr = 1;
				dwc_modify_reg32(&global_regs->gintmsk, gintmsk.d32, 0);
				pcd_stop(_core_if);
				
				/*
				 * Initialize the Core for Host mode.
				 */ 
				hcd_start(_core_if);
				_core_if->op_state = B_HOST;
			}
		} else {
			gotgctl.d32 = 0;
			gotgctl.b.hnpreq = 1;
			gotgctl.b.devhnpen = 1;
			dwc_modify_reg32(&global_regs->gotgctl, gotgctl.d32, 0);
			DWC_DEBUGPL(DBG_ANY, "HNP Failed\n");
			DWC_ERROR("Device Not Connected/Responding\n");
		}
	}
	if (gotgint.b.hstnegdet) {
		
		/* The disconnect interrupt is set at the same time as
		 * Host Negotiation Detected.  During the mode
		 * switch all interrupts are cleared so the disconnect
		 * interrupt handler will not get executed.
		 */ 
		DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " 
				"Host Negotiation Detected++ (%s)\n", 
				(dwc_otg_is_host_mode(_core_if) ? "Host" :
				 "Device"));
		if (dwc_otg_is_device_mode(_core_if)) {
			DWC_DEBUGPL(DBG_ANY, "a_suspend->a_peripheral (%d)\n",
				     _core_if->op_state);
			hcd_disconnect(_core_if);
			pcd_start(_core_if);
			_core_if->op_state = A_PERIPHERAL;
		} else {
			
			/*
			 * Need to disable SOF interrupt immediately. When
			 * switching from device to host, the PCD interrupt
			 * handler won't handle the interrupt if host mode is
			 * already set. The HCD interrupt handler won't get
			 * called if the HCD state is HALT. This means that
			 * the interrupt does not get handled and Linux
			 * complains loudly.
			 */ 
			gintmsk.d32 = 0;
			gintmsk.b.sofintr = 1;
			dwc_modify_reg32(&global_regs->gintmsk, gintmsk.d32,
					  0);
			pcd_stop(_core_if);
			hcd_start(_core_if);
			_core_if->op_state = A_HOST;
		}
	}
	if (gotgint.b.adevtoutchng) {
		DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " 
			     "A-Device Timeout Change++\n");
	}
	if (gotgint.b.debdone) {
		DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " 
			     "Debounce Done++\n");
	}
	
	/* Clear GOTGINT */ 
	dwc_write_reg32(&_core_if->core_global_regs->gotgint, gotgint.d32);
	return 1;
}
コード例 #14
0
/**
 * dwc_otg_device_start
 *
 * Start device mode on the chip.
 */
int dwc_otg_device_start(dwc_otg_device_t *_dev)
{
	int ret = 0, i;
	diepmsk_data_t diepmsk = { .d32 = 0 };
	doepmsk_data_t doepmsk = { .d32 = 0 };

	if(!_dev)
	{
		DWC_ERROR("%s passed a null device structure pointer!\n", __func__);
		return -EINVAL;
	}

	DWC_VERBOSE("%s(%p)\n", __func__, _dev);

	// Enable Interrupts
	if(dwc_otg_device_enable_interrupts(_dev))
		DWC_WARNING("Failed to enable device interrupts.\n");

	// Clear D(I|O)PCTLs.
	for(i = 0; i < _dev->core->num_eps; i++)
	{
		dwc_otg_write_reg32(&_dev->core->in_ep_registers[i]->diepctl, 0);
		dwc_otg_write_reg32(&_dev->core->out_ep_registers[i]->doepctl, 0);
	}

	// Enable EP Interrupts
	diepmsk.b.xfercompl = 1;
	diepmsk.b.ahberr = 1;
	diepmsk.b.timeout = 1;
	diepmsk.b.epdisabled = 1;
	diepmsk.b.inepnakeff = 1;
	dwc_otg_write_reg32(&_dev->core->device_registers->diepmsk, diepmsk.d32);

	doepmsk.b.xfercompl = 1;
	doepmsk.b.setup = 1;
	doepmsk.b.back2backsetup = 1;
	doepmsk.b.epdisabled = 1;
	dwc_otg_write_reg32(&_dev->core->device_registers->doepmsk, doepmsk.d32);
	
	// Reset the device.
	dwc_otg_device_usb_reset(_dev);

	// We're all set up, tell the usb_gadget framework
	// that we exist.
	ret = usb_gadget_register_controller(_dev);
	if(ret)
	{
		DWC_ERROR("Failed to register controller.\n");
		return ret;
	}
	

	return 0;
}

/**
 * dwc_otg_device_stop
 *
 * Stop device mode on the chip.
 */
void dwc_otg_device_stop(dwc_otg_device_t *_dev)
{
	if(!_dev)
	{
		DWC_WARNING("%s passed a null device structure pointer!\n", __func__);
		return;
	}

	if(dwc_otg_device_disable_interrupts(_dev))
		DWC_WARNING("Failed to disable device interrupts.\n");
}

/**
 * dwc_otg_device_enable_interrupts
 *
 * enables interrupts.
 */
int dwc_otg_device_enable_interrupts(dwc_otg_device_t *_dev)
{
	gintmsk_data_t gintmsk = { .d32 = 0 };

	DWC_VERBOSE("%s(%p)\n", __func__, _dev);

	gintmsk.b.usbreset = 1;
	gintmsk.b.usbsuspend = 1;
	gintmsk.b.disconnect = 1;
	gintmsk.b.inepintr = 1;
	gintmsk.b.outepintr = 1;
	gintmsk.b.enumdone = 1;
	gintmsk.b.otgintr = 1;
	gintmsk.b.epmismatch = 1;
	dwc_otg_modify_reg32(&_dev->core->registers->gintmsk, 0, gintmsk.d32);

	return 0;
}

/**
 * dwc_otg_device_disable_interrupts
 *
 * disables interrupts.
 */
int dwc_otg_device_disable_interrupts(dwc_otg_device_t *_dev)
{
	gintmsk_data_t gintmsk = { .d32 = 0 };

	DWC_VERBOSE("%s(%p)\n", __func__, _dev);

	gintmsk.b.usbreset = 1;
	gintmsk.b.usbsuspend = 1;
	gintmsk.b.disconnect = 1;
	gintmsk.b.inepintr = 1;
	gintmsk.b.outepintr = 1;
	gintmsk.b.enumdone = 1;
	gintmsk.b.otgintr = 1;
	gintmsk.b.epmismatch = 1;
	dwc_otg_modify_reg32(&_dev->core->registers->gintmsk, gintmsk.d32, 0);

	return 0;
}

/**
 * dwc_otg_device_usb_reset
 *
 * Resets the USB connection and re-enables EP0.
 */
int dwc_otg_device_usb_reset(dwc_otg_device_t *_dev)
{
	diepmsk_data_t diepmsk = { .d32 = 0 };
	doepmsk_data_t doepmsk = { .d32 = 0 };
	dcfg_data_t dcfg = { .d32 = 0 };
	daint_data_t daint = { .d32 = 0 };
	unsigned long flags;

	DWC_VERBOSE("%s(%p)\n", __func__, _dev);

	spin_lock_irqsave(&_dev->core->lock, flags);

	// Clear Device Address
	dcfg.d32 = dwc_otg_read_reg32(&_dev->core->device_registers->dcfg);
	dcfg.b.devaddr = 0;
	dcfg.b.epmscnt = 1; // TODO: if shared fifo
	dwc_otg_write_reg32(&_dev->core->device_registers->dcfg, dcfg.d32);

	// Reset global NAK registers.
	_dev->core->global_in_nak_count = 0;
	_dev->core->global_out_nak_count = 0;
	dwc_otg_core_clear_global_in_nak(_dev->core);
	dwc_otg_core_clear_global_out_nak(_dev->core);

	// Reset EPs
	dwc_otg_core_ep_reset(_dev->core);

	// Enable interrupts on EP0
	daint.b.inep0 = 1;
	daint.b.outep0 = 1;
	dwc_otg_write_reg32(&_dev->core->device_registers->daintmsk, daint.d32);

	// Clear EP0 interrupts
	dwc_otg_write_reg32(&_dev->core->in_ep_registers[0]->diepint, 0xffffffff);
	dwc_otg_write_reg32(&_dev->core->out_ep_registers[0]->doepint, 0xffffffff);

	// Enable EP Interrupts
	diepmsk.b.xfercompl = 1;
	diepmsk.b.ahberr = 1;
	diepmsk.b.timeout = 1;
	diepmsk.b.epdisabled = 1;
	diepmsk.b.inepnakeff = 1;
	dwc_otg_write_reg32(&_dev->core->device_registers->diepmsk, diepmsk.d32);

	doepmsk.b.xfercompl = 1;
	doepmsk.b.setup = 1;
	doepmsk.b.back2backsetup = 1;
	doepmsk.b.epdisabled = 1;
	dwc_otg_write_reg32(&_dev->core->device_registers->doepmsk, doepmsk.d32);
	
	spin_unlock_irqrestore(&_dev->core->lock, flags);

	// Notify gadget driver
	if(dwc_otg_gadget_driver && dwc_otg_gadget_driver->resume)
		dwc_otg_gadget_driver->resume(&dwc_otg_gadget);

	return 0;
}

/** The EP0 USB descriptor! */
static struct usb_endpoint_descriptor ep0_descriptor = {
	.bLength = USB_DT_ENDPOINT_SIZE,
	.bDescriptorType = USB_DT_ENDPOINT,

	.bEndpointAddress = 0,
	.bmAttributes = 0,
};

/** The OUT request for EP0. */
static dwc_otg_core_request_t ep0_out_request = {
	.request_type = DWC_EP_TYPE_CONTROL,
	.direction = DWC_OTG_REQUEST_OUT,
	.completed_handler = &dwc_otg_device_complete_ep0,
	.dont_free = 1,	
	.buffer_length = 64,
	.dma_buffer = NULL,
};

/** The IN request for EP0. */
static dwc_otg_core_request_t ep0_in_request = {
	.request_type = DWC_EP_TYPE_CONTROL,
	.direction = DWC_OTG_REQUEST_IN,
	.dont_free = 1,
	.buffer_length = 64,
	.dma_buffer = NULL,
};

/**
 * The device USB IRQ handler.
 */
irqreturn_t dwc_otg_device_irq(int _irq, void *_dev)
{
	dwc_otg_device_t *dev = (dwc_otg_device_t*)_dev;
	dwc_otg_core_t *core = dev->core;
	gintsts_data_t gintsts = { .d32 = 0 };
	gintsts_data_t gintclr = { .d32 = 0 };

	gintsts.d32 = dwc_otg_read_reg32(&core->registers->gintsts) & dwc_otg_read_reg32(&core->registers->gintmsk);
	DWC_VERBOSE("%s(%d, %p) gintsts=0x%08x\n", __func__, _irq, _dev, gintsts.d32);

	if(gintsts.b.otgintr)
	{
		gotgint_data_t gotgint;
		
		gotgint.d32 = dwc_otg_read_reg32(&core->registers->gotgint);
		DWC_DEBUG("otgintr 0x%08x\n", gotgint.d32);

		if(gotgint.b.sesenddet)
		{
			dcfg_data_t dcfg = { .d32 = dwc_otg_read_reg32(&core->device_registers->dcfg) };

			DWC_DEBUG("session end detected\n");

			dcfg.b.nzstsouthshk = 1;
			dwc_otg_write_reg32(&core->device_registers->dcfg, dcfg.d32);

			DWC_DEBUG("DCFG=0x%08x\n", dcfg.d32);

			schedule_work(&dev->disconnect_work);
		}

		// Clear OTG interrupts
		dwc_otg_write_reg32(&core->registers->gotgint, 0xffffffff);

		gintclr.b.otgintr = 1;
	}

	if(gintsts.b.enumdone)
	{
		int dwcSpeed;
		int i;
		dsts_data_t dsts;

		DWC_DEBUG("enumdone\n");

		// Enable EP0.
		dwc_otg_core_enable_ep(core, &core->endpoints[0], &ep0_descriptor);

		//ep0_in_request.queued = 0;
		//ep0_out_request.queued = 0;
		
		// Listen for setup packets on EP0.
		dwc_otg_device_receive_ep0(_dev);

		// Tell gadget driver our top speed.
		dsts.d32 = dwc_otg_read_reg32(&core->device_registers->dsts);
		switch (dsts.b.enumspd)
		{
		case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ:
			dwc_otg_gadget.speed = USB_SPEED_HIGH;
			dwcSpeed = DWC_OTG_HIGH_SPEED;
			break;

		case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ:
		case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ:
			dwc_otg_gadget.speed = USB_SPEED_FULL;
			dwcSpeed = DWC_OTG_FULL_SPEED;
			break;

		case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ:
			dwc_otg_gadget.speed = USB_SPEED_LOW;
			dwcSpeed = DWC_OTG_LOW_SPEED;
			break;
		}
		
		// Update the endpoint speeds.
		for(i = 1; i < core->num_eps; i++)
			core->endpoints[i].speed = dwcSpeed;

		gintclr.b.enumdone = 1;
	}

	if(gintsts.b.usbreset)
	{
		DWC_DEBUG("usbreset\n");
		
		//schedule_work(&dev->reset_work);
		dwc_otg_device_usb_reset(dev);

		gintclr.b.usbreset = 1;
	}

	if(gintsts.b.usbsuspend)
	{
		DWC_DEBUG("usbsuspend\n");
		
		schedule_work(&dev->suspend_work);

		gintclr.b.usbsuspend = 1;
	}

	if(gintsts.b.disconnect)
	{
		DWC_DEBUG("disconnect\n");

		schedule_work(&dev->disconnect_work);
		gintclr.b.disconnect = 1;
	}

	if(gintsts.b.epmismatch)
	{
		dtknq1_data_t t1;
		int t2, t3, t4;
		int i;
		int num_tokens;
		int doneEPs = 0;
		dwc_otg_core_request_t *req;

		DWC_ERROR("EP Mismatch.\n");

		dwc_otg_core_set_global_in_nak(dev->core);
		for(i = 0; i < dev->core->num_eps; i++)
		{
			dwc_otg_core_ep_t *ep = &core->endpoints[i];
			req = list_first_entry(&ep->transfer_queue, dwc_otg_core_request_t, queue_pointer);
			if(req && req->direction == DWC_OTG_REQUEST_IN)
			{
				dwc_otg_core_cancel_ep(dev->core, ep);
			}
		}
		dwc_otg_core_clear_global_in_nak(dev->core);

		t1.d32 = dwc_otg_read_reg32(&dev->core->device_registers->dtknqr1);
		t2 = dwc_otg_read_reg32(&dev->core->device_registers->dtknqr2);
		t3 = dwc_otg_read_reg32(&dev->core->device_registers->dtknqr3_dthrctl);
		t4 = dwc_otg_read_reg32(&dev->core->device_registers->dtknqr4_fifoemptymsk);

		num_tokens = min((int)dev->core->hwcfg2.b.dev_token_q_depth, (int)t1.b.intknwptr);

		DWC_PRINT("Requeing %d requests.\n", num_tokens);

		for(i = 0; i < num_tokens; i++)
		{
			int ep;

#define GET_BITS(x, start, len) (((x) << ((sizeof(x)*8)-(len)-(start))) >> ((sizeof(x)*8)-(len)))

			if(i < 6)
				ep = GET_BITS((int)t1.b.epnums0_5, 8 + (i*4), 4);
			else if(i < 14)
				ep = GET_BITS(t2, (i-6)*4, 4);
			else if(i < 21)
				ep = GET_BITS(t3, (i-14)*4, 4);
			else if(i < 30)
				ep = GET_BITS(t4, (i-21)*4, 4);
			else
				break;

			if(doneEPs & (1 << ep))
				break;

			DWC_ERROR("Requeing %d.\n", ep);
	
			req = list_first_entry(&core->endpoints[i].transfer_queue, dwc_otg_core_request_t, queue_pointer);
			if(req && req->direction == DWC_OTG_REQUEST_IN)
				dwc_otg_core_start_request(core, req);
			else
				DWC_ERROR("Tried to requeue invalid request %p.\n", req);

			doneEPs |= (1 << ep);
		}

		for(i = 0; i < dev->core->num_eps; i++)
		{
			if(doneEPs & (1 << i))
				continue;
	
			req = list_first_entry(&core->endpoints[i].transfer_queue, dwc_otg_core_request_t, queue_pointer);
			if(req && req->direction == DWC_OTG_REQUEST_IN)
				dwc_otg_core_start_request(core, req);
			//else
			//	DWC_ERROR("Tried to requeue invalid request %p.\n", req);
		}

		gintclr.b.epmismatch = 1;
	}

	if(gintsts.b.inepintr || gintsts.b.outepintr)
	{
		daint_data_t daint = { .d32 = 0 };
		int i;

		DWC_DEBUG("epint\n");

		daint.d32 = dwc_otg_read_reg32(&core->device_registers->daint) & dwc_otg_read_reg32(&core->device_registers->daintmsk);

		for(i = 0; i < core->num_eps; i++)
		{
			if(daint.ep.in & 1)
				dwc_otg_device_handle_in_interrupt(dev, i);

			if(daint.ep.out & 1)
				dwc_otg_device_handle_out_interrupt(dev, i);

			daint.ep.out >>= 1;
			daint.ep.in >>= 1;
		}
	}

	// Clear the interrupts
	gintsts.d32 &= ~gintclr.d32;
	dwc_otg_write_reg32(&core->registers->gintsts, gintclr.d32);
	return gintsts.d32 != 0 ? IRQ_NONE : IRQ_HANDLED;
}

/**
 * The in endpoint interrupt handler.
 */
irqreturn_t dwc_otg_device_handle_in_interrupt(dwc_otg_device_t *_dev, int _ep)
{
	dwc_otg_core_ep_t *ep = &_dev->core->endpoints[_ep];
	diepint_data_t depint = { .d32 = 0 };
	diepint_data_t depclr = { .d32 = 0 };

	DWC_VERBOSE("%s(%p, %d)\n", __func__, _dev, _ep);

	depint.d32 = dwc_otg_read_reg32(&ep->in_registers->diepint) & dwc_otg_read_reg32(&_dev->core->device_registers->diepmsk);

	if(!ep->exists)
	{
		DWC_ERROR("%s called on non-existant in endpoint %d.\n", __func__, _ep);
		return -ENXIO;
	}

	if(depint.b.inepnakeff)
	{
		DWC_DEBUG("in nak eff %s\n", ep->name);

		complete_all(&ep->nakeff_completion);
		init_completion(&ep->nakeff_completion);

		depclr.b.inepnakeff = 1;
	}

	if(depint.b.epdisabled)
	{
		DWC_DEBUG("in epdisabled %s\n", ep->name);

		complete_all(&ep->disabled_completion);
		init_completion(&ep->disabled_completion);

		depclr.b.epdisabled = 1;
	}

	/*if(depint.b.intknepmis)
	{
		gintsts_data_t gintsts = { .d32 = 0 };

		DWC_DEBUG("in tn mis\n");

		gintsts.b.epmismatch = 1;
		dwc_otg_modify_reg32(&_dev->core->registers->gintsts, gintsts.d32, 0);
	}*/

	if(depint.b.intktxfemp)
	{
		DWC_DEBUG("in tx f emp\n");

		// Do nothing... ;_;
		
		depclr.b.intktxfemp = 1;
	}

	if(depint.b.timeout)
	{
		DWC_DEBUG("in timeout %s\n", ep->name);

		// Do something!?
		// Cancel sending?!
		
		depclr.b.timeout = 1;
	}

	if(depint.b.ahberr)
	{
		DWC_DEBUG("in ahberr\n");

		// Do nothing again... :'(
		
		depclr.b.ahberr = 1;
	}

	if(depint.b.xfercompl)
	{
		dwc_otg_core_request_t *req = list_first_entry(&ep->transfer_queue, dwc_otg_core_request_t, queue_pointer);

		DWC_DEBUG("in xfercompl %s\n", ep->name);

		if(list_empty(&ep->transfer_queue) || req->direction != DWC_OTG_REQUEST_IN)
		{
			DWC_ERROR("XferCompl received when we weren't transferring anything!\n");
		}
		else
			dwc_otg_core_complete_request(_dev->core, req);
		
		depclr.b.xfercompl = 1;
	}

	// Clear interrupts
	dwc_otg_write_reg32(&ep->in_registers->diepint, depclr.d32);
	
	return 0;
}

/**
 * The out endpoint interrupt handler.
 */
irqreturn_t dwc_otg_device_handle_out_interrupt(dwc_otg_device_t *_dev, int _ep)
{
	dwc_otg_core_ep_t *ep = &_dev->core->endpoints[_ep];
	doepint_data_t depint = { .d32 = 0 };
	doepint_data_t depclr = { .d32 = 0 };

	DWC_VERBOSE("%s(%p, %d)\n", __func__, _dev, _ep);

	depint.d32 = dwc_otg_read_reg32(&ep->out_registers->doepint) & dwc_otg_read_reg32(&_dev->core->device_registers->doepmsk);

	if(!ep->exists)
	{
		DWC_ERROR("%s called on non-existant in endpoint %d.\n", __func__, _ep);
		return -ENXIO;
	}

	if(depint.b.setup)
	{
		dwc_otg_core_request_t *req;

		// We received a setup packet, yaaay! 
		DWC_VERBOSE("setup\n");

		// If we had a setup packet, set the flag on the
		// request that says it was prepended by a setup
		// token.
		req = list_first_entry(&ep->transfer_queue, dwc_otg_core_request_t, queue_pointer);
		if(!list_empty(&ep->transfer_queue) && req->direction == DWC_OTG_REQUEST_OUT)
		{
			req->setup = 1;
			dwc_otg_core_complete_request(_dev->core, req);
		}
		else
			DWC_ERROR("Setup Packet Received without us initiating a transfer!\n");

		depclr.b.setup = 1;
	}

	if(depint.b.back2backsetup)
	{
		// Do nothing
		DWC_DEBUG("back2backsetup\n");

		depclr.b.back2backsetup = 1;
	}

	if(depint.b.epdisabled)
	{
		DWC_DEBUG("out epdisabled\n");

		complete_all(&ep->disabled_completion);
		init_completion(&ep->disabled_completion);

		depclr.b.epdisabled = 1;
	}

	if(depint.b.ahberr)
	{
		// Do nothing again... :'(
		DWC_DEBUG("out ahberr\n");

		depclr.b.ahberr = 1;
	}
		
	if(depint.b.outtknepdis)
	{
		// Nothing to do again! Wah... :''(
		DWC_DEBUG("out tkn epdis\n");

		depclr.b.outtknepdis = 1;
	}

	if(depint.b.xfercompl)
	{
		dwc_otg_core_request_t *req = list_first_entry(&ep->transfer_queue, dwc_otg_core_request_t, queue_pointer);

		DWC_DEBUG("out xfercompl\n");

		// Yay! :D

		if(list_empty(&ep->transfer_queue) || req->direction != DWC_OTG_REQUEST_OUT)
		{
			DWC_ERROR("XferCompl received when we weren't transferring anything!\n");
		}
		else
			dwc_otg_core_complete_request(_dev->core, req);

		depclr.b.xfercompl = 1;
	}

	// Clear interrupts
	dwc_otg_write_reg32(&ep->out_registers->doepint, depclr.d32);

	return 0;
}

/**
 * dwc_otg_device_receive_ep0
 */
void dwc_otg_device_receive_ep0(dwc_otg_device_t *_dev)
{
	ep0_out_request.data = _dev;
	ep0_out_request.length = 8; //ep0_out_request.buffer_length;

	if(ep0_out_request.dma_buffer == NULL)
	{
		ep0_out_request.dma_buffer = dma_alloc_coherent(NULL, ep0_out_request.buffer_length, &ep0_out_request.dma_address, GFP_KERNEL);
		if(!ep0_out_request.dma_buffer)
		{
			DWC_ERROR("Failed to allocate setup buffer for EP0!\n");
			return;
		}

		// As we've allocated a DMA buffer, we don't
		// need another. :D -- Ricky26
		ep0_out_request.buffer = ep0_out_request.dma_buffer;

		// Setup the in request, as we might need it as a response.
		ep0_in_request.length = ep0_in_request.buffer_length;
		ep0_in_request.dma_buffer = dma_alloc_coherent(NULL, ep0_in_request.buffer_length, &ep0_in_request.dma_address, GFP_KERNEL);
		if(!ep0_in_request.dma_buffer)
		{
			DWC_ERROR("Failed to allocate in buffer for EP0!\n");
			return;
		}

		ep0_in_request.buffer = ep0_in_request.dma_buffer;
	}

	DWC_VERBOSE("%s: enqueue (%p):%d\n", __func__, &ep0_out_request, ep0_out_request.length);
	dwc_otg_core_enqueue_request(_dev->core, &_dev->core->endpoints[0], &ep0_out_request);
}

/**
 * dwc_otg_device_send_ep0
 */
void dwc_otg_device_send_ep0(dwc_otg_device_t *_dev)
{
	ep0_in_request.data = _dev;
	dwc_otg_core_enqueue_request(_dev->core, &_dev->core->endpoints[0], &ep0_in_request);
}

/**
 * dwc_otg_device_complete_ep0
 *
 * This is called to complete an interrupt on EP0.
 */
void dwc_otg_device_complete_ep0(dwc_otg_core_request_t *_req)
{
	dwc_otg_core_t *core = _req->core;
	dwc_otg_device_t *dev = (dwc_otg_device_t*)_req->data;
	
	if(_req->cancelled)
	{
		DWC_VERBOSE("Shutting down EP0 control channel.\n");
		return;
	}

	if(_req->setup)
	{
		struct usb_ctrlrequest *packet = (struct usb_ctrlrequest*)_req->buffer;

#if defined(VERBOSE)&&defined(DEBUG)
		uint8_t *b = (uint8_t*)packet;
		
		DWC_PRINT("EP0 Sent us a setup packet! :3\n");
		DWC_PRINT("%02x %02x %02x %02x %02x %02x %02x %02x\n", 
				b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
		DWC_PRINT("RT %d, R %d, w %d\n", packet->bRequestType & 0x7F, packet->bRequest, packet->wIndex);
#endif

		if((packet->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
		{
			int set = 0;

			switch(packet->bRequest)
			{
				case USB_REQ_GET_STATUS:
					{
						uint16_t *result = (uint16_t*)ep0_in_request.buffer;

						switch (packet->bRequestType & USB_RECIP_MASK)
						{
						case USB_RECIP_DEVICE:
							*result = 0x1 | (1 << dev->remote_wakeup); // Self powered, remote wakeup enabled.
							break;

						case USB_RECIP_INTERFACE:
							*result = 0;
							break;

						case USB_RECIP_ENDPOINT:
							{
								// TODO: check this!
								int epnum = packet->wIndex & 0xf;
								if(epnum < core->num_eps &&
										list_empty(&core->endpoints[epnum].transfer_queue))
								{
									*result = 0;
									break;
								}

								*result = 1;
							}
							break;
						}
						
						ep0_in_request.length = 2;
						dwc_otg_device_send_ep0(dev);
					}
					goto exit;

				case USB_REQ_SET_FEATURE:
					set = 1;
					// fall through
				case USB_REQ_CLEAR_FEATURE:

					switch (packet->bRequestType & USB_RECIP_MASK)
					{
						case USB_RECIP_DEVICE:
							switch(packet->wValue)
							{
								case USB_DEVICE_REMOTE_WAKEUP:
									dev->remote_wakeup = set;
									break;
							}
							break;

						case USB_RECIP_ENDPOINT:
							{
								int ep = packet->wValue;
								if(ep == 0 || ep > core->num_eps)
									goto stall;

								dwc_otg_core_stall_ep(core, &core->endpoints[ep], set);
							}
							break;

					}
					goto send_zlp;

				case USB_REQ_SET_ADDRESS:
					{
						dcfg_data_t dcfg = { .d32 = 0 };

						dcfg.d32 = dwc_otg_read_reg32(&core->device_registers->dcfg);
						dcfg.b.devaddr = packet->wValue;
						dwc_otg_write_reg32(&core->device_registers->dcfg, dcfg.d32);

						DWC_VERBOSE("Received address %d.\n", dcfg.b.devaddr);
					}
					goto send_zlp;
			}
		}

		// If we got here we don't know how to handle
		// the setup packet, so let's ask the gadget driver to
		// do it, and then blame it if it doesn't work...

		DWC_VERBOSE("Passing setup packet to gadget driver.\n");
		if(dwc_otg_gadget_driver == NULL)
		{
			DWC_WARNING("No gadget driver yet, stalling.\n");
			goto stall;
		}

		if(dwc_otg_gadget_driver->setup(&dwc_otg_gadget, packet))
		{
			DWC_ERROR("Got a setup packet we don't handle properly yet.\n");
			goto stall;
		}

		goto exit;
	}
	else if(ep0_out_request.length == 0)
	{
		DWC_VERBOSE("Received a ZLP from host.\n");
	}
	else
	{
		DWC_ERROR("EP0 sent us some shit we don't know how to handle.\n");
	}

	goto exit;

stall:
	//dwc_otg_core_stall_ep(core, &core->endpoints[0], 1);
	{
		depctl_data_t depctl = { .d32 = dwc_otg_read_reg32(&core->in_ep_registers[0]->diepctl) };
		depctl.b.stall = 1;
		depctl.b.cnak = 1;
		dwc_otg_write_reg32(&core->in_ep_registers[0]->diepctl, depctl.d32);
	}
	goto exit;

send_zlp:
	ep0_in_request.length = 0;
	dwc_otg_device_send_ep0(dev);

exit:
	dwc_otg_device_receive_ep0(dev);
}
コード例 #15
0
ファイル: otg_cil_intr.c プロジェクト: 4pao/openwrt
void w_conn_id_status_change(struct work_struct *p)
{
	dwc_otg_core_if_t *core_if = container_of(p, dwc_otg_core_if_t, w_conn_id);

	uint32_t count = 0;
        gotgctl_data_t gotgctl = { .d32 = 0 };

        gotgctl.d32 = dwc_read_reg32(&core_if->core_global_regs->gotgctl);
	DWC_DEBUGPL(DBG_CIL, "gotgctl=%0x\n", gotgctl.d32);
	DWC_DEBUGPL(DBG_CIL, "gotgctl.b.conidsts=%d\n", gotgctl.b.conidsts);

        /* B-Device connector (Device Mode) */
        if (gotgctl.b.conidsts) {
		dwc_otg_pcd_t *pcd;

                /* Wait for switch to device mode. */
                while (!dwc_otg_is_device_mode(core_if)){
                        DWC_PRINT("Waiting for Peripheral Mode, Mode=%s\n",
                                  (dwc_otg_is_host_mode(core_if)?"Host":"Peripheral"));
                        MDELAY(100);
                        if (++count > 10000) *(uint32_t*)NULL=0;
                }
                core_if->op_state = B_PERIPHERAL;
		dwc_otg_core_init(core_if);
		dwc_otg_enable_global_interrupts(core_if);

		pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p;
		if(unlikely(!pcd)) {
			DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__);
			BUG();
		}
		SPIN_LOCK(&pcd->lock);

                pcd_start(core_if);

		SPIN_UNLOCK(&pcd->lock);
        } else {
                /* A-Device connector (Host Mode) */
                while (!dwc_otg_is_host_mode(core_if)) {
                        DWC_PRINT("Waiting for Host Mode, Mode=%s\n",
                                  (dwc_otg_is_host_mode(core_if)?"Host":"Peripheral"));
                        MDELAY(100);
                        if (++count > 10000) *(uint32_t*)NULL=0;
                }
                core_if->op_state = A_HOST;
                /*
                 * Initialize the Core for Host mode.
                 */
		dwc_otg_core_init(core_if);
		dwc_otg_enable_global_interrupts(core_if);
                hcd_start(core_if);
        }
}


/**
 * This function handles the Connector ID Status Change Interrupt.  It
 * reads the OTG Interrupt Register (GOTCTL) to determine whether this
 * is a Device to Host Mode transition or a Host Mode to Device
 * Transition.
 *
 * This only occurs when the cable is connected/removed from the PHY
 * connector.
 *
 * @param core_if Programming view of DWC_otg controller.
 */
int32_t dwc_otg_handle_conn_id_status_change_intr(dwc_otg_core_if_t *core_if)
{

	/*
	 * Need to disable SOF interrupt immediately. If switching from device
	 * to host, the PCD interrupt handler won't handle the interrupt if
	 * host mode is already set. The HCD interrupt handler won't get
	 * called if the HCD state is HALT. This means that the interrupt does
	 * not get handled and Linux complains loudly.
	 */
	gintmsk_data_t gintmsk = { .d32 = 0 };
	gintsts_data_t gintsts = { .d32 = 0 };

	gintmsk.b.sofintr = 1;
	dwc_modify_reg32(&core_if->core_global_regs->gintmsk, gintmsk.d32, 0);

	DWC_DEBUGPL(DBG_CIL, " ++Connector ID Status Change Interrupt++  (%s)\n",
                    (dwc_otg_is_host_mode(core_if)?"Host":"Device"));

	/*
	 * Need to schedule a work, as there are possible DELAY function calls
 	*/
	queue_work(core_if->wq_otg, &core_if->w_conn_id);

	/* Set flag and clear interrupt */
	gintsts.b.conidstschng = 1;
	dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32);

	return 1;
}

/**
 * This interrupt indicates that a device is initiating the Session
 * Request Protocol to request the host to turn on bus power so a new
 * session can begin. The handler responds by turning on bus power. If
 * the DWC_otg controller is in low power mode, the handler brings the
 * controller out of low power mode before turning on bus power.
 *
 * @param core_if Programming view of DWC_otg controller.
 */
int32_t dwc_otg_handle_session_req_intr(dwc_otg_core_if_t *core_if)
{
        hprt0_data_t hprt0;
	gintsts_data_t gintsts;

#ifndef DWC_HOST_ONLY
	DWC_DEBUGPL(DBG_ANY, "++Session Request Interrupt++\n");

        if (dwc_otg_is_device_mode(core_if)) {
                DWC_PRINT("SRP: Device mode\n");
        } else {
		DWC_PRINT("SRP: Host mode\n");

		/* Turn on the port power bit. */
		hprt0.d32 = dwc_otg_read_hprt0(core_if);
		hprt0.b.prtpwr = 1;
		dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32);

		/* Start the Connection timer. So a message can be displayed
		 * if connect does not occur within 10 seconds. */
		hcd_session_start(core_if);
        }
#endif

	/* Clear interrupt */
	gintsts.d32 = 0;
	gintsts.b.sessreqintr = 1;
	dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32);

	return 1;
}


void w_wakeup_detected(struct work_struct *p)
{
	struct delayed_work *dw = container_of(p, struct delayed_work, work);
	dwc_otg_core_if_t *core_if = container_of(dw, dwc_otg_core_if_t, w_wkp);

        /*
	 * Clear the Resume after 70ms. (Need 20 ms minimum. Use 70 ms
	 * so that OPT tests pass with all PHYs).
	 */
        hprt0_data_t hprt0 = {.d32=0};
        hprt0.d32 = dwc_otg_read_hprt0(core_if);
        DWC_DEBUGPL(DBG_ANY,"Resume: HPRT0=%0x\n", hprt0.d32);
//      MDELAY(70);
        hprt0.b.prtres = 0; /* Resume */
        dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32);
        DWC_DEBUGPL(DBG_ANY,"Clear Resume: HPRT0=%0x\n", dwc_read_reg32(core_if->host_if->hprt0));
}
/**
 * This interrupt indicates that the DWC_otg controller has detected a
 * resume or remote wakeup sequence. If the DWC_otg controller is in
 * low power mode, the handler must brings the controller out of low
 * power mode. The controller automatically begins resume
 * signaling. The handler schedules a time to stop resume signaling.
 */
int32_t dwc_otg_handle_wakeup_detected_intr(dwc_otg_core_if_t *core_if)
{
	gintsts_data_t gintsts;

	DWC_DEBUGPL(DBG_ANY, "++Resume and Remote Wakeup Detected Interrupt++\n");

        if (dwc_otg_is_device_mode(core_if)) {
                dctl_data_t dctl = {.d32=0};
                DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n",
                            dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts));
#ifdef PARTIAL_POWER_DOWN
                if (core_if->hwcfg4.b.power_optimiz) {
                        pcgcctl_data_t power = {.d32=0};

                        power.d32 = dwc_read_reg32(core_if->pcgcctl);
                        DWC_DEBUGPL(DBG_CIL, "PCGCCTL=%0x\n", power.d32);

                        power.b.stoppclk = 0;
                        dwc_write_reg32(core_if->pcgcctl, power.d32);

                        power.b.pwrclmp = 0;
                        dwc_write_reg32(core_if->pcgcctl, power.d32);

                        power.b.rstpdwnmodule = 0;
                        dwc_write_reg32(core_if->pcgcctl, power.d32);
                }
#endif
                /* Clear the Remote Wakeup Signalling */
                dctl.b.rmtwkupsig = 1;
                dwc_modify_reg32(&core_if->dev_if->dev_global_regs->dctl,
                                  dctl.d32, 0);

                if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) {
                        core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
                }

        } else {
		pcgcctl_data_t pcgcctl = {.d32=0};

		/* Restart the Phy Clock */
	        pcgcctl.b.stoppclk = 1;
	        dwc_modify_reg32(core_if->pcgcctl, pcgcctl.d32, 0);

	        queue_delayed_work(core_if->wq_otg, &core_if->w_wkp, ((70 * HZ / 1000) + 1));
        }

	/* Clear interrupt */
	gintsts.d32 = 0;
	gintsts.b.wkupintr = 1;
	dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32);

	return 1;
}

/**
 * This interrupt indicates that a device has been disconnected from
 * the root port.
 */
int32_t dwc_otg_handle_disconnect_intr(dwc_otg_core_if_t *core_if)
{
	gintsts_data_t gintsts;

	DWC_DEBUGPL(DBG_ANY, "++Disconnect Detected Interrupt++ (%s) %s\n",
                    (dwc_otg_is_host_mode(core_if)?"Host":"Device"),
                    op_state_str(core_if));

/** @todo Consolidate this if statement. */
#ifndef DWC_HOST_ONLY
        if (core_if->op_state == B_HOST) {
		dwc_otg_pcd_t *pcd;

                /* If in device mode Disconnect and stop the HCD, then
                 * start the PCD. */
                hcd_disconnect(core_if);

		pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p;
		if(unlikely(!pcd)) {
			DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__);
			BUG();
		}
		SPIN_LOCK(&pcd->lock);

                pcd_start(core_if);

		SPIN_UNLOCK(&pcd->lock);
                core_if->op_state = B_PERIPHERAL;
        } else if (dwc_otg_is_device_mode(core_if)) {
                gotgctl_data_t gotgctl = { .d32 = 0 };
                gotgctl.d32 = dwc_read_reg32(&core_if->core_global_regs->gotgctl);
                if (gotgctl.b.hstsethnpen==1) {
                        /* Do nothing, if HNP in process the OTG
                         * interrupt "Host Negotiation Detected"
                         * interrupt will do the mode switch.
                         */
                } else if (gotgctl.b.devhnpen == 0) {
			dwc_otg_pcd_t *pcd;

                        /* If in device mode Disconnect and stop the HCD, then
                         * start the PCD. */
                        hcd_disconnect(core_if);

			pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p;
			if(unlikely(!pcd)) {
				DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__);
				BUG();
			}
			SPIN_LOCK(&pcd->lock);

                        pcd_start(core_if);

			SPIN_UNLOCK(&pcd->lock);

                        core_if->op_state = B_PERIPHERAL;
                } else {
                        DWC_DEBUGPL(DBG_ANY,"!a_peripheral && !devhnpen\n");
                }
        } else {
                if (core_if->op_state == A_HOST) {
                        /* A-Cable still connected but device disconnected. */
                        hcd_disconnect(core_if);
                }
        }
#endif

	gintsts.d32 = 0;
	gintsts.b.disconnect = 1;
	dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32);
	return 1;
}
/**
 * This interrupt indicates that SUSPEND state has been detected on
 * the USB.
 *
 * For HNP the USB Suspend interrupt signals the change from
 * "a_peripheral" to "a_host".
 *
 * When power management is enabled the core will be put in low power
 * mode.
 */
int32_t dwc_otg_handle_usb_suspend_intr(dwc_otg_core_if_t *core_if)
{
        dsts_data_t dsts;
        gintsts_data_t gintsts;

        DWC_DEBUGPL(DBG_ANY,"USB SUSPEND\n");

        if (dwc_otg_is_device_mode(core_if)) {
		dwc_otg_pcd_t *pcd;

                /* Check the Device status register to determine if the Suspend
                 * state is active. */
                dsts.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts);
                DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n", dsts.d32);
                DWC_DEBUGPL(DBG_PCD, "DSTS.Suspend Status=%d "
                            "HWCFG4.power Optimize=%d\n",
                            dsts.b.suspsts, core_if->hwcfg4.b.power_optimiz);


#ifdef PARTIAL_POWER_DOWN
/** @todo Add a module parameter for power management. */
                if (dsts.b.suspsts && core_if->hwcfg4.b.power_optimiz) {
                        pcgcctl_data_t power = {.d32=0};
                        DWC_DEBUGPL(DBG_CIL, "suspend\n");

                        power.b.pwrclmp = 1;
                        dwc_write_reg32(core_if->pcgcctl, power.d32);

                        power.b.rstpdwnmodule = 1;
                        dwc_modify_reg32(core_if->pcgcctl, 0, power.d32);

                        power.b.stoppclk = 1;
                        dwc_modify_reg32(core_if->pcgcctl, 0, power.d32);
                } else {
                        DWC_DEBUGPL(DBG_ANY,"disconnect?\n");
                }
#endif
                /* PCD callback for suspend. */
		pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p;
		if(unlikely(!pcd)) {
			DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__);
			BUG();
		}
		SPIN_LOCK(&pcd->lock);

                pcd_suspend(core_if);

		SPIN_UNLOCK(&pcd->lock);
        } else {
コード例 #16
0
/**
 * This function initialized the PCD portion of the driver.
 *
 */
int pcd_init(
	struct platform_device *_dev
	)
{
	dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev);
	struct sprd_usb_platform_data *pdata= _dev->dev.platform_data;
	int retval = 0;
	int irq;
	int plug_irq;

	DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _dev);

	device_dwc_otg = &(_dev->dev);
	if (dwc_otg_is_dma_enable(otg_dev->core_if)) {
		_dev->dev.dma_mask = &dwc_otg_pcd_dmamask;
		_dev->dev.coherent_dma_mask = dwc_otg_pcd_dmamask;
	} else {
		_dev->dev.dma_mask = (void *)0;
		_dev->dev.coherent_dma_mask = 0;
	}

	wake_lock_init(&usb_wake_lock, WAKE_LOCK_SUSPEND, "usb_work");
//	wake_lock(&usb_wake_lock);
	otg_dev->pcd = dwc_otg_pcd_init(otg_dev->core_if);

	if (!otg_dev->pcd) {
		DWC_ERROR("dwc_otg_pcd_init failed\n");
		return -ENOMEM;
	}

	gadget_wrapper = alloc_wrapper(_dev);

	/*
	 * Initialize EP structures
	 */
	gadget_add_eps(gadget_wrapper);

	/*
	 * Setup interupt handler
	 */
	irq = platform_get_irq(_dev, 0);
	DWC_DEBUGPL(DBG_ANY, "registering handler for irq%d\n", irq);
	retval = request_irq(irq, dwc_otg_pcd_irq,
			IRQF_SHARED, gadget_wrapper->gadget.name,
			otg_dev->pcd);
	//SA_SHIRQ, gadget_wrapper->gadget.name,
	if (retval != 0) {
		DWC_ERROR("request of irq%d failed\n", irq);
		free_wrapper(gadget_wrapper);
		return -EBUSY;
	}
	/*
	 * initialize a timer for checking cable type.
	 */
#ifdef USB_SETUP_TIMEOUT_RESTART
	{
		setup_timer(&setup_transfer_timer,
			setup_transfer_timer_fun,
			(unsigned long)gadget_wrapper);
		setup_transfer_timer_start = 0;
	}
#endif
	INIT_DELAYED_WORK(&gadget_wrapper->cable2pc, cable2pc_detect_works);
	gadget_wrapper->cable2pc_wq = create_singlethread_workqueue("usb 2 pc wq");

#ifdef CONFIG_USB_EXTERNAL_DETECT
	register_otg_func(NULL, dwc_peripheral_start, otg_dev);
#else

	/*
	 * setup usb cable detect interupt
	 */
#ifndef CONFIG_MFD_SM5504
	{
		plug_irq = usb_alloc_vbus_irq(pdata->gpio_chgdet);
		if (plug_irq < 0) {
			pr_warning("cannot alloc vbus irq\n");
			return -EBUSY;
		}
		usb_set_vbus_irq_type(plug_irq, VBUS_PLUG_IN);
		#ifdef CONFIG_SC_FPGA
		gadget_wrapper->vbus = 1;
		#else
		gadget_wrapper->vbus = usb_get_vbus_state();
		#endif
		pr_info("now usb vbus is :%d\n", gadget_wrapper->vbus);
		retval = request_irq(plug_irq, usb_detect_handler, IRQF_SHARED | IRQF_NO_SUSPEND,
				"usb detect", otg_dev->pcd);
#ifndef CONFIG_MUIC_CABLE_DETECT
		disable_irq(plug_irq);
#endif
	}
	//gadget_wrapper->vbus = 1;//used when debug in FPGA, which doesn't have vbus operation
#endif
	spin_lock_init(&gadget_wrapper->lock);
#ifdef CONFIG_SC_FPGA
	gadget_wrapper->vbus = 1;
#endif
	INIT_WORK(&gadget_wrapper->detect_work, usb_detect_works);
	gadget_wrapper->detect_wq = create_singlethread_workqueue("usb detect wq");
#endif
	/*
	 * register a switch device for sending pnp message,
	 * for the user app need be notified immediately
	 * when plug in & plug out happen;
	 */
	gadget_wrapper->sdev.name = "charger_cable";
	retval = switch_dev_register(&gadget_wrapper->sdev);
	if (retval){
		pr_warning("register switch dev error:%s\n", __func__);
	}

	dwc_otg_pcd_start(gadget_wrapper->pcd, &fops);

	/*
	 * dwc driver is ok, check if the cable is insert, if no,
	 * shutdown udc for saving power.
	 */
	if (!gadget_wrapper->vbus){
		pr_debug("vbus is not power now \n");
		gadget_wrapper->udc_startup = 1;
		__udc_shutdown();
	}
	gadget_wrapper->udc_startup = gadget_wrapper->vbus;
	gadget_wrapper->enabled = 0;

	retval = usb_add_gadget_udc(&_dev->dev, &gadget_wrapper->gadget);
	if (!retval)
		return retval;

	return retval;
}