/** @file * * Copyright (c) 2020, ARM Limited. All rights reserved. * Copyright (c) 2017-2018, Andrey Warkentin * Copyright (c) 2015-2016, Linaro Limited. All rights reserved. * * SPDX-License-Identifier: BSD-2-Clause-Patent * **/ #include #include "DwUsbHostDxe.h" #include "DwcHw.h" /* * Excessive (10s) timeout for reset. */ #define DW_HC_RESET_TIMEOUT_MS (10000) /* * TimerPeriodic to account for timeout processing * within DwHcTransfer. */ #define TimerForTransfer TimerRelative /* * https://www.quicklogic.com/assets/pdf/data-sheets/QL-Hi-Speed-USB-2.0-OTG-Controller-Data-Sheet.pdf */ typedef enum { XFER_NOT_HALTED, XFER_ERROR, XFER_CSPLIT, XFER_NAK, XFER_STALL, XFER_FRMOVRUN, XFER_DONE } CHANNEL_HALT_REASON; typedef struct { BOOLEAN Splitting; BOOLEAN SplitStart; UINT32 Tries; } SPLIT_CONTROL; EFI_STATUS DwHcInit ( IN DWUSB_OTGHC_DEV *DwHc, IN EFI_EVENT Timeout ); EFI_STATUS DwCoreInit ( IN DWUSB_OTGHC_DEV *DwHc, IN EFI_EVENT Timeout ); EFI_STATUS Wait4Bit ( IN EFI_EVENT Timeout, IN UINT32 Reg, IN UINT32 Mask, IN BOOLEAN Set ) { UINT32 Value; do { Value = MmioRead32 (Reg); if (!Set) { Value = ~Value; } if ((Value & Mask) == Mask) { return EFI_SUCCESS; } } while (EFI_ERROR (gBS->CheckEvent (Timeout))); return EFI_TIMEOUT; } CHANNEL_HALT_REASON Wait4Chhltd ( IN DWUSB_OTGHC_DEV *DwHc, IN EFI_EVENT Timeout, IN UINT32 Channel, IN UINT32 *Sub, IN UINT32 *Toggle, IN BOOLEAN IgnoreAck, IN SPLIT_CONTROL *Split ) { EFI_STATUS Status; UINT32 Hcint, Hctsiz; UINT32 HcintCompHltAck = DWC2_HCINT_XFERCOMP; MicroSecondDelay (100); Status = Wait4Bit (Timeout, DwHc->DwUsbBase + HCINT (Channel), DWC2_HCINT_CHHLTD, 1); if (EFI_ERROR (Status)) { return XFER_NOT_HALTED; } MicroSecondDelay (100); Hcint = MmioRead32 (DwHc->DwUsbBase + HCINT (Channel)); ASSERT ((Hcint & DWC2_HCINT_CHHLTD) != 0); Hcint &= ~DWC2_HCINT_CHHLTD; if (!IgnoreAck || (Split->Splitting && Split->SplitStart)) { HcintCompHltAck |= DWC2_HCINT_ACK; } else { Hcint &= ~DWC2_HCINT_ACK; } if ((Hcint & DWC2_HCINT_XACTERR) != 0) { return XFER_ERROR; } if ((Hcint & DWC2_HCINT_NYET) != 0) { return XFER_CSPLIT; } if ((Hcint & DWC2_HCINT_NAK) != 0) { return XFER_NAK; } if ((Hcint & DWC2_HCINT_STALL) != 0) { return XFER_STALL; } if ((Hcint & DWC2_HCINT_FRMOVRUN) != 0) { return XFER_FRMOVRUN; } if (Split->Splitting && Split->SplitStart && ((Hcint & DWC2_HCINT_ACK) != 0)) { Split->SplitStart = FALSE; Split->Tries = 0; return XFER_CSPLIT; } if (Hcint != HcintCompHltAck) { DEBUG ((DEBUG_ERROR, "Wait4Chhltd: Channel %u HCINT 0x%x %a%a\n", Channel, Hcint, IgnoreAck ? "IgnoreAck " : "", Split->SplitStart ? "split start" : (Split->Splitting ? "split complete" : ""))); return XFER_ERROR; } Hctsiz = MmioRead32 (DwHc->DwUsbBase + HCTSIZ (Channel)); *Sub = (Hctsiz & DWC2_HCTSIZ_XFERSIZE_MASK) >> DWC2_HCTSIZ_XFERSIZE_OFFSET; *Toggle = (Hctsiz & DWC2_HCTSIZ_PID_MASK) >> DWC2_HCTSIZ_PID_OFFSET; return XFER_DONE; } VOID DwOtgHcInit ( IN DWUSB_OTGHC_DEV *DwHc, IN UINT8 HcNum, IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, IN UINT8 DeviceSpeed, IN UINT8 DevAddr, IN UINT8 Endpoint, IN UINT8 EpDir, IN UINT8 EpType, IN UINT16 MaxPacket, IN SPLIT_CONTROL *SplitControl ) { UINT32 Split = 0; UINT32 Hcchar = (DevAddr << DWC2_HCCHAR_DEVADDR_OFFSET) | (Endpoint << DWC2_HCCHAR_EPNUM_OFFSET) | (EpDir << DWC2_HCCHAR_EPDIR_OFFSET) | (EpType << DWC2_HCCHAR_EPTYPE_OFFSET) | (MaxPacket << DWC2_HCCHAR_MPS_OFFSET) | ((DeviceSpeed == EFI_USB_SPEED_LOW) ? DWC2_HCCHAR_LSPDDEV : 0); MmioWrite32 (DwHc->DwUsbBase + HCINT (HcNum), 0x3FFF); MmioWrite32 (DwHc->DwUsbBase + HCCHAR (HcNum), Hcchar); if (SplitControl->Splitting) { Split = DWC2_HCSPLT_SPLTENA | ((Translator->TranslatorPortNumber) << DWC2_HCSPLT_PRTADDR_OFFSET) | ((Translator->TranslatorHubAddress) << DWC2_HCSPLT_HUBADDR_OFFSET); if (!SplitControl->SplitStart) { Split |= DWC2_HCSPLT_COMPSPLT; } } MmioWrite32 (DwHc->DwUsbBase + HCSPLT (HcNum), Split); } EFI_STATUS DwCoreReset ( IN DWUSB_OTGHC_DEV *DwHc, IN EFI_EVENT Timeout ) { EFI_STATUS Status; Status = Wait4Bit (Timeout, DwHc->DwUsbBase + GRSTCTL, DWC2_GRSTCTL_AHBIDLE, 1); if (Status) { DEBUG ((DEBUG_ERROR, "DwCoreReset: AHBIDLE Timeout!\n")); return Status; } MmioWrite32 (DwHc->DwUsbBase + GRSTCTL, DWC2_GRSTCTL_CSFTRST); Status = Wait4Bit (Timeout, DwHc->DwUsbBase + GRSTCTL, DWC2_GRSTCTL_CSFTRST, 0); if (Status) { DEBUG ((DEBUG_ERROR, "DwCoreReset: CSFTRST Timeout!\n")); return Status; } MicroSecondDelay (100000); return EFI_SUCCESS; } STATIC EFI_STATUS DwHcTransfer ( IN DWUSB_OTGHC_DEV *DwHc, IN EFI_EVENT Timeout, IN UINT32 Channel, IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, IN UINT8 DeviceSpeed, IN UINT8 DeviceAddress, IN UINTN MaximumPacketLength, IN OUT UINT32 *Pid, IN UINT32 TransferDirection, IN OUT VOID *Data, IN OUT UINTN *DataLength, IN UINT32 EpAddress, IN UINT32 EpType, OUT UINT32 *TransferResult, IN BOOLEAN IgnoreAck ) { UINT32 TxferLen; UINT32 Done = 0; UINT32 NumPackets; UINT32 Sub; UINT32 Ret = 0; UINT32 StopTransfer = 0; EFI_STATUS Status = EFI_SUCCESS; SPLIT_CONTROL Split = { 0 }; EFI_TPL Tpl = gBS->RaiseTPL (TPL_NOTIFY); *TransferResult = EFI_USB_NOERROR; do { RestartXfer: if (DeviceSpeed == EFI_USB_SPEED_LOW || DeviceSpeed == EFI_USB_SPEED_FULL) { Split.Splitting = TRUE; Split.SplitStart = TRUE; Split.Tries = 0; } TxferLen = *DataLength - Done; if (TxferLen > DWC2_MAX_TRANSFER_SIZE) { TxferLen = DWC2_MAX_TRANSFER_SIZE - MaximumPacketLength + 1; } if (TxferLen > DWC2_DATA_BUF_SIZE) { TxferLen = DWC2_DATA_BUF_SIZE - MaximumPacketLength + 1; } if (Split.Splitting || TxferLen == 0) { NumPackets = 1; } else { NumPackets = (TxferLen + MaximumPacketLength - 1) / MaximumPacketLength; if (NumPackets > DWC2_MAX_PACKET_COUNT) { NumPackets = DWC2_MAX_PACKET_COUNT; TxferLen = NumPackets * MaximumPacketLength; } } if (TransferDirection) { // in TxferLen = NumPackets * MaximumPacketLength; } else { CopyMem (DwHc->AlignedBuffer, Data + Done, TxferLen); ArmDataSynchronizationBarrier (); } RestartChannel: MmioWrite32 (DwHc->DwUsbBase + HCDMA (Channel), (UINTN)DwHc->AlignedBufferBusAddress); DwOtgHcInit (DwHc, Channel, Translator, DeviceSpeed, DeviceAddress, EpAddress, TransferDirection, EpType, MaximumPacketLength, &Split); MmioWrite32 (DwHc->DwUsbBase + HCTSIZ (Channel), (TxferLen << DWC2_HCTSIZ_XFERSIZE_OFFSET) | (NumPackets << DWC2_HCTSIZ_PKTCNT_OFFSET) | (*Pid << DWC2_HCTSIZ_PID_OFFSET)); MmioAndThenOr32 (DwHc->DwUsbBase + HCCHAR (Channel), ~(DWC2_HCCHAR_MULTICNT_MASK | DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS), ((1 << DWC2_HCCHAR_MULTICNT_OFFSET) | DWC2_HCCHAR_CHEN)); Ret = Wait4Chhltd (DwHc, Timeout, Channel, &Sub, Pid, IgnoreAck, &Split); if (Ret == XFER_NOT_HALTED) { *TransferResult = EFI_USB_ERR_TIMEOUT; MmioOr32 (DwHc->DwUsbBase + HCCHAR (Channel), DWC2_HCCHAR_CHDIS); Status = gBS->SetTimer (Timeout, TimerRelative, EFI_TIMER_PERIOD_MILLISECONDS (1)); ASSERT_EFI_ERROR (Status); if (EFI_ERROR (Status)) { break; } Status = Wait4Bit (Timeout, DwHc->DwUsbBase + HCINT (Channel), DWC2_HCINT_CHHLTD, 1); if (Status == EFI_SUCCESS) { Status = EFI_TIMEOUT; } else { DEBUG ((DEBUG_ERROR, "Channel %u did not halt\n", Channel)); Status = EFI_DEVICE_ERROR; } break; } else if (Ret == XFER_STALL) { *TransferResult = EFI_USB_ERR_STALL; Status = EFI_DEVICE_ERROR; break; } else if (Ret == XFER_CSPLIT) { ASSERT (Split.Splitting); if (Split.Tries++ < 3) { goto RestartChannel; } goto RestartXfer; } else if (Ret == XFER_ERROR) { *TransferResult = EFI_USB_ERR_CRC | EFI_USB_ERR_TIMEOUT | EFI_USB_ERR_BITSTUFF | EFI_USB_ERR_SYSTEM; Status = EFI_DEVICE_ERROR; break; } else if (Ret == XFER_FRMOVRUN) { goto RestartChannel; } else if (Ret == XFER_NAK) { if (Split.Splitting && (EpType == DWC2_HCCHAR_EPTYPE_CONTROL)) { goto RestartXfer; } *TransferResult = EFI_USB_ERR_NAK; Status = EFI_DEVICE_ERROR; break; } if (TransferDirection) { // in ArmDataSynchronizationBarrier (); TxferLen -= Sub; CopyMem (Data + Done, DwHc->AlignedBuffer, TxferLen); if (Sub) { StopTransfer = 1; } } Done += TxferLen; } while (Done < *DataLength && !StopTransfer); MmioWrite32 (DwHc->DwUsbBase + HCINTMSK (Channel), 0); MmioWrite32 (DwHc->DwUsbBase + HCINT (Channel), 0xFFFFFFFF); *DataLength = Done; gBS->RestoreTPL (Tpl); ASSERT (!EFI_ERROR (Status) || *TransferResult != EFI_USB_NOERROR); return Status; } STATIC DWUSB_DEFERRED_REQ * DwHcFindDeferredTransfer ( IN DWUSB_OTGHC_DEV *DwHc, IN UINT8 DeviceAddress, IN UINT8 EndPointAddress ) { LIST_ENTRY *Entry; EFI_LIST_FOR_EACH (Entry, &DwHc->DeferredList) { DWUSB_DEFERRED_REQ *Req = EFI_LIST_CONTAINER (Entry, DWUSB_DEFERRED_REQ, List); if (Req->DeviceAddress == DeviceAddress && Req->EpAddress == (EndPointAddress & 0xF) && Req->TransferDirection == ((EndPointAddress >> 7) & 0x01)) { return Req; } } return NULL; } STATIC VOID DwHcDeferredTransfer ( IN DWUSB_DEFERRED_REQ *Req ) { EFI_STATUS Status; EFI_EVENT TimeoutEvt = NULL; Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimeoutEvt); ASSERT_EFI_ERROR (Status); if (EFI_ERROR (Status)) { goto Exit; } Status = gBS->SetTimer (TimeoutEvt, TimerForTransfer, EFI_TIMER_PERIOD_MILLISECONDS (Req->TimeOut)); ASSERT_EFI_ERROR (Status); if (EFI_ERROR (Status)) { goto Exit; } Req->TransferResult = EFI_USB_NOERROR; Status = DwHcTransfer (Req->DwHc, TimeoutEvt, Req->Channel, Req->Translator, Req->DeviceSpeed, Req->DeviceAddress, Req->MaximumPacketLength, &Req->Pid, Req->TransferDirection, Req->Data, &Req->DataLength, Req->EpAddress, Req->EpType, &Req->TransferResult, Req->IgnoreAck); if (Req->EpType == DWC2_HCCHAR_EPTYPE_INTR && Status == EFI_DEVICE_ERROR && Req->TransferResult == EFI_USB_ERR_NAK) { /* * Swallow the NAK, the upper layer expects us to resubmit automatically. */ goto Exit; } Req->CallbackFunction (Req->Data, Req->DataLength, Req->CallbackContext, Req->TransferResult); Exit: if (TimeoutEvt != NULL) { gBS->CloseEvent (TimeoutEvt); } } /** EFI_USB2_HC_PROTOCOL APIs **/ EFI_STATUS EFIAPI DwHcGetCapability ( IN EFI_USB2_HC_PROTOCOL *This, OUT UINT8 *MaxSpeed, OUT UINT8 *PortNumber, OUT UINT8 *Is64BitCapable ) { if ((MaxSpeed == NULL) || (PortNumber == NULL) || (Is64BitCapable == NULL)) { return EFI_INVALID_PARAMETER; } *MaxSpeed = EFI_USB_SPEED_HIGH; *PortNumber = 1; *Is64BitCapable = 1; return EFI_SUCCESS; } EFI_STATUS EFIAPI DwHcReset ( IN EFI_USB2_HC_PROTOCOL *This, IN UINT16 Attributes ) { EFI_STATUS Status; EFI_EVENT TimeoutEvt = NULL; DWUSB_OTGHC_DEV *DwHc; DwHc = DWHC_FROM_THIS (This); switch (Attributes) { case EFI_USB_HC_RESET_GLOBAL: case EFI_USB_HC_RESET_HOST_CONTROLLER: break; case EFI_USB_HC_RESET_GLOBAL_WITH_DEBUG: case EFI_USB_HC_RESET_HOST_WITH_DEBUG: Status = EFI_UNSUPPORTED; goto Exit; default: Status = EFI_INVALID_PARAMETER; goto Exit; } Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimeoutEvt); ASSERT_EFI_ERROR (Status); if (EFI_ERROR (Status)) { goto Exit; } Status = gBS->SetTimer (TimeoutEvt, TimerRelative, EFI_TIMER_PERIOD_MILLISECONDS (DW_HC_RESET_TIMEOUT_MS)); ASSERT_EFI_ERROR (Status); if (EFI_ERROR (Status)) { goto Exit; } Status = DwCoreInit (DwHc, TimeoutEvt); if (Status != EFI_SUCCESS) { DEBUG ((DEBUG_ERROR, "DwCoreInit failed\n")); goto Exit; } Status = DwHcInit (DwHc, TimeoutEvt); if (Status != EFI_SUCCESS) { DEBUG ((DEBUG_ERROR, "DwHcInit failed\n")); goto Exit; } MmioAndThenOr32 (DwHc->DwUsbBase + HPRT0, ~(DWC2_HPRT0_PRTENA | DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG | DWC2_HPRT0_PRTOVRCURRCHNG), DWC2_HPRT0_PRTRST); MicroSecondDelay (50000); MmioAnd32 (DwHc->DwUsbBase + HPRT0, ~(DWC2_HPRT0_PRTENA | DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG | DWC2_HPRT0_PRTOVRCURRCHNG | DWC2_HPRT0_PRTRST)); Exit: if (TimeoutEvt != NULL) { gBS->CloseEvent (TimeoutEvt); } return Status; } EFI_STATUS EFIAPI DwHcGetState ( IN EFI_USB2_HC_PROTOCOL *This, OUT EFI_USB_HC_STATE *State ) { DWUSB_OTGHC_DEV *DwHc; DwHc = DWHC_FROM_THIS (This); if (State == NULL) { return EFI_INVALID_PARAMETER; } *State = DwHc->DwHcState; return EFI_SUCCESS; } EFI_STATUS EFIAPI DwHcSetState ( IN EFI_USB2_HC_PROTOCOL *This, IN EFI_USB_HC_STATE State ) { DWUSB_OTGHC_DEV *DwHc; EFI_STATUS Status; DwHc = DWHC_FROM_THIS (This); switch (State) { case EfiUsbHcStateHalt: case EfiUsbHcStateOperational: case EfiUsbHcStateSuspend: DwHc->DwHcState = State; Status = EFI_SUCCESS; break; default: Status = EFI_INVALID_PARAMETER; break; } return Status; } EFI_STATUS EFIAPI DwHcGetRootHubPortStatus ( IN EFI_USB2_HC_PROTOCOL *This, IN UINT8 PortNumber, OUT EFI_USB_PORT_STATUS *PortStatus ) { DWUSB_OTGHC_DEV *DwHc; UINT32 Hprt0; if (PortNumber > DWC2_HC_PORT) { return EFI_INVALID_PARAMETER; } if (PortStatus == NULL) { return EFI_INVALID_PARAMETER; } DwHc = DWHC_FROM_THIS (This); PortStatus->PortStatus = 0; PortStatus->PortChangeStatus = 0; Hprt0 = MmioRead32 (DwHc->DwUsbBase + HPRT0); if (Hprt0 & DWC2_HPRT0_PRTCONNSTS) { PortStatus->PortStatus |= USB_PORT_STAT_CONNECTION; } if (Hprt0 & DWC2_HPRT0_PRTENA) { PortStatus->PortStatus |= USB_PORT_STAT_ENABLE; } if (Hprt0 & DWC2_HPRT0_PRTSUSP) { PortStatus->PortStatus |= USB_PORT_STAT_SUSPEND; } if (Hprt0 & DWC2_HPRT0_PRTOVRCURRACT) { PortStatus->PortStatus |= USB_PORT_STAT_OVERCURRENT; } if (Hprt0 & DWC2_HPRT0_PRTRST) { PortStatus->PortStatus |= USB_PORT_STAT_RESET; } if (Hprt0 & DWC2_HPRT0_PRTPWR) { PortStatus->PortStatus |= USB_PORT_STAT_POWER; } PortStatus->PortStatus |= USB_PORT_STAT_HIGH_SPEED; if (Hprt0 & DWC2_HPRT0_PRTENCHNG) { // PortStatus->PortChangeStatus |= USB_PORT_STAT_C_ENABLE; } if (Hprt0 & DWC2_HPRT0_PRTCONNDET) { PortStatus->PortChangeStatus |= USB_PORT_STAT_C_CONNECTION; } if (Hprt0 & DWC2_HPRT0_PRTOVRCURRCHNG) { PortStatus->PortChangeStatus |= USB_PORT_STAT_C_OVERCURRENT; } return EFI_SUCCESS; } EFI_STATUS EFIAPI DwHcSetRootHubPortFeature ( IN EFI_USB2_HC_PROTOCOL *This, IN UINT8 PortNumber, IN EFI_USB_PORT_FEATURE PortFeature ) { DWUSB_OTGHC_DEV *DwHc; UINT32 Hprt0; EFI_STATUS Status = EFI_SUCCESS; if (PortNumber > DWC2_HC_PORT) { Status = EFI_INVALID_PARAMETER; goto Exit; } DwHc = DWHC_FROM_THIS (This); switch (PortFeature) { case EfiUsbPortEnable: break; case EfiUsbPortSuspend: Hprt0 = MmioRead32 (DwHc->DwUsbBase + HPRT0); Hprt0 &= ~(DWC2_HPRT0_PRTENA | DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG | DWC2_HPRT0_PRTOVRCURRCHNG); Hprt0 |= DWC2_HPRT0_PRTSUSP; MmioWrite32 (DwHc->DwUsbBase + HPRT0, Hprt0); break; case EfiUsbPortReset: MmioAndThenOr32 (DwHc->DwUsbBase + HPRT0, ~(DWC2_HPRT0_PRTENA | DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG | DWC2_HPRT0_PRTOVRCURRCHNG), DWC2_HPRT0_PRTRST); MicroSecondDelay (50000); MmioAnd32 (DwHc->DwUsbBase + HPRT0, ~DWC2_HPRT0_PRTRST); break; case EfiUsbPortPower: Hprt0 = MmioRead32 (DwHc->DwUsbBase + HPRT0); Hprt0 &= ~(DWC2_HPRT0_PRTENA | DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG | DWC2_HPRT0_PRTOVRCURRCHNG); Hprt0 |= DWC2_HPRT0_PRTPWR; MmioWrite32 (DwHc->DwUsbBase + HPRT0, Hprt0); break; case EfiUsbPortOwner: break; default: Status = EFI_INVALID_PARAMETER; break; } Exit: return Status; } EFI_STATUS EFIAPI DwHcClearRootHubPortFeature ( IN EFI_USB2_HC_PROTOCOL *This, IN UINT8 PortNumber, IN EFI_USB_PORT_FEATURE PortFeature ) { DWUSB_OTGHC_DEV *DwHc; UINT32 Hprt0; EFI_STATUS Status = EFI_SUCCESS; if (PortNumber > DWC2_HC_PORT) { Status = EFI_INVALID_PARAMETER; goto Exit; } DwHc = DWHC_FROM_THIS (This); switch (PortFeature) { case EfiUsbPortEnable: Hprt0 = MmioRead32 (DwHc->DwUsbBase + HPRT0); Hprt0 &= ~(DWC2_HPRT0_PRTENA | DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG | DWC2_HPRT0_PRTOVRCURRCHNG); Hprt0 |= DWC2_HPRT0_PRTENA; MmioWrite32 (DwHc->DwUsbBase + HPRT0, Hprt0); break; case EfiUsbPortReset: MmioAndThenOr32 (DwHc->DwUsbBase + HPRT0, ~(DWC2_HPRT0_PRTENA | DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG | DWC2_HPRT0_PRTOVRCURRCHNG), DWC2_HPRT0_PRTRST); MicroSecondDelay (50000); MmioAnd32 (DwHc->DwUsbBase + HPRT0, ~DWC2_HPRT0_PRTRST); break; case EfiUsbPortSuspend: MmioWrite32 (DwHc->DwUsbBase + PCGCCTL, 0); MicroSecondDelay (40000); Hprt0 = MmioRead32 (DwHc->DwUsbBase + HPRT0); Hprt0 &= ~(DWC2_HPRT0_PRTENA | DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG | DWC2_HPRT0_PRTOVRCURRCHNG); Hprt0 |= DWC2_HPRT0_PRTRES; MmioWrite32 (DwHc->DwUsbBase + HPRT0, Hprt0); Hprt0 &= ~DWC2_HPRT0_PRTSUSP; MicroSecondDelay (150000); Hprt0 &= ~DWC2_HPRT0_PRTRES; MmioWrite32 (DwHc->DwUsbBase + HPRT0, Hprt0); break; case EfiUsbPortPower: Hprt0 = MmioRead32 (DwHc->DwUsbBase + HPRT0); Hprt0 &= ~(DWC2_HPRT0_PRTENA | DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG | DWC2_HPRT0_PRTOVRCURRCHNG); Hprt0 &= ~DWC2_HPRT0_PRTPWR; MmioWrite32 (DwHc->DwUsbBase + HPRT0, Hprt0); break; case EfiUsbPortOwner: break; case EfiUsbPortConnectChange: Hprt0 = MmioRead32 (DwHc->DwUsbBase + HPRT0); Hprt0 &= ~DWC2_HPRT0_PRTCONNDET; MmioWrite32 (DwHc->DwUsbBase + HPRT0, Hprt0); break; case EfiUsbPortResetChange: break; case EfiUsbPortEnableChange: Hprt0 = MmioRead32 (DwHc->DwUsbBase + HPRT0); Hprt0 &= ~DWC2_HPRT0_PRTENCHNG; MmioWrite32 (DwHc->DwUsbBase + HPRT0, Hprt0); break; case EfiUsbPortSuspendChange: break; case EfiUsbPortOverCurrentChange: Hprt0 = MmioRead32 (DwHc->DwUsbBase + HPRT0); Hprt0 &= ~DWC2_HPRT0_PRTOVRCURRCHNG; MmioWrite32 (DwHc->DwUsbBase + HPRT0, Hprt0); break; default: Status = EFI_INVALID_PARAMETER; break; } Exit: return Status; } EFI_STATUS EFIAPI DwHcControlTransfer ( IN EFI_USB2_HC_PROTOCOL *This, IN UINT8 DeviceAddress, IN UINT8 DeviceSpeed, IN UINTN MaximumPacketLength, IN EFI_USB_DEVICE_REQUEST *Request, IN EFI_USB_DATA_DIRECTION TransferDirection, IN OUT VOID *Data, IN OUT UINTN *DataLength, IN UINTN TimeOut, IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, OUT UINT32 *TransferResult ) { DWUSB_OTGHC_DEV *DwHc; EFI_STATUS Status; UINT32 Pid; UINTN Length; EFI_USB_DATA_DIRECTION StatusDirection; UINT32 Direction; EFI_EVENT TimeoutEvt = NULL; if ((Request == NULL) || (TransferResult == NULL)) { return EFI_INVALID_PARAMETER; } if ((TransferDirection != EfiUsbDataIn) && (TransferDirection != EfiUsbDataOut) && (TransferDirection != EfiUsbNoData)) { return EFI_INVALID_PARAMETER; } if ((TransferDirection == EfiUsbNoData) && ((Data != NULL) || (*DataLength != 0))) { return EFI_INVALID_PARAMETER; } if ((TransferDirection != EfiUsbNoData) && ((Data == NULL) || (*DataLength == 0))) { return EFI_INVALID_PARAMETER; } if ((MaximumPacketLength != 8) && (MaximumPacketLength != 16) && (MaximumPacketLength != 32) && (MaximumPacketLength != 64)) { return EFI_INVALID_PARAMETER; } if ((DeviceSpeed == EFI_USB_SPEED_LOW) && (MaximumPacketLength != 8)) { return EFI_INVALID_PARAMETER; } DwHc = DWHC_FROM_THIS (This); Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimeoutEvt); ASSERT_EFI_ERROR (Status); if (EFI_ERROR (Status)) { goto Exit; } Status = gBS->SetTimer (TimeoutEvt, TimerForTransfer, EFI_TIMER_PERIOD_MILLISECONDS (TimeOut)); ASSERT_EFI_ERROR (Status); if (EFI_ERROR (Status)) { goto Exit; } Pid = DWC2_HC_PID_SETUP; Length = 8; Status = DwHcTransfer (DwHc, TimeoutEvt, DWC2_HC_CHANNEL, Translator, DeviceSpeed, DeviceAddress, MaximumPacketLength, &Pid, 0, Request, &Length, 0, DWC2_HCCHAR_EPTYPE_CONTROL, TransferResult, 1); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Setup Stage Error for device 0x%x: 0x%x\n", DeviceAddress, *TransferResult)); goto Exit; } if (Data) { Pid = DWC2_HC_PID_DATA1; if (TransferDirection == EfiUsbDataIn) { Direction = 1; } else { Direction = 0; } Status = DwHcTransfer (DwHc, TimeoutEvt, DWC2_HC_CHANNEL, Translator, DeviceSpeed, DeviceAddress, MaximumPacketLength, &Pid, Direction, Data, DataLength, 0, DWC2_HCCHAR_EPTYPE_CONTROL, TransferResult, 0); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Data Stage Error for device 0x%x: 0x%x\n", DeviceAddress, *TransferResult)); goto Exit; } } if ((TransferDirection == EfiUsbDataOut) || (TransferDirection == EfiUsbNoData)) { StatusDirection = 1; } else { StatusDirection = 0; } Pid = DWC2_HC_PID_DATA1; Length = 0; Status = DwHcTransfer (DwHc, TimeoutEvt, DWC2_HC_CHANNEL, Translator, DeviceSpeed, DeviceAddress, MaximumPacketLength, &Pid, StatusDirection, DwHc->StatusBuffer, &Length, 0, DWC2_HCCHAR_EPTYPE_CONTROL, TransferResult, 1); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Status Stage Error for 0x%x: 0x%x\n", DeviceAddress, *TransferResult)); goto Exit; } Exit: if (TimeoutEvt != NULL) { gBS->CloseEvent (TimeoutEvt); } if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "RequestType 0x%x\n", Request->RequestType)); DEBUG ((DEBUG_ERROR, "Request 0x%x\n", Request->Request)); DEBUG ((DEBUG_ERROR, "Value 0x%x\n", Request->Value)); DEBUG ((DEBUG_ERROR, "Index 0x%x\n", Request->Index)); DEBUG ((DEBUG_ERROR, "Length 0x%x\n", Request->Length)); } return Status; } EFI_STATUS EFIAPI DwHcBulkTransfer ( IN EFI_USB2_HC_PROTOCOL *This, IN UINT8 DeviceAddress, IN UINT8 EndPointAddress, IN UINT8 DeviceSpeed, IN UINTN MaximumPacketLength, IN UINT8 DataBuffersNumber, IN OUT VOID *Data[EFI_USB_MAX_BULK_BUFFER_NUM], IN OUT UINTN *DataLength, IN OUT UINT8 *DataToggle, IN UINTN TimeOut, IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, OUT UINT32 *TransferResult ) { DWUSB_OTGHC_DEV *DwHc; EFI_STATUS Status; UINT8 TransferDirection; UINT8 EpAddress; UINT32 Pid; EFI_EVENT TimeoutEvt = NULL; if ((Data == NULL) || (Data[0] == NULL) || (DataLength == NULL) || (*DataLength == 0) || (TransferResult == NULL)) { return EFI_INVALID_PARAMETER; } if ((*DataToggle != 0) && (*DataToggle != 1)) { return EFI_INVALID_PARAMETER; } if ((DeviceSpeed == EFI_USB_SPEED_LOW) || (DeviceSpeed == EFI_USB_SPEED_SUPER)) { return EFI_INVALID_PARAMETER; } if (((DeviceSpeed == EFI_USB_SPEED_FULL) && (MaximumPacketLength > 64)) || ((DeviceSpeed == EFI_USB_SPEED_HIGH) && (MaximumPacketLength > 512))) return EFI_INVALID_PARAMETER; DwHc = DWHC_FROM_THIS (This); Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimeoutEvt); ASSERT_EFI_ERROR (Status); if (EFI_ERROR (Status)) { goto Exit; } Status = gBS->SetTimer (TimeoutEvt, TimerForTransfer, EFI_TIMER_PERIOD_MILLISECONDS (TimeOut)); ASSERT_EFI_ERROR (Status); if (EFI_ERROR (Status)) { goto Exit; } Status = EFI_DEVICE_ERROR; TransferDirection = (EndPointAddress >> 7) & 0x01; EpAddress = EndPointAddress & 0x0F; Pid = (*DataToggle << 1); Status = DwHcTransfer (DwHc, TimeoutEvt, DWC2_HC_CHANNEL_BULK, Translator, DeviceSpeed, DeviceAddress, MaximumPacketLength, &Pid, TransferDirection, Data[0], DataLength, EpAddress, DWC2_HCCHAR_EPTYPE_BULK, TransferResult, 1); *DataToggle = (Pid >> 1); Exit: if (TimeoutEvt != NULL) { gBS->CloseEvent (TimeoutEvt); } return Status; } EFI_STATUS EFIAPI DwHcAsyncInterruptTransfer ( IN EFI_USB2_HC_PROTOCOL *This, IN UINT8 DeviceAddress, IN UINT8 EndPointAddress, IN UINT8 DeviceSpeed, IN UINTN MaximumPacketLength, IN BOOLEAN IsNewTransfer, IN OUT UINT8 *DataToggle, IN UINTN PollingInterval, IN UINTN DataLength, IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, IN EFI_ASYNC_USB_TRANSFER_CALLBACK CallbackFunction, IN VOID *Context OPTIONAL ) { DWUSB_OTGHC_DEV *DwHc; EFI_STATUS Status; EFI_TPL PreviousTpl; VOID *Data = NULL; DWUSB_DEFERRED_REQ *FoundReq = NULL; DWUSB_DEFERRED_REQ *NewReq = NULL; if (!(EndPointAddress & USB_ENDPOINT_DIR_IN)) { return EFI_INVALID_PARAMETER; } DwHc = DWHC_FROM_THIS (This); PreviousTpl = gBS->RaiseTPL (TPL_NOTIFY); FoundReq = DwHcFindDeferredTransfer (DwHc, DeviceAddress, EndPointAddress); if (IsNewTransfer) { if (FoundReq != NULL) { Status = EFI_INVALID_PARAMETER; goto Done; } if (DataLength == 0) { Status = EFI_INVALID_PARAMETER; goto Done; } if ((*DataToggle != 1) && (*DataToggle != 0)) { Status = EFI_INVALID_PARAMETER; goto Done; } if ((PollingInterval > 255) || (PollingInterval < 1)) { Status = EFI_INVALID_PARAMETER; goto Done; } if (CallbackFunction == NULL) { Status = EFI_INVALID_PARAMETER; goto Done; } } if (!IsNewTransfer) { if (FoundReq == NULL) { DEBUG ((DEBUG_ERROR, "%u:%u> transfer not found\n", DeviceAddress, EndPointAddress & 0xF)); Status = EFI_INVALID_PARAMETER; goto Done; } *DataToggle = FoundReq->Pid >> 1; FreePool (FoundReq->Data); RemoveEntryList (&FoundReq->List); FreePool (FoundReq); Status = EFI_SUCCESS; goto Done; } NewReq = AllocateZeroPool (sizeof *NewReq); if (NewReq == NULL) { DEBUG ((DEBUG_ERROR, "DwHcAsyncInterruptTransfer: failed to allocate req")); Status = EFI_OUT_OF_RESOURCES; goto Done; } Data = AllocateZeroPool (DataLength); if (Data == NULL) { DEBUG ((DEBUG_ERROR, "DwHcAsyncInterruptTransfer: failed to allocate buffer\n")); Status = EFI_OUT_OF_RESOURCES; goto Done; } InitializeListHead (&NewReq->List); NewReq->FrameInterval = PollingInterval; NewReq->TargetFrame = DwHc->CurrentFrame + NewReq->FrameInterval; NewReq->DwHc = DwHc; NewReq->Channel = DWC2_HC_CHANNEL_ASYNC; NewReq->Translator = Translator; NewReq->DeviceSpeed = DeviceSpeed; NewReq->DeviceAddress = DeviceAddress; NewReq->MaximumPacketLength = MaximumPacketLength; NewReq->TransferDirection = (EndPointAddress >> 7) & 0x01; NewReq->Data = Data; NewReq->DataLength = DataLength; NewReq->Pid = *DataToggle << 1; NewReq->EpAddress = EndPointAddress & 0x0F; NewReq->EpType = DWC2_HCCHAR_EPTYPE_INTR; NewReq->IgnoreAck = FALSE; NewReq->CallbackFunction = CallbackFunction; NewReq->CallbackContext = Context; NewReq->TimeOut = 1000; /* 1000 ms */ InsertTailList (&DwHc->DeferredList, &NewReq->List); Status = EFI_SUCCESS; Done: gBS->RestoreTPL (PreviousTpl); if (Status != EFI_SUCCESS) { if (Data != NULL) { FreePool (Data); } if (NewReq != NULL) { FreePool (NewReq); } } return Status; } EFI_STATUS EFIAPI DwHcSyncInterruptTransfer ( IN EFI_USB2_HC_PROTOCOL *This, IN UINT8 DeviceAddress, IN UINT8 EndPointAddress, IN UINT8 DeviceSpeed, IN UINTN MaximumPacketLength, IN OUT VOID *Data, IN OUT UINTN *DataLength, IN OUT UINT8 *DataToggle, IN UINTN TimeOut, IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, OUT UINT32 *TransferResult ) { DWUSB_OTGHC_DEV *DwHc; EFI_STATUS Status; EFI_EVENT TimeoutEvt; UINT8 TransferDirection; UINT8 EpAddress; UINT32 Pid; DwHc = DWHC_FROM_THIS (This); if (Data == NULL || DataLength == NULL || DataToggle == NULL || TransferResult == NULL) { return EFI_INVALID_PARAMETER; } if (*DataLength == 0) { return EFI_INVALID_PARAMETER; } if ((*DataToggle != 0) && (*DataToggle != 1)) { return EFI_INVALID_PARAMETER; } if (((DeviceSpeed == EFI_USB_SPEED_LOW) && (MaximumPacketLength != 8)) || ((DeviceSpeed == EFI_USB_SPEED_FULL) && (MaximumPacketLength > 64)) || ((DeviceSpeed == EFI_USB_SPEED_HIGH) && (MaximumPacketLength > 3072))) { return EFI_INVALID_PARAMETER; } Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimeoutEvt); if (EFI_ERROR (Status)) { return Status; } Status = gBS->SetTimer (TimeoutEvt, TimerForTransfer, EFI_TIMER_PERIOD_MILLISECONDS (TimeOut)); if (EFI_ERROR (Status)) { goto Exit; } TransferDirection = (EndPointAddress >> 7) & 0x01; EpAddress = EndPointAddress & 0x0F; Pid = (*DataToggle << 1); Status = DwHcTransfer (DwHc, TimeoutEvt, DWC2_HC_CHANNEL_SYNC, Translator, DeviceSpeed, DeviceAddress, MaximumPacketLength, &Pid, TransferDirection, Data, DataLength, EpAddress, DWC2_HCCHAR_EPTYPE_INTR, TransferResult, 0); *DataToggle = (Pid >> 1); Exit: if (TimeoutEvt != NULL) { gBS->CloseEvent (TimeoutEvt); } return Status; } EFI_STATUS EFIAPI DwHcIsochronousTransfer ( IN EFI_USB2_HC_PROTOCOL *This, IN UINT8 DeviceAddress, IN UINT8 EndPointAddress, IN UINT8 DeviceSpeed, IN UINTN MaximumPacketLength, IN UINT8 DataBuffersNumber, IN OUT VOID *Data[EFI_USB_MAX_ISO_BUFFER_NUM], IN UINTN DataLength, IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, OUT UINT32 *TransferResult ) { DEBUG ((DEBUG_ERROR, "Iso\n")); return EFI_UNSUPPORTED; } EFI_STATUS EFIAPI DwHcAsyncIsochronousTransfer ( IN EFI_USB2_HC_PROTOCOL *This, IN UINT8 DeviceAddress, IN UINT8 EndPointAddress, IN UINT8 DeviceSpeed, IN UINTN MaximumPacketLength, IN UINT8 DataBuffersNumber, IN OUT VOID *Data[EFI_USB_MAX_ISO_BUFFER_NUM], IN UINTN DataLength, IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, IN EFI_ASYNC_USB_TRANSFER_CALLBACK IsochronousCallBack, IN VOID *Context ) { DEBUG ((DEBUG_ERROR, "AsyncIso\n")); return EFI_UNSUPPORTED; } /** Supported Functions **/ VOID InitFslspClkSel ( IN DWUSB_OTGHC_DEV *DwHc ) { UINT32 PhyClk; PhyClk = DWC2_HCFG_FSLSPCLKSEL_30_60_MHZ; MmioAndThenOr32 (DwHc->DwUsbBase + HCFG, ~DWC2_HCFG_FSLSPCLKSEL_MASK, PhyClk << DWC2_HCFG_FSLSPCLKSEL_OFFSET); } VOID DwFlushTxFifo ( IN DWUSB_OTGHC_DEV *DwHc, IN EFI_EVENT Timeout, IN INT32 Num ) { EFI_STATUS Status; MmioWrite32 (DwHc->DwUsbBase + GRSTCTL, DWC2_GRSTCTL_TXFFLSH | (Num << DWC2_GRSTCTL_TXFNUM_OFFSET)); Status = Wait4Bit (Timeout, DwHc->DwUsbBase + GRSTCTL, DWC2_GRSTCTL_TXFFLSH, 0); if (Status) DEBUG ((DEBUG_ERROR, "DwFlushTxFifo: Timeout!\n")); MicroSecondDelay (1); } VOID DwFlushRxFifo ( IN DWUSB_OTGHC_DEV *DwHc, IN EFI_EVENT Timeout ) { EFI_STATUS Status; MmioWrite32 (DwHc->DwUsbBase + GRSTCTL, DWC2_GRSTCTL_RXFFLSH); Status = Wait4Bit (Timeout, DwHc->DwUsbBase + GRSTCTL, DWC2_GRSTCTL_RXFFLSH, 0); if (Status) DEBUG ((DEBUG_ERROR, "DwFlushRxFifo: Timeout!\n")); MicroSecondDelay (1); } EFI_STATUS DwHcInit ( IN DWUSB_OTGHC_DEV *DwHc, IN EFI_EVENT Timeout ) { UINT32 NpTxFifoSz = 0; UINT32 pTxFifoSz = 0; UINT32 Hprt0 = 0; INT32 i, Status, NumChannels; MmioWrite32 (DwHc->DwUsbBase + PCGCCTL, 0); InitFslspClkSel (DwHc); MmioWrite32 (DwHc->DwUsbBase + GRXFSIZ, DWC2_HOST_RX_FIFO_SIZE); NpTxFifoSz |= DWC2_HOST_NPERIO_TX_FIFO_SIZE << DWC2_FIFOSIZE_DEPTH_OFFSET; NpTxFifoSz |= DWC2_HOST_RX_FIFO_SIZE << DWC2_FIFOSIZE_STARTADDR_OFFSET; MmioWrite32 (DwHc->DwUsbBase + GNPTXFSIZ, NpTxFifoSz); pTxFifoSz |= DWC2_HOST_PERIO_TX_FIFO_SIZE << DWC2_FIFOSIZE_DEPTH_OFFSET; pTxFifoSz |= (DWC2_HOST_RX_FIFO_SIZE + DWC2_HOST_NPERIO_TX_FIFO_SIZE) << DWC2_FIFOSIZE_STARTADDR_OFFSET; MmioWrite32 (DwHc->DwUsbBase + HPTXFSIZ, pTxFifoSz); MmioAnd32 (DwHc->DwUsbBase + GOTGCTL, ~(DWC2_GOTGCTL_HSTSETHNPEN)); DwFlushTxFifo (DwHc, Timeout, 0x10); DwFlushRxFifo (DwHc, Timeout); NumChannels = MmioRead32 (DwHc->DwUsbBase + GHWCFG2); NumChannels &= DWC2_HWCFG2_NUM_HOST_CHAN_MASK; NumChannels >>= DWC2_HWCFG2_NUM_HOST_CHAN_OFFSET; NumChannels += 1; DEBUG ((DEBUG_INFO, "Host has %u channels\n", NumChannels)); for (i = 0; i < NumChannels; i++) MmioAndThenOr32 (DwHc->DwUsbBase + HCCHAR (i), ~(DWC2_HCCHAR_CHEN | DWC2_HCCHAR_EPDIR), DWC2_HCCHAR_CHDIS); for (i = 0; i < NumChannels; i++) { MmioAndThenOr32 (DwHc->DwUsbBase + HCCHAR (i), ~DWC2_HCCHAR_EPDIR, (DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS)); Status = Wait4Bit (Timeout, DwHc->DwUsbBase + HCCHAR (i), DWC2_HCCHAR_CHEN, 0); if (Status) { DEBUG ((DEBUG_ERROR, "DwHcInit: Timeout!\n")); return Status; } } if (MmioRead32 (DwHc->DwUsbBase + GINTSTS) & DWC2_GINTSTS_CURMODE_HOST) { Hprt0 = MmioRead32 (DwHc->DwUsbBase + HPRT0); Hprt0 &= ~(DWC2_HPRT0_PRTENA | DWC2_HPRT0_PRTCONNDET); Hprt0 &= ~(DWC2_HPRT0_PRTENCHNG | DWC2_HPRT0_PRTOVRCURRCHNG); if (!(Hprt0 & DWC2_HPRT0_PRTPWR)) { Hprt0 |= DWC2_HPRT0_PRTPWR; MmioWrite32 (DwHc->DwUsbBase + HPRT0, Hprt0); } } return EFI_SUCCESS; } EFI_STATUS DwCoreInit ( IN DWUSB_OTGHC_DEV *DwHc, IN EFI_EVENT Timeout ) { UINT32 AhbCfg = 0; UINT32 UsbCfg = 0; EFI_STATUS Status; UsbCfg = MmioRead32 (DwHc->DwUsbBase + GUSBCFG); UsbCfg |= DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV; UsbCfg &= ~DWC2_GUSBCFG_TERM_SEL_DL_PULSE; MmioWrite32 (DwHc->DwUsbBase + GUSBCFG, UsbCfg); Status = DwCoreReset (DwHc, Timeout); if (Status != EFI_SUCCESS) { DEBUG ((DEBUG_ERROR, "DwCoreReset failed\n")); return Status; } UsbCfg &= ~(DWC2_GUSBCFG_ULPI_UTMI_SEL | DWC2_GUSBCFG_PHYIF); UsbCfg |= CONFIG_DWC2_PHY_TYPE << DWC2_GUSBCFG_ULPI_UTMI_SEL_OFFSET; UsbCfg &= ~DWC2_GUSBCFG_DDRSEL; MmioWrite32 (DwHc->DwUsbBase + GUSBCFG, UsbCfg); Status = DwCoreReset (DwHc, Timeout); if (Status != EFI_SUCCESS) { DEBUG ((DEBUG_ERROR, "DwCoreReset 2 failed\n")); return Status; } UsbCfg = MmioRead32 (DwHc->DwUsbBase + GUSBCFG); UsbCfg &= ~(DWC2_GUSBCFG_ULPI_FSLS | DWC2_GUSBCFG_ULPI_CLK_SUS_M); MmioWrite32 (DwHc->DwUsbBase + GUSBCFG, UsbCfg); AhbCfg &= ~DWC2_GAHBCFG_AXI_BURST4_MASK; AhbCfg |= DWC2_GAHBCFG_DMAENABLE | DWC2_GAHBCFG_WAIT_AXI_WRITES; MmioWrite32 (DwHc->DwUsbBase + GAHBCFG, AhbCfg); MmioAnd32 (DwHc->DwUsbBase + GUSBCFG, ~(DWC2_GUSBCFG_HNPCAP | DWC2_GUSBCFG_SRPCAP)); return EFI_SUCCESS; } VOID DestroyDwUsbHc ( IN DWUSB_OTGHC_DEV *DwHc ) { UINT32 Pages; EFI_TPL PreviousTpl; if (DwHc == NULL) { return; } if (DwHc->PeriodicEvent != NULL) { PreviousTpl = gBS->RaiseTPL (TPL_NOTIFY); gBS->CloseEvent (DwHc->PeriodicEvent); gBS->RestoreTPL (PreviousTpl); } if (DwHc->ExitBootServiceEvent != NULL) { gBS->CloseEvent (DwHc->ExitBootServiceEvent); } Pages = EFI_SIZE_TO_PAGES (DWC2_DATA_BUF_SIZE); DmaUnmap (DwHc->AlignedBufferMapping); DmaFreeBuffer (Pages, DwHc->AlignedBuffer); Pages = EFI_SIZE_TO_PAGES (DWC2_STATUS_BUF_SIZE); FreePages (DwHc->StatusBuffer, Pages); gBS->FreePool (DwHc); } STATIC VOID EFIAPI DwUsbHcExitBootService ( IN EFI_EVENT Event, IN VOID *Context ) { DWUSB_OTGHC_DEV *DwHc; DwHc = (DWUSB_OTGHC_DEV*)Context; DwHcQuiesce (DwHc); } STATIC UINT32 FramesPassed ( IN DWUSB_OTGHC_DEV *DwHc ) { UINT32 MicroFrameStart = DwHc->LastMicroFrame; UINT32 MicroFrameEnd = MmioRead32 (DwHc->DwUsbBase + HFNUM) & DWC2_HFNUM_FRNUM_MASK; UINT32 MicroFramesPassed; DwHc->LastMicroFrame = (UINT16)MicroFrameEnd; if (MicroFrameEnd < MicroFrameStart) { /* * Being delayed by 0x8000 microframes is 262 seconds. * Unlikely. Also, we can't really do better unless we * start polling time (which is tedious in EFI...). */ MicroFrameEnd += DWC2_HFNUM_FRNUM_MASK + 1; } MicroFramesPassed = MicroFrameEnd - MicroFrameStart; /* * Round up. We're supposedly getting called every * 8 microframes anyway. This means we'll end up * going a bit faster, which is okay. */ return ALIGN_VALUE (MicroFramesPassed, 8) / 8; } STATIC VOID DwHcPeriodicHandler ( IN EFI_EVENT Event, IN VOID *Context ) { UINT32 Frame; LIST_ENTRY *Entry; LIST_ENTRY *NextEntry; DWUSB_OTGHC_DEV *DwHc = Context; DwHc->CurrentFrame += FramesPassed (DwHc); Frame = DwHc->CurrentFrame; EFI_LIST_FOR_EACH_SAFE (Entry, NextEntry, &DwHc->DeferredList) { DWUSB_DEFERRED_REQ *Req = EFI_LIST_CONTAINER (Entry, DWUSB_DEFERRED_REQ, List); if (Frame >= Req->TargetFrame) { Req->TargetFrame = Frame + Req->FrameInterval; DwHcDeferredTransfer (Req); } } } EFI_STATUS CreateDwUsbHc ( OUT DWUSB_OTGHC_DEV **OutDwHc ) { DWUSB_OTGHC_DEV *DwHc; UINT32 Pages; UINTN BufferSize; EFI_STATUS Status; DwHc = AllocateZeroPool (sizeof (DWUSB_OTGHC_DEV)); if (DwHc == NULL) { return EFI_OUT_OF_RESOURCES; } DwHc->Signature = DWUSB_OTGHC_DEV_SIGNATURE; DwHc->DwUsbOtgHc.GetCapability = DwHcGetCapability; DwHc->DwUsbOtgHc.Reset = DwHcReset; DwHc->DwUsbOtgHc.GetState = DwHcGetState; DwHc->DwUsbOtgHc.SetState = DwHcSetState; DwHc->DwUsbOtgHc.ControlTransfer = DwHcControlTransfer; DwHc->DwUsbOtgHc.BulkTransfer = DwHcBulkTransfer; DwHc->DwUsbOtgHc.AsyncInterruptTransfer = DwHcAsyncInterruptTransfer; DwHc->DwUsbOtgHc.SyncInterruptTransfer = DwHcSyncInterruptTransfer; DwHc->DwUsbOtgHc.IsochronousTransfer = DwHcIsochronousTransfer; DwHc->DwUsbOtgHc.AsyncIsochronousTransfer = DwHcAsyncIsochronousTransfer; DwHc->DwUsbOtgHc.GetRootHubPortStatus = DwHcGetRootHubPortStatus; DwHc->DwUsbOtgHc.SetRootHubPortFeature = DwHcSetRootHubPortFeature; DwHc->DwUsbOtgHc.ClearRootHubPortFeature = DwHcClearRootHubPortFeature; DwHc->DwUsbOtgHc.MajorRevision = 0x02; DwHc->DwUsbOtgHc.MinorRevision = 0x00; DwHc->DwUsbBase = BCM2836_USB_BASE_ADDRESS; Pages = EFI_SIZE_TO_PAGES (DWC2_STATUS_BUF_SIZE); DwHc->StatusBuffer = AllocatePages (Pages); if (DwHc->StatusBuffer == NULL) { DEBUG ((DEBUG_ERROR, "CreateDwUsbHc: No pages available for StatusBuffer\n")); return EFI_OUT_OF_RESOURCES; } Pages = EFI_SIZE_TO_PAGES (DWC2_DATA_BUF_SIZE); Status = DmaAllocateBuffer (EfiBootServicesData, Pages, (VOID**)&DwHc->AlignedBuffer); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "CreateDwUsbHc: DmaAllocateBuffer: %r\n", Status)); return Status; } BufferSize = EFI_PAGES_TO_SIZE (Pages); Status = DmaMap (MapOperationBusMasterCommonBuffer, DwHc->AlignedBuffer, &BufferSize, &DwHc->AlignedBufferBusAddress, &DwHc->AlignedBufferMapping); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "CreateDwUsbHc: DmaMap: %r\n", Status)); return Status; } InitializeListHead (&DwHc->DeferredList); Status = gBS->CreateEventEx ( EVT_NOTIFY_SIGNAL, TPL_NOTIFY, DwUsbHcExitBootService, DwHc, &gEfiEventExitBootServicesGuid, &DwHc->ExitBootServiceEvent ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "CreateDwUsbHc: DwUsbHcExitBootService: %r\n", Status)); return Status; } Status = gBS->CreateEvent ( EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_NOTIFY, DwHcPeriodicHandler, DwHc, &DwHc->PeriodicEvent ); if (Status != EFI_SUCCESS) { DEBUG ((DEBUG_ERROR, "CreateDwUsbHc: DwHcPeriodicHandler: %r\n", Status)); return Status; } Status = gBS->SetTimer (DwHc->PeriodicEvent, TimerPeriodic, EFI_TIMER_PERIOD_MILLISECONDS (1)); if (Status != EFI_SUCCESS) { DEBUG ((DEBUG_ERROR, "CreateDwUsbHc: PeriodicEvent: %r\n", Status)); return Status; } *OutDwHc = DwHc; return EFI_SUCCESS; } VOID DwHcQuiesce ( IN DWUSB_OTGHC_DEV *DwHc ) { if (DwHc->PeriodicEvent != NULL) { EFI_TPL PreviousTpl; PreviousTpl = gBS->RaiseTPL (TPL_NOTIFY); gBS->CloseEvent (DwHc->PeriodicEvent); DwHc->PeriodicEvent = NULL; gBS->RestoreTPL (PreviousTpl); } MmioAndThenOr32 (DwHc->DwUsbBase + HPRT0, ~(DWC2_HPRT0_PRTENA | DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG | DWC2_HPRT0_PRTOVRCURRCHNG), DWC2_HPRT0_PRTRST); MicroSecondDelay (50000); MmioWrite32 (DwHc->DwUsbBase + GRSTCTL, DWC2_GRSTCTL_CSFTRST); MicroSecondDelay (100000); }