/** @file
|
|
Copyright 2017, 2020 NXP
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
**/
|
|
#include <Library/BaseLib.h>
|
#include <Library/DebugLib.h>
|
#include <Library/IoLib.h>
|
#include <Library/NonDiscoverableDeviceRegistrationLib.h>
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include "UsbHcd.h"
|
|
STATIC
|
VOID
|
XhciSetBeatBurstLength (
|
IN UINTN UsbReg
|
)
|
{
|
DWC3 *Dwc3Reg;
|
|
Dwc3Reg = (VOID *)(UsbReg + DWC3_REG_OFFSET);
|
|
MmioAndThenOr32 ((UINTN)&Dwc3Reg->GSBusCfg0, ~USB3_ENABLE_BEAT_BURST_MASK,
|
USB3_ENABLE_BEAT_BURST);
|
|
MmioOr32 ((UINTN)&Dwc3Reg->GSBusCfg1, USB3_SET_BEAT_BURST_LIMIT);
|
}
|
|
STATIC
|
VOID
|
Dwc3SetFladj (
|
IN DWC3 *Dwc3Reg,
|
IN UINT32 Val
|
)
|
{
|
MmioOr32 ((UINTN)&Dwc3Reg->GFLAdj, GFLADJ_30MHZ_REG_SEL |
|
GFLADJ_30MHZ (Val));
|
}
|
|
STATIC
|
VOID
|
Dwc3SetMode (
|
IN DWC3 *Dwc3Reg,
|
IN UINT32 Mode
|
)
|
{
|
MmioAndThenOr32 ((UINTN)&Dwc3Reg->GCtl,
|
~(DWC3_GCTL_PRTCAPDIR (DWC3_GCTL_PRTCAP_OTG)),
|
DWC3_GCTL_PRTCAPDIR (Mode));
|
}
|
|
/**
|
This function issues phy reset and core soft reset
|
|
@param Dwc3Reg Pointer to DWC3 register.
|
|
**/
|
STATIC
|
VOID
|
Dwc3CoreSoftReset (
|
IN DWC3 *Dwc3Reg
|
)
|
{
|
//
|
// Put core in reset before resetting PHY
|
//
|
MmioOr32 ((UINTN)&Dwc3Reg->GCtl, DWC3_GCTL_CORESOFTRESET);
|
|
//
|
// Assert USB2 PHY reset
|
//
|
MmioOr32 ((UINTN)&Dwc3Reg->GUsb3PipeCtl[0], DWC3_GUSB3PIPECTL_PHYSOFTRST);
|
|
//
|
// Assert USB3 PHY reset
|
//
|
MmioOr32 ((UINTN)&Dwc3Reg->GUsb2PhyCfg, DWC3_GUSB2PHYCFG_PHYSOFTRST);
|
|
MemoryFence ();
|
|
//
|
// Clear USB3 PHY reset
|
//
|
MmioAnd32 ((UINTN)&Dwc3Reg->GUsb3PipeCtl[0], ~DWC3_GUSB3PIPECTL_PHYSOFTRST);
|
|
//
|
// Clear USB2 PHY reset
|
//
|
MmioAnd32 ((UINTN)&Dwc3Reg->GUsb2PhyCfg, ~DWC3_GUSB2PHYCFG_PHYSOFTRST);
|
|
MemoryFence ();
|
|
//
|
// Take core out of reset, PHYs are stable now
|
//
|
MmioAnd32 ((UINTN)&Dwc3Reg->GCtl, ~DWC3_GCTL_CORESOFTRESET);
|
}
|
|
/**
|
This function performs low-level initialization of DWC3 Core
|
|
@param Dwc3Reg Pointer to DWC3 register.
|
|
**/
|
STATIC
|
EFI_STATUS
|
Dwc3CoreInit (
|
IN DWC3 *Dwc3Reg
|
)
|
{
|
UINT32 Revision;
|
UINT32 Reg;
|
UINTN Dwc3Hwparams1;
|
|
Revision = MmioRead32 ((UINTN)&Dwc3Reg->GSnpsId);
|
//
|
// This should read as 0x5533, ascii of U3(DWC_usb3) followed by revision num
|
//
|
if ((Revision & DWC3_GSNPSID_MASK) != DWC3_SYNOPSYS_ID) {
|
DEBUG ((DEBUG_ERROR,"This is not a DesignWare USB3 DRD Core.\n"));
|
return EFI_NOT_FOUND;
|
}
|
|
Dwc3CoreSoftReset (Dwc3Reg);
|
|
Reg = MmioRead32 ((UINTN)&Dwc3Reg->GCtl);
|
Reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
|
Reg &= ~DWC3_GCTL_DISSCRAMBLE;
|
|
Dwc3Hwparams1 = MmioRead32 ((UINTN)&Dwc3Reg->GHwParams1);
|
|
if (DWC3_GHWPARAMS1_EN_PWROPT (Dwc3Hwparams1) ==
|
DWC3_GHWPARAMS1_EN_PWROPT_CLK) {
|
Reg &= ~DWC3_GCTL_DSBLCLKGTNG;
|
} else {
|
DEBUG ((DEBUG_WARN,"No power optimization available.\n"));
|
}
|
|
if ((Revision & DWC3_RELEASE_MASK) < DWC3_RELEASE_190a) {
|
Reg |= DWC3_GCTL_U2RSTECN;
|
}
|
|
MmioWrite32 ((UINTN)&Dwc3Reg->GCtl, Reg);
|
|
return EFI_SUCCESS;
|
}
|
|
STATIC
|
EFI_STATUS
|
XhciCoreInit (
|
IN UINTN UsbReg
|
)
|
{
|
EFI_STATUS Status;
|
DWC3 *Dwc3Reg;
|
|
Dwc3Reg = (VOID *)(UsbReg + DWC3_REG_OFFSET);
|
|
Status = Dwc3CoreInit (Dwc3Reg);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "Dwc3CoreInit Failed for controller 0x%x (0x%r) \n",
|
UsbReg, Status));
|
|
return Status;
|
}
|
|
Dwc3SetMode (Dwc3Reg, DWC3_GCTL_PRTCAP_HOST);
|
|
Dwc3SetFladj (Dwc3Reg, GFLADJ_30MHZ_DEFAULT);
|
|
return Status;
|
}
|
|
NON_DISCOVERABLE_DEVICE_INIT
|
EFIAPI
|
InitializeUsbController (
|
IN UINTN UsbReg
|
)
|
{
|
EFI_STATUS Status;
|
|
Status = XhciCoreInit (UsbReg);
|
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "USB Controller init Failed for %d (0x%r)\n",
|
UsbReg, Status));
|
return (VOID *)EFI_DEVICE_ERROR;
|
}
|
|
//
|
// Change beat burst and outstanding pipelined transfers requests
|
//
|
XhciSetBeatBurstLength (UsbReg);
|
|
return EFI_SUCCESS;
|
}
|
|
/**
|
This function gets registered as a callback to perform USB controller intialization
|
|
@param Event Event whose notification function is being invoked.
|
@param Context Pointer to the notification function's context.
|
|
**/
|
VOID
|
EFIAPI
|
UsbEndOfDxeCallback (
|
IN EFI_EVENT Event,
|
IN VOID *Context
|
)
|
{
|
EFI_STATUS Status;
|
UINT32 NumUsbController;
|
UINT32 ControllerAddr;
|
UINT32 Index;
|
|
gBS->CloseEvent (Event);
|
|
NumUsbController = PcdGet32 (PcdNumUsbController);
|
|
for (Index = 0; Index < NumUsbController; Index++) {
|
ControllerAddr = PcdGet64 (PcdUsbBaseAddr) +
|
(Index * PcdGet32 (PcdUsbSize));
|
|
Status = RegisterNonDiscoverableMmioDevice (
|
NonDiscoverableDeviceTypeXhci,
|
NonDiscoverableDeviceDmaTypeNonCoherent,
|
InitializeUsbController (ControllerAddr),
|
NULL,
|
1,
|
ControllerAddr, PcdGet32 (PcdUsbSize)
|
);
|
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "Failed to register USB device 0x%x, error 0x%r \n",
|
ControllerAddr, Status));
|
}
|
}
|
}
|
|
/**
|
The Entry Point of module. It follows the standard UEFI driver model.
|
|
@param[in] ImageHandle The firmware allocated handle for the EFI image.
|
@param[in] SystemTable A pointer to the EFI System Table.
|
|
@retval EFI_SUCCESS The entry point is executed successfully.
|
@retval other Some error occurs when executing this entry point.
|
|
**/
|
EFI_STATUS
|
EFIAPI
|
InitializeUsbHcd (
|
IN EFI_HANDLE ImageHandle,
|
IN EFI_SYSTEM_TABLE *SystemTable
|
)
|
{
|
EFI_STATUS Status;
|
EFI_EVENT EndOfDxeEvent;
|
|
Status = gBS->CreateEventEx (
|
EVT_NOTIFY_SIGNAL,
|
TPL_CALLBACK,
|
UsbEndOfDxeCallback,
|
NULL,
|
&gEfiEndOfDxeEventGroupGuid,
|
&EndOfDxeEvent
|
);
|
|
return Status;
|
}
|