Exemplo n.º 1
0
/**
 * Create the device files
 */
void dwc_otg_attr_create (struct device *_dev)
{
    int retval;
    
    retval = device_create_file(_dev, &dev_attr_regoffset);
    retval += device_create_file(_dev, &dev_attr_regvalue);
    retval += device_create_file(_dev, &dev_attr_mode);
    retval += device_create_file(_dev, &dev_attr_hnpcapable);
    retval += device_create_file(_dev, &dev_attr_srpcapable);
    retval += device_create_file(_dev, &dev_attr_hnp);
    retval += device_create_file(_dev, &dev_attr_srp);
    retval += device_create_file(_dev, &dev_attr_buspower);
    retval += device_create_file(_dev, &dev_attr_bussuspend);
    retval += device_create_file(_dev, &dev_attr_busconnected);
    retval += device_create_file(_dev, &dev_attr_gotgctl);
    retval += device_create_file(_dev, &dev_attr_gusbcfg);
    retval += device_create_file(_dev, &dev_attr_grxfsiz);
    retval += device_create_file(_dev, &dev_attr_gnptxfsiz);
    retval += device_create_file(_dev, &dev_attr_gpvndctl);
    retval += device_create_file(_dev, &dev_attr_ggpio);
    retval += device_create_file(_dev, &dev_attr_guid);
    retval += device_create_file(_dev, &dev_attr_gsnpsid);
    retval += device_create_file(_dev, &dev_attr_devspeed);
    retval += device_create_file(_dev, &dev_attr_enumspeed);
    retval += device_create_file(_dev, &dev_attr_hptxfsiz);
    retval += device_create_file(_dev, &dev_attr_hprt0);
    retval += device_create_file(_dev, &dev_attr_remote_wakeup);
    retval += device_create_file(_dev, &dev_attr_regdump);
    retval += device_create_file(_dev, &dev_attr_hcddump);
    retval += device_create_file(_dev, &dev_attr_hcd_frrem);
    retval += device_create_file(_dev, &dev_attr_rd_reg_test);
    retval += device_create_file(_dev, &dev_attr_wr_reg_test);

    if(retval != 0)
    {
        DWC_PRINT("cannot create sysfs device files.\n");
        // DWC_PRINT("killing own sysfs device files!\n");
        dwc_otg_attr_remove(_dev);
    }
}
Exemplo n.º 2
0
/**
 * 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) 
{
	uint32_t count = 0;
	gintsts_data_t gintsts = {.d32 = 0};
	gintmsk_data_t gintmsk = {.d32 = 0};
	gotgctl_data_t gotgctl = {.d32 = 0};
	
	/*
	 * 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.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"));
	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) {
		
		/* 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_start(_core_if);
	} 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);
	}
	
	/* 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) 
{
	gintsts_data_t gintsts;
	
#ifndef DWC_HOST_ONLY
	hprt0_data_t hprt0;
	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;
}

/** 
 * 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 {
		
		/*
		 * 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};
		pcgcctl_data_t pcgcctl = {.d32 = 0};
		
		/* Restart the Phy Clock */ 
		pcgcctl.b.stoppclk = 1;
		dwc_modify_reg32(_core_if->pcgcctl, pcgcctl.d32, 0);
		UDELAY(10);
		
		/* Now wait for 70 ms. */ 
		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));
	}
	
	/* 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;
	printk(KERN_ERR "  Disconnect Detected Interrupt++ (%s) %s\n", 
		 (dwc_otg_is_host_mode(_core_if) ? "Host" : "Device"),
		 op_state_str(_core_if));
	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) {
		
		/* If in device mode Disconnect and stop the HCD, then
		 * start the PCD. */ 
		hcd_disconnect(_core_if);
		pcd_start(_core_if);
		_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) {
			/* If in device mode Disconnect and stop the HCD, then
			 * start the PCD. */ 
			hcd_disconnect(_core_if);
			pcd_start(_core_if);
			_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)) {
		
		/* 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_suspend(_core_if);
	} else {
		if (_core_if->op_state == A_PERIPHERAL) {
Exemplo n.º 3
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);
}