From dbfd80d87363fe855eee7eadd029ff9c43e44bb2 Mon Sep 17 00:00:00 2001 From: Olivier Martin Date: Wed, 5 Mar 2014 04:32:48 +0000 Subject: [PATCH] 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 git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@15314 6f19259b-4bc3-4df7-8a09-765794883524 --- .../Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c | 636 ++++++++++++++++++ .../Drivers/Isp1761UsbDxe/Isp1761UsbDxe.h | 122 ++++ .../Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf | 44 ++ EmbeddedPkg/EmbeddedPkg.dec | 6 + 4 files changed, 808 insertions(+) create mode 100644 EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c create mode 100644 EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.h create mode 100644 EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf 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.
+ + 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 +#include +#include +#include +#include +#include + +#include + +#include + +#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.
+ + 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.
+# +# 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 -- 2.39.2