public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Leif Lindholm" <leif@nuviainc.com>
To: Ard Biesheuvel <ard.biesheuvel@arm.com>
Cc: devel@edk2.groups.io
Subject: Re: [PATCH edk2-platforms v3 8/8] Platform/ARM/VExpressPkg: incorporate ISP 1761 USB host driver
Date: Mon, 4 May 2020 12:00:22 +0100	[thread overview]
Message-ID: <20200504110022.GE21486@vanye> (raw)
In-Reply-To: <20200430171650.24139-9-ard.biesheuvel@arm.com>

On Thu, Apr 30, 2020 at 19:16:49 +0200, Ard Biesheuvel wrote:
> Incorporate the ISP 1761 USB host driver from EmbeddedPkg, which is
> only used on obsolete ARM development platforms and does not follow
> the UEFI driver model. This will allow us to drop it from the core EDK2
> repository.
> 
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
> ---
>  Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc                |   2 +-
>  Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf                |   2 +-
>  Platform/ARM/VExpressPkg/ArmVExpress.dsc.inc                     |   2 +-
>  Platform/ARM/VExpressPkg/ArmVExpressPkg.dec                      |   3 +
>  Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c   | 636 ++++++++++++++++++++
>  Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.h   | 123 ++++
>  Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf |  39 ++
>  7 files changed, 804 insertions(+), 3 deletions(-)
> 
> diff --git a/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc b/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc
> index bee7913feb52..144dd4f8b8e9 100644
> --- a/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc
> +++ b/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.dsc
> @@ -157,7 +157,7 @@ [PcdsFixedAtBuild.common]
>    gArmTokenSpaceGuid.PcdGicInterruptInterfaceBase|0x2C002000
>  
>    # ISP1761 USB OTG Controller
> -  gEmbeddedTokenSpaceGuid.PcdIsp1761BaseAddress|0x1B000000
> +  gArmVExpressTokenSpaceGuid.PcdIsp1761BaseAddress|0x1B000000
>  
>    # Ethernet (SMSC LAN9118)
>    gArmVExpressTokenSpaceGuid.PcdLan9118DxeBaseAddress|0x1A000000
> diff --git a/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf b/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf
> index b133375e1a11..f98de162e634 100644
> --- a/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf
> +++ b/Platform/ARM/VExpressPkg/ArmVExpress-CTA15-A7.fdf
> @@ -126,7 +126,7 @@ [FV.FvMain]
>    #
>    # USB support
>    #
> -  INF EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf
> +  INF Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf
>  
>    #
>    # Android Fastboot
> diff --git a/Platform/ARM/VExpressPkg/ArmVExpress.dsc.inc b/Platform/ARM/VExpressPkg/ArmVExpress.dsc.inc
> index 912ad5e5a1ec..bde3437b56d7 100644
> --- a/Platform/ARM/VExpressPkg/ArmVExpress.dsc.inc
> +++ b/Platform/ARM/VExpressPkg/ArmVExpress.dsc.inc
> @@ -455,4 +455,4 @@ [Components.common]
>  [Components.ARM]
>  
>    # ISP1761 USB OTG Controller
> -  EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf
> +  Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf
> diff --git a/Platform/ARM/VExpressPkg/ArmVExpressPkg.dec b/Platform/ARM/VExpressPkg/ArmVExpressPkg.dec
> index e42905aabf2b..f78c5ce7c764 100644
> --- a/Platform/ARM/VExpressPkg/ArmVExpressPkg.dec
> +++ b/Platform/ARM/VExpressPkg/ArmVExpressPkg.dec
> @@ -67,3 +67,6 @@ [PcdsFixedAtBuild.common]
>    # The default feature mask below disables full duplex negotiation, since full
>    # duplex operation is suspected to be broken in the driver.
>    gArmVExpressTokenSpaceGuid.PcdLan9118NegotiationFeatureMask|0xFFFFFEBF|UINT32|0x00000028
> +
> +  # ISP1761 USB OTG Controller
> +  gArmVExpressTokenSpaceGuid.PcdIsp1761BaseAddress|0|UINT32|0x00000029
> diff --git a/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c b/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c
> new file mode 100644
> index 000000000000..c23c0ecf737d
> --- /dev/null
> +++ b/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c
> @@ -0,0 +1,636 @@
> +/** @file
> +
> +  WARNING:
> +  This driver fails to follow the UEFI driver model without a good
> +  reason, and only remains in the tree because it is still used by
> +  a small number of platforms. It will be removed when no longer used.
> +  New platforms should not use it, and no one should use this as
> +  reference code for developing new drivers.
> +
> +  Copyright (c) 2013-2015, ARM Ltd. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#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 explicitly 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 CheckInterrupts 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/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.h b/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.h
> new file mode 100644
> index 000000000000..f7155d48d8ad
> --- /dev/null
> +++ b/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.h
> @@ -0,0 +1,123 @@
> +/** @file
> +
> +  WARNING:
> +  This driver fails to follow the UEFI driver model without a good
> +  reason, and only remains in the tree because it is still used by
> +  a small number of platforms. It will be removed when no longer used.
> +  New platforms should not use it, and no one should use this as
> +  reference code for developing new drivers.
> +
> +  Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#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/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf b/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf
> new file mode 100644
> index 000000000000..b161547bf73a
> --- /dev/null
> +++ b/Platform/ARM/VExpressPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.inf
> @@ -0,0 +1,39 @@
> +#/** @file
> +#
> +#  Copyright (c) 2013-2015, ARM Ltd. All rights reserved.<BR>
> +#
> +#  SPDX-License-Identifier: BSD-2-Clause-Patent
> +#

Comment only:
This file, unlike some other .infs in this series, does not include
the WARNING header. Feel free to fold one in before pushing.

> +#
> +#**/
> +
> +[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
> +  UefiBootServicesTableLib
> +  UefiDriverEntryPoint
> +
> +[Protocols]
> +  gEfiDriverBindingProtocolGuid
> +  gUsbDeviceProtocolGuid
> +
> +[Packages]
> +  EmbeddedPkg/EmbeddedPkg.dec
> +  MdePkg/MdePkg.dec
> +  MdeModulePkg/MdeModulePkg.dec
> +  Platform/ARM/VExpressPkg/ArmVExpressPkg.dec
> +
> +[Pcd]
> +  gArmVExpressTokenSpaceGuid.PcdIsp1761BaseAddress
> -- 
> 2.17.1
> 

  reply	other threads:[~2020-05-04 11:00 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-04-30 17:16 [PATCH edk2-platforms v3 0/8] move obsolete platform drivers out of core edk2 Ard Biesheuvel
2020-04-30 17:16 ` [PATCH edk2-platforms v3 1/8] Omap35xxPkg/LcdGraphicsOutputDxe: add missing protocol reference Ard Biesheuvel
2020-04-30 17:16 ` [PATCH edk2-platforms v3 2/8] Platform/ARM/VExpressPkg: incorporate PL180 driver Ard Biesheuvel
2020-04-30 17:16 ` [PATCH edk2-platforms v3 3/8] Platform/ARM/JunoPkg: incorporate SiI3132 SATA controller driver Ard Biesheuvel
2020-05-04 10:57   ` Leif Lindholm
2020-04-30 17:16 ` [PATCH edk2-platforms v3 4/8] Silicon/Synopsys/DesignWare: import eMMC DXE driver from EmbeddedPkg Ard Biesheuvel
2020-05-04 10:58   ` Leif Lindholm
2020-05-04 11:40     ` [edk2-devel] " Philippe Mathieu-Daudé
2020-05-04 11:44       ` Philippe Mathieu-Daudé
2020-04-30 17:16 ` [PATCH edk2-platforms v3 5/8] Platform/HiKey: switch to relocated version of eMMC driver Ard Biesheuvel
2020-04-30 17:16 ` [PATCH edk2-platforms v3 6/8] Platform/ARM/VExpressPkg: incorporate Lan91x driver Ard Biesheuvel
2020-05-04 11:41   ` [edk2-devel] " Philippe Mathieu-Daudé
2020-05-04 11:44     ` Philippe Mathieu-Daudé
2020-04-30 17:16 ` [PATCH edk2-platforms v3 7/8] Platform/ARM/VExpressPkg: incorporate Lan9118 driver Ard Biesheuvel
2020-05-04 11:42   ` [edk2-devel] " Philippe Mathieu-Daudé
2020-05-04 11:44     ` Philippe Mathieu-Daudé
2020-04-30 17:16 ` [PATCH edk2-platforms v3 8/8] Platform/ARM/VExpressPkg: incorporate ISP 1761 USB host driver Ard Biesheuvel
2020-05-04 11:00   ` Leif Lindholm [this message]
2020-05-04 11:00 ` [PATCH edk2-platforms v3 0/8] move obsolete platform drivers out of core edk2 Leif Lindholm
2020-05-04 13:19   ` Ard Biesheuvel

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200504110022.GE21486@vanye \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox