--- /dev/null
+/** @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
--- /dev/null
+/** @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