EmbeddedPkg/Isp1761UsbDxe: Driver for the NXP ISP1761's USB peripheral controller

This driver doesn't support OTG - it simply sets the NXP ISP1761 in pure
peripheral mode.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Olivier Martin <olivier.martin@arm.com>



git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@15314 6f19259b-4bc3-4df7-8a09-765794883524
diff --git a/EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c b/EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c
new file mode 100644
index 0000000..77d4f2a
--- /dev/null
+++ b/EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c
@@ -0,0 +1,636 @@
+/** @file

+

+  Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>

+

+  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 <Library/TimerLib.h>

+#include <Library/DebugLib.h>

+#include <Library/UefiBootServicesTableLib.h>

+#include <Library/UefiDriverEntryPoint.h>

+#include <Library/IoLib.h>

+#include <Library/MemoryAllocationLib.h>

+

+#include <IndustryStandard/Usb.h>

+

+#include <Protocol/UsbDevice.h>

+

+#include "Isp1761UsbDxe.h"

+

+/*

+  Driver for using the NXP ISP1761 as a USB Peripheral controller.

+  Doesn't use USB OTG - just sets it in Pure Peripheral mode.

+

+  The ISP1582 datasheet has a little more info on the Peripheral controller

+  registers than the ISP1761 datasheet

+

+  We don't do string descriptors. They're optional.

+  We currently assume the device has one configuration, one interface, one IN

+  endpoint, and one OUT endpoint (plus the default control endpoint).

+

+  In fact, this driver is the minimum required to implement fastboot.

+*/

+

+// TODO Make sure the controller isn't sending empty packets when it shouldn't

+// (check behaviour in cases when Buffer Length isn't explcitly set)

+

+// ISP1582 Datasheet:

+// "Data transfers preceding the status stage must first be fully

+// completed before the STATUS bit can be set."

+// This variable stores whether some control data has been pended in the EP0TX

+// Tx buffer, so that when an EP0TX interrupt is received we can set the STATUS

+// bit to go to the Status stage of the control transfer.

+STATIC BOOLEAN mControlTxPending = FALSE;

+

+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;

+

+// The time between interrupt polls, in units of 100 nanoseconds

+// 10 Microseconds

+#define ISP1761_INTERRUPT_POLL_PERIOD 10000

+

+STATIC

+VOID

+SelectEndpoint (

+  IN UINT8 Endpoint

+  )

+{

+  // The DMA Endpoint Index must not point to the same as the

+  // Endpoint Index Register.

+  WRITE_REG32 (ISP1761_DMA_ENDPOINT_INDEX, ((Endpoint + 2) % ISP1761_NUM_ENDPOINTS));

+  WRITE_REG32 (ISP1761_ENDPOINT_INDEX, Endpoint);

+}

+

+// Enable going to the Data stage of a control transfer

+STATIC

+VOID

+DataStageEnable (

+  IN UINT8 Endpoint

+  )

+{

+  SelectEndpoint (Endpoint);

+  WRITE_REG32 (ISP1761_CTRL_FUNCTION, ISP1761_CTRL_FUNCTION_DSEN);

+}

+

+// Go to the Status stage of a successful control transfer

+STATIC

+VOID

+StatusAcknowledge (

+  IN UINT8 Endpoint

+)

+{

+  SelectEndpoint (Endpoint);

+  WRITE_REG32 (ISP1761_CTRL_FUNCTION, ISP1761_CTRL_FUNCTION_STATUS);

+}

+

+// Read the FIFO for the endpoint indexed by Endpoint, into the buffer pointed

+// at by Buffer, whose size is *Size bytes.

+//

+// If *Size is less than the number of bytes in the FIFO, return EFI_BUFFER_TOO_SMALL

+//

+// Update *Size with the number of bytes of data in the FIFO.

+STATIC

+EFI_STATUS

+ReadEndpointBuffer (

+  IN      UINT8   Endpoint,

+  IN OUT  UINTN  *Size,

+  IN OUT  VOID   *Buffer

+  )

+{

+  UINT16  NumBytesAvailable;

+  UINT32  Val32;

+  UINTN   Index;

+  UINTN   NumBytesRead;

+

+  SelectEndpoint (Endpoint);

+

+  NumBytesAvailable = READ_REG16 (ISP1761_BUFFER_LENGTH);

+

+  if (NumBytesAvailable > *Size) {

+    *Size = NumBytesAvailable;

+    return EFI_BUFFER_TOO_SMALL;

+  }

+  *Size = NumBytesAvailable;

+

+  /* -- NB! --

+    The datasheet says the Data Port is 16 bits but it actually appears to

+    be 32 bits.

+   */

+

+  // Read 32-bit chunks

+  for (Index = 0; Index < NumBytesAvailable / 4; Index++) {

+    ((UINT32 *) Buffer)[Index] = READ_REG32 (ISP1761_DATA_PORT);

+  }

+

+  // Read remaining bytes

+

+  // Round NumBytesAvailable down to nearest power of 4

+  NumBytesRead = NumBytesAvailable & (~0x3);

+  if (NumBytesRead != NumBytesAvailable) {

+    Val32 = READ_REG32 (ISP1761_DATA_PORT);

+    // Copy each required byte of 32-bit word into buffer

+    for (Index = 0; Index < NumBytesAvailable % 4; Index++) {

+      ((UINT8 *) Buffer)[NumBytesRead + Index] = Val32 >> (Index * 8);

+    }

+  }

+  return EFI_SUCCESS;

+}

+

+/*

+  Write an endpoint buffer. Parameters:

+  Endpoint        Endpoint index (see Endpoint Index Register in datasheet)

+  MaxPacketSize   The MaxPacketSize this endpoint is configured for

+  Size            The size of the Buffer

+  Buffer          The data

+

+  Assumes MaxPacketSize is a multiple of 4.

+  (It seems that all valid values for MaxPacketSize _are_ multiples of 4)

+*/

+STATIC

+EFI_STATUS

+WriteEndpointBuffer (

+  IN       UINT8   Endpoint,

+  IN       UINTN   MaxPacketSize,

+  IN       UINTN   Size,

+  IN CONST VOID   *Buffer

+  )

+{

+  UINTN    Index;

+  UINT32  *DwordBuffer;

+

+  DwordBuffer = (UINT32 *) Buffer;

+  SelectEndpoint (Endpoint);

+

+  /* -- NB! --

+    The datasheet says the Data Port is 16 bits but it actually appears to

+    be 32 bits.

+   */

+

+  // Write packets of size MaxPacketSize

+  while (Size > MaxPacketSize) {

+    for (Index = 0; Index < MaxPacketSize / 4; Index++) {

+      WRITE_REG32 (ISP1761_DATA_PORT, DwordBuffer[Index]);

+    }

+    Size -= MaxPacketSize;

+    DwordBuffer += (MaxPacketSize / sizeof (UINT32));

+  }

+

+  // Write remaining data

+

+  if (Size > 0) {

+    WRITE_REG32 (ISP1761_BUFFER_LENGTH, Size);

+

+    while (Size > 4) {

+      WRITE_REG32 (ISP1761_DATA_PORT, DwordBuffer[0]);

+      Size -= 4;

+      DwordBuffer++;

+    }

+

+    if (Size > 0) {

+      WRITE_REG32 (ISP1761_DATA_PORT, DwordBuffer[0]);

+    }

+  }

+

+  return EFI_SUCCESS;

+}

+

+STATIC

+EFI_STATUS

+HandleGetDescriptor (

+  IN USB_DEVICE_REQUEST  *Request

+  )

+{

+  EFI_STATUS  Status;

+  UINT8       DescriptorType;

+  UINTN       ResponseSize;

+  VOID       *ResponseData;

+

+  ResponseSize = 0;

+  ResponseData = NULL;

+  Status = EFI_SUCCESS;

+

+  // 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));

+    break;

+  default:

+    DEBUG ((EFI_D_INFO, "USB: Didn't understand request for descriptor 0x%04x\n", Request->Value));

+    Status = EFI_NOT_FOUND;

+    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"));

+    }

+

+    DataStageEnable (ISP1761_EP0TX);

+    Status = WriteEndpointBuffer (

+              ISP1761_EP0TX,

+              MAX_PACKET_SIZE_CONTROL,

+              ResponseSize,

+              ResponseData

+              );

+    if (!EFI_ERROR (Status)) {

+      // Setting this value should cause us to go to the Status stage on the

+      // next EP0TX interrupt

+      mControlTxPending = TRUE;

+    }

+  }

+

+  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);

+  // USB Spec: "The USB device does not change its device address until after

+  // the Status stage of this request is completed successfully."

+  // ISP1582 datasheet: "The new device address is activated when the

+  // device receives an acknowledgment from the host for the empty packet

+  // token". (StatusAcknowledge causes an empty packet to be sent).

+  // So, we write the Address register _before_ acking the SET_ADDRESS.

+  DEBUG ((EFI_D_INFO, "USB: Setting address to %d\n", Request->Value));

+  WRITE_REG32 (ISP1761_ADDRESS, Request->Value | ISP1761_ADDRESS_DEVEN);

+  StatusAcknowledge (ISP1761_EP0TX);

+

+  return EFI_SUCCESS;

+}

+

+// Move the device to the Configured state.

+// (This code only supports one configuration for a device, so the configuration

+//  index is ignored)

+STATIC

+EFI_STATUS

+HandleSetConfiguration (

+  IN USB_DEVICE_REQUEST  *Request

+  )

+{

+  USB_ENDPOINT_DESCRIPTOR  *EPDesc;

+  UINTN                     Index;

+  UINT8                     EndpointIndex;

+

+  ASSERT (Request->RequestType == USB_DEV_SET_CONFIGURATION_REQ_TYPE);

+  DEBUG ((EFI_D_INFO, "USB: Setting configuration.\n"));

+

+  // Configure endpoints

+  for (Index = 0; Index < mInterfaceDescriptor->NumEndpoints; Index++) {

+    EPDesc = &mEndpointDescriptors[Index];

+

+    // To simplify for now, assume endpoints aren't "sparse", and are in order.

+    ASSERT ((EPDesc->EndpointAddress & 0xF) == ((Index / 2) + 1));

+

+    // Convert from USB endpoint index to ISP1761 endpoint Index

+    // USB:     Endpoint number is bits [3:0], IN/OUT is bit [7]

+    // ISP1761: Endpoint number is bits [4:1], IN/OUT is bit [0]

+    EndpointIndex = ((EPDesc->EndpointAddress & 0xF) << 1) |

+                    ((EPDesc->EndpointAddress & BIT7) >> 7);

+    SelectEndpoint (EndpointIndex);

+    // Set endpoint type (Bulk/Isochronous/Interrupt)

+    WRITE_REG32 (ISP1761_ENDPOINT_MAX_PACKET_SIZE, EPDesc->MaxPacketSize);

+    // Hardware foible (bug?): Although the datasheet seems to suggest it should

+    // automatically be set to MaxPacketSize, the Buffer Length register appears

+    // to be reset to 0, which causes an empty packet to be sent in response to

+    // the first IN token of the session. The NOEMPKT field of the Endpoint Type

+    // register sounds like it might fix this problem, but it doesn't

+    // (it's "applicable only in the DMA mode").

+    WRITE_REG32 (ISP1761_BUFFER_LENGTH, EPDesc->MaxPacketSize);

+    WRITE_REG32 (ISP1761_ENDPOINT_TYPE, (EPDesc->Attributes & 0x3) |

+                                        ISP1761_ENDPOINT_TYPE_ENABLE);

+  }

+

+  StatusAcknowledge (ISP1761_EP0TX);

+  return EFI_SUCCESS;

+}

+

+STATIC

+EFI_STATUS

+HandleDeviceRequest (

+  IN USB_DEVICE_REQUEST  *Request

+  )

+{

+  EFI_STATUS  Status;

+

+  Status = EFI_SUCCESS;

+

+  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      DcInterrupts;

+  UINTN       NumBytes;

+  UINTN       MoreBytes;

+  UINT8       Packet[512];

+  VOID       *DataPacket;

+  UINT32      HandledInterrupts;

+  UINT32      UnhandledInterrupts;

+  EFI_STATUS  Status;

+

+  // Set bits in HandledInterrupts to mark the interrupt source handled.

+  HandledInterrupts = 0;

+

+  WRITE_REG32 (ISP1761_DEVICE_UNLOCK, ISP1761_DEVICE_UNLOCK_MAGIC);

+

+  DcInterrupts = READ_REG32 (ISP1761_DC_INTERRUPT);

+  if (DcInterrupts & ISP1761_DC_INTERRUPT_SUSP) {

+    DEBUG ((EFI_D_INFO, "USB: Suspend\n"));

+    HandledInterrupts |= ISP1761_DC_INTERRUPT_SUSP;

+  }

+  if (DcInterrupts & ISP1761_DC_INTERRUPT_RESUME) {

+    DEBUG ((EFI_D_INFO, "USB: Resume\n"));

+    HandledInterrupts |= ISP1761_DC_INTERRUPT_RESUME;

+  }

+  if (DcInterrupts & ISP1761_DC_INTERRUPT_EP0SETUP) {

+    NumBytes = 512;

+    ReadEndpointBuffer (0x20, &NumBytes, &Packet);

+    ASSERT (NumBytes == 8);

+    HandleDeviceRequest ((USB_DEVICE_REQUEST *) Packet);

+    HandledInterrupts |= ISP1761_DC_INTERRUPT_EP0SETUP;

+  }

+  if (DcInterrupts & ISP1761_DC_INTERRUPT_EP0RX) {

+    HandledInterrupts |= ISP1761_DC_INTERRUPT_EP0RX;

+  }

+  if (DcInterrupts & ISP1761_DC_INTERRUPT_EP0TX) {

+    if (mControlTxPending) {

+      // We previously put some data in the Control Endpoint's IN (Tx) FIFO.

+      // We assume that that data has now been sent in response to the IN token

+      // that triggered this interrupt. We can therefore go to the Status stage

+      // of the control transfer.

+      StatusAcknowledge (ISP1761_EP0TX);

+      mControlTxPending = FALSE;

+    }

+    HandledInterrupts |= ISP1761_DC_INTERRUPT_EP0TX;

+  }

+  if (DcInterrupts & ISP1761_DC_INTERRUPT_EP1RX) {

+    NumBytes = 512;

+    DataPacket = AllocatePool (NumBytes);

+    Status = ReadEndpointBuffer (ISP1761_EP1RX, &NumBytes, DataPacket);

+    if (EFI_ERROR (Status) || NumBytes == 0) {

+      if (EFI_ERROR (Status)) {

+        DEBUG ((EFI_D_ERROR, "Couldn't read EP1RX data: %r\n", Status));

+      }

+      FreePool (DataPacket);

+    } else {

+      // Signal this event again so we poll again ASAP

+      gBS->SignalEvent (Event);

+      mDataReceivedCallback (NumBytes, DataPacket);

+    }

+    HandledInterrupts |= ISP1761_DC_INTERRUPT_EP1RX;

+  }

+  if (DcInterrupts & ISP1761_DC_INTERRUPT_EP1TX) {

+    mDataSentCallback (1);

+    HandledInterrupts |= ISP1761_DC_INTERRUPT_EP1TX;

+  }

+  if (DcInterrupts & (ISP1761_DC_INTERRUPT_SOF | ISP1761_DC_INTERRUPT_PSOF)) {

+    // Don't care about SOFs or pseudo-SOFs

+    HandledInterrupts |= (ISP1761_DC_INTERRUPT_SOF | ISP1761_DC_INTERRUPT_PSOF);

+  }

+  if (ISP1761_DC_INTERRUPT_BRESET) {

+    HandledInterrupts |= ISP1761_DC_INTERRUPT_BRESET;

+  }

+  if (ISP1761_DC_INTERRUPT_HS_STAT) {

+    HandledInterrupts |= ISP1761_DC_INTERRUPT_HS_STAT;

+  }

+  if (ISP1761_DC_INTERRUPT_VBUS) {

+    HandledInterrupts |= ISP1761_DC_INTERRUPT_VBUS;

+  }

+

+  UnhandledInterrupts = DcInterrupts & (~HandledInterrupts) & ISP1761_DC_INTERRUPT_MASK;

+  if (UnhandledInterrupts) {

+    DEBUG ((EFI_D_ERROR, "USB: Unhandled DC Interrupts: 0x%08x\n",

+      UnhandledInterrupts));

+  }

+

+  // Check if we received any more data while we were handling the interrupt.

+  SelectEndpoint (ISP1761_EP1RX);

+  MoreBytes = READ_REG16 (ISP1761_BUFFER_LENGTH);

+  if (MoreBytes) {

+    HandledInterrupts &= ~ISP1761_DC_INTERRUPT_EP1RX;

+  }

+

+  WRITE_REG32 (ISP1761_DC_INTERRUPT, HandledInterrupts);

+}

+

+EFI_STATUS

+Isp1761PeriphSend (

+  IN        UINT8  EndpointIndex,

+  IN        UINTN  Size,

+  IN  CONST VOID  *Buffer

+  )

+{

+  return WriteEndpointBuffer (

+          (EndpointIndex << 1) | 0x1, //Convert to ISP1761 endpoint index, Tx

+          MAX_PACKET_SIZE_BULK,

+          Size,

+          Buffer

+          );

+}

+

+EFI_STATUS

+EFIAPI

+Isp1761PeriphStart (

+  IN USB_DEVICE_DESCRIPTOR   *DeviceDescriptor,

+  IN VOID                   **Descriptors,

+  IN USB_DEVICE_RX_CALLBACK   RxCallback,

+  IN USB_DEVICE_TX_CALLBACK   TxCallback

+  )

+{

+  UINT16                    OtgStatus;

+  UINT8                    *Ptr;

+  EFI_STATUS                Status;

+  EFI_EVENT                 TimerEvent;

+

+  ASSERT (DeviceDescriptor != NULL);

+  ASSERT (Descriptors[0] != NULL);

+  ASSERT (RxCallback != NULL);

+  ASSERT (TxCallback != NULL);

+

+  WRITE_REG32 (ISP1761_DEVICE_UNLOCK, ISP1761_DEVICE_UNLOCK_MAGIC);

+

+  WRITE_REG32 (ISP1761_SW_RESET_REG, ISP1761_SW_RESET_ALL);

+  while (READ_REG32 (ISP1761_SW_RESET_REG) & ISP1761_SW_RESET_ALL) {

+    //busy wait

+  }

+  WRITE_REG32 (ISP1761_MODE, ISP1761_MODE_SFRESET);

+  while (READ_REG32 (ISP1761_MODE) & ISP1761_MODE_SFRESET) {

+    //busy wait

+  }

+  DEBUG ((EFI_D_INFO, "USB: Software reset done\n"));

+

+  WRITE_REG32 (ISP1761_DC_INTERRUPT_ENABLE, 0x03FFFFFF);

+  WRITE_REG32 (ISP1761_OTG_INTERRUPT_ENABLE_RISE, 0x07FF);

+

+  WRITE_REG8 (ISP1761_ADDRESS, ISP1761_ADDRESS_DEVEN);

+  WRITE_REG8 (ISP1761_MODE, ISP1761_MODE_WKUPCS | ISP1761_MODE_CLKAON);

+

+  // Use port 1 as peripheral controller (magic - disagrees with datasheet)

+  WRITE_REG32 (ISP1761_OTG_CTRL_SET, 0xffff0000);

+  WRITE_REG32 (ISP1761_OTG_CTRL_SET, 0x000014d1);

+

+  OtgStatus = READ_REG16 (ISP1761_OTG_STATUS);

+  if ((OtgStatus & ISP1761_OTG_STATUS_B_SESS_END) != 0) {

+    DEBUG ((EFI_D_ERROR, "USB: Vbus not powered.\n"));

+  }

+  if ((OtgStatus & ISP1761_OTG_STATUS_A_B_SESS_VLD) == 0) {

+    DEBUG ((EFI_D_ERROR, "USB: Session not valid.\n"));

+  }

+

+  // Configure Control endpoints

+  SelectEndpoint (0x20);

+  WRITE_REG32 (ISP1761_ENDPOINT_MAX_PACKET_SIZE, MAX_PACKET_SIZE_CONTROL);

+  WRITE_REG32 (ISP1761_ENDPOINT_TYPE, ISP1761_ENDPOINT_TYPE_ENABLE);

+  SelectEndpoint (0x0);

+  WRITE_REG32 (ISP1761_ENDPOINT_MAX_PACKET_SIZE, MAX_PACKET_SIZE_CONTROL);

+  WRITE_REG32 (ISP1761_ENDPOINT_TYPE, ISP1761_ENDPOINT_TYPE_ENABLE);

+  SelectEndpoint (0x1);

+  WRITE_REG32 (ISP1761_ENDPOINT_MAX_PACKET_SIZE, MAX_PACKET_SIZE_CONTROL);

+  WRITE_REG32 (ISP1761_ENDPOINT_TYPE, ISP1761_ENDPOINT_TYPE_ENABLE);

+

+  // Interrupt on all ACK and NAK

+  WRITE_REG32 (ISP1761_INTERRUPT_CONFIG, ISP1761_INTERRUPT_CONFIG_ACK_ONLY);

+

+  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,

+                  ISP1761_INTERRUPT_POLL_PERIOD

+                  );

+  ASSERT_EFI_ERROR (Status);

+

+  return Status;

+}

+

+USB_DEVICE_PROTOCOL mUsbDevice = {

+  Isp1761PeriphStart,

+  Isp1761PeriphSend

+};

+

+

+EFI_STATUS

+EFIAPI

+Isp1761PeriphEntryPoint (

+  IN EFI_HANDLE                            ImageHandle,

+  IN EFI_SYSTEM_TABLE                      *SystemTable

+  )

+{

+  UINT32      DeviceId;

+  EFI_HANDLE  Handle;

+

+  DeviceId = READ_REG32 (ISP1761_DEVICE_ID);

+

+  if (DeviceId != ISP1761_DEVICE_ID_VAL) {

+    DEBUG ((EFI_D_ERROR,

+      "ERROR: Read incorrect device ID for ISP1761: 0x%08x, expected 0x%08x\n",

+      DeviceId , ISP1761_DEVICE_ID_VAL

+      ));

+    return EFI_DEVICE_ERROR;

+  }

+

+  Handle = NULL;

+  return gBS->InstallProtocolInterface (

+    &Handle,

+    &gUsbDeviceProtocolGuid,

+    EFI_NATIVE_INTERFACE,

+    &mUsbDevice

+    );

+}

diff --git a/EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.h b/EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.h
new file mode 100644
index 0000000..9e60e15
--- /dev/null
+++ b/EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.h
@@ -0,0 +1,122 @@
+/** @file

+

+  Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>

+

+  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.

+

+**/

+

+#ifndef __ISP1761_USB_DXE_H__

+#define __ISP1761_USB_DXE_H__

+

+#define ISP1761_USB_BASE FixedPcdGet32 (PcdIsp1761BaseAddress)

+

+#define READ_REG32(Offset) MmioRead32 (ISP1761_USB_BASE + Offset)

+#define READ_REG16(Offset) (UINT16) READ_REG32 (Offset)

+#define WRITE_REG32(Offset, Val)  MmioWrite32 (ISP1761_USB_BASE + Offset, Val)

+#define WRITE_REG16(Offset, Val)  MmioWrite32 (ISP1761_USB_BASE + Offset, (UINT32) Val)

+#define WRITE_REG8(Offset, Val)   MmioWrite32 (ISP1761_USB_BASE + Offset, (UINT32) Val)

+

+// Max packet size in bytes (For Full Speed USB 64 is the only valid value)

+#define MAX_PACKET_SIZE_CONTROL     64

+

+#define MAX_PACKET_SIZE_BULK        512

+

+// 8 Endpoints, in and out. Don't count the Endpoint 0 setup buffer

+#define ISP1761_NUM_ENDPOINTS               16

+

+// Endpoint Indexes

+#define ISP1761_EP0SETUP                    0x20

+#define ISP1761_EP0RX                       0x00

+#define ISP1761_EP0TX                       0x01

+#define ISP1761_EP1RX                       0x02

+#define ISP1761_EP1TX                       0x03

+

+// DcInterrupt bits

+#define ISP1761_DC_INTERRUPT_BRESET         BIT0

+#define ISP1761_DC_INTERRUPT_SOF            BIT1

+#define ISP1761_DC_INTERRUPT_PSOF           BIT2

+#define ISP1761_DC_INTERRUPT_SUSP           BIT3

+#define ISP1761_DC_INTERRUPT_RESUME         BIT4

+#define ISP1761_DC_INTERRUPT_HS_STAT        BIT5

+#define ISP1761_DC_INTERRUPT_DMA            BIT6

+#define ISP1761_DC_INTERRUPT_VBUS           BIT7

+#define ISP1761_DC_INTERRUPT_EP0SETUP       BIT8

+#define ISP1761_DC_INTERRUPT_EP0RX          BIT10

+#define ISP1761_DC_INTERRUPT_EP0TX          BIT11

+#define ISP1761_DC_INTERRUPT_EP1RX          BIT12

+#define ISP1761_DC_INTERRUPT_EP1TX          BIT13

+// All valid peripheral controller interrupts

+#define ISP1761_DC_INTERRUPT_MASK           0x003FFFDFF

+

+#define ISP1761_ADDRESS                     0x200

+#define ISP1761_ADDRESS_DEVEN               BIT7

+

+#define ISP1761_MODE                        0x20C

+#define ISP1761_MODE_DATA_BUS_WIDTH         BIT8

+#define ISP1761_MODE_CLKAON                 BIT7

+#define ISP1761_MODE_SFRESET                BIT4

+#define ISP1761_MODE_WKUPCS                 BIT2

+

+#define ISP1761_ENDPOINT_MAX_PACKET_SIZE    0x204

+

+#define ISP1761_ENDPOINT_TYPE               0x208

+#define ISP1761_ENDPOINT_TYPE_NOEMPKT       BIT4

+#define ISP1761_ENDPOINT_TYPE_ENABLE        BIT3

+

+#define ISP1761_INTERRUPT_CONFIG            0x210

+// Interrupt config value to only interrupt on ACK of IN and OUT tokens

+#define ISP1761_INTERRUPT_CONFIG_ACK_ONLY   BIT2 | BIT5 | BIT6

+

+#define ISP1761_DC_INTERRUPT                0x218

+#define ISP1761_DC_INTERRUPT_ENABLE         0x214

+

+#define ISP1761_CTRL_FUNCTION               0x228

+#define ISP1761_CTRL_FUNCTION_VENDP         BIT3

+#define ISP1761_CTRL_FUNCTION_DSEN          BIT2

+#define ISP1761_CTRL_FUNCTION_STATUS        BIT1

+

+#define ISP1761_DEVICE_UNLOCK               0x27C

+#define ISP1761_DEVICE_UNLOCK_MAGIC         0xAA37

+

+#define ISP1761_SW_RESET_REG                0x30C

+#define ISP1761_SW_RESET_ALL                BIT0

+

+#define ISP1761_DEVICE_ID                   0x370

+

+#define ISP1761_OTG_CTRL_SET                0x374

+#define ISP1761_OTG_CTRL_CLR                OTG_CTRL_SET + 2

+#define ISP1761_OTG_CTRL_OTG_DISABLE        BIT10

+#define ISP1761_OTG_CTRL_VBUS_CHRG          BIT6

+#define ISP1761_OTG_CTRL_VBUS_DISCHRG       BIT5

+#define ISP1761_OTG_CTRL_DM_PULLDOWN        BIT2

+#define ISP1761_OTG_CTRL_DP_PULLDOWN        BIT1

+#define ISP1761_OTG_CTRL_DP_PULLUP          BIT0

+

+#define ISP1761_OTG_STATUS                  0x378

+#define ISP1761_OTG_STATUS_B_SESS_END       BIT7

+#define ISP1761_OTG_STATUS_A_B_SESS_VLD     BIT1

+

+#define ISP1761_OTG_INTERRUPT_LATCH_SET     0x37C

+#define ISP1761_OTG_INTERRUPT_LATCH_CLR     0x37E

+#define ISP1761_OTG_INTERRUPT_ENABLE_RISE   0x384

+

+#define ISP1761_DMA_ENDPOINT_INDEX          0x258

+

+#define ISP1761_ENDPOINT_INDEX              0x22c

+#define ISP1761_DATA_PORT                   0x220

+#define ISP1761_BUFFER_LENGTH               0x21c

+

+// Device ID Values

+#define PHILLIPS_VENDOR_ID_VAL 0x04cc

+#define ISP1761_PRODUCT_ID_VAL 0x1761

+#define ISP1761_DEVICE_ID_VAL ((ISP1761_PRODUCT_ID_VAL << 16) |\

+                               PHILLIPS_VENDOR_ID_VAL)

+

+#endif //ifndef __ISP1761_USB_DXE_H__

diff --git a/EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf b/EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf
new file mode 100644
index 0000000..28a41b5
--- /dev/null
+++ b/EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf
@@ -0,0 +1,44 @@
+#/** @file

+#

+#  Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>

+#

+#  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.

+#

+#

+#**/

+

+[Defines]

+  INF_VERSION                    = 0x00010005

+  BASE_NAME                      = Isp1761PeriphDxe

+  FILE_GUID                      = 72d78ea6-4dee-11e3-8100-f3842a48d0a0

+  MODULE_TYPE                    = UEFI_DRIVER

+  VERSION_STRING                 = 1.0

+  ENTRY_POINT                    = Isp1761PeriphEntryPoint

+

+[Sources.common]

+  Isp1761UsbDxe.c

+

+[LibraryClasses]

+  DebugLib

+  IoLib

+  MemoryAllocationLib

+  TimerLib

+  UefiBootServicesTableLib

+  UefiDriverEntryPoint

+

+[Protocols]

+  gEfiDriverBindingProtocolGuid

+  gUsbDeviceProtocolGuid

+

+[Packages]

+  MdePkg/MdePkg.dec

+  MdeModulePkg/MdeModulePkg.dec

+  EmbeddedPkg/EmbeddedPkg.dec

+

+[Pcd]

+  gEmbeddedTokenSpaceGuid.PcdIsp1761BaseAddress
\ No newline at end of file
diff --git a/EmbeddedPkg/EmbeddedPkg.dec b/EmbeddedPkg/EmbeddedPkg.dec
index 97d2a16..95fb60e 100644
--- a/EmbeddedPkg/EmbeddedPkg.dec
+++ b/EmbeddedPkg/EmbeddedPkg.dec
@@ -133,10 +133,16 @@
   gEmbeddedTokenSpaceGuid.PcdPrePiCpuMemorySize|32|UINT8|0x00000010

   gEmbeddedTokenSpaceGuid.PcdPrePiCpuIoSize|0|UINT8|0x00000011

 

+  # ISP1761 USB OTG Controller

+  gEmbeddedTokenSpaceGuid.PcdIsp1761BaseAddress|0|UINT32|0x00000021

+

 [PcdsFixedAtBuild.AARCH64]

   gEmbeddedTokenSpaceGuid.PcdPrePiCpuMemorySize|48|UINT8|0x00000010

   gEmbeddedTokenSpaceGuid.PcdPrePiCpuIoSize|0|UINT8|0x00000011

 

+  # ISP1761 USB OTG Controller

+  gEmbeddedTokenSpaceGuid.PcdIsp1761BaseAddress|0|UINT64|0x00000021

+

 [PcdsFixedAtBuild.IA32]

   gEmbeddedTokenSpaceGuid.PcdPrePiCpuMemorySize|36|UINT8|0x00000010

   gEmbeddedTokenSpaceGuid.PcdPrePiCpuIoSize|16|UINT8|0x00000011