audk/MdeModulePkg/Bus/Pci/EhciDxe/EhciReg.c

676 lines
16 KiB
C
Raw Normal View History

/** @file
The EHCI register operation routines.
MdeModulePkg: Refine casting expression result to bigger size There are cases that the operands of an expression are all with rank less than UINT64/INT64 and the result of the expression is explicitly cast to UINT64/INT64 to fit the target size. An example will be: UINT32 a,b; // a and b can be any unsigned int type with rank less than UINT64, like // UINT8, UINT16, etc. UINT64 c; c = (UINT64) (a + b); Some static code checkers may warn that the expression result might overflow within the rank of "int" (integer promotions) and the result is then cast to a bigger size. The commit refines codes by the following rules: 1). When the expression is possible to overflow the range of unsigned int/ int: c = (UINT64)a + b; 2). When the expression will not overflow within the rank of "int", remove the explicit type casts: c = a + b; 3). When the expression will be cast to pointer of possible greater size: UINT32 a,b; VOID *c; c = (VOID *)(UINTN)(a + b); --> c = (VOID *)((UINTN)a + b); 4). When one side of a comparison expression contains only operands with rank less than UINT32: UINT8 a; UINT16 b; UINTN c; if ((UINTN)(a + b) > c) {...} --> if (((UINT32)a + b) > c) {...} For rule 4), if we remove the 'UINTN' type cast like: if (a + b > c) {...} The VS compiler will complain with warning C4018 (signed/unsigned mismatch, level 3 warning) due to promoting 'a + b' to type 'int'. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Hao Wu <hao.a.wu@intel.com> Reviewed-by: Feng Tian <feng.tian@intel.com>
2017-02-24 03:01:34 +01:00
Copyright (c) 2007 - 2017, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "Ehci.h"
/**
Read EHCI capability register.
@param Ehc The EHCI device.
@param Offset Capability register address.
@return The register content read.
@retval If err, return 0xffff.
**/
UINT32
EhcReadCapRegister (
IN USB2_HC_DEV *Ehc,
IN UINT32 Offset
)
{
UINT32 Data;
EFI_STATUS Status;
Status = Ehc->PciIo->Mem.Read (
Ehc->PciIo,
EfiPciIoWidthUint32,
EHC_BAR_INDEX,
(UINT64) Offset,
1,
&Data
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "EhcReadCapRegister: Pci Io read error - %r at %d\n", Status, Offset));
Data = 0xFFFF;
}
return Data;
}
/**
Read EHCI debug port register.
@param Ehc The EHCI device.
@param Offset Debug port register offset.
@return The register content read.
@retval If err, return 0xffff.
**/
UINT32
EhcReadDbgRegister (
IN CONST USB2_HC_DEV *Ehc,
IN UINT32 Offset
)
{
UINT32 Data;
EFI_STATUS Status;
Status = Ehc->PciIo->Mem.Read (
Ehc->PciIo,
EfiPciIoWidthUint32,
Ehc->DebugPortBarNum,
MdeModulePkg: Refine casting expression result to bigger size There are cases that the operands of an expression are all with rank less than UINT64/INT64 and the result of the expression is explicitly cast to UINT64/INT64 to fit the target size. An example will be: UINT32 a,b; // a and b can be any unsigned int type with rank less than UINT64, like // UINT8, UINT16, etc. UINT64 c; c = (UINT64) (a + b); Some static code checkers may warn that the expression result might overflow within the rank of "int" (integer promotions) and the result is then cast to a bigger size. The commit refines codes by the following rules: 1). When the expression is possible to overflow the range of unsigned int/ int: c = (UINT64)a + b; 2). When the expression will not overflow within the rank of "int", remove the explicit type casts: c = a + b; 3). When the expression will be cast to pointer of possible greater size: UINT32 a,b; VOID *c; c = (VOID *)(UINTN)(a + b); --> c = (VOID *)((UINTN)a + b); 4). When one side of a comparison expression contains only operands with rank less than UINT32: UINT8 a; UINT16 b; UINTN c; if ((UINTN)(a + b) > c) {...} --> if (((UINT32)a + b) > c) {...} For rule 4), if we remove the 'UINTN' type cast like: if (a + b > c) {...} The VS compiler will complain with warning C4018 (signed/unsigned mismatch, level 3 warning) due to promoting 'a + b' to type 'int'. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Hao Wu <hao.a.wu@intel.com> Reviewed-by: Feng Tian <feng.tian@intel.com>
2017-02-24 03:01:34 +01:00
Ehc->DebugPortOffset + Offset,
1,
&Data
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "EhcReadDbgRegister: Pci Io read error - %r at %d\n", Status, Offset));
Data = 0xFFFF;
}
return Data;
}
/**
Check whether the host controller has an in-use debug port.
@param[in] Ehc The Enhanced Host Controller to query.
@param[in] PortNumber If PortNumber is not NULL, then query whether
PortNumber is an in-use debug port on Ehc. (PortNumber
is taken in UEFI notation, i.e., zero-based.)
Otherwise, query whether Ehc has any in-use debug
port.
@retval TRUE PortNumber is an in-use debug port on Ehc (if PortNumber is
not NULL), or some port on Ehc is an in-use debug port
(otherwise).
@retval FALSE PortNumber is not an in-use debug port on Ehc (if PortNumber
is not NULL), or no port on Ehc is an in-use debug port
(otherwise).
**/
BOOLEAN
EhcIsDebugPortInUse (
IN CONST USB2_HC_DEV *Ehc,
IN CONST UINT8 *PortNumber OPTIONAL
)
{
UINT32 State;
if (Ehc->DebugPortNum == 0) {
//
// The host controller has no debug port.
//
return FALSE;
}
//
// The Debug Port Number field in HCSPARAMS is one-based.
//
if (PortNumber != NULL && *PortNumber != Ehc->DebugPortNum - 1) {
//
// The caller specified a port, but it's not the debug port of the host
// controller.
//
return FALSE;
}
//
// Deduce usage from the Control Register.
//
State = EhcReadDbgRegister(Ehc, 0);
return (State & USB_DEBUG_PORT_IN_USE_MASK) == USB_DEBUG_PORT_IN_USE_MASK;
}
/**
Read EHCI Operation register.
@param Ehc The EHCI device.
@param Offset The operation register offset.
@return The register content read.
@retval If err, return 0xffff.
**/
UINT32
EhcReadOpReg (
IN USB2_HC_DEV *Ehc,
IN UINT32 Offset
)
{
UINT32 Data;
EFI_STATUS Status;
ASSERT (Ehc->CapLen != 0);
Status = Ehc->PciIo->Mem.Read (
Ehc->PciIo,
EfiPciIoWidthUint32,
EHC_BAR_INDEX,
MdeModulePkg: Refine casting expression result to bigger size There are cases that the operands of an expression are all with rank less than UINT64/INT64 and the result of the expression is explicitly cast to UINT64/INT64 to fit the target size. An example will be: UINT32 a,b; // a and b can be any unsigned int type with rank less than UINT64, like // UINT8, UINT16, etc. UINT64 c; c = (UINT64) (a + b); Some static code checkers may warn that the expression result might overflow within the rank of "int" (integer promotions) and the result is then cast to a bigger size. The commit refines codes by the following rules: 1). When the expression is possible to overflow the range of unsigned int/ int: c = (UINT64)a + b; 2). When the expression will not overflow within the rank of "int", remove the explicit type casts: c = a + b; 3). When the expression will be cast to pointer of possible greater size: UINT32 a,b; VOID *c; c = (VOID *)(UINTN)(a + b); --> c = (VOID *)((UINTN)a + b); 4). When one side of a comparison expression contains only operands with rank less than UINT32: UINT8 a; UINT16 b; UINTN c; if ((UINTN)(a + b) > c) {...} --> if (((UINT32)a + b) > c) {...} For rule 4), if we remove the 'UINTN' type cast like: if (a + b > c) {...} The VS compiler will complain with warning C4018 (signed/unsigned mismatch, level 3 warning) due to promoting 'a + b' to type 'int'. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Hao Wu <hao.a.wu@intel.com> Reviewed-by: Feng Tian <feng.tian@intel.com>
2017-02-24 03:01:34 +01:00
Ehc->CapLen + Offset,
1,
&Data
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "EhcReadOpReg: Pci Io Read error - %r at %d\n", Status, Offset));
Data = 0xFFFF;
}
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 USB2_HC_DEV *Ehc,
IN UINT32 Offset,
IN UINT32 Data
)
{
EFI_STATUS Status;
ASSERT (Ehc->CapLen != 0);
Status = Ehc->PciIo->Mem.Write (
Ehc->PciIo,
EfiPciIoWidthUint32,
EHC_BAR_INDEX,
MdeModulePkg: Refine casting expression result to bigger size There are cases that the operands of an expression are all with rank less than UINT64/INT64 and the result of the expression is explicitly cast to UINT64/INT64 to fit the target size. An example will be: UINT32 a,b; // a and b can be any unsigned int type with rank less than UINT64, like // UINT8, UINT16, etc. UINT64 c; c = (UINT64) (a + b); Some static code checkers may warn that the expression result might overflow within the rank of "int" (integer promotions) and the result is then cast to a bigger size. The commit refines codes by the following rules: 1). When the expression is possible to overflow the range of unsigned int/ int: c = (UINT64)a + b; 2). When the expression will not overflow within the rank of "int", remove the explicit type casts: c = a + b; 3). When the expression will be cast to pointer of possible greater size: UINT32 a,b; VOID *c; c = (VOID *)(UINTN)(a + b); --> c = (VOID *)((UINTN)a + b); 4). When one side of a comparison expression contains only operands with rank less than UINT32: UINT8 a; UINT16 b; UINTN c; if ((UINTN)(a + b) > c) {...} --> if (((UINT32)a + b) > c) {...} For rule 4), if we remove the 'UINTN' type cast like: if (a + b > c) {...} The VS compiler will complain with warning C4018 (signed/unsigned mismatch, level 3 warning) due to promoting 'a + b' to type 'int'. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Hao Wu <hao.a.wu@intel.com> Reviewed-by: Feng Tian <feng.tian@intel.com>
2017-02-24 03:01:34 +01:00
Ehc->CapLen + Offset,
1,
&Data
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "EhcWriteOpReg: Pci Io Write error: %r at %d\n", Status, Offset));
}
}
/**
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 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 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 operation register.
@param Bit The bit 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 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;
}
gBS->Stall (EHC_SYNC_POLL_INTERVAL);
}
return EFI_TIMEOUT;
}
/**
Add support for UEFI Over Legacy (UoL) feature, stop
the legacy USB SMI support.
@param Ehc The EHCI device.
**/
VOID
EhcClearLegacySupport (
IN USB2_HC_DEV *Ehc
)
{
UINT32 ExtendCap;
EFI_PCI_IO_PROTOCOL *PciIo;
UINT32 Value;
UINT32 TimeOut;
DEBUG ((EFI_D_INFO, "EhcClearLegacySupport: called to clear legacy support\n"));
PciIo = Ehc->PciIo;
ExtendCap = (Ehc->HcCapParams >> 8) & 0xFF;
PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap, 1, &Value);
PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap + 0x4, 1, &Value);
PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap, 1, &Value);
Value |= (0x1 << 24);
PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, ExtendCap, 1, &Value);
TimeOut = 40;
while (TimeOut-- != 0) {
gBS->Stall (500);
PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap, 1, &Value);
if ((Value & 0x01010000) == 0x01000000) {
break;
}
}
PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap, 1, &Value);
PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap + 0x4, 1, &Value);
}
/**
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_SUCCESS Synchronized with the hardware.
@retval EFI_TIMEOUT Time out happened while waiting door bell to set.
**/
EFI_STATUS
EhcSetAndWaitDoorBell (
IN 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 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_SUCCESS The periodical schedule is enabled.
@retval EFI_TIMEOUT Time out happened while enabling periodic schedule.
**/
EFI_STATUS
EhcEnablePeriodSchd (
IN 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.
@return Others Failed to enable the asynchronous scheudle.
**/
EFI_STATUS
EhcEnableAsyncSchd (
IN 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;
}
/**
Whether Ehc is halted.
@param Ehc The EHCI device.
@retval TRUE The controller is halted.
@retval FALSE It isn't halted.
**/
BOOLEAN
EhcIsHalt (
IN USB2_HC_DEV *Ehc
)
{
return EHC_REG_BIT_IS_SET (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT);
}
/**
Whether system error occurred.
@param Ehc The EHCI device.
@return TRUE System error happened.
@return FALSE No system error.
**/
BOOLEAN
EhcIsSysError (
IN 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_SUCCESS The host controller is reset.
@return Others Failed to reset the host.
**/
EFI_STATUS
EhcResetHC (
IN 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_SUCCESS The EHCI is halt.
@retval EFI_TIMEOUT Failed to halt the controller before Timeout.
**/
EFI_STATUS
EhcHaltHC (
IN 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.
@return Others Failed to set the EHCI to run.
**/
EFI_STATUS
EhcRunHC (
IN 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;
}
/**
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.
@return EFI_SUCCESS The EHCI has come out of halt state.
@return EFI_TIMEOUT Time out happened.
**/
EFI_STATUS
EhcInitHC (
IN USB2_HC_DEV *Ehc
)
{
EFI_STATUS Status;
UINT32 Index;
UINT32 RegVal;
// This ASSERT crashes the BeagleBoard. There is some issue in the USB stack.
// This ASSERT needs to be removed so the BeagleBoard will boot. When we fix
// the USB stack we can put this ASSERT back in
// ASSERT (EhcIsHalt (Ehc));
//
// Allocate the periodic frame and associated memeory
// management facilities if not already done.
//
if (Ehc->PeriodFrame != NULL) {
EhcFreeSched (Ehc);
}
Status = EhcInitSched (Ehc);
if (EFI_ERROR (Status)) {
return Status;
}
//
// 1. Clear USBINTR to disable all the interrupt. UEFI works by polling
//
EhcWriteOpReg (Ehc, EHC_USBINTR_OFFSET, 0);
//
// 2. Start the Host Controller
//
EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_RUN);
//
// 3. Power up all ports if EHCI has Port Power Control (PPC) support
//
if (Ehc->HcStructParams & HCSP_PPC) {
for (Index = 0; Index < (UINT8) (Ehc->HcStructParams & HCSP_NPORTS); Index++) {
//
// Do not clear port status bits on initialization. Otherwise devices will
// not enumerate properly at startup.
//
RegVal = EhcReadOpReg(Ehc, (UINT32)(EHC_PORT_STAT_OFFSET + (4 * Index)));
RegVal &= ~PORTSC_CHANGE_MASK;
RegVal |= PORTSC_POWER;
EhcWriteOpReg (Ehc, (UINT32) (EHC_PORT_STAT_OFFSET + (4 * Index)), RegVal);
}
}
//
// Wait roothub port power stable
//
gBS->Stall (EHC_ROOT_PORT_RECOVERY_STALL);
//
// 4. Set all ports routing to EHC
//
EhcSetOpRegBit (Ehc, EHC_CONFIG_FLAG_OFFSET, CONFIGFLAG_ROUTE_EHC);
Status = EhcEnablePeriodSchd (Ehc, EHC_GENERIC_TIMEOUT);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "EhcInitHC: failed to enable period schedule\n"));
return Status;
}
Status = EhcEnableAsyncSchd (Ehc, EHC_GENERIC_TIMEOUT);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "EhcInitHC: failed to enable async schedule\n"));
return Status;
}
return EFI_SUCCESS;
}