blob: d093e67df75ce863a1cd4fe243de9adb037cec5c [file] [log] [blame]
/** @file
Copyright (c) 2015, Linaro Limited. All rights reserved.
Copyright (c) 2015, Hisilicon Limited. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include <IndustryStandard/Usb.h>
#include <Library/TimerLib.h>
#include <Library/DebugLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiDriverEntryPoint.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/IoLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UncachedMemoryAllocationLib.h>
#include <Library/CacheMaintenanceLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/BaseLib.h>
#include <Protocol/UsbDevice.h>
#include <Guid/ArmGlobalVariableHob.h>
#include "DwUsbDxe.h"
STATIC dwc_otg_dev_dma_desc_t *g_dma_desc,*g_dma_desc_ep0,*g_dma_desc_in;
STATIC USB_DEVICE_REQUEST *p_ctrlreq;
STATIC VOID *rx_buf;
STATIC UINT32 rx_desc_bytes = 0;
STATIC UINTN mNumDataBytes;
#define USB_BLOCK_HIGH_SPEED_SIZE 512
#define DATA_SIZE 32768
#define CMD_SIZE 512
#define MATCH_CMD_LITERAL(Cmd, Buf) !AsciiStrnCmp (Cmd, Buf, sizeof (Cmd) - 1)
STATIC USB_DEVICE_DESCRIPTOR *mDeviceDescriptor;
// The config descriptor, interface descriptor, and endpoint descriptors in a
// buffer (in that order)
STATIC VOID *mDescriptors;
// Convenience pointers to those descriptors inside the buffer:
STATIC USB_INTERFACE_DESCRIPTOR *mInterfaceDescriptor;
STATIC USB_CONFIG_DESCRIPTOR *mConfigDescriptor;
STATIC USB_ENDPOINT_DESCRIPTOR *mEndpointDescriptors;
STATIC USB_DEVICE_RX_CALLBACK mDataReceivedCallback;
STATIC USB_DEVICE_TX_CALLBACK mDataSentCallback;
STATIC EFI_USB_STRING_DESCRIPTOR mLangStringDescriptor = {
4,
USB_DESC_TYPE_STRING,
{0x409}
};
STATIC EFI_USB_STRING_DESCRIPTOR mManufacturerStringDescriptor = {
18,
USB_DESC_TYPE_STRING,
{'9', '6', 'B', 'o', 'a', 'r', 'd', 's'}
};
STATIC EFI_USB_STRING_DESCRIPTOR mProductStringDescriptor = {
12,
USB_DESC_TYPE_STRING,
{'H', 'i', 'K', 'e', 'y'}
};
STATIC EFI_USB_STRING_DESCRIPTOR mSerialStringDescriptor = {
34,
USB_DESC_TYPE_STRING,
{'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}
};
// The time between interrupt polls, in units of 100 nanoseconds
// 10 Microseconds
#define DW_INTERRUPT_POLL_PERIOD 10000
STATIC int usb_drv_port_speed(void) /*To detect which mode was run, high speed or full speed*/
{
/*
* 2'b00: High speed (PHY clock is running at 30 or 60 MHz)
*/
UINT32 val = READ_REG32(DSTS) & 2;
return (!val);
}
STATIC VOID reset_endpoints(void)
{
/* EP0 IN ACTIVE NEXT=1 */
WRITE_REG32(DIEPCTL0, 0x8800);
/* EP0 OUT ACTIVE */
WRITE_REG32(DOEPCTL0, 0x8000);
/* Clear any pending OTG Interrupts */
WRITE_REG32(GOTGINT, 0xFFFFFFFF);
/* Clear any pending interrupts */
WRITE_REG32(GINTSTS, 0xFFFFFFFF);
WRITE_REG32(DIEPINT0, 0xFFFFFFFF);
WRITE_REG32(DOEPINT0, 0xFFFFFFFF);
WRITE_REG32(DIEPINT1, 0xFFFFFFFF);
WRITE_REG32(DOEPINT1, 0xFFFFFFFF);
/* IN EP interrupt mask */
WRITE_REG32(DIEPMSK, 0x0D);
/* OUT EP interrupt mask */
WRITE_REG32(DOEPMSK, 0x0D);
/* Enable interrupts on Ep0 */
WRITE_REG32(DAINTMSK, 0x00010001);
/* EP0 OUT Transfer Size:64 Bytes, 1 Packet, 3 Setup Packet, Read to receive setup packet*/
WRITE_REG32(DOEPTSIZ0, 0x60080040);
//notes that:the compulsive conversion is expectable.
g_dma_desc_ep0->status.b.bs = 0x3;
g_dma_desc_ep0->status.b.mtrf = 0;
g_dma_desc_ep0->status.b.sr = 0;
g_dma_desc_ep0->status.b.l = 1;
g_dma_desc_ep0->status.b.ioc = 1;
g_dma_desc_ep0->status.b.sp = 0;
g_dma_desc_ep0->status.b.bytes = 64;
g_dma_desc_ep0->buf = (UINT32)(UINTN)(p_ctrlreq);
g_dma_desc_ep0->status.b.sts = 0;
g_dma_desc_ep0->status.b.bs = 0x0;
WRITE_REG32(DOEPDMA0, (unsigned long)(g_dma_desc_ep0));
/* EP0 OUT ENABLE CLEARNAK */
WRITE_REG32(DOEPCTL0, (READ_REG32(DOEPCTL0) | 0x84000000));
}
STATIC VOID ep_tx(IN UINT8 ep, CONST VOID *ptr, UINT32 len)
{
UINT32 blocksize;
UINT32 packets;
/* EPx OUT ACTIVE */
WRITE_REG32(DIEPCTL(ep), (READ_REG32(DIEPCTL(ep))) | 0x8000);
if(!ep) {
blocksize = 64;
} else {
blocksize = usb_drv_port_speed() ? USB_BLOCK_HIGH_SPEED_SIZE : 64;
}
packets = (len + blocksize - 1) / blocksize;
if (!len) { //send a null packet
/* one empty packet */
g_dma_desc_in->status.b.bs = 0x3;
g_dma_desc_in->status.b.l = 1;
g_dma_desc_in->status.b.ioc = 1;
g_dma_desc_in->status.b.sp = 1;
g_dma_desc_in->status.b.bytes = 0;
g_dma_desc_in->buf = 0;
g_dma_desc_in->status.b.sts = 0;
g_dma_desc_in->status.b.bs = 0x0;
WRITE_REG32(DIEPDMA(ep), (unsigned long)(g_dma_desc_in)); // DMA Address (DMAAddr) is zero
} else { //prepare to send a packet
/*WRITE_REG32((len | (packets << 19)), DIEPTSIZ(ep));*/ // packets+transfer size
WRITE_REG32(DIEPTSIZ(ep), len | (packets << 19));
//flush cache
WriteBackDataCacheRange ((void*)ptr, len);
g_dma_desc_in->status.b.bs = 0x3;
g_dma_desc_in->status.b.l = 1;
g_dma_desc_in->status.b.ioc = 1;
g_dma_desc_in->status.b.sp = 1;
g_dma_desc_in->status.b.bytes = len;
g_dma_desc_in->buf = (UINT32)((UINTN)ptr);
g_dma_desc_in->status.b.sts = 0;
g_dma_desc_in->status.b.bs = 0x0;
WRITE_REG32(DIEPDMA(ep), (unsigned long)(g_dma_desc_in)); // ptr is DMA address
}
asm("dsb sy");
asm("isb sy");
/* epena & cnak*/
WRITE_REG32(DIEPCTL(ep), READ_REG32(DIEPCTL(ep)) | 0x84000800);
return;
}
STATIC VOID ep_rx(unsigned ep, UINT32 len)
{
/* EPx UNSTALL */
WRITE_REG32(DOEPCTL(ep), ((READ_REG32(DOEPCTL(ep))) & (~0x00200000)));
/* EPx OUT ACTIVE */
WRITE_REG32(DOEPCTL(ep), (READ_REG32(DOEPCTL(ep)) | 0x8000));
if (len >= DATA_SIZE)
rx_desc_bytes = DATA_SIZE;
else
rx_desc_bytes = len;
rx_buf = AllocatePool (DATA_SIZE);
ASSERT (rx_buf != NULL);
InvalidateDataCacheRange (rx_buf, len);
g_dma_desc->status.b.bs = 0x3;
g_dma_desc->status.b.mtrf = 0;
g_dma_desc->status.b.sr = 0;
g_dma_desc->status.b.l = 1;
g_dma_desc->status.b.ioc = 1;
g_dma_desc->status.b.sp = 0;
g_dma_desc->status.b.bytes = rx_desc_bytes;
g_dma_desc->buf = (UINT32)((UINTN)rx_buf);
g_dma_desc->status.b.sts = 0;
g_dma_desc->status.b.bs = 0x0;
asm("dsb sy");
asm("isb sy");
WRITE_REG32(DOEPDMA(ep), (UINT32)((UINTN)g_dma_desc));
/* EPx OUT ENABLE CLEARNAK */
WRITE_REG32(DOEPCTL(ep), (READ_REG32(DOEPCTL(ep)) | 0x84000000));
}
STATIC
EFI_STATUS
HandleGetDescriptor (
IN USB_DEVICE_REQUEST *Request
)
{
UINT8 DescriptorType;
UINTN ResponseSize;
VOID *ResponseData;
CHAR16 SerialNo[16];
UINTN SerialNoLen;
EFI_STATUS Status;
ResponseSize = 0;
ResponseData = NULL;
// Pretty confused if bmRequestType is anything but this:
ASSERT (Request->RequestType == USB_DEV_GET_DESCRIPTOR_REQ_TYPE);
// Choose the response
DescriptorType = Request->Value >> 8;
switch (DescriptorType) {
case USB_DESC_TYPE_DEVICE:
DEBUG ((EFI_D_INFO, "USB: Got a request for device descriptor\n"));
ResponseSize = sizeof (USB_DEVICE_DESCRIPTOR);
ResponseData = mDeviceDescriptor;
break;
case USB_DESC_TYPE_CONFIG:
DEBUG ((EFI_D_INFO, "USB: Got a request for config descriptor\n"));
ResponseSize = mConfigDescriptor->TotalLength;
ResponseData = mDescriptors;
break;
case USB_DESC_TYPE_STRING:
DEBUG ((EFI_D_INFO, "USB: Got a request for String descriptor %d\n", Request->Value & 0xFF));
switch (Request->Value & 0xff) {
case 0:
ResponseSize = mLangStringDescriptor.Length;
ResponseData = &mLangStringDescriptor;
break;
case 1:
ResponseSize = mManufacturerStringDescriptor.Length;
ResponseData = &mManufacturerStringDescriptor;
break;
case 2:
ResponseSize = mProductStringDescriptor.Length;
ResponseData = &mProductStringDescriptor;
break;
case 3:
Status = gRT->GetVariable (
(CHAR16*)L"SerialNo",
&gArmGlobalVariableGuid,
NULL,
&SerialNoLen,
SerialNo
);
if (EFI_ERROR (Status) == 0) {
CopyMem (mSerialStringDescriptor.String, SerialNo, SerialNoLen);
}
ResponseSize = mSerialStringDescriptor.Length;
ResponseData = &mSerialStringDescriptor;
break;
}
break;
default:
DEBUG ((EFI_D_INFO, "USB: Didn't understand request for descriptor 0x%04x\n", Request->Value));
break;
}
// Send the response
if (ResponseData) {
ASSERT (ResponseSize != 0);
if (Request->Length < ResponseSize) {
// Truncate response
ResponseSize = Request->Length;
} else if (Request->Length > ResponseSize) {
DEBUG ((EFI_D_INFO, "USB: Info: ResponseSize < wLength\n"));
}
ep_tx(0, ResponseData, ResponseSize);
}
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
HandleSetAddress (
IN USB_DEVICE_REQUEST *Request
)
{
// Pretty confused if bmRequestType is anything but this:
ASSERT (Request->RequestType == USB_DEV_SET_ADDRESS_REQ_TYPE);
DEBUG ((EFI_D_INFO, "USB: Setting address to %d\n", Request->Value));
reset_endpoints();
WRITE_REG32(DCFG, (READ_REG32(DCFG) & ~0x7F0) | (Request->Value << 4));
ep_tx(0, 0, 0);
return EFI_SUCCESS;
}
int usb_drv_request_endpoint(unsigned int type, int dir)
{
unsigned int ep = 1; /*FIXME*/
int ret;
unsigned long newbits;
ret = (int)ep | dir;
newbits = (type << 18) | 0x10000000;
/*
* (type << 18):Endpoint Type (EPType)
* 0x10000000:Endpoint Enable (EPEna)
* 0x000C000:Endpoint Type (EPType);Hardcoded to 00 for control.
* (ep<<22):TxFIFO Number (TxFNum)
* 0x20000:NAK Status (NAKSts);The core is transmitting NAK handshakes on this endpoint.
*/
if (dir) { // IN: to host
WRITE_REG32(DIEPCTL(ep), ((READ_REG32(DIEPCTL(ep)))& ~0x000C0000) | newbits | (ep<<22)|0x20000);
} else { // OUT: to device
WRITE_REG32(DOEPCTL(ep), ((READ_REG32(DOEPCTL(ep))) & ~0x000C0000) | newbits);
}
return ret;
}
STATIC
EFI_STATUS
HandleSetConfiguration (
IN USB_DEVICE_REQUEST *Request
)
{
ASSERT (Request->RequestType == USB_DEV_SET_CONFIGURATION_REQ_TYPE);
// Cancel all transfers
reset_endpoints();
usb_drv_request_endpoint(2, 0);
usb_drv_request_endpoint(2, 0x80);
WRITE_REG32(DIEPCTL1, (READ_REG32(DIEPCTL1)) | 0x10088800);
/* Enable interrupts on all endpoints */
WRITE_REG32(DAINTMSK, 0xFFFFFFFF);
ep_rx(1, CMD_SIZE);
ep_tx(0, 0, 0);
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
HandleDeviceRequest (
IN USB_DEVICE_REQUEST *Request
)
{
EFI_STATUS Status;
switch (Request->Request) {
case USB_DEV_GET_DESCRIPTOR:
Status = HandleGetDescriptor (Request);
break;
case USB_DEV_SET_ADDRESS:
Status = HandleSetAddress (Request);
break;
case USB_DEV_SET_CONFIGURATION:
Status = HandleSetConfiguration (Request);
break;
default:
DEBUG ((EFI_D_ERROR,
"Didn't understand RequestType 0x%x Request 0x%x\n",
Request->RequestType, Request->Request));
Status = EFI_INVALID_PARAMETER;
break;
}
return Status;
}
// Instead of actually registering interrupt handlers, we poll the controller's
// interrupt source register in this function.
STATIC
VOID
CheckInterrupts (
IN EFI_EVENT Event,
IN VOID *Context
)
{
UINT32 ints = READ_REG32(GINTSTS); // interrupt register
UINT32 epints;
/*
* bus reset
* The core sets this bit to indicate that a reset is detected on the USB.
*/
if (ints & 0x1000) {
WRITE_REG32(DCFG, 0x800004);
reset_endpoints();
}
/*
* enumeration done, we now know the speed
* The core sets this bit to indicate that speed enumeration is complete. The
* application must read the Device Status (DSTS) register to obtain the
* enumerated speed.
*/
if (ints & 0x2000) {
/* Set up the maximum packet sizes accordingly */
unsigned long maxpacket = usb_drv_port_speed() ? USB_BLOCK_HIGH_SPEED_SIZE : 64;
//Set Maximum In Packet Size (MPS)
WRITE_REG32(DIEPCTL1, ((READ_REG32(DIEPCTL1)) & ~0x000003FF) | maxpacket);
//Set Maximum Out Packet Size (MPS)
WRITE_REG32(DOEPCTL1, ((READ_REG32(DOEPCTL1)) & ~0x000003FF) | maxpacket);
}
/*
* IN EP event
* The core sets this bit to indicate that an interrupt is pending on one of the IN
* endpoints of the core (in Device mode). The application must read the
* Device All Endpoints Interrupt (DAINT) register to determine the exact
* number of the IN endpoint on which the interrupt occurred, and then read
* the corresponding Device IN Endpoint-n Interrupt (DIEPINTn) register to
* determine the exact cause of the interrupt. The application must clear the
* appropriate status bit in the corresponding DIEPINTn register to clear this bit.
*/
if (ints & 0x40000) {
epints = READ_REG32(DIEPINT0);
WRITE_REG32(DIEPINT0, epints);
if (epints & 0x1) /* Transfer Completed Interrupt (XferCompl) */
DEBUG ((EFI_D_INFO, "INT: IN TX completed.DIEPTSIZ(0) = 0x%x.\n", READ_REG32(DIEPTSIZ0)));
epints = READ_REG32(DIEPINT1);
WRITE_REG32(DIEPINT1, epints);
if (epints & 0x1)
DEBUG ((EFI_D_INFO, "ep1: IN TX completed\n"));
}
/*
* OUT EP event
* The core sets this bit to indicate that an interrupt is pending on one of the
* OUT endpoints of the core (in Device mode). The application must read the
* Device All Endpoints Interrupt (DAINT) register to determine the exact
* number of the OUT endpoint on which the interrupt occurred, and then read
* the corresponding Device OUT Endpoint-n Interrupt (DOEPINTn) register
* to determine the exact cause of the interrupt. The application must clear the
* appropriate status bit in the corresponding DOEPINTn register to clear this bit.
*/
if (ints & 0x80000) {
/* indicates the status of an endpoint
* with respect to USB- and AHB-related events. */
epints = READ_REG32(DOEPINT0);
if(epints) {
WRITE_REG32(DOEPINT0, epints);
if (epints & 0x1)
DEBUG ((EFI_D_INFO,"INT: EP0 RX completed. DOEPTSIZ(0) = 0x%x.\n", READ_REG32(DOEPTSIZ0)));
/*
*
IN Token Received When TxFIFO is Empty (INTknTXFEmp)
* Indicates that an IN token was received when the associated TxFIFO (periodic/nonperiodic)
* was empty. This interrupt is asserted on the endpoint for which the IN token
* was received.
*/
if (epints & 0x8) { /* SETUP phase done */
// PRINT_DEBUG("Setup phase \n");
WRITE_REG32(DIEPCTL0, READ_REG32(DIEPCTL0) | 0x08000000);
WRITE_REG32(DOEPCTL0, READ_REG32(DOEPCTL0) | 0x08000000);
/*clear IN EP intr*/
WRITE_REG32(DIEPINT0, 0xffffffff);
HandleDeviceRequest((USB_DEVICE_REQUEST *)p_ctrlreq);
}
/* Make sure EP0 OUT is set up to accept the next request */
/* memset(p_ctrlreq, 0, NUM_ENDPOINTS*8); */
WRITE_REG32(DOEPTSIZ0, 0x60080040);
/*
* IN Token Received When TxFIFO is Empty (INTknTXFEmp)
* Indicates that an IN token was received when the associated TxFIFO (periodic/nonperiodic)
* was empty. This interrupt is asserted on the endpoint for which the IN token
* was received.
*/
g_dma_desc_ep0->status.b.bs = 0x3;
g_dma_desc_ep0->status.b.mtrf = 0;
g_dma_desc_ep0->status.b.sr = 0;
g_dma_desc_ep0->status.b.l = 1;
g_dma_desc_ep0->status.b.ioc = 1;
g_dma_desc_ep0->status.b.sp = 0;
g_dma_desc_ep0->status.b.bytes = 64;
g_dma_desc_ep0->buf = (UINT32)(UINTN)(p_ctrlreq);
g_dma_desc_ep0->status.b.sts = 0;
g_dma_desc_ep0->status.b.bs = 0x0;
WRITE_REG32(DOEPDMA0, (unsigned long)(g_dma_desc_ep0));
// endpoint enable; clear NAK
WRITE_REG32(DOEPCTL0, 0x84000000);
}
epints = (READ_REG32(DOEPINT1));
if(epints) {
WRITE_REG32(DOEPINT1, epints);
/* Transfer Completed Interrupt (XferCompl);Transfer completed */
if (epints & 0x1) {
asm("dsb sy");
asm("isb sy");
UINT32 bytes = rx_desc_bytes - g_dma_desc->status.b.bytes;
UINT32 len = 0;
if (MATCH_CMD_LITERAL ("download", rx_buf)) {
mNumDataBytes = AsciiStrHexToUint64 (rx_buf + sizeof ("download"));
} else {
if (mNumDataBytes != 0)
mNumDataBytes -= bytes;
}
mDataReceivedCallback (bytes, rx_buf);
if (mNumDataBytes == 0)
len = CMD_SIZE;
else if (mNumDataBytes > DATA_SIZE)
len = DATA_SIZE;
else
len = mNumDataBytes;
ep_rx(1, len);
}
}
}
//WRITE_REG32 clear ints
WRITE_REG32(GINTSTS, ints);
}
EFI_STATUS
DwUsbSend (
IN UINT8 EndpointIndex,
IN UINTN Size,
IN CONST VOID *Buffer
)
{
ep_tx(EndpointIndex, Buffer, Size);
return 0;
}
STATIC VOID phy_init()
{
UINT32 val;
//setup clock
val = PHY_READ_REG32(0x200);
val |= BIT4;
PHY_WRITE_REG32(0x200, val);
//setup phy
val = PHY_READ_REG32(SC_PERIPH_RSTDIS0);
val |= RST0_USBOTG_BUS | RST0_POR_PICOPHY |
RST0_USBOTG | RST0_USBOTG_32K;
PHY_WRITE_REG32(SC_PERIPH_RSTDIS0, val);
val = PHY_READ_REG32(SC_PERIPH_CTRL5);
val &= ~CTRL5_PICOPHY_BC_MODE;
val |= CTRL5_USBOTG_RES_SEL | CTRL5_PICOPHY_ACAENB;
PHY_WRITE_REG32(SC_PERIPH_CTRL5, val);
val = PHY_READ_REG32(SC_PERIPH_CTRL4);
val &= ~(CTRL4_PICO_SIDDQ | CTRL4_PICO_OGDISABLE);
val |= CTRL4_PICO_VBUSVLDEXT | CTRL4_PICO_VBUSVLDEXTSEL |
CTRL4_OTG_PHY_SEL;
PHY_WRITE_REG32(SC_PERIPH_CTRL4, val);
PHY_WRITE_REG32(SC_PERIPH_CTRL8, EYE_PATTERN_PARA);
}
STATIC VOID usb_init()
{
VOID* buf;
buf = UncachedAllocatePages (1);
g_dma_desc = buf;
g_dma_desc_ep0 = g_dma_desc + sizeof(struct dwc_otg_dev_dma_desc);
g_dma_desc_in = g_dma_desc_ep0 + sizeof(struct dwc_otg_dev_dma_desc);
p_ctrlreq = (USB_DEVICE_REQUEST *)g_dma_desc_in + sizeof(struct dwc_otg_dev_dma_desc);
SetMem(g_dma_desc, sizeof(struct dwc_otg_dev_dma_desc), 0);
SetMem(g_dma_desc_ep0, sizeof(struct dwc_otg_dev_dma_desc), 0);
SetMem(g_dma_desc_in, sizeof(struct dwc_otg_dev_dma_desc), 0);
/*Reset usb controller.*/
/* Wait for OTG AHB master idle */
while (!((READ_REG32(GRSTCTL)) & 0x80000000));
/* OTG: Assert Software Reset */
WRITE_REG32(GRSTCTL, 1);
/* Wait for OTG to ack reset */
while ((READ_REG32(GRSTCTL)) & 1);
/* Wait for OTG AHB master idle */
while (!((READ_REG32(GRSTCTL)) & 0x80000000));
WRITE_REG32(GDFIFOCFG, DATA_FIFO_CONFIG);
WRITE_REG32(GRXFSIZ, RX_SIZE);
WRITE_REG32(GNPTXFSIZ, ENDPOINT_TX_SIZE);
WRITE_REG32(DIEPTXF1, DATA_IN_ENDPOINT_TX_FIFO1);
/*
* set Periodic TxFIFO Empty Level,
* Non-Periodic TxFIFO Empty Level,
* Enable DMA, Unmask Global Intr
*/
WRITE_REG32(GAHBCFG, 0x1a1);
/*select 8bit UTMI+, ULPI Inerface*/
WRITE_REG32(GUSBCFG, 0x2400);
/* Detect usb work mode,host or device? */
while ((READ_REG32(GINTSTS)) & 1);
MicroSecondDelay(1);
/*Init global and device mode csr register.*/
/*set Non-Zero-Length status out handshake */
WRITE_REG32(DCFG, 0x800004);
/* Interrupt unmask: IN event, OUT event, bus reset */
WRITE_REG32(GINTMSK, 0xC3C08);
while ((READ_REG32(GINTSTS)) & 0x2000);
/* Clear any pending OTG Interrupts */
WRITE_REG32(GOTGINT, 0xFFFFFFFF);
/* Clear any pending interrupts */
WRITE_REG32(GINTSTS, 0xFFFFFFFF);
WRITE_REG32(GINTMSK, 0xFFFFFFFF);
WRITE_REG32(GOTGINT, READ_REG32(GOTGINT) & (~0x3000));
/*endpoint settings cfg*/
reset_endpoints();
/*init finish. and ready to transfer data*/
/* Soft Disconnect */
WRITE_REG32(DCTL, 0x802);
MicroSecondDelay(1);
/* Soft Reconnect */
WRITE_REG32(DCTL, 0x800);
}
EFI_STATUS
EFIAPI
DwUsbStart (
IN USB_DEVICE_DESCRIPTOR *DeviceDescriptor,
IN VOID **Descriptors,
IN USB_DEVICE_RX_CALLBACK RxCallback,
IN USB_DEVICE_TX_CALLBACK TxCallback
)
{
UINT8 *Ptr;
EFI_STATUS Status;
EFI_EVENT TimerEvent;
ASSERT (DeviceDescriptor != NULL);
ASSERT (Descriptors[0] != NULL);
ASSERT (RxCallback != NULL);
ASSERT (TxCallback != NULL);
usb_init();
mDeviceDescriptor = DeviceDescriptor;
mDescriptors = Descriptors[0];
// Right now we just support one configuration
ASSERT (mDeviceDescriptor->NumConfigurations == 1);
// ... and one interface
mConfigDescriptor = (USB_CONFIG_DESCRIPTOR *)mDescriptors;
ASSERT (mConfigDescriptor->NumInterfaces == 1);
Ptr = ((UINT8 *) mDescriptors) + sizeof (USB_CONFIG_DESCRIPTOR);
mInterfaceDescriptor = (USB_INTERFACE_DESCRIPTOR *) Ptr;
Ptr += sizeof (USB_INTERFACE_DESCRIPTOR);
mEndpointDescriptors = (USB_ENDPOINT_DESCRIPTOR *) Ptr;
mDataReceivedCallback = RxCallback;
mDataSentCallback = TxCallback;
// Register a timer event so CheckInterupts gets called periodically
Status = gBS->CreateEvent (
EVT_TIMER | EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
CheckInterrupts,
NULL,
&TimerEvent
);
ASSERT_EFI_ERROR (Status);
if (EFI_ERROR (Status)) {
return Status;
}
Status = gBS->SetTimer (
TimerEvent,
TimerPeriodic,
DW_INTERRUPT_POLL_PERIOD
);
ASSERT_EFI_ERROR (Status);
return Status;
}
USB_DEVICE_PROTOCOL mUsbDevice = {
DwUsbStart,
DwUsbSend
};
EFI_STATUS
EFIAPI
DwUsbEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_HANDLE Handle;
phy_init();
Handle = NULL;
return gBS->InstallProtocolInterface (
&Handle,
&gUsbDeviceProtocolGuid,
EFI_NATIVE_INTERFACE,
&mUsbDevice
);
}