mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-28 12:25:19 +01:00
1315 lines
36 KiB
C
1315 lines
36 KiB
C
|
/** @file
|
||
|
PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid
|
||
|
which is used to enable recovery function from USB Drivers.
|
||
|
|
||
|
Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
|
||
|
|
||
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||
|
|
||
|
**/
|
||
|
|
||
|
#include "EhcPeim.h"
|
||
|
|
||
|
//
|
||
|
// Two arrays used to translate the EHCI port state (change)
|
||
|
// to the UEFI protocol's port state (change).
|
||
|
//
|
||
|
USB_PORT_STATE_MAP mUsbPortStateMap[] = {
|
||
|
{PORTSC_CONN, USB_PORT_STAT_CONNECTION},
|
||
|
{PORTSC_ENABLED, USB_PORT_STAT_ENABLE},
|
||
|
{PORTSC_SUSPEND, USB_PORT_STAT_SUSPEND},
|
||
|
{PORTSC_OVERCUR, USB_PORT_STAT_OVERCURRENT},
|
||
|
{PORTSC_RESET, USB_PORT_STAT_RESET},
|
||
|
{PORTSC_POWER, USB_PORT_STAT_POWER},
|
||
|
{PORTSC_OWNER, USB_PORT_STAT_OWNER}
|
||
|
};
|
||
|
|
||
|
USB_PORT_STATE_MAP mUsbPortChangeMap[] = {
|
||
|
{PORTSC_CONN_CHANGE, USB_PORT_STAT_C_CONNECTION},
|
||
|
{PORTSC_ENABLE_CHANGE, USB_PORT_STAT_C_ENABLE},
|
||
|
{PORTSC_OVERCUR_CHANGE, USB_PORT_STAT_C_OVERCURRENT}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
Read Ehc Operation register.
|
||
|
|
||
|
@param Ehc The EHCI device.
|
||
|
@param Offset The operation register offset.
|
||
|
|
||
|
@retval the register content read.
|
||
|
|
||
|
**/
|
||
|
UINT32
|
||
|
EhcReadOpReg (
|
||
|
IN PEI_USB2_HC_DEV *Ehc,
|
||
|
IN UINT32 Offset
|
||
|
)
|
||
|
{
|
||
|
UINT32 Data;
|
||
|
|
||
|
ASSERT (Ehc->CapLen != 0);
|
||
|
|
||
|
Data = MmioRead32 (Ehc->UsbHostControllerBaseAddress + Ehc->CapLen + Offset);
|
||
|
|
||
|
return Data;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Write the data to the EHCI operation register.
|
||
|
|
||
|
@param Ehc The EHCI device.
|
||
|
@param Offset EHCI operation register offset.
|
||
|
@param Data The data to write.
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
EhcWriteOpReg (
|
||
|
IN PEI_USB2_HC_DEV *Ehc,
|
||
|
IN UINT32 Offset,
|
||
|
IN UINT32 Data
|
||
|
)
|
||
|
{
|
||
|
|
||
|
ASSERT (Ehc->CapLen != 0);
|
||
|
|
||
|
MmioWrite32(Ehc->UsbHostControllerBaseAddress + Ehc->CapLen + Offset, Data);
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Set one bit of the operational register while keeping other bits.
|
||
|
|
||
|
@param Ehc The EHCI device.
|
||
|
@param Offset The offset of the operational register.
|
||
|
@param Bit The bit mask of the register to set.
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
EhcSetOpRegBit (
|
||
|
IN PEI_USB2_HC_DEV *Ehc,
|
||
|
IN UINT32 Offset,
|
||
|
IN UINT32 Bit
|
||
|
)
|
||
|
{
|
||
|
UINT32 Data;
|
||
|
|
||
|
Data = EhcReadOpReg (Ehc, Offset);
|
||
|
Data |= Bit;
|
||
|
EhcWriteOpReg (Ehc, Offset, Data);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Clear one bit of the operational register while keeping other bits.
|
||
|
|
||
|
@param Ehc The EHCI device.
|
||
|
@param Offset The offset of the operational register.
|
||
|
@param Bit The bit mask of the register to clear.
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
EhcClearOpRegBit (
|
||
|
IN PEI_USB2_HC_DEV *Ehc,
|
||
|
IN UINT32 Offset,
|
||
|
IN UINT32 Bit
|
||
|
)
|
||
|
{
|
||
|
UINT32 Data;
|
||
|
|
||
|
Data = EhcReadOpReg (Ehc, Offset);
|
||
|
Data &= ~Bit;
|
||
|
EhcWriteOpReg (Ehc, Offset, Data);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Wait the operation register's bit as specified by Bit
|
||
|
to become set (or clear).
|
||
|
|
||
|
@param Ehc The EHCI device.
|
||
|
@param Offset The offset of the operational register.
|
||
|
@param Bit The bit mask of the register to wait for.
|
||
|
@param WaitToSet Wait the bit to set or clear.
|
||
|
@param Timeout The time to wait before abort (in millisecond).
|
||
|
|
||
|
@retval EFI_SUCCESS The bit successfully changed by host controller.
|
||
|
@retval EFI_TIMEOUT The time out occurred.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
EhcWaitOpRegBit (
|
||
|
IN PEI_USB2_HC_DEV *Ehc,
|
||
|
IN UINT32 Offset,
|
||
|
IN UINT32 Bit,
|
||
|
IN BOOLEAN WaitToSet,
|
||
|
IN UINT32 Timeout
|
||
|
)
|
||
|
{
|
||
|
UINT32 Index;
|
||
|
|
||
|
for (Index = 0; Index < Timeout / EHC_SYNC_POLL_INTERVAL + 1; Index++) {
|
||
|
if (EHC_REG_BIT_IS_SET (Ehc, Offset, Bit) == WaitToSet) {
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
MicroSecondDelay (EHC_SYNC_POLL_INTERVAL);
|
||
|
}
|
||
|
|
||
|
return EFI_TIMEOUT;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Read EHCI capability register.
|
||
|
|
||
|
@param Ehc The EHCI device.
|
||
|
@param Offset Capability register address.
|
||
|
|
||
|
@retval the register content read.
|
||
|
|
||
|
**/
|
||
|
UINT32
|
||
|
EhcReadCapRegister (
|
||
|
IN PEI_USB2_HC_DEV *Ehc,
|
||
|
IN UINT32 Offset
|
||
|
)
|
||
|
{
|
||
|
UINT32 Data;
|
||
|
|
||
|
Data = MmioRead32(Ehc->UsbHostControllerBaseAddress + Offset);
|
||
|
|
||
|
return Data;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Set door bell and wait it to be ACKed by host controller.
|
||
|
This function is used to synchronize with the hardware.
|
||
|
|
||
|
@param Ehc The EHCI device.
|
||
|
@param Timeout The time to wait before abort (in millisecond, ms).
|
||
|
|
||
|
@retval EFI_TIMEOUT Time out happened while waiting door bell to set.
|
||
|
@retval EFI_SUCCESS Synchronized with the hardware.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
EhcSetAndWaitDoorBell (
|
||
|
IN PEI_USB2_HC_DEV *Ehc,
|
||
|
IN UINT32 Timeout
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
UINT32 Data;
|
||
|
|
||
|
EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_IAAD);
|
||
|
|
||
|
Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_IAA, TRUE, Timeout);
|
||
|
|
||
|
//
|
||
|
// ACK the IAA bit in USBSTS register. Make sure other
|
||
|
// interrupt bits are not ACKed. These bits are WC (Write Clean).
|
||
|
//
|
||
|
Data = EhcReadOpReg (Ehc, EHC_USBSTS_OFFSET);
|
||
|
Data &= ~USBSTS_INTACK_MASK;
|
||
|
Data |= USBSTS_IAA;
|
||
|
|
||
|
EhcWriteOpReg (Ehc, EHC_USBSTS_OFFSET, Data);
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Clear all the interrutp status bits, these bits
|
||
|
are Write-Clean.
|
||
|
|
||
|
@param Ehc The EHCI device.
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
EhcAckAllInterrupt (
|
||
|
IN PEI_USB2_HC_DEV *Ehc
|
||
|
)
|
||
|
{
|
||
|
EhcWriteOpReg (Ehc, EHC_USBSTS_OFFSET, USBSTS_INTACK_MASK);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Enable the periodic schedule then wait EHC to
|
||
|
actually enable it.
|
||
|
|
||
|
@param Ehc The EHCI device.
|
||
|
@param Timeout The time to wait before abort (in millisecond, ms).
|
||
|
|
||
|
@retval EFI_TIMEOUT Time out happened while enabling periodic schedule.
|
||
|
@retval EFI_SUCCESS The periodical schedule is enabled.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
EhcEnablePeriodSchd (
|
||
|
IN PEI_USB2_HC_DEV *Ehc,
|
||
|
IN UINT32 Timeout
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
|
||
|
EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_ENABLE_PERIOD);
|
||
|
|
||
|
Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_PERIOD_ENABLED, TRUE, Timeout);
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Enable asynchrounous schedule.
|
||
|
|
||
|
@param Ehc The EHCI device.
|
||
|
@param Timeout Time to wait before abort.
|
||
|
|
||
|
@retval EFI_SUCCESS The EHCI asynchronous schedule is enabled.
|
||
|
@retval Others Failed to enable the asynchronous scheudle.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
EhcEnableAsyncSchd (
|
||
|
IN PEI_USB2_HC_DEV *Ehc,
|
||
|
IN UINT32 Timeout
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
|
||
|
EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_ENABLE_ASYNC);
|
||
|
|
||
|
Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_ASYNC_ENABLED, TRUE, Timeout);
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Check whether Ehc is halted.
|
||
|
|
||
|
@param Ehc The EHCI device.
|
||
|
|
||
|
@retval TRUE The controller is halted.
|
||
|
@retval FALSE The controller isn't halted.
|
||
|
|
||
|
**/
|
||
|
BOOLEAN
|
||
|
EhcIsHalt (
|
||
|
IN PEI_USB2_HC_DEV *Ehc
|
||
|
)
|
||
|
{
|
||
|
return EHC_REG_BIT_IS_SET (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Check whether system error occurred.
|
||
|
|
||
|
@param Ehc The EHCI device.
|
||
|
|
||
|
@retval TRUE System error happened.
|
||
|
@retval FALSE No system error.
|
||
|
|
||
|
**/
|
||
|
BOOLEAN
|
||
|
EhcIsSysError (
|
||
|
IN PEI_USB2_HC_DEV *Ehc
|
||
|
)
|
||
|
{
|
||
|
return EHC_REG_BIT_IS_SET (Ehc, EHC_USBSTS_OFFSET, USBSTS_SYS_ERROR);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Reset the host controller.
|
||
|
|
||
|
@param Ehc The EHCI device.
|
||
|
@param Timeout Time to wait before abort (in millisecond, ms).
|
||
|
|
||
|
@retval EFI_TIMEOUT The transfer failed due to time out.
|
||
|
@retval Others Failed to reset the host.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
EhcResetHC (
|
||
|
IN PEI_USB2_HC_DEV *Ehc,
|
||
|
IN UINT32 Timeout
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
|
||
|
//
|
||
|
// Host can only be reset when it is halt. If not so, halt it
|
||
|
//
|
||
|
if (!EHC_REG_BIT_IS_SET (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT)) {
|
||
|
Status = EhcHaltHC (Ehc, Timeout);
|
||
|
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
return Status;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_RESET);
|
||
|
Status = EhcWaitOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_RESET, FALSE, Timeout);
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Halt the host controller.
|
||
|
|
||
|
@param Ehc The EHCI device.
|
||
|
@param Timeout Time to wait before abort.
|
||
|
|
||
|
@retval EFI_TIMEOUT Failed to halt the controller before Timeout.
|
||
|
@retval EFI_SUCCESS The EHCI is halt.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
EhcHaltHC (
|
||
|
IN PEI_USB2_HC_DEV *Ehc,
|
||
|
IN UINT32 Timeout
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
|
||
|
EhcClearOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_RUN);
|
||
|
Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT, TRUE, Timeout);
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Set the EHCI to run.
|
||
|
|
||
|
@param Ehc The EHCI device.
|
||
|
@param Timeout Time to wait before abort.
|
||
|
|
||
|
@retval EFI_SUCCESS The EHCI is running.
|
||
|
@retval Others Failed to set the EHCI to run.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
EhcRunHC (
|
||
|
IN PEI_USB2_HC_DEV *Ehc,
|
||
|
IN UINT32 Timeout
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
|
||
|
EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_RUN);
|
||
|
Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT, FALSE, Timeout);
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Power On All EHCI Ports.
|
||
|
|
||
|
@param Ehc The EHCI device.
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
EhcPowerOnAllPorts (
|
||
|
IN PEI_USB2_HC_DEV *Ehc
|
||
|
)
|
||
|
{
|
||
|
UINT8 PortNumber;
|
||
|
UINT8 Index;
|
||
|
UINT32 RegVal;
|
||
|
|
||
|
PortNumber = (UINT8)(Ehc->HcStructParams & HCSP_NPORTS);
|
||
|
for (Index = 0; Index < PortNumber; Index++) {
|
||
|
//
|
||
|
// Do not clear port status bits on initialization. Otherwise devices will
|
||
|
// not enumerate properly at startup.
|
||
|
//
|
||
|
RegVal = EhcReadOpReg(Ehc, EHC_PORT_STAT_OFFSET + 4 * Index);
|
||
|
RegVal &= ~PORTSC_CHANGE_MASK;
|
||
|
RegVal |= PORTSC_POWER;
|
||
|
EhcWriteOpReg (Ehc, EHC_PORT_STAT_OFFSET + 4 * Index, RegVal);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Initialize the HC hardware.
|
||
|
EHCI spec lists the five things to do to initialize the hardware.
|
||
|
1. Program CTRLDSSEGMENT.
|
||
|
2. Set USBINTR to enable interrupts.
|
||
|
3. Set periodic list base.
|
||
|
4. Set USBCMD, interrupt threshold, frame list size etc.
|
||
|
5. Write 1 to CONFIGFLAG to route all ports to EHCI.
|
||
|
|
||
|
@param Ehc The EHCI device.
|
||
|
|
||
|
@retval EFI_SUCCESS The EHCI has come out of halt state.
|
||
|
@retval EFI_TIMEOUT Time out happened.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
EhcInitHC (
|
||
|
IN PEI_USB2_HC_DEV *Ehc
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
EFI_PHYSICAL_ADDRESS TempPtr;
|
||
|
UINTN PageNumber;
|
||
|
|
||
|
ASSERT (EhcIsHalt (Ehc));
|
||
|
|
||
|
//
|
||
|
// Allocate the periodic frame and associated memeory
|
||
|
// management facilities if not already done.
|
||
|
//
|
||
|
if (Ehc->PeriodFrame != NULL) {
|
||
|
EhcFreeSched (Ehc);
|
||
|
}
|
||
|
PageNumber = sizeof(PEI_URB)/PAGESIZE +1;
|
||
|
Status = PeiServicesAllocatePages (
|
||
|
EfiBootServicesCode,
|
||
|
PageNumber,
|
||
|
&TempPtr
|
||
|
);
|
||
|
Ehc->Urb = (PEI_URB *) ((UINTN) TempPtr);
|
||
|
if (Ehc->Urb == NULL) {
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
EhcPowerOnAllPorts (Ehc);
|
||
|
MicroSecondDelay (EHC_ROOT_PORT_RECOVERY_STALL);
|
||
|
|
||
|
Status = EhcInitSched (Ehc);
|
||
|
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
return Status;
|
||
|
}
|
||
|
//
|
||
|
// 1. Program the CTRLDSSEGMENT register with the high 32 bit addr
|
||
|
//
|
||
|
EhcWriteOpReg (Ehc, EHC_CTRLDSSEG_OFFSET, Ehc->High32bitAddr);
|
||
|
|
||
|
//
|
||
|
// 2. Clear USBINTR to disable all the interrupt. UEFI works by polling
|
||
|
//
|
||
|
EhcWriteOpReg (Ehc, EHC_USBINTR_OFFSET, 0);
|
||
|
|
||
|
//
|
||
|
// 3. Program periodic frame list, already done in EhcInitSched
|
||
|
// 4. Start the Host Controller
|
||
|
//
|
||
|
EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_RUN);
|
||
|
|
||
|
//
|
||
|
// 5. Set all ports routing to EHC
|
||
|
//
|
||
|
EhcSetOpRegBit (Ehc, EHC_CONFIG_FLAG_OFFSET, CONFIGFLAG_ROUTE_EHC);
|
||
|
|
||
|
//
|
||
|
// Wait roothub port power stable
|
||
|
//
|
||
|
MicroSecondDelay (EHC_ROOT_PORT_RECOVERY_STALL);
|
||
|
|
||
|
Status = EhcEnablePeriodSchd (Ehc, EHC_GENERIC_TIMEOUT);
|
||
|
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
Status = EhcEnableAsyncSchd (Ehc, EHC_GENERIC_TIMEOUT);
|
||
|
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Submits bulk transfer to a bulk endpoint of a USB device.
|
||
|
|
||
|
@param PeiServices The pointer of EFI_PEI_SERVICES.
|
||
|
@param This The pointer of PEI_USB2_HOST_CONTROLLER_PPI.
|
||
|
@param DeviceAddress Target device address.
|
||
|
@param EndPointAddress Endpoint number and its direction in bit 7.
|
||
|
@param DeviceSpeed Device speed, Low speed device doesn't support
|
||
|
bulk transfer.
|
||
|
@param MaximumPacketLength Maximum packet size the endpoint is capable of
|
||
|
sending or receiving.
|
||
|
@param Data Array of pointers to the buffers of data to transmit
|
||
|
from or receive into.
|
||
|
@param DataLength The lenght of the data buffer.
|
||
|
@param DataToggle On input, the initial data toggle for the transfer;
|
||
|
On output, it is updated to to next data toggle to use of
|
||
|
the subsequent bulk transfer.
|
||
|
@param TimeOut Indicates the maximum time, in millisecond, which the
|
||
|
transfer is allowed to complete.
|
||
|
If Timeout is 0, then the caller must wait for the function
|
||
|
to be completed until EFI_SUCCESS or EFI_DEVICE_ERROR is returned.
|
||
|
@param Translator A pointr to the transaction translator data.
|
||
|
@param TransferResult A pointer to the detailed result information of the
|
||
|
bulk transfer.
|
||
|
|
||
|
@retval EFI_SUCCESS The transfer was completed successfully.
|
||
|
@retval EFI_OUT_OF_RESOURCES The transfer failed due to lack of resource.
|
||
|
@retval EFI_INVALID_PARAMETER Parameters are invalid.
|
||
|
@retval EFI_TIMEOUT The transfer failed due to timeout.
|
||
|
@retval EFI_DEVICE_ERROR The transfer failed due to host controller error.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
EhcBulkTransfer (
|
||
|
IN EFI_PEI_SERVICES **PeiServices,
|
||
|
IN PEI_USB2_HOST_CONTROLLER_PPI *This,
|
||
|
IN UINT8 DeviceAddress,
|
||
|
IN UINT8 EndPointAddress,
|
||
|
IN UINT8 DeviceSpeed,
|
||
|
IN UINTN MaximumPacketLength,
|
||
|
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
|
||
|
)
|
||
|
{
|
||
|
PEI_USB2_HC_DEV *Ehc;
|
||
|
PEI_URB *Urb;
|
||
|
EFI_STATUS Status;
|
||
|
|
||
|
//
|
||
|
// Validate the parameters
|
||
|
//
|
||
|
if ((DataLength == NULL) || (*DataLength == 0) ||
|
||
|
(Data == NULL) || (Data[0] == NULL) || (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_FULL) && (MaximumPacketLength > 64)) ||
|
||
|
((EFI_USB_SPEED_HIGH == DeviceSpeed) && (MaximumPacketLength > 512))) {
|
||
|
return EFI_INVALID_PARAMETER;
|
||
|
}
|
||
|
|
||
|
Ehc =PEI_RECOVERY_USB_EHC_DEV_FROM_EHCI_THIS(This);
|
||
|
*TransferResult = EFI_USB_ERR_SYSTEM;
|
||
|
Status = EFI_DEVICE_ERROR;
|
||
|
|
||
|
if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) {
|
||
|
EhcAckAllInterrupt (Ehc);
|
||
|
goto ON_EXIT;
|
||
|
}
|
||
|
|
||
|
EhcAckAllInterrupt (Ehc);
|
||
|
|
||
|
//
|
||
|
// Create a new URB, insert it into the asynchronous
|
||
|
// schedule list, then poll the execution status.
|
||
|
//
|
||
|
Urb = EhcCreateUrb (
|
||
|
Ehc,
|
||
|
DeviceAddress,
|
||
|
EndPointAddress,
|
||
|
DeviceSpeed,
|
||
|
*DataToggle,
|
||
|
MaximumPacketLength,
|
||
|
Translator,
|
||
|
EHC_BULK_TRANSFER,
|
||
|
NULL,
|
||
|
Data[0],
|
||
|
*DataLength,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
1
|
||
|
);
|
||
|
|
||
|
if (Urb == NULL) {
|
||
|
Status = EFI_OUT_OF_RESOURCES;
|
||
|
goto ON_EXIT;
|
||
|
}
|
||
|
|
||
|
EhcLinkQhToAsync (Ehc, Urb->Qh);
|
||
|
Status = EhcExecTransfer (Ehc, Urb, TimeOut);
|
||
|
EhcUnlinkQhFromAsync (Ehc, Urb->Qh);
|
||
|
|
||
|
*TransferResult = Urb->Result;
|
||
|
*DataLength = Urb->Completed;
|
||
|
*DataToggle = Urb->DataToggle;
|
||
|
|
||
|
if (*TransferResult == EFI_USB_NOERROR) {
|
||
|
Status = EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
EhcAckAllInterrupt (Ehc);
|
||
|
EhcFreeUrb (Ehc, Urb);
|
||
|
|
||
|
ON_EXIT:
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Retrieves the number of root hub ports.
|
||
|
|
||
|
@param[in] PeiServices The pointer to the PEI Services Table.
|
||
|
@param[in] This The pointer to this instance of the
|
||
|
PEI_USB2_HOST_CONTROLLER_PPI.
|
||
|
@param[out] PortNumber The pointer to the number of the root hub ports.
|
||
|
|
||
|
@retval EFI_SUCCESS The port number was retrieved successfully.
|
||
|
@retval EFI_INVALID_PARAMETER PortNumber is NULL.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
EhcGetRootHubPortNumber (
|
||
|
IN EFI_PEI_SERVICES **PeiServices,
|
||
|
IN PEI_USB2_HOST_CONTROLLER_PPI *This,
|
||
|
OUT UINT8 *PortNumber
|
||
|
)
|
||
|
{
|
||
|
|
||
|
PEI_USB2_HC_DEV *EhcDev;
|
||
|
EhcDev = PEI_RECOVERY_USB_EHC_DEV_FROM_EHCI_THIS (This);
|
||
|
|
||
|
if (PortNumber == NULL) {
|
||
|
return EFI_INVALID_PARAMETER;
|
||
|
}
|
||
|
|
||
|
*PortNumber = (UINT8)(EhcDev->HcStructParams & HCSP_NPORTS);
|
||
|
return EFI_SUCCESS;
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Clears a feature for the specified root hub port.
|
||
|
|
||
|
@param PeiServices The pointer of EFI_PEI_SERVICES.
|
||
|
@param This The pointer of PEI_USB2_HOST_CONTROLLER_PPI.
|
||
|
@param PortNumber Specifies the root hub port whose feature
|
||
|
is requested to be cleared.
|
||
|
@param PortFeature Indicates the feature selector associated with the
|
||
|
feature clear request.
|
||
|
|
||
|
@retval EFI_SUCCESS The feature specified by PortFeature was cleared
|
||
|
for the USB root hub port specified by PortNumber.
|
||
|
@retval EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
EhcClearRootHubPortFeature (
|
||
|
IN EFI_PEI_SERVICES **PeiServices,
|
||
|
IN PEI_USB2_HOST_CONTROLLER_PPI *This,
|
||
|
IN UINT8 PortNumber,
|
||
|
IN EFI_USB_PORT_FEATURE PortFeature
|
||
|
)
|
||
|
{
|
||
|
PEI_USB2_HC_DEV *Ehc;
|
||
|
UINT32 Offset;
|
||
|
UINT32 State;
|
||
|
UINT32 TotalPort;
|
||
|
EFI_STATUS Status;
|
||
|
|
||
|
Ehc = PEI_RECOVERY_USB_EHC_DEV_FROM_EHCI_THIS (This);
|
||
|
Status = EFI_SUCCESS;
|
||
|
|
||
|
TotalPort = (Ehc->HcStructParams & HCSP_NPORTS);
|
||
|
|
||
|
if (PortNumber >= TotalPort) {
|
||
|
Status = EFI_INVALID_PARAMETER;
|
||
|
goto ON_EXIT;
|
||
|
}
|
||
|
|
||
|
Offset = EHC_PORT_STAT_OFFSET + (4 * PortNumber);
|
||
|
State = EhcReadOpReg (Ehc, Offset);
|
||
|
State &= ~PORTSC_CHANGE_MASK;
|
||
|
|
||
|
switch (PortFeature) {
|
||
|
case EfiUsbPortEnable:
|
||
|
//
|
||
|
// Clear PORT_ENABLE feature means disable port.
|
||
|
//
|
||
|
State &= ~PORTSC_ENABLED;
|
||
|
EhcWriteOpReg (Ehc, Offset, State);
|
||
|
break;
|
||
|
|
||
|
case EfiUsbPortSuspend:
|
||
|
//
|
||
|
// A write of zero to this bit is ignored by the host
|
||
|
// controller. The host controller will unconditionally
|
||
|
// set this bit to a zero when:
|
||
|
// 1. software sets the Forct Port Resume bit to a zero from a one.
|
||
|
// 2. software sets the Port Reset bit to a one frome a zero.
|
||
|
//
|
||
|
State &= ~PORSTSC_RESUME;
|
||
|
EhcWriteOpReg (Ehc, Offset, State);
|
||
|
break;
|
||
|
|
||
|
case EfiUsbPortReset:
|
||
|
//
|
||
|
// Clear PORT_RESET means clear the reset signal.
|
||
|
//
|
||
|
State &= ~PORTSC_RESET;
|
||
|
EhcWriteOpReg (Ehc, Offset, State);
|
||
|
break;
|
||
|
|
||
|
case EfiUsbPortOwner:
|
||
|
//
|
||
|
// Clear port owner means this port owned by EHC
|
||
|
//
|
||
|
State &= ~PORTSC_OWNER;
|
||
|
EhcWriteOpReg (Ehc, Offset, State);
|
||
|
break;
|
||
|
|
||
|
case EfiUsbPortConnectChange:
|
||
|
//
|
||
|
// Clear connect status change
|
||
|
//
|
||
|
State |= PORTSC_CONN_CHANGE;
|
||
|
EhcWriteOpReg (Ehc, Offset, State);
|
||
|
break;
|
||
|
|
||
|
case EfiUsbPortEnableChange:
|
||
|
//
|
||
|
// Clear enable status change
|
||
|
//
|
||
|
State |= PORTSC_ENABLE_CHANGE;
|
||
|
EhcWriteOpReg (Ehc, Offset, State);
|
||
|
break;
|
||
|
|
||
|
case EfiUsbPortOverCurrentChange:
|
||
|
//
|
||
|
// Clear PortOverCurrent change
|
||
|
//
|
||
|
State |= PORTSC_OVERCUR_CHANGE;
|
||
|
EhcWriteOpReg (Ehc, Offset, State);
|
||
|
break;
|
||
|
|
||
|
case EfiUsbPortPower:
|
||
|
case EfiUsbPortSuspendChange:
|
||
|
case EfiUsbPortResetChange:
|
||
|
//
|
||
|
// Not supported or not related operation
|
||
|
//
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
Status = EFI_INVALID_PARAMETER;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
ON_EXIT:
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Sets a feature for the specified root hub port.
|
||
|
|
||
|
@param PeiServices The pointer of EFI_PEI_SERVICES
|
||
|
@param This The pointer of PEI_USB2_HOST_CONTROLLER_PPI
|
||
|
@param PortNumber Root hub port to set.
|
||
|
@param PortFeature Feature to set.
|
||
|
|
||
|
@retval EFI_SUCCESS The feature specified by PortFeature was set.
|
||
|
@retval EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid.
|
||
|
@retval EFI_TIMEOUT The time out occurred.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
EhcSetRootHubPortFeature (
|
||
|
IN EFI_PEI_SERVICES **PeiServices,
|
||
|
IN PEI_USB2_HOST_CONTROLLER_PPI *This,
|
||
|
IN UINT8 PortNumber,
|
||
|
IN EFI_USB_PORT_FEATURE PortFeature
|
||
|
)
|
||
|
{
|
||
|
PEI_USB2_HC_DEV *Ehc;
|
||
|
UINT32 Offset;
|
||
|
UINT32 State;
|
||
|
UINT32 TotalPort;
|
||
|
EFI_STATUS Status;
|
||
|
|
||
|
Ehc = PEI_RECOVERY_USB_EHC_DEV_FROM_EHCI_THIS (This);
|
||
|
Status = EFI_SUCCESS;
|
||
|
|
||
|
TotalPort = (Ehc->HcStructParams & HCSP_NPORTS);
|
||
|
|
||
|
if (PortNumber >= TotalPort) {
|
||
|
Status = EFI_INVALID_PARAMETER;
|
||
|
goto ON_EXIT;
|
||
|
}
|
||
|
|
||
|
Offset = (UINT32) (EHC_PORT_STAT_OFFSET + (4 * PortNumber));
|
||
|
State = EhcReadOpReg (Ehc, Offset);
|
||
|
|
||
|
//
|
||
|
// Mask off the port status change bits, these bits are
|
||
|
// write clean bit
|
||
|
//
|
||
|
State &= ~PORTSC_CHANGE_MASK;
|
||
|
|
||
|
switch (PortFeature) {
|
||
|
case EfiUsbPortEnable:
|
||
|
//
|
||
|
// Sofeware can't set this bit, Port can only be enable by
|
||
|
// EHCI as a part of the reset and enable
|
||
|
//
|
||
|
State |= PORTSC_ENABLED;
|
||
|
EhcWriteOpReg (Ehc, Offset, State);
|
||
|
break;
|
||
|
|
||
|
case EfiUsbPortSuspend:
|
||
|
State |= PORTSC_SUSPEND;
|
||
|
EhcWriteOpReg (Ehc, Offset, State);
|
||
|
break;
|
||
|
|
||
|
case EfiUsbPortReset:
|
||
|
//
|
||
|
// Make sure Host Controller not halt before reset it
|
||
|
//
|
||
|
if (EhcIsHalt (Ehc)) {
|
||
|
Status = EhcRunHC (Ehc, EHC_GENERIC_TIMEOUT);
|
||
|
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Set one to PortReset bit must also set zero to PortEnable bit
|
||
|
//
|
||
|
State |= PORTSC_RESET;
|
||
|
State &= ~PORTSC_ENABLED;
|
||
|
EhcWriteOpReg (Ehc, Offset, State);
|
||
|
break;
|
||
|
|
||
|
case EfiUsbPortPower:
|
||
|
//
|
||
|
// Not supported, ignore the operation
|
||
|
//
|
||
|
Status = EFI_SUCCESS;
|
||
|
break;
|
||
|
|
||
|
case EfiUsbPortOwner:
|
||
|
State |= PORTSC_OWNER;
|
||
|
EhcWriteOpReg (Ehc, Offset, State);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
Status = EFI_INVALID_PARAMETER;
|
||
|
}
|
||
|
|
||
|
ON_EXIT:
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Retrieves the current status of a USB root hub port.
|
||
|
|
||
|
@param PeiServices The pointer of EFI_PEI_SERVICES.
|
||
|
@param This The pointer of PEI_USB2_HOST_CONTROLLER_PPI.
|
||
|
@param PortNumber The root hub port to retrieve the state from.
|
||
|
@param PortStatus Variable to receive the port state.
|
||
|
|
||
|
@retval EFI_SUCCESS The status of the USB root hub port specified.
|
||
|
by PortNumber was returned in PortStatus.
|
||
|
@retval EFI_INVALID_PARAMETER PortNumber is invalid.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
EhcGetRootHubPortStatus (
|
||
|
IN EFI_PEI_SERVICES **PeiServices,
|
||
|
IN PEI_USB2_HOST_CONTROLLER_PPI *This,
|
||
|
IN UINT8 PortNumber,
|
||
|
OUT EFI_USB_PORT_STATUS *PortStatus
|
||
|
)
|
||
|
{
|
||
|
PEI_USB2_HC_DEV *Ehc;
|
||
|
UINT32 Offset;
|
||
|
UINT32 State;
|
||
|
UINT32 TotalPort;
|
||
|
UINTN Index;
|
||
|
UINTN MapSize;
|
||
|
EFI_STATUS Status;
|
||
|
|
||
|
if (PortStatus == NULL) {
|
||
|
return EFI_INVALID_PARAMETER;
|
||
|
}
|
||
|
|
||
|
Ehc = PEI_RECOVERY_USB_EHC_DEV_FROM_EHCI_THIS(This);
|
||
|
Status = EFI_SUCCESS;
|
||
|
|
||
|
TotalPort = (Ehc->HcStructParams & HCSP_NPORTS);
|
||
|
|
||
|
if (PortNumber >= TotalPort) {
|
||
|
Status = EFI_INVALID_PARAMETER;
|
||
|
goto ON_EXIT;
|
||
|
}
|
||
|
|
||
|
Offset = (UINT32) (EHC_PORT_STAT_OFFSET + (4 * PortNumber));
|
||
|
PortStatus->PortStatus = 0;
|
||
|
PortStatus->PortChangeStatus = 0;
|
||
|
|
||
|
State = EhcReadOpReg (Ehc, Offset);
|
||
|
|
||
|
//
|
||
|
// Identify device speed. If in K state, it is low speed.
|
||
|
// If the port is enabled after reset, the device is of
|
||
|
// high speed. The USB bus driver should retrieve the actual
|
||
|
// port speed after reset.
|
||
|
//
|
||
|
if (EHC_BIT_IS_SET (State, PORTSC_LINESTATE_K)) {
|
||
|
PortStatus->PortStatus |= USB_PORT_STAT_LOW_SPEED;
|
||
|
|
||
|
} else if (EHC_BIT_IS_SET (State, PORTSC_ENABLED)) {
|
||
|
PortStatus->PortStatus |= USB_PORT_STAT_HIGH_SPEED;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Convert the EHCI port/port change state to UEFI status
|
||
|
//
|
||
|
MapSize = sizeof (mUsbPortStateMap) / sizeof (USB_PORT_STATE_MAP);
|
||
|
|
||
|
for (Index = 0; Index < MapSize; Index++) {
|
||
|
if (EHC_BIT_IS_SET (State, mUsbPortStateMap[Index].HwState)) {
|
||
|
PortStatus->PortStatus = (UINT16) (PortStatus->PortStatus | mUsbPortStateMap[Index].UefiState);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
MapSize = sizeof (mUsbPortChangeMap) / sizeof (USB_PORT_STATE_MAP);
|
||
|
|
||
|
for (Index = 0; Index < MapSize; Index++) {
|
||
|
if (EHC_BIT_IS_SET (State, mUsbPortChangeMap[Index].HwState)) {
|
||
|
PortStatus->PortChangeStatus = (UINT16) (PortStatus->PortChangeStatus | mUsbPortChangeMap[Index].UefiState);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ON_EXIT:
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Submits control transfer to a target USB device.
|
||
|
|
||
|
@param PeiServices The pointer of EFI_PEI_SERVICES.
|
||
|
@param This The pointer of PEI_USB2_HOST_CONTROLLER_PPI.
|
||
|
@param DeviceAddress The target device address.
|
||
|
@param DeviceSpeed Target device speed.
|
||
|
@param MaximumPacketLength Maximum packet size the default control transfer
|
||
|
endpoint is capable of sending or receiving.
|
||
|
@param Request USB device request to send.
|
||
|
@param TransferDirection Specifies the data direction for the data stage.
|
||
|
@param Data Data buffer to be transmitted or received from USB device.
|
||
|
@param DataLength The size (in bytes) of the data buffer.
|
||
|
@param TimeOut Indicates the maximum timeout, in millisecond.
|
||
|
If Timeout is 0, then the caller must wait for the function
|
||
|
to be completed until EFI_SUCCESS or EFI_DEVICE_ERROR is returned.
|
||
|
@param Translator Transaction translator to be used by this device.
|
||
|
@param TransferResult Return the result of this control transfer.
|
||
|
|
||
|
@retval EFI_SUCCESS Transfer was completed successfully.
|
||
|
@retval EFI_OUT_OF_RESOURCES The transfer failed due to lack of resources.
|
||
|
@retval EFI_INVALID_PARAMETER Some parameters are invalid.
|
||
|
@retval EFI_TIMEOUT Transfer failed due to timeout.
|
||
|
@retval EFI_DEVICE_ERROR Transfer failed due to host controller or device error.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
EhcControlTransfer (
|
||
|
IN EFI_PEI_SERVICES **PeiServices,
|
||
|
IN PEI_USB2_HOST_CONTROLLER_PPI *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
|
||
|
)
|
||
|
{
|
||
|
PEI_USB2_HC_DEV *Ehc;
|
||
|
PEI_URB *Urb;
|
||
|
UINT8 Endpoint;
|
||
|
EFI_STATUS Status;
|
||
|
|
||
|
//
|
||
|
// Validate parameters
|
||
|
//
|
||
|
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) ||
|
||
|
((DeviceSpeed == EFI_USB_SPEED_FULL) && (MaximumPacketLength > 64)) ||
|
||
|
((EFI_USB_SPEED_HIGH == DeviceSpeed) && (MaximumPacketLength > 512))) {
|
||
|
return EFI_INVALID_PARAMETER;
|
||
|
}
|
||
|
|
||
|
Ehc = PEI_RECOVERY_USB_EHC_DEV_FROM_EHCI_THIS (This);
|
||
|
|
||
|
Status = EFI_DEVICE_ERROR;
|
||
|
*TransferResult = EFI_USB_ERR_SYSTEM;
|
||
|
|
||
|
if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) {
|
||
|
EhcAckAllInterrupt (Ehc);
|
||
|
goto ON_EXIT;
|
||
|
}
|
||
|
|
||
|
EhcAckAllInterrupt (Ehc);
|
||
|
|
||
|
//
|
||
|
// Create a new URB, insert it into the asynchronous
|
||
|
// schedule list, then poll the execution status.
|
||
|
//
|
||
|
//
|
||
|
// Encode the direction in address, although default control
|
||
|
// endpoint is bidirectional. EhcCreateUrb expects this
|
||
|
// combination of Ep addr and its direction.
|
||
|
//
|
||
|
Endpoint = (UINT8) (0 | ((TransferDirection == EfiUsbDataIn) ? 0x80 : 0));
|
||
|
Urb = EhcCreateUrb (
|
||
|
Ehc,
|
||
|
DeviceAddress,
|
||
|
Endpoint,
|
||
|
DeviceSpeed,
|
||
|
0,
|
||
|
MaximumPacketLength,
|
||
|
Translator,
|
||
|
EHC_CTRL_TRANSFER,
|
||
|
Request,
|
||
|
Data,
|
||
|
*DataLength,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
1
|
||
|
);
|
||
|
|
||
|
if (Urb == NULL) {
|
||
|
Status = EFI_OUT_OF_RESOURCES;
|
||
|
goto ON_EXIT;
|
||
|
}
|
||
|
|
||
|
EhcLinkQhToAsync (Ehc, Urb->Qh);
|
||
|
Status = EhcExecTransfer (Ehc, Urb, TimeOut);
|
||
|
EhcUnlinkQhFromAsync (Ehc, Urb->Qh);
|
||
|
|
||
|
//
|
||
|
// Get the status from URB. The result is updated in EhcCheckUrbResult
|
||
|
// which is called by EhcExecTransfer
|
||
|
//
|
||
|
*TransferResult = Urb->Result;
|
||
|
*DataLength = Urb->Completed;
|
||
|
|
||
|
if (*TransferResult == EFI_USB_NOERROR) {
|
||
|
Status = EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
EhcAckAllInterrupt (Ehc);
|
||
|
EhcFreeUrb (Ehc, Urb);
|
||
|
|
||
|
ON_EXIT:
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
One notified function to stop the Host Controller at the end of PEI
|
||
|
|
||
|
@param[in] PeiServices Pointer to PEI Services Table.
|
||
|
@param[in] NotifyDescriptor Pointer to the descriptor for the Notification event that
|
||
|
caused this function to execute.
|
||
|
@param[in] Ppi Pointer to the PPI data associated with this function.
|
||
|
|
||
|
@retval EFI_SUCCESS The function completes successfully
|
||
|
@retval others
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
EhcEndOfPei (
|
||
|
IN EFI_PEI_SERVICES **PeiServices,
|
||
|
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
|
||
|
IN VOID *Ppi
|
||
|
)
|
||
|
{
|
||
|
PEI_USB2_HC_DEV *Ehc;
|
||
|
|
||
|
Ehc = PEI_RECOVERY_USB_EHC_DEV_FROM_THIS_NOTIFY (NotifyDescriptor);
|
||
|
|
||
|
EhcHaltHC (Ehc, EHC_GENERIC_TIMEOUT);
|
||
|
|
||
|
EhcFreeSched (Ehc);
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
@param FileHandle Handle of the file being invoked.
|
||
|
@param PeiServices Describes the list of possible PEI Services.
|
||
|
|
||
|
@retval EFI_SUCCESS PPI successfully installed.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
EhcPeimEntry (
|
||
|
IN EFI_PEI_FILE_HANDLE FileHandle,
|
||
|
IN CONST EFI_PEI_SERVICES **PeiServices
|
||
|
)
|
||
|
{
|
||
|
PEI_USB_CONTROLLER_PPI *ChipSetUsbControllerPpi;
|
||
|
EFI_STATUS Status;
|
||
|
UINT8 Index;
|
||
|
UINTN ControllerType;
|
||
|
UINTN BaseAddress;
|
||
|
UINTN MemPages;
|
||
|
PEI_USB2_HC_DEV *EhcDev;
|
||
|
EFI_PHYSICAL_ADDRESS TempPtr;
|
||
|
|
||
|
//
|
||
|
// Shadow this PEIM to run from memory
|
||
|
//
|
||
|
if (!EFI_ERROR (PeiServicesRegisterForShadow (FileHandle))) {
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
Status = PeiServicesLocatePpi (
|
||
|
&gPeiUsbControllerPpiGuid,
|
||
|
0,
|
||
|
NULL,
|
||
|
(VOID **) &ChipSetUsbControllerPpi
|
||
|
);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
return EFI_UNSUPPORTED;
|
||
|
}
|
||
|
|
||
|
Index = 0;
|
||
|
while (TRUE) {
|
||
|
Status = ChipSetUsbControllerPpi->GetUsbController (
|
||
|
(EFI_PEI_SERVICES **) PeiServices,
|
||
|
ChipSetUsbControllerPpi,
|
||
|
Index,
|
||
|
&ControllerType,
|
||
|
&BaseAddress
|
||
|
);
|
||
|
//
|
||
|
// When status is error, meant no controller is found
|
||
|
//
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// This PEIM is for UHC type controller.
|
||
|
//
|
||
|
if (ControllerType != PEI_EHCI_CONTROLLER) {
|
||
|
Index++;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
MemPages = sizeof (PEI_USB2_HC_DEV) / PAGESIZE + 1;
|
||
|
Status = PeiServicesAllocatePages (
|
||
|
EfiBootServicesCode,
|
||
|
MemPages,
|
||
|
&TempPtr
|
||
|
);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
return EFI_OUT_OF_RESOURCES;
|
||
|
}
|
||
|
|
||
|
ZeroMem((VOID *)(UINTN)TempPtr, MemPages*PAGESIZE);
|
||
|
EhcDev = (PEI_USB2_HC_DEV *) ((UINTN) TempPtr);
|
||
|
|
||
|
EhcDev->Signature = USB2_HC_DEV_SIGNATURE;
|
||
|
|
||
|
IoMmuInit (&EhcDev->IoMmu);
|
||
|
|
||
|
EhcDev->UsbHostControllerBaseAddress = (UINT32) BaseAddress;
|
||
|
|
||
|
|
||
|
EhcDev->HcStructParams = EhcReadCapRegister (EhcDev, EHC_HCSPARAMS_OFFSET);
|
||
|
EhcDev->HcCapParams = EhcReadCapRegister (EhcDev, EHC_HCCPARAMS_OFFSET);
|
||
|
EhcDev->CapLen = EhcReadCapRegister (EhcDev, EHC_CAPLENGTH_OFFSET) & 0x0FF;
|
||
|
//
|
||
|
// Initialize Uhc's hardware
|
||
|
//
|
||
|
Status = InitializeUsbHC (EhcDev);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
EhcDev->Usb2HostControllerPpi.ControlTransfer = EhcControlTransfer;
|
||
|
EhcDev->Usb2HostControllerPpi.BulkTransfer = EhcBulkTransfer;
|
||
|
EhcDev->Usb2HostControllerPpi.GetRootHubPortNumber = EhcGetRootHubPortNumber;
|
||
|
EhcDev->Usb2HostControllerPpi.GetRootHubPortStatus = EhcGetRootHubPortStatus;
|
||
|
EhcDev->Usb2HostControllerPpi.SetRootHubPortFeature = EhcSetRootHubPortFeature;
|
||
|
EhcDev->Usb2HostControllerPpi.ClearRootHubPortFeature = EhcClearRootHubPortFeature;
|
||
|
|
||
|
EhcDev->PpiDescriptor.Flags = (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST);
|
||
|
EhcDev->PpiDescriptor.Guid = &gPeiUsb2HostControllerPpiGuid;
|
||
|
EhcDev->PpiDescriptor.Ppi = &EhcDev->Usb2HostControllerPpi;
|
||
|
|
||
|
Status = PeiServicesInstallPpi (&EhcDev->PpiDescriptor);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
Index++;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
EhcDev->EndOfPeiNotifyList.Flags = (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST);
|
||
|
EhcDev->EndOfPeiNotifyList.Guid = &gEfiEndOfPeiSignalPpiGuid;
|
||
|
EhcDev->EndOfPeiNotifyList.Notify = EhcEndOfPei;
|
||
|
|
||
|
PeiServicesNotifyPpi (&EhcDev->EndOfPeiNotifyList);
|
||
|
|
||
|
Index++;
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
@param EhcDev EHCI Device.
|
||
|
|
||
|
@retval EFI_SUCCESS EHCI successfully initialized.
|
||
|
@retval EFI_ABORTED EHCI was failed to be initialized.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
InitializeUsbHC (
|
||
|
IN PEI_USB2_HC_DEV *EhcDev
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
|
||
|
|
||
|
EhcResetHC (EhcDev, EHC_RESET_TIMEOUT);
|
||
|
|
||
|
Status = EhcInitHC (EhcDev);
|
||
|
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
return EFI_ABORTED;
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|