summaryrefslogtreecommitdiff
path: root/EmbeddedPkg
diff options
context:
space:
mode:
authorOlivier Martin <olivier.martin@arm.com>2014-03-05 04:32:48 +0000
committeroliviermartin <oliviermartin@6f19259b-4bc3-4df7-8a09-765794883524>2014-03-05 04:32:48 +0000
commitdbfd80d87363fe855eee7eadd029ff9c43e44bb2 (patch)
tree4609a9e34a8fbe9078e0594a3ab30fc4d478c4be /EmbeddedPkg
parentd972f80b0866404f1141a3d2848cb7ecd023acfb (diff)
downloadedk2-platforms-dbfd80d87363fe855eee7eadd029ff9c43e44bb2.tar.xz
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
Diffstat (limited to 'EmbeddedPkg')
-rw-r--r--EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c636
-rw-r--r--EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.h122
-rw-r--r--EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf44
-rw-r--r--EmbeddedPkg/EmbeddedPkg.dec6
4 files changed, 808 insertions, 0 deletions
diff --git a/EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c b/EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c
new file mode 100644
index 0000000000..77d4f2a837
--- /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 0000000000..9e60e155d7
--- /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 0000000000..28a41b5d5f
--- /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 97d2a165a9..95fb60e880 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