| /** @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 = AllocateZeroPool (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 | |
| ); | |
| } |