blob: ee676249d8f965b5d39c17d57978ad2e2dba9bed [file] [log] [blame]
/** @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
);
}