EmbeddedPkg/Isp1761UsbDxe: Driver for the NXP ISP1761's USB peripheral controller
authorOlivier Martin <olivier.martin@arm.com>
Wed, 5 Mar 2014 04:32:48 +0000 (04:32 +0000)
committeroliviermartin <oliviermartin@6f19259b-4bc3-4df7-8a09-765794883524>
Wed, 5 Mar 2014 04:32:48 +0000 (04:32 +0000)
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

EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c [new file with mode: 0644]
EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.h [new file with mode: 0644]
EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf [new file with mode: 0644]
EmbeddedPkg/EmbeddedPkg.dec

diff --git a/EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c b/EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c
new file mode 100644 (file)
index 0000000..77d4f2a
--- /dev/null
@@ -0,0 +1,636 @@
+/** @file\r
+\r
+  Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>\r
+\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <Library/TimerLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiDriverEntryPoint.h>\r
+#include <Library/IoLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+\r
+#include <IndustryStandard/Usb.h>\r
+\r
+#include <Protocol/UsbDevice.h>\r
+\r
+#include "Isp1761UsbDxe.h"\r
+\r
+/*\r
+  Driver for using the NXP ISP1761 as a USB Peripheral controller.\r
+  Doesn't use USB OTG - just sets it in Pure Peripheral mode.\r
+\r
+  The ISP1582 datasheet has a little more info on the Peripheral controller\r
+  registers than the ISP1761 datasheet\r
+\r
+  We don't do string descriptors. They're optional.\r
+  We currently assume the device has one configuration, one interface, one IN\r
+  endpoint, and one OUT endpoint (plus the default control endpoint).\r
+\r
+  In fact, this driver is the minimum required to implement fastboot.\r
+*/\r
+\r
+// TODO Make sure the controller isn't sending empty packets when it shouldn't\r
+// (check behaviour in cases when Buffer Length isn't explcitly set)\r
+\r
+// ISP1582 Datasheet:\r
+// "Data transfers preceding the status stage must first be fully\r
+// completed before the STATUS bit can be set."\r
+// This variable stores whether some control data has been pended in the EP0TX\r
+// Tx buffer, so that when an EP0TX interrupt is received we can set the STATUS\r
+// bit to go to the Status stage of the control transfer.\r
+STATIC BOOLEAN mControlTxPending = FALSE;\r
+\r
+STATIC USB_DEVICE_DESCRIPTOR    *mDeviceDescriptor;\r
+\r
+// The config descriptor, interface descriptor, and endpoint descriptors in a\r
+// buffer (in that order)\r
+STATIC VOID                     *mDescriptors;\r
+// Convenience pointers to those descriptors inside the buffer:\r
+STATIC USB_INTERFACE_DESCRIPTOR *mInterfaceDescriptor;\r
+STATIC USB_CONFIG_DESCRIPTOR    *mConfigDescriptor;\r
+STATIC USB_ENDPOINT_DESCRIPTOR  *mEndpointDescriptors;\r
+\r
+STATIC USB_DEVICE_RX_CALLBACK   mDataReceivedCallback;\r
+STATIC USB_DEVICE_TX_CALLBACK   mDataSentCallback;\r
+\r
+// The time between interrupt polls, in units of 100 nanoseconds\r
+// 10 Microseconds\r
+#define ISP1761_INTERRUPT_POLL_PERIOD 10000\r
+\r
+STATIC\r
+VOID\r
+SelectEndpoint (\r
+  IN UINT8 Endpoint\r
+  )\r
+{\r
+  // The DMA Endpoint Index must not point to the same as the\r
+  // Endpoint Index Register.\r
+  WRITE_REG32 (ISP1761_DMA_ENDPOINT_INDEX, ((Endpoint + 2) % ISP1761_NUM_ENDPOINTS));\r
+  WRITE_REG32 (ISP1761_ENDPOINT_INDEX, Endpoint);\r
+}\r
+\r
+// Enable going to the Data stage of a control transfer\r
+STATIC\r
+VOID\r
+DataStageEnable (\r
+  IN UINT8 Endpoint\r
+  )\r
+{\r
+  SelectEndpoint (Endpoint);\r
+  WRITE_REG32 (ISP1761_CTRL_FUNCTION, ISP1761_CTRL_FUNCTION_DSEN);\r
+}\r
+\r
+// Go to the Status stage of a successful control transfer\r
+STATIC\r
+VOID\r
+StatusAcknowledge (\r
+  IN UINT8 Endpoint\r
+)\r
+{\r
+  SelectEndpoint (Endpoint);\r
+  WRITE_REG32 (ISP1761_CTRL_FUNCTION, ISP1761_CTRL_FUNCTION_STATUS);\r
+}\r
+\r
+// Read the FIFO for the endpoint indexed by Endpoint, into the buffer pointed\r
+// at by Buffer, whose size is *Size bytes.\r
+//\r
+// If *Size is less than the number of bytes in the FIFO, return EFI_BUFFER_TOO_SMALL\r
+//\r
+// Update *Size with the number of bytes of data in the FIFO.\r
+STATIC\r
+EFI_STATUS\r
+ReadEndpointBuffer (\r
+  IN      UINT8   Endpoint,\r
+  IN OUT  UINTN  *Size,\r
+  IN OUT  VOID   *Buffer\r
+  )\r
+{\r
+  UINT16  NumBytesAvailable;\r
+  UINT32  Val32;\r
+  UINTN   Index;\r
+  UINTN   NumBytesRead;\r
+\r
+  SelectEndpoint (Endpoint);\r
+\r
+  NumBytesAvailable = READ_REG16 (ISP1761_BUFFER_LENGTH);\r
+\r
+  if (NumBytesAvailable > *Size) {\r
+    *Size = NumBytesAvailable;\r
+    return EFI_BUFFER_TOO_SMALL;\r
+  }\r
+  *Size = NumBytesAvailable;\r
+\r
+  /* -- NB! --\r
+    The datasheet says the Data Port is 16 bits but it actually appears to\r
+    be 32 bits.\r
+   */\r
+\r
+  // Read 32-bit chunks\r
+  for (Index = 0; Index < NumBytesAvailable / 4; Index++) {\r
+    ((UINT32 *) Buffer)[Index] = READ_REG32 (ISP1761_DATA_PORT);\r
+  }\r
+\r
+  // Read remaining bytes\r
+\r
+  // Round NumBytesAvailable down to nearest power of 4\r
+  NumBytesRead = NumBytesAvailable & (~0x3);\r
+  if (NumBytesRead != NumBytesAvailable) {\r
+    Val32 = READ_REG32 (ISP1761_DATA_PORT);\r
+    // Copy each required byte of 32-bit word into buffer\r
+    for (Index = 0; Index < NumBytesAvailable % 4; Index++) {\r
+      ((UINT8 *) Buffer)[NumBytesRead + Index] = Val32 >> (Index * 8);\r
+    }\r
+  }\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/*\r
+  Write an endpoint buffer. Parameters:\r
+  Endpoint        Endpoint index (see Endpoint Index Register in datasheet)\r
+  MaxPacketSize   The MaxPacketSize this endpoint is configured for\r
+  Size            The size of the Buffer\r
+  Buffer          The data\r
+\r
+  Assumes MaxPacketSize is a multiple of 4.\r
+  (It seems that all valid values for MaxPacketSize _are_ multiples of 4)\r
+*/\r
+STATIC\r
+EFI_STATUS\r
+WriteEndpointBuffer (\r
+  IN       UINT8   Endpoint,\r
+  IN       UINTN   MaxPacketSize,\r
+  IN       UINTN   Size,\r
+  IN CONST VOID   *Buffer\r
+  )\r
+{\r
+  UINTN    Index;\r
+  UINT32  *DwordBuffer;\r
+\r
+  DwordBuffer = (UINT32 *) Buffer;\r
+  SelectEndpoint (Endpoint);\r
+\r
+  /* -- NB! --\r
+    The datasheet says the Data Port is 16 bits but it actually appears to\r
+    be 32 bits.\r
+   */\r
+\r
+  // Write packets of size MaxPacketSize\r
+  while (Size > MaxPacketSize) {\r
+    for (Index = 0; Index < MaxPacketSize / 4; Index++) {\r
+      WRITE_REG32 (ISP1761_DATA_PORT, DwordBuffer[Index]);\r
+    }\r
+    Size -= MaxPacketSize;\r
+    DwordBuffer += (MaxPacketSize / sizeof (UINT32));\r
+  }\r
+\r
+  // Write remaining data\r
+\r
+  if (Size > 0) {\r
+    WRITE_REG32 (ISP1761_BUFFER_LENGTH, Size);\r
+\r
+    while (Size > 4) {\r
+      WRITE_REG32 (ISP1761_DATA_PORT, DwordBuffer[0]);\r
+      Size -= 4;\r
+      DwordBuffer++;\r
+    }\r
+\r
+    if (Size > 0) {\r
+      WRITE_REG32 (ISP1761_DATA_PORT, DwordBuffer[0]);\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+HandleGetDescriptor (\r
+  IN USB_DEVICE_REQUEST  *Request\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  UINT8       DescriptorType;\r
+  UINTN       ResponseSize;\r
+  VOID       *ResponseData;\r
+\r
+  ResponseSize = 0;\r
+  ResponseData = NULL;\r
+  Status = EFI_SUCCESS;\r
+\r
+  // Pretty confused if bmRequestType is anything but this:\r
+  ASSERT (Request->RequestType == USB_DEV_GET_DESCRIPTOR_REQ_TYPE);\r
+\r
+  // Choose the response\r
+  DescriptorType = Request->Value >> 8;\r
+  switch (DescriptorType) {\r
+  case USB_DESC_TYPE_DEVICE:\r
+    DEBUG ((EFI_D_INFO, "USB: Got a request for device descriptor\n"));\r
+    ResponseSize = sizeof (USB_DEVICE_DESCRIPTOR);\r
+    ResponseData = mDeviceDescriptor;\r
+    break;\r
+  case USB_DESC_TYPE_CONFIG:\r
+    DEBUG ((EFI_D_INFO, "USB: Got a request for config descriptor\n"));\r
+    ResponseSize = mConfigDescriptor->TotalLength;\r
+    ResponseData = mDescriptors;\r
+    break;\r
+  case USB_DESC_TYPE_STRING:\r
+    DEBUG ((EFI_D_INFO, "USB: Got a request for String descriptor %d\n", Request->Value & 0xFF));\r
+    break;\r
+  default:\r
+    DEBUG ((EFI_D_INFO, "USB: Didn't understand request for descriptor 0x%04x\n", Request->Value));\r
+    Status = EFI_NOT_FOUND;\r
+    break;\r
+  }\r
+\r
+  // Send the response\r
+  if (ResponseData) {\r
+    ASSERT (ResponseSize != 0);\r
+\r
+    if (Request->Length < ResponseSize) {\r
+      // Truncate response\r
+      ResponseSize = Request->Length;\r
+    } else if (Request->Length > ResponseSize) {\r
+      DEBUG ((EFI_D_INFO, "USB: Info: ResponseSize < wLength\n"));\r
+    }\r
+\r
+    DataStageEnable (ISP1761_EP0TX);\r
+    Status = WriteEndpointBuffer (\r
+              ISP1761_EP0TX,\r
+              MAX_PACKET_SIZE_CONTROL,\r
+              ResponseSize,\r
+              ResponseData\r
+              );\r
+    if (!EFI_ERROR (Status)) {\r
+      // Setting this value should cause us to go to the Status stage on the\r
+      // next EP0TX interrupt\r
+      mControlTxPending = TRUE;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+HandleSetAddress (\r
+  IN USB_DEVICE_REQUEST  *Request\r
+  )\r
+{\r
+  // Pretty confused if bmRequestType is anything but this:\r
+  ASSERT (Request->RequestType == USB_DEV_SET_ADDRESS_REQ_TYPE);\r
+  // USB Spec: "The USB device does not change its device address until after\r
+  // the Status stage of this request is completed successfully."\r
+  // ISP1582 datasheet: "The new device address is activated when the\r
+  // device receives an acknowledgment from the host for the empty packet\r
+  // token". (StatusAcknowledge causes an empty packet to be sent).\r
+  // So, we write the Address register _before_ acking the SET_ADDRESS.\r
+  DEBUG ((EFI_D_INFO, "USB: Setting address to %d\n", Request->Value));\r
+  WRITE_REG32 (ISP1761_ADDRESS, Request->Value | ISP1761_ADDRESS_DEVEN);\r
+  StatusAcknowledge (ISP1761_EP0TX);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+// Move the device to the Configured state.\r
+// (This code only supports one configuration for a device, so the configuration\r
+//  index is ignored)\r
+STATIC\r
+EFI_STATUS\r
+HandleSetConfiguration (\r
+  IN USB_DEVICE_REQUEST  *Request\r
+  )\r
+{\r
+  USB_ENDPOINT_DESCRIPTOR  *EPDesc;\r
+  UINTN                     Index;\r
+  UINT8                     EndpointIndex;\r
+\r
+  ASSERT (Request->RequestType == USB_DEV_SET_CONFIGURATION_REQ_TYPE);\r
+  DEBUG ((EFI_D_INFO, "USB: Setting configuration.\n"));\r
+\r
+  // Configure endpoints\r
+  for (Index = 0; Index < mInterfaceDescriptor->NumEndpoints; Index++) {\r
+    EPDesc = &mEndpointDescriptors[Index];\r
+\r
+    // To simplify for now, assume endpoints aren't "sparse", and are in order.\r
+    ASSERT ((EPDesc->EndpointAddress & 0xF) == ((Index / 2) + 1));\r
+\r
+    // Convert from USB endpoint index to ISP1761 endpoint Index\r
+    // USB:     Endpoint number is bits [3:0], IN/OUT is bit [7]\r
+    // ISP1761: Endpoint number is bits [4:1], IN/OUT is bit [0]\r
+    EndpointIndex = ((EPDesc->EndpointAddress & 0xF) << 1) |\r
+                    ((EPDesc->EndpointAddress & BIT7) >> 7);\r
+    SelectEndpoint (EndpointIndex);\r
+    // Set endpoint type (Bulk/Isochronous/Interrupt)\r
+    WRITE_REG32 (ISP1761_ENDPOINT_MAX_PACKET_SIZE, EPDesc->MaxPacketSize);\r
+    // Hardware foible (bug?): Although the datasheet seems to suggest it should\r
+    // automatically be set to MaxPacketSize, the Buffer Length register appears\r
+    // to be reset to 0, which causes an empty packet to be sent in response to\r
+    // the first IN token of the session. The NOEMPKT field of the Endpoint Type\r
+    // register sounds like it might fix this problem, but it doesn't\r
+    // (it's "applicable only in the DMA mode").\r
+    WRITE_REG32 (ISP1761_BUFFER_LENGTH, EPDesc->MaxPacketSize);\r
+    WRITE_REG32 (ISP1761_ENDPOINT_TYPE, (EPDesc->Attributes & 0x3) |\r
+                                        ISP1761_ENDPOINT_TYPE_ENABLE);\r
+  }\r
+\r
+  StatusAcknowledge (ISP1761_EP0TX);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+HandleDeviceRequest (\r
+  IN USB_DEVICE_REQUEST  *Request\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+  switch (Request->Request) {\r
+  case USB_DEV_GET_DESCRIPTOR:\r
+    Status = HandleGetDescriptor (Request);\r
+    break;\r
+  case USB_DEV_SET_ADDRESS:\r
+    Status = HandleSetAddress (Request);\r
+    break;\r
+  case USB_DEV_SET_CONFIGURATION:\r
+    Status = HandleSetConfiguration (Request);\r
+    break;\r
+  default:\r
+    DEBUG ((EFI_D_ERROR,\r
+      "Didn't understand RequestType 0x%x Request 0x%x\n",\r
+      Request->RequestType, Request->Request));\r
+      Status = EFI_INVALID_PARAMETER;\r
+    break;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+// Instead of actually registering interrupt handlers, we poll the controller's\r
+//  interrupt source register in this function.\r
+STATIC\r
+VOID\r
+CheckInterrupts (\r
+  IN EFI_EVENT  Event,\r
+  IN VOID      *Context\r
+  )\r
+{\r
+  UINT32      DcInterrupts;\r
+  UINTN       NumBytes;\r
+  UINTN       MoreBytes;\r
+  UINT8       Packet[512];\r
+  VOID       *DataPacket;\r
+  UINT32      HandledInterrupts;\r
+  UINT32      UnhandledInterrupts;\r
+  EFI_STATUS  Status;\r
+\r
+  // Set bits in HandledInterrupts to mark the interrupt source handled.\r
+  HandledInterrupts = 0;\r
+\r
+  WRITE_REG32 (ISP1761_DEVICE_UNLOCK, ISP1761_DEVICE_UNLOCK_MAGIC);\r
+\r
+  DcInterrupts = READ_REG32 (ISP1761_DC_INTERRUPT);\r
+  if (DcInterrupts & ISP1761_DC_INTERRUPT_SUSP) {\r
+    DEBUG ((EFI_D_INFO, "USB: Suspend\n"));\r
+    HandledInterrupts |= ISP1761_DC_INTERRUPT_SUSP;\r
+  }\r
+  if (DcInterrupts & ISP1761_DC_INTERRUPT_RESUME) {\r
+    DEBUG ((EFI_D_INFO, "USB: Resume\n"));\r
+    HandledInterrupts |= ISP1761_DC_INTERRUPT_RESUME;\r
+  }\r
+  if (DcInterrupts & ISP1761_DC_INTERRUPT_EP0SETUP) {\r
+    NumBytes = 512;\r
+    ReadEndpointBuffer (0x20, &NumBytes, &Packet);\r
+    ASSERT (NumBytes == 8);\r
+    HandleDeviceRequest ((USB_DEVICE_REQUEST *) Packet);\r
+    HandledInterrupts |= ISP1761_DC_INTERRUPT_EP0SETUP;\r
+  }\r
+  if (DcInterrupts & ISP1761_DC_INTERRUPT_EP0RX) {\r
+    HandledInterrupts |= ISP1761_DC_INTERRUPT_EP0RX;\r
+  }\r
+  if (DcInterrupts & ISP1761_DC_INTERRUPT_EP0TX) {\r
+    if (mControlTxPending) {\r
+      // We previously put some data in the Control Endpoint's IN (Tx) FIFO.\r
+      // We assume that that data has now been sent in response to the IN token\r
+      // that triggered this interrupt. We can therefore go to the Status stage\r
+      // of the control transfer.\r
+      StatusAcknowledge (ISP1761_EP0TX);\r
+      mControlTxPending = FALSE;\r
+    }\r
+    HandledInterrupts |= ISP1761_DC_INTERRUPT_EP0TX;\r
+  }\r
+  if (DcInterrupts & ISP1761_DC_INTERRUPT_EP1RX) {\r
+    NumBytes = 512;\r
+    DataPacket = AllocatePool (NumBytes);\r
+    Status = ReadEndpointBuffer (ISP1761_EP1RX, &NumBytes, DataPacket);\r
+    if (EFI_ERROR (Status) || NumBytes == 0) {\r
+      if (EFI_ERROR (Status)) {\r
+        DEBUG ((EFI_D_ERROR, "Couldn't read EP1RX data: %r\n", Status));\r
+      }\r
+      FreePool (DataPacket);\r
+    } else {\r
+      // Signal this event again so we poll again ASAP\r
+      gBS->SignalEvent (Event);\r
+      mDataReceivedCallback (NumBytes, DataPacket);\r
+    }\r
+    HandledInterrupts |= ISP1761_DC_INTERRUPT_EP1RX;\r
+  }\r
+  if (DcInterrupts & ISP1761_DC_INTERRUPT_EP1TX) {\r
+    mDataSentCallback (1);\r
+    HandledInterrupts |= ISP1761_DC_INTERRUPT_EP1TX;\r
+  }\r
+  if (DcInterrupts & (ISP1761_DC_INTERRUPT_SOF | ISP1761_DC_INTERRUPT_PSOF)) {\r
+    // Don't care about SOFs or pseudo-SOFs\r
+    HandledInterrupts |= (ISP1761_DC_INTERRUPT_SOF | ISP1761_DC_INTERRUPT_PSOF);\r
+  }\r
+  if (ISP1761_DC_INTERRUPT_BRESET) {\r
+    HandledInterrupts |= ISP1761_DC_INTERRUPT_BRESET;\r
+  }\r
+  if (ISP1761_DC_INTERRUPT_HS_STAT) {\r
+    HandledInterrupts |= ISP1761_DC_INTERRUPT_HS_STAT;\r
+  }\r
+  if (ISP1761_DC_INTERRUPT_VBUS) {\r
+    HandledInterrupts |= ISP1761_DC_INTERRUPT_VBUS;\r
+  }\r
+\r
+  UnhandledInterrupts = DcInterrupts & (~HandledInterrupts) & ISP1761_DC_INTERRUPT_MASK;\r
+  if (UnhandledInterrupts) {\r
+    DEBUG ((EFI_D_ERROR, "USB: Unhandled DC Interrupts: 0x%08x\n",\r
+      UnhandledInterrupts));\r
+  }\r
+\r
+  // Check if we received any more data while we were handling the interrupt.\r
+  SelectEndpoint (ISP1761_EP1RX);\r
+  MoreBytes = READ_REG16 (ISP1761_BUFFER_LENGTH);\r
+  if (MoreBytes) {\r
+    HandledInterrupts &= ~ISP1761_DC_INTERRUPT_EP1RX;\r
+  }\r
+\r
+  WRITE_REG32 (ISP1761_DC_INTERRUPT, HandledInterrupts);\r
+}\r
+\r
+EFI_STATUS\r
+Isp1761PeriphSend (\r
+  IN        UINT8  EndpointIndex,\r
+  IN        UINTN  Size,\r
+  IN  CONST VOID  *Buffer\r
+  )\r
+{\r
+  return WriteEndpointBuffer (\r
+          (EndpointIndex << 1) | 0x1, //Convert to ISP1761 endpoint index, Tx\r
+          MAX_PACKET_SIZE_BULK,\r
+          Size,\r
+          Buffer\r
+          );\r
+}\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+Isp1761PeriphStart (\r
+  IN USB_DEVICE_DESCRIPTOR   *DeviceDescriptor,\r
+  IN VOID                   **Descriptors,\r
+  IN USB_DEVICE_RX_CALLBACK   RxCallback,\r
+  IN USB_DEVICE_TX_CALLBACK   TxCallback\r
+  )\r
+{\r
+  UINT16                    OtgStatus;\r
+  UINT8                    *Ptr;\r
+  EFI_STATUS                Status;\r
+  EFI_EVENT                 TimerEvent;\r
+\r
+  ASSERT (DeviceDescriptor != NULL);\r
+  ASSERT (Descriptors[0] != NULL);\r
+  ASSERT (RxCallback != NULL);\r
+  ASSERT (TxCallback != NULL);\r
+\r
+  WRITE_REG32 (ISP1761_DEVICE_UNLOCK, ISP1761_DEVICE_UNLOCK_MAGIC);\r
+\r
+  WRITE_REG32 (ISP1761_SW_RESET_REG, ISP1761_SW_RESET_ALL);\r
+  while (READ_REG32 (ISP1761_SW_RESET_REG) & ISP1761_SW_RESET_ALL) {\r
+    //busy wait\r
+  }\r
+  WRITE_REG32 (ISP1761_MODE, ISP1761_MODE_SFRESET);\r
+  while (READ_REG32 (ISP1761_MODE) & ISP1761_MODE_SFRESET) {\r
+    //busy wait\r
+  }\r
+  DEBUG ((EFI_D_INFO, "USB: Software reset done\n"));\r
+\r
+  WRITE_REG32 (ISP1761_DC_INTERRUPT_ENABLE, 0x03FFFFFF);\r
+  WRITE_REG32 (ISP1761_OTG_INTERRUPT_ENABLE_RISE, 0x07FF);\r
+\r
+  WRITE_REG8 (ISP1761_ADDRESS, ISP1761_ADDRESS_DEVEN);\r
+  WRITE_REG8 (ISP1761_MODE, ISP1761_MODE_WKUPCS | ISP1761_MODE_CLKAON);\r
+\r
+  // Use port 1 as peripheral controller (magic - disagrees with datasheet)\r
+  WRITE_REG32 (ISP1761_OTG_CTRL_SET, 0xffff0000);\r
+  WRITE_REG32 (ISP1761_OTG_CTRL_SET, 0x000014d1);\r
+\r
+  OtgStatus = READ_REG16 (ISP1761_OTG_STATUS);\r
+  if ((OtgStatus & ISP1761_OTG_STATUS_B_SESS_END) != 0) {\r
+    DEBUG ((EFI_D_ERROR, "USB: Vbus not powered.\n"));\r
+  }\r
+  if ((OtgStatus & ISP1761_OTG_STATUS_A_B_SESS_VLD) == 0) {\r
+    DEBUG ((EFI_D_ERROR, "USB: Session not valid.\n"));\r
+  }\r
+\r
+  // Configure Control endpoints\r
+  SelectEndpoint (0x20);\r
+  WRITE_REG32 (ISP1761_ENDPOINT_MAX_PACKET_SIZE, MAX_PACKET_SIZE_CONTROL);\r
+  WRITE_REG32 (ISP1761_ENDPOINT_TYPE, ISP1761_ENDPOINT_TYPE_ENABLE);\r
+  SelectEndpoint (0x0);\r
+  WRITE_REG32 (ISP1761_ENDPOINT_MAX_PACKET_SIZE, MAX_PACKET_SIZE_CONTROL);\r
+  WRITE_REG32 (ISP1761_ENDPOINT_TYPE, ISP1761_ENDPOINT_TYPE_ENABLE);\r
+  SelectEndpoint (0x1);\r
+  WRITE_REG32 (ISP1761_ENDPOINT_MAX_PACKET_SIZE, MAX_PACKET_SIZE_CONTROL);\r
+  WRITE_REG32 (ISP1761_ENDPOINT_TYPE, ISP1761_ENDPOINT_TYPE_ENABLE);\r
+\r
+  // Interrupt on all ACK and NAK\r
+  WRITE_REG32 (ISP1761_INTERRUPT_CONFIG, ISP1761_INTERRUPT_CONFIG_ACK_ONLY);\r
+\r
+  mDeviceDescriptor = DeviceDescriptor;\r
+  mDescriptors = Descriptors[0];\r
+\r
+  // Right now we just support one configuration\r
+  ASSERT (mDeviceDescriptor->NumConfigurations == 1);\r
+  // ... and one interface\r
+  mConfigDescriptor = (USB_CONFIG_DESCRIPTOR *)mDescriptors;\r
+  ASSERT (mConfigDescriptor->NumInterfaces == 1);\r
+\r
+  Ptr = ((UINT8 *) mDescriptors) + sizeof (USB_CONFIG_DESCRIPTOR);\r
+  mInterfaceDescriptor = (USB_INTERFACE_DESCRIPTOR *) Ptr;\r
+  Ptr += sizeof (USB_INTERFACE_DESCRIPTOR);\r
+\r
+  mEndpointDescriptors = (USB_ENDPOINT_DESCRIPTOR *) Ptr;\r
+\r
+  mDataReceivedCallback = RxCallback;\r
+  mDataSentCallback = TxCallback;\r
+\r
+  // Register a timer event so CheckInterupts gets called periodically\r
+  Status = gBS->CreateEvent (\r
+                  EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
+                  TPL_CALLBACK,\r
+                  CheckInterrupts,\r
+                  NULL,\r
+                  &TimerEvent\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = gBS->SetTimer (\r
+                  TimerEvent,\r
+                  TimerPeriodic,\r
+                  ISP1761_INTERRUPT_POLL_PERIOD\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  return Status;\r
+}\r
+\r
+USB_DEVICE_PROTOCOL mUsbDevice = {\r
+  Isp1761PeriphStart,\r
+  Isp1761PeriphSend\r
+};\r
+\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+Isp1761PeriphEntryPoint (\r
+  IN EFI_HANDLE                            ImageHandle,\r
+  IN EFI_SYSTEM_TABLE                      *SystemTable\r
+  )\r
+{\r
+  UINT32      DeviceId;\r
+  EFI_HANDLE  Handle;\r
+\r
+  DeviceId = READ_REG32 (ISP1761_DEVICE_ID);\r
+\r
+  if (DeviceId != ISP1761_DEVICE_ID_VAL) {\r
+    DEBUG ((EFI_D_ERROR,\r
+      "ERROR: Read incorrect device ID for ISP1761: 0x%08x, expected 0x%08x\n",\r
+      DeviceId , ISP1761_DEVICE_ID_VAL\r
+      ));\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  Handle = NULL;\r
+  return gBS->InstallProtocolInterface (\r
+    &Handle,\r
+    &gUsbDeviceProtocolGuid,\r
+    EFI_NATIVE_INTERFACE,\r
+    &mUsbDevice\r
+    );\r
+}\r
diff --git a/EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.h b/EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.h
new file mode 100644 (file)
index 0000000..9e60e15
--- /dev/null
@@ -0,0 +1,122 @@
+/** @file\r
+\r
+  Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>\r
+\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __ISP1761_USB_DXE_H__\r
+#define __ISP1761_USB_DXE_H__\r
+\r
+#define ISP1761_USB_BASE FixedPcdGet32 (PcdIsp1761BaseAddress)\r
+\r
+#define READ_REG32(Offset) MmioRead32 (ISP1761_USB_BASE + Offset)\r
+#define READ_REG16(Offset) (UINT16) READ_REG32 (Offset)\r
+#define WRITE_REG32(Offset, Val)  MmioWrite32 (ISP1761_USB_BASE + Offset, Val)\r
+#define WRITE_REG16(Offset, Val)  MmioWrite32 (ISP1761_USB_BASE + Offset, (UINT32) Val)\r
+#define WRITE_REG8(Offset, Val)   MmioWrite32 (ISP1761_USB_BASE + Offset, (UINT32) Val)\r
+\r
+// Max packet size in bytes (For Full Speed USB 64 is the only valid value)\r
+#define MAX_PACKET_SIZE_CONTROL     64\r
+\r
+#define MAX_PACKET_SIZE_BULK        512\r
+\r
+// 8 Endpoints, in and out. Don't count the Endpoint 0 setup buffer\r
+#define ISP1761_NUM_ENDPOINTS               16\r
+\r
+// Endpoint Indexes\r
+#define ISP1761_EP0SETUP                    0x20\r
+#define ISP1761_EP0RX                       0x00\r
+#define ISP1761_EP0TX                       0x01\r
+#define ISP1761_EP1RX                       0x02\r
+#define ISP1761_EP1TX                       0x03\r
+\r
+// DcInterrupt bits\r
+#define ISP1761_DC_INTERRUPT_BRESET         BIT0\r
+#define ISP1761_DC_INTERRUPT_SOF            BIT1\r
+#define ISP1761_DC_INTERRUPT_PSOF           BIT2\r
+#define ISP1761_DC_INTERRUPT_SUSP           BIT3\r
+#define ISP1761_DC_INTERRUPT_RESUME         BIT4\r
+#define ISP1761_DC_INTERRUPT_HS_STAT        BIT5\r
+#define ISP1761_DC_INTERRUPT_DMA            BIT6\r
+#define ISP1761_DC_INTERRUPT_VBUS           BIT7\r
+#define ISP1761_DC_INTERRUPT_EP0SETUP       BIT8\r
+#define ISP1761_DC_INTERRUPT_EP0RX          BIT10\r
+#define ISP1761_DC_INTERRUPT_EP0TX          BIT11\r
+#define ISP1761_DC_INTERRUPT_EP1RX          BIT12\r
+#define ISP1761_DC_INTERRUPT_EP1TX          BIT13\r
+// All valid peripheral controller interrupts\r
+#define ISP1761_DC_INTERRUPT_MASK           0x003FFFDFF\r
+\r
+#define ISP1761_ADDRESS                     0x200\r
+#define ISP1761_ADDRESS_DEVEN               BIT7\r
+\r
+#define ISP1761_MODE                        0x20C\r
+#define ISP1761_MODE_DATA_BUS_WIDTH         BIT8\r
+#define ISP1761_MODE_CLKAON                 BIT7\r
+#define ISP1761_MODE_SFRESET                BIT4\r
+#define ISP1761_MODE_WKUPCS                 BIT2\r
+\r
+#define ISP1761_ENDPOINT_MAX_PACKET_SIZE    0x204\r
+\r
+#define ISP1761_ENDPOINT_TYPE               0x208\r
+#define ISP1761_ENDPOINT_TYPE_NOEMPKT       BIT4\r
+#define ISP1761_ENDPOINT_TYPE_ENABLE        BIT3\r
+\r
+#define ISP1761_INTERRUPT_CONFIG            0x210\r
+// Interrupt config value to only interrupt on ACK of IN and OUT tokens\r
+#define ISP1761_INTERRUPT_CONFIG_ACK_ONLY   BIT2 | BIT5 | BIT6\r
+\r
+#define ISP1761_DC_INTERRUPT                0x218\r
+#define ISP1761_DC_INTERRUPT_ENABLE         0x214\r
+\r
+#define ISP1761_CTRL_FUNCTION               0x228\r
+#define ISP1761_CTRL_FUNCTION_VENDP         BIT3\r
+#define ISP1761_CTRL_FUNCTION_DSEN          BIT2\r
+#define ISP1761_CTRL_FUNCTION_STATUS        BIT1\r
+\r
+#define ISP1761_DEVICE_UNLOCK               0x27C\r
+#define ISP1761_DEVICE_UNLOCK_MAGIC         0xAA37\r
+\r
+#define ISP1761_SW_RESET_REG                0x30C\r
+#define ISP1761_SW_RESET_ALL                BIT0\r
+\r
+#define ISP1761_DEVICE_ID                   0x370\r
+\r
+#define ISP1761_OTG_CTRL_SET                0x374\r
+#define ISP1761_OTG_CTRL_CLR                OTG_CTRL_SET + 2\r
+#define ISP1761_OTG_CTRL_OTG_DISABLE        BIT10\r
+#define ISP1761_OTG_CTRL_VBUS_CHRG          BIT6\r
+#define ISP1761_OTG_CTRL_VBUS_DISCHRG       BIT5\r
+#define ISP1761_OTG_CTRL_DM_PULLDOWN        BIT2\r
+#define ISP1761_OTG_CTRL_DP_PULLDOWN        BIT1\r
+#define ISP1761_OTG_CTRL_DP_PULLUP          BIT0\r
+\r
+#define ISP1761_OTG_STATUS                  0x378\r
+#define ISP1761_OTG_STATUS_B_SESS_END       BIT7\r
+#define ISP1761_OTG_STATUS_A_B_SESS_VLD     BIT1\r
+\r
+#define ISP1761_OTG_INTERRUPT_LATCH_SET     0x37C\r
+#define ISP1761_OTG_INTERRUPT_LATCH_CLR     0x37E\r
+#define ISP1761_OTG_INTERRUPT_ENABLE_RISE   0x384\r
+\r
+#define ISP1761_DMA_ENDPOINT_INDEX          0x258\r
+\r
+#define ISP1761_ENDPOINT_INDEX              0x22c\r
+#define ISP1761_DATA_PORT                   0x220\r
+#define ISP1761_BUFFER_LENGTH               0x21c\r
+\r
+// Device ID Values\r
+#define PHILLIPS_VENDOR_ID_VAL 0x04cc\r
+#define ISP1761_PRODUCT_ID_VAL 0x1761\r
+#define ISP1761_DEVICE_ID_VAL ((ISP1761_PRODUCT_ID_VAL << 16) |\\r
+                               PHILLIPS_VENDOR_ID_VAL)\r
+\r
+#endif //ifndef __ISP1761_USB_DXE_H__\r
diff --git a/EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf b/EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf
new file mode 100644 (file)
index 0000000..28a41b5
--- /dev/null
@@ -0,0 +1,44 @@
+#/** @file\r
+#\r
+#  Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>\r
+#\r
+#  This program and the accompanying materials\r
+#  are licensed and made available under the terms and conditions of the BSD License\r
+#  which accompanies this distribution. The full text of the license may be found at\r
+#  http://opensource.org/licenses/bsd-license.php\r
+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+#\r
+#**/\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = Isp1761PeriphDxe\r
+  FILE_GUID                      = 72d78ea6-4dee-11e3-8100-f3842a48d0a0\r
+  MODULE_TYPE                    = UEFI_DRIVER\r
+  VERSION_STRING                 = 1.0\r
+  ENTRY_POINT                    = Isp1761PeriphEntryPoint\r
+\r
+[Sources.common]\r
+  Isp1761UsbDxe.c\r
+\r
+[LibraryClasses]\r
+  DebugLib\r
+  IoLib\r
+  MemoryAllocationLib\r
+  TimerLib\r
+  UefiBootServicesTableLib\r
+  UefiDriverEntryPoint\r
+\r
+[Protocols]\r
+  gEfiDriverBindingProtocolGuid\r
+  gUsbDeviceProtocolGuid\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+  EmbeddedPkg/EmbeddedPkg.dec\r
+\r
+[Pcd]\r
+  gEmbeddedTokenSpaceGuid.PcdIsp1761BaseAddress
\ No newline at end of file
index 97d2a165a9490ca1a6390f4a79100abe6e7a547f..95fb60e880198fa8a35202ef998c8ccc13f4400b 100644 (file)
   gEmbeddedTokenSpaceGuid.PcdPrePiCpuMemorySize|32|UINT8|0x00000010\r
   gEmbeddedTokenSpaceGuid.PcdPrePiCpuIoSize|0|UINT8|0x00000011\r
 \r
   gEmbeddedTokenSpaceGuid.PcdPrePiCpuMemorySize|32|UINT8|0x00000010\r
   gEmbeddedTokenSpaceGuid.PcdPrePiCpuIoSize|0|UINT8|0x00000011\r
 \r
+  # ISP1761 USB OTG Controller\r
+  gEmbeddedTokenSpaceGuid.PcdIsp1761BaseAddress|0|UINT32|0x00000021\r
+\r
 [PcdsFixedAtBuild.AARCH64]\r
   gEmbeddedTokenSpaceGuid.PcdPrePiCpuMemorySize|48|UINT8|0x00000010\r
   gEmbeddedTokenSpaceGuid.PcdPrePiCpuIoSize|0|UINT8|0x00000011\r
 \r
 [PcdsFixedAtBuild.AARCH64]\r
   gEmbeddedTokenSpaceGuid.PcdPrePiCpuMemorySize|48|UINT8|0x00000010\r
   gEmbeddedTokenSpaceGuid.PcdPrePiCpuIoSize|0|UINT8|0x00000011\r
 \r
+  # ISP1761 USB OTG Controller\r
+  gEmbeddedTokenSpaceGuid.PcdIsp1761BaseAddress|0|UINT64|0x00000021\r
+\r
 [PcdsFixedAtBuild.IA32]\r
   gEmbeddedTokenSpaceGuid.PcdPrePiCpuMemorySize|36|UINT8|0x00000010\r
   gEmbeddedTokenSpaceGuid.PcdPrePiCpuIoSize|16|UINT8|0x00000011\r
 [PcdsFixedAtBuild.IA32]\r
   gEmbeddedTokenSpaceGuid.PcdPrePiCpuMemorySize|36|UINT8|0x00000010\r
   gEmbeddedTokenSpaceGuid.PcdPrePiCpuIoSize|16|UINT8|0x00000011\r