/** @file | |
Copyright (c) 2015-2017, Linaro. 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/ArmLib.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/DwUsb.h> | |
#include <Protocol/UsbDevice.h> | |
#include "DwUsbDxe.h" | |
#define USB_TYPE_LENGTH 16 | |
#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) | |
// The time between interrupt polls, in units of 100 nanoseconds | |
// 10 Microseconds | |
#define DW_INTERRUPT_POLL_PERIOD 10000 | |
EFI_GUID gDwUsbProtocolGuid = DW_USB_PROTOCOL_GUID; | |
STATIC dwc_otg_dev_dma_desc_t *gDmaDesc,*gDmaDescEp0,*gDmaDescIn; | |
STATIC USB_DEVICE_REQUEST *gCtrlReq; | |
STATIC VOID *RxBuf; | |
STATIC UINTN RxDescBytes = 0; | |
STATIC UINTN mNumDataBytes; | |
STATIC DW_USB_PROTOCOL *DwUsb; | |
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; | |
/* To detect which mode was run, high speed or full speed */ | |
STATIC | |
UINTN | |
UsbDrvPortSpeed ( | |
VOID | |
) | |
{ | |
/* | |
* 2'b00: High speed (PHY clock is running at 30 or 60 MHz) | |
*/ | |
UINT32 Val = READ_REG32 (DSTS) & 2; | |
return (!Val); | |
} | |
STATIC | |
VOID | |
ResetEndpoints ( | |
VOID | |
) | |
{ | |
/* EP0 IN ACTIVE NEXT=1 */ | |
WRITE_REG32 (DIEPCTL0, DXEPCTL_USBACTEP | BIT11); | |
/* EP0 OUT ACTIVE */ | |
WRITE_REG32 (DOEPCTL0, DXEPCTL_USBACTEP); | |
/* Clear any pending OTG Interrupts */ | |
WRITE_REG32 (GOTGINT, ~0); | |
/* Clear any pending interrupts */ | |
WRITE_REG32 (GINTSTS, ~0); | |
WRITE_REG32 (DIEPINT0, ~0); | |
WRITE_REG32 (DOEPINT0, ~0); | |
WRITE_REG32 (DIEPINT1, ~0); | |
WRITE_REG32 (DOEPINT1, ~0); | |
/* IN EP interrupt mask */ | |
WRITE_REG32 (DIEPMSK, DXEPMSK_TIMEOUTMSK | DXEPMSK_AHBERMSK | DXEPMSK_XFERCOMPLMSK); | |
/* OUT EP interrupt mask */ | |
WRITE_REG32 (DOEPMSK, DXEPMSK_TIMEOUTMSK | DXEPMSK_AHBERMSK | DXEPMSK_XFERCOMPLMSK); | |
/* Enable interrupts on Ep0 */ | |
WRITE_REG32 (DAINTMSK, (1 << DAINTMSK_OUTEPMSK_SHIFT) | (1 << DAINTMSK_INEPMSK_SHIFT)); | |
/* EP0 OUT Transfer Size:64 Bytes, 1 Packet, 3 Setup Packet, Read to receive setup packet*/ | |
WRITE_REG32 (DOEPTSIZ0, DXEPTSIZ_SUPCNT(3) | DXEPTSIZ_PKTCNT(1) | DXEPTSIZ_XFERSIZE(64)); | |
//notes that:the compulsive conversion is expectable. | |
gDmaDescEp0->status.b.bs = 0x3; | |
gDmaDescEp0->status.b.mtrf = 0; | |
gDmaDescEp0->status.b.sr = 0; | |
gDmaDescEp0->status.b.l = 1; | |
gDmaDescEp0->status.b.ioc = 1; | |
gDmaDescEp0->status.b.sp = 0; | |
gDmaDescEp0->status.b.bytes = 64; | |
gDmaDescEp0->buf = (UINT32)(UINTN)(gCtrlReq); | |
gDmaDescEp0->status.b.sts = 0; | |
gDmaDescEp0->status.b.bs = 0x0; | |
WRITE_REG32 (DOEPDMA0, (UINT32)(UINTN)(gDmaDescEp0)); | |
/* EP0 OUT ENABLE CLEARNAK */ | |
WRITE_REG32 (DOEPCTL0, (READ_REG32 (DOEPCTL0) | DXEPCTL_EPENA | DXEPCTL_CNAK)); | |
} | |
STATIC | |
VOID | |
EpTx ( | |
IN UINT8 Ep, | |
IN CONST VOID *Ptr, | |
IN UINTN Len | |
) | |
{ | |
UINT32 BlockSize; | |
UINT32 Packets; | |
/* EPx OUT ACTIVE */ | |
WRITE_REG32 (DIEPCTL (Ep), (READ_REG32 (DIEPCTL (Ep))) | DXEPCTL_USBACTEP); | |
if (!Ep) { | |
BlockSize = 64; | |
} else { | |
BlockSize = UsbDrvPortSpeed () ? USB_BLOCK_HIGH_SPEED_SIZE : 64; | |
} | |
Packets = (Len + BlockSize - 1) / BlockSize; | |
if (!Len) { | |
/* send one empty packet */ | |
gDmaDescIn->status.b.bs = 0x3; | |
gDmaDescIn->status.b.l = 1; | |
gDmaDescIn->status.b.ioc = 1; | |
gDmaDescIn->status.b.sp = 1; | |
gDmaDescIn->status.b.bytes = 0; | |
gDmaDescIn->buf = 0; | |
gDmaDescIn->status.b.sts = 0; | |
gDmaDescIn->status.b.bs = 0x0; | |
WRITE_REG32 (DIEPDMA (Ep), (UINT32)(UINTN)(gDmaDescIn)); // DMA Address (DMAAddr) is zero | |
} else { | |
WRITE_REG32 (DIEPTSIZ (Ep), Len | (Packets << 19)); | |
//flush cache | |
WriteBackDataCacheRange ((VOID *)Ptr, Len); | |
gDmaDescIn->status.b.bs = 0x3; | |
gDmaDescIn->status.b.l = 1; | |
gDmaDescIn->status.b.ioc = 1; | |
gDmaDescIn->status.b.sp = 1; | |
gDmaDescIn->status.b.bytes = Len; | |
gDmaDescIn->buf = (UINT32)((UINTN)Ptr); | |
gDmaDescIn->status.b.sts = 0; | |
gDmaDescIn->status.b.bs = 0x0; | |
WRITE_REG32 (DIEPDMA (Ep), (UINT32)(UINTN)(gDmaDescIn)); // Ptr is DMA address | |
} | |
ArmDataSynchronizationBarrier (); | |
/* epena & cnak */ | |
WRITE_REG32 (DIEPCTL (Ep), READ_REG32 (DIEPCTL (Ep)) | DXEPCTL_EPENA | DXEPCTL_CNAK | BIT11); | |
} | |
STATIC | |
VOID | |
EpRx ( | |
IN UINTN Ep, | |
IN UINTN Len | |
) | |
{ | |
/* EPx UNSTALL */ | |
WRITE_REG32 (DOEPCTL (Ep), ((READ_REG32 (DOEPCTL (Ep))) & (~DXEPCTL_STALL))); | |
/* EPx OUT ACTIVE */ | |
WRITE_REG32 (DOEPCTL (Ep), (READ_REG32 (DOEPCTL (Ep)) | DXEPCTL_USBACTEP)); | |
if (Len >= DATA_SIZE) { | |
RxDescBytes = DATA_SIZE; | |
} else { | |
RxDescBytes = Len; | |
} | |
RxBuf = AllocatePool (DATA_SIZE); | |
ASSERT (RxBuf != NULL); | |
InvalidateDataCacheRange (RxBuf, Len); | |
gDmaDesc->status.b.bs = 0x3; | |
gDmaDesc->status.b.mtrf = 0; | |
gDmaDesc->status.b.sr = 0; | |
gDmaDesc->status.b.l = 1; | |
gDmaDesc->status.b.ioc = 1; | |
gDmaDesc->status.b.sp = 0; | |
gDmaDesc->status.b.bytes = (UINT32)RxDescBytes; | |
gDmaDesc->buf = (UINT32)((UINTN)RxBuf); | |
gDmaDesc->status.b.sts = 0; | |
gDmaDesc->status.b.bs = 0x0; | |
ArmDataSynchronizationBarrier (); | |
WRITE_REG32 (DOEPDMA (Ep), (UINT32)((UINTN)gDmaDesc)); | |
/* EPx OUT ENABLE CLEARNAK */ | |
WRITE_REG32 (DOEPCTL (Ep), (READ_REG32 (DOEPCTL (Ep)) | DXEPCTL_EPENA | DXEPCTL_CNAK)); | |
} | |
STATIC | |
EFI_STATUS | |
HandleGetDescriptor ( | |
IN USB_DEVICE_REQUEST *Request | |
) | |
{ | |
UINT8 DescriptorType; | |
UINTN ResponseSize; | |
VOID *ResponseData; | |
EFI_USB_STRING_DESCRIPTOR *Descriptor = NULL; | |
UINTN DescriptorSize; | |
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 ((DEBUG_INFO, "USB: Got a request for device descriptor\n")); | |
ResponseSize = sizeof (USB_DEVICE_DESCRIPTOR); | |
ResponseData = mDeviceDescriptor; | |
break; | |
case USB_DESC_TYPE_CONFIG: | |
DEBUG ((DEBUG_INFO, "USB: Got a request for config descriptor\n")); | |
ResponseSize = mConfigDescriptor->TotalLength; | |
ResponseData = mDescriptors; | |
break; | |
case USB_DESC_TYPE_STRING: | |
DEBUG ((DEBUG_INFO, "USB: Got a request for String descriptor %d\n", Request->Value & 0xFF)); | |
switch (Request->Value & 0xff) { | |
case 0: | |
DescriptorSize = sizeof (EFI_USB_STRING_DESCRIPTOR) + | |
LANG_LENGTH * sizeof (CHAR16) + 1; | |
Descriptor = (EFI_USB_STRING_DESCRIPTOR *)AllocateZeroPool (DescriptorSize); | |
ASSERT (Descriptor != NULL); | |
Descriptor->Length = LANG_LENGTH * sizeof (CHAR16); | |
Descriptor->DescriptorType = USB_DESC_TYPE_STRING; | |
DwUsb->GetLang (Descriptor->String, &Descriptor->Length); | |
ResponseSize = Descriptor->Length; | |
ResponseData = Descriptor; | |
break; | |
case 1: | |
DescriptorSize = sizeof (EFI_USB_STRING_DESCRIPTOR) + | |
MANU_FACTURER_STRING_LENGTH * sizeof (CHAR16) + 1; | |
Descriptor = (EFI_USB_STRING_DESCRIPTOR *)AllocateZeroPool (DescriptorSize); | |
ASSERT (Descriptor != NULL); | |
Descriptor->Length = MANU_FACTURER_STRING_LENGTH * sizeof (CHAR16); | |
Descriptor->DescriptorType = USB_DESC_TYPE_STRING; | |
DwUsb->GetManuFacturer (Descriptor->String, &Descriptor->Length); | |
ResponseSize = Descriptor->Length; | |
ResponseData = Descriptor; | |
break; | |
case 2: | |
DescriptorSize = sizeof (EFI_USB_STRING_DESCRIPTOR) + | |
PRODUCT_STRING_LENGTH * sizeof (CHAR16) + 1; | |
Descriptor = (EFI_USB_STRING_DESCRIPTOR *)AllocateZeroPool (DescriptorSize); | |
ASSERT (Descriptor != NULL); | |
Descriptor->Length = PRODUCT_STRING_LENGTH * sizeof (CHAR16); | |
Descriptor->DescriptorType = USB_DESC_TYPE_STRING; | |
DwUsb->GetProduct (Descriptor->String, &Descriptor->Length); | |
ResponseSize = Descriptor->Length; | |
ResponseData = Descriptor; | |
break; | |
case 3: | |
DescriptorSize = sizeof (EFI_USB_STRING_DESCRIPTOR) + | |
SERIAL_STRING_LENGTH * sizeof (CHAR16) + 1; | |
Descriptor = (EFI_USB_STRING_DESCRIPTOR *)AllocateZeroPool (DescriptorSize); | |
ASSERT (Descriptor != NULL); | |
Descriptor->Length = SERIAL_STRING_LENGTH * sizeof (CHAR16); | |
Descriptor->DescriptorType = USB_DESC_TYPE_STRING; | |
DwUsb->GetSerialNo (Descriptor->String, &Descriptor->Length); | |
ResponseSize = Descriptor->Length; | |
ResponseData = Descriptor; | |
break; | |
} | |
break; | |
default: | |
DEBUG ((DEBUG_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 ((DEBUG_INFO, "USB: Info: ResponseSize < wLength\n")); | |
} | |
EpTx (0, ResponseData, ResponseSize); | |
} | |
if (Descriptor) { | |
FreePool (Descriptor); | |
} | |
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 ((DEBUG_INFO, "USB: Setting address to %d\n", Request->Value)); | |
ResetEndpoints (); | |
WRITE_REG32 (DCFG, (READ_REG32 (DCFG) & ~DCFG_DEVADDR_MASK) | DCFG_DEVADDR(Request->Value)); | |
EpTx (0, 0, 0); | |
return EFI_SUCCESS; | |
} | |
STATIC | |
UINTN | |
UsbDrvRequestEndpoint ( | |
IN UINTN Type, | |
IN UINTN Dir | |
) | |
{ | |
UINTN Ep = 1; | |
UINTN Ret, NewBits; | |
Ret = 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))) & ~DXEPCTL_EPTYPE_MASK) | NewBits | (Ep << 22) | DXEPCTL_NAKSTS); | |
} else { // OUT: to device | |
WRITE_REG32 (DOEPCTL (Ep), ((READ_REG32 (DOEPCTL (Ep))) & ~DXEPCTL_EPTYPE_MASK) | NewBits); | |
} | |
return Ret; | |
} | |
STATIC | |
EFI_STATUS | |
HandleSetConfiguration ( | |
IN USB_DEVICE_REQUEST *Request | |
) | |
{ | |
ASSERT (Request->RequestType == USB_DEV_SET_CONFIGURATION_REQ_TYPE); | |
// Cancel all transfers | |
ResetEndpoints (); | |
UsbDrvRequestEndpoint (2, 0); | |
UsbDrvRequestEndpoint (2, 0x80); | |
WRITE_REG32 (DIEPCTL1, (READ_REG32 (DIEPCTL1)) | BIT28 | BIT19 | DXEPCTL_USBACTEP | BIT11); | |
/* Enable interrupts on all endpoints */ | |
WRITE_REG32 (DAINTMSK, ~0); | |
EpRx (1, CMD_SIZE); | |
EpTx (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 ((DEBUG_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, EpInts; | |
// interrupt register | |
Ints = READ_REG32 (GINTSTS); | |
/* | |
* bus reset | |
* The core sets this bit to indicate that a reset is detected on the USB. | |
*/ | |
if (Ints & GINTSTS_USBRST) { | |
WRITE_REG32 (DCFG, DCFG_DESCDMA | DCFG_NZ_STS_OUT_HSHK); | |
ResetEndpoints (); | |
} | |
/* | |
* 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 & GINTSTS_ENUMDONE) { | |
/* Set up the maximum packet sizes accordingly */ | |
UINTN MaxPacket = UsbDrvPortSpeed () ? USB_BLOCK_HIGH_SPEED_SIZE : 64; | |
//Set Maximum In Packet Size (MPS) | |
WRITE_REG32 (DIEPCTL1, ((READ_REG32 (DIEPCTL1)) & ~DXEPCTL_MPS_MASK) | MaxPacket); | |
//Set Maximum Out Packet Size (MPS) | |
WRITE_REG32 (DOEPCTL1, ((READ_REG32 (DOEPCTL1)) & ~DXEPCTL_MPS_MASK) | 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 & GINTSTS_IEPINT) { | |
EpInts = READ_REG32 (DIEPINT0); | |
WRITE_REG32 (DIEPINT0, EpInts); | |
if (EpInts & DXEPINT_XFERCOMPL) { | |
DEBUG ((DEBUG_INFO, "INT: IN TX completed.DIEPTSIZ (0) = 0x%x.\n", READ_REG32 (DIEPTSIZ0))); | |
} | |
EpInts = READ_REG32 (DIEPINT1); | |
WRITE_REG32 (DIEPINT1, EpInts); | |
if (EpInts & DXEPINT_XFERCOMPL) { | |
DEBUG ((DEBUG_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 & GINTSTS_OEPINT) { | |
/* 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 & DXEPINT_XFERCOMPL) { | |
DEBUG ((DEBUG_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 & BIT3) { /* SETUP phase done */ | |
WRITE_REG32 (DIEPCTL0, READ_REG32 (DIEPCTL0) | DXEPCTL_SNAK); | |
WRITE_REG32 (DOEPCTL0, READ_REG32 (DOEPCTL0) | DXEPCTL_SNAK); | |
/*clear IN EP intr*/ | |
WRITE_REG32 (DIEPINT0, ~0); | |
HandleDeviceRequest((USB_DEVICE_REQUEST *)gCtrlReq); | |
} | |
/* Make sure EP0 OUT is set up to accept the next request */ | |
WRITE_REG32 (DOEPTSIZ0, DXEPTSIZ_SUPCNT(3) | DXEPTSIZ_PKTCNT(1) | DXEPTSIZ_XFERSIZE(64)); | |
/* | |
* 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. | |
*/ | |
gDmaDescEp0->status.b.bs = 0x3; | |
gDmaDescEp0->status.b.mtrf = 0; | |
gDmaDescEp0->status.b.sr = 0; | |
gDmaDescEp0->status.b.l = 1; | |
gDmaDescEp0->status.b.ioc = 1; | |
gDmaDescEp0->status.b.sp = 0; | |
gDmaDescEp0->status.b.bytes = 64; | |
gDmaDescEp0->buf = (UINT32)(UINTN)(gCtrlReq); | |
gDmaDescEp0->status.b.sts = 0; | |
gDmaDescEp0->status.b.bs = 0x0; | |
WRITE_REG32 (DOEPDMA0, (UINT32)(UINTN)(gDmaDescEp0)); | |
// endpoint enable; clear NAK | |
WRITE_REG32 (DOEPCTL0, DXEPCTL_EPENA | DXEPCTL_CNAK); | |
} | |
EpInts = (READ_REG32 (DOEPINT1)); | |
if (EpInts) { | |
WRITE_REG32 (DOEPINT1, EpInts); | |
/* Transfer Completed Interrupt (XferCompl);Transfer completed */ | |
if (EpInts & DXEPINT_XFERCOMPL) { | |
UINTN Bytes = RxDescBytes - gDmaDesc->status.b.bytes; | |
UINTN Len = 0; | |
ArmDataSynchronizationBarrier (); | |
if (MATCH_CMD_LITERAL ("download:", RxBuf)) { | |
mNumDataBytes = AsciiStrHexToUint64 (RxBuf + sizeof ("download:")); | |
} else { | |
if (mNumDataBytes != 0) { | |
mNumDataBytes -= Bytes; | |
} | |
} | |
mDataReceivedCallback (Bytes, RxBuf); | |
if (mNumDataBytes == 0) { | |
Len = CMD_SIZE; | |
} else if (mNumDataBytes > DATA_SIZE) { | |
Len = DATA_SIZE; | |
} else { | |
Len = mNumDataBytes; | |
} | |
EpRx (1, Len); | |
} | |
} | |
} | |
//WRITE_REG32 clear ints | |
WRITE_REG32 (GINTSTS, Ints); | |
} | |
EFI_STATUS | |
DwUsbSend ( | |
IN UINT8 EndpointIndex, | |
IN UINTN Size, | |
IN CONST VOID *Buffer | |
) | |
{ | |
EpTx (EndpointIndex, Buffer, Size); | |
return EFI_SUCCESS; | |
} | |
STATIC | |
VOID | |
DwUsbInit ( | |
VOID | |
) | |
{ | |
VOID *Buf; | |
UINT32 Data; | |
Buf = UncachedAllocatePages (16); | |
gDmaDesc = Buf; | |
gDmaDescEp0 = gDmaDesc + sizeof (dwc_otg_dev_dma_desc_t); | |
gDmaDescIn = gDmaDescEp0 + sizeof (dwc_otg_dev_dma_desc_t); | |
gCtrlReq = (USB_DEVICE_REQUEST *)gDmaDescIn + sizeof (dwc_otg_dev_dma_desc_t); | |
ZeroMem (gDmaDesc, sizeof (dwc_otg_dev_dma_desc_t)); | |
ZeroMem (gDmaDescEp0, sizeof (dwc_otg_dev_dma_desc_t)); | |
ZeroMem (gDmaDescIn, sizeof (dwc_otg_dev_dma_desc_t)); | |
/*Reset usb controller.*/ | |
/* Wait for OTG AHB master idle */ | |
do { | |
Data = READ_REG32 (GRSTCTL) & GRSTCTL_AHBIDLE; | |
} while (Data == 0); | |
/* OTG: Assert Software Reset */ | |
WRITE_REG32 (GRSTCTL, GRSTCTL_CSFTRST); | |
/* Wait for OTG to ack reset */ | |
while (READ_REG32 (GRSTCTL) & GRSTCTL_CSFTRST); | |
/* Wait for OTG AHB master idle */ | |
while ((READ_REG32 (GRSTCTL) & GRSTCTL_AHBIDLE) == 0); | |
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); | |
WRITE_REG32 (DIEPTXF2, DATA_IN_ENDPOINT_TX_FIFO2); | |
WRITE_REG32 (DIEPTXF3, DATA_IN_ENDPOINT_TX_FIFO3); | |
WRITE_REG32 (DIEPTXF4, DATA_IN_ENDPOINT_TX_FIFO4); | |
WRITE_REG32 (DIEPTXF5, DATA_IN_ENDPOINT_TX_FIFO5); | |
WRITE_REG32 (DIEPTXF6, DATA_IN_ENDPOINT_TX_FIFO6); | |
WRITE_REG32 (DIEPTXF7, DATA_IN_ENDPOINT_TX_FIFO7); | |
WRITE_REG32 (DIEPTXF8, DATA_IN_ENDPOINT_TX_FIFO8); | |
WRITE_REG32 (DIEPTXF9, DATA_IN_ENDPOINT_TX_FIFO9); | |
WRITE_REG32 (DIEPTXF10, DATA_IN_ENDPOINT_TX_FIFO10); | |
WRITE_REG32 (DIEPTXF11, DATA_IN_ENDPOINT_TX_FIFO11); | |
WRITE_REG32 (DIEPTXF12, DATA_IN_ENDPOINT_TX_FIFO12); | |
WRITE_REG32 (DIEPTXF13, DATA_IN_ENDPOINT_TX_FIFO13); | |
WRITE_REG32 (DIEPTXF14, DATA_IN_ENDPOINT_TX_FIFO14); | |
WRITE_REG32 (DIEPTXF15, DATA_IN_ENDPOINT_TX_FIFO15); | |
/* | |
* set Periodic TxFIFO Empty Level, | |
* Non-Periodic TxFIFO Empty Level, | |
* Enable DMA, Unmask Global Intr | |
*/ | |
WRITE_REG32 (GAHBCFG, GAHBCFG_CTRL_MASK); | |
/*select 8bit UTMI+, ULPI Inerface*/ | |
WRITE_REG32 (GUSBCFG, 0x2400); | |
/* Detect usb work mode,host or device? */ | |
do { | |
Data = READ_REG32 (GINTSTS); | |
} while (Data & GINTSTS_CURMODE_HOST); | |
MicroSecondDelay (3); | |
/*Init global and device mode csr register.*/ | |
/*set Non-Zero-Length status out handshake */ | |
Data = (0x20 << DCFG_EPMISCNT_SHIFT) | DCFG_NZ_STS_OUT_HSHK; | |
WRITE_REG32 (DCFG, Data); | |
/* Interrupt unmask: IN event, OUT event, bus reset */ | |
Data = GINTSTS_OEPINT | GINTSTS_IEPINT | GINTSTS_ENUMDONE | GINTSTS_USBRST; | |
WRITE_REG32 (GINTMSK, Data); | |
do { | |
Data = READ_REG32 (GINTSTS) & GINTSTS_ENUMDONE; | |
} while (Data); | |
/* Clear any pending OTG Interrupts */ | |
WRITE_REG32 (GOTGINT, ~0); | |
/* Clear any pending interrupts */ | |
WRITE_REG32 (GINTSTS, ~0); | |
WRITE_REG32 (GINTMSK, ~0); | |
Data = READ_REG32 (GOTGINT); | |
Data &= ~0x3000; | |
WRITE_REG32 (GOTGINT, Data); | |
/* endpoint settings cfg */ | |
ResetEndpoints (); | |
MicroSecondDelay (1); | |
/* init finish. and ready to transfer data */ | |
/* Soft Disconnect */ | |
WRITE_REG32 (DCTL, DCTL_PWRONPRGDONE | DCTL_SFTDISCON); | |
MicroSecondDelay (10000); | |
/* Soft Reconnect */ | |
WRITE_REG32 (DCTL, DCTL_PWRONPRGDONE); | |
} | |
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); | |
DwUsbInit(); | |
mDeviceDescriptor = DeviceDescriptor; | |
mDescriptors = Descriptors[0]; | |
// Right now we just support one configuration | |
ASSERT (mDeviceDescriptor->NumConfigurations == 1); | |
mDeviceDescriptor->StrManufacturer = 1; | |
mDeviceDescriptor->StrProduct = 2; | |
mDeviceDescriptor->StrSerialNumber = 3; | |
// ... 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_STATUS Status; | |
Status = gBS->LocateProtocol (&gDwUsbProtocolGuid, NULL, (VOID **) &DwUsb); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = DwUsb->PhyInit(USB_DEVICE_MODE); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
return gBS->InstallProtocolInterface ( | |
&ImageHandle, | |
&gUsbDeviceProtocolGuid, | |
EFI_NATIVE_INTERFACE, | |
&mUsbDevice | |
); | |
} |