/** @file
*
* Copyright (c) 2016, Hisilicon Limited. All rights reserved.
* Copyright (c) 2016, Linaro Limited. All rights reserved.
*
* 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 "VirtualEhciPciIo.h"
#include <Protocol/PciRootBridgeIo.h>
UINT32 mUsbMemBase;
UINTN mSegmentNumber = 0;
// Use 0xFF for the virtual PCI devices
UINTN mBusNumber = 0xFF;
UINTN mDeviceNumber = 0;
UINTN mFunctionNumber = 0;
typedef struct {
EFI_PHYSICAL_ADDRESS HostAddress;
EFI_PHYSICAL_ADDRESS DeviceAddress;
UINTN NumberOfBytes;
EFI_PCI_IO_PROTOCOL_OPERATION Operation;
BOOLEAN DoubleBuffer;
} MEM_MAP_INFO_INSTANCE;
EFI_CPU_ARCH_PROTOCOL *gCpu;
EHCI_PCI_CONFIG mEhciPciConfig = {
{
0x00,//UINT16 VendorId;
0x00,//UINT16 DeviceId;
0x00,//UINT16 Command;
0x0010,//UINT16 Status;
0x00,//UINT8 RevisionID;
{
PCI_IF_EHCI,//UINT8 ClassCode[3];
PCI_CLASS_SERIAL_USB,
PCI_CLASS_SERIAL
},
0x00,//UINT8 CacheLineSize;
0x00,//UINT8 LatencyTimer;
0x00,//UINT8 HeaderType;
0x00//UINT8 BIST;
},
{
{
0x00,//UINT32 Bar[6];
0x00,
0x00,
0x00,
0x00,
0x00
},
0x00,//UINT32 CISPtr;
0x00,//UINT16 SubsystemVendorID;
0x00,//UINT16 SubsystemID;
0x00,//UINT32 ExpansionRomBar;
0x40,//UINT8 CapabilityPtr;
{
0x00,//UINT8 Reserved1[3];
0x00,
0x00
},
0x00,//UINT32 Reserved2;
0x00,//UINT8 InterruptLine;
0x00,//UINT8 InterruptPin;
0x00,//UINT8 MinGnt;
0x00//UINT8 MaxLat;
},
0x0A,// UINT8 CapabilityID offset 0x40
0x00,// UINT8 NextItemPtr
0x2000 //UINT16 DebugPort
};
EFI_STATUS
EhciPciIoPollMem (
IN EFI_PCI_IO_PROTOCOL *This,
IN EFI_PCI_IO_PROTOCOL_WIDTH Width,
IN UINT8 BarIndex,
IN UINT64 Offset,
IN UINT64 Mask,
IN UINT64 Value,
IN UINT64 Delay,
OUT UINT64 *Result
)
{
ASSERT (FALSE);
return EFI_UNSUPPORTED;
}
EFI_STATUS
EhciPciIoPollIo (
IN EFI_PCI_IO_PROTOCOL *This,
IN EFI_PCI_IO_PROTOCOL_WIDTH Width,
IN UINT8 BarIndex,
IN UINT64 Offset,
IN UINT64 Mask,
IN UINT64 Value,
IN UINT64 Delay,
OUT UINT64 *Result
)
{
ASSERT (FALSE);
return EFI_UNSUPPORTED;
}
EFI_STATUS
EhciPciIoMemRead (
IN EFI_PCI_IO_PROTOCOL *This,
IN EFI_PCI_IO_PROTOCOL_WIDTH Width,
IN UINT8 BarIndex,
IN UINT64 Offset,
IN UINTN Count,
IN OUT VOID *Buffer
)
{
UINT32 i;
if ((UINT32)Width >= EfiPciIoWidthMaximum) {
return EFI_INVALID_PARAMETER;
}
if (Buffer == NULL) {
return EFI_INVALID_PARAMETER;
}
if (BarIndex != 0) {
return EFI_INVALID_PARAMETER;
}
Width = Width & 0x03;
//
// Loop for each iteration and move the data
//
switch (Width) {
case EfiPciWidthUint8:
for (i = 0; i < Count; i++){
*((UINT8 *)Buffer + i)= MmioRead8(mUsbMemBase + Offset + i);
}
break;
case EfiPciWidthUint16:
for (i = 0; i < Count; i++){
*((UINT16 *)Buffer + i)= MmioRead16(mUsbMemBase + Offset + i * 2);
}
break;
case EfiPciWidthUint32:
for (i = 0; i < Count; i++){
*((UINT32 *)Buffer + i)= MmioRead32(mUsbMemBase + Offset + i * 4);
}
break;
case EfiPciWidthUint64:
for (i = 0; i < Count; i++){
*((UINT64 *)Buffer + i)= MmioRead64(mUsbMemBase + Offset + i * 8);
}
break;
default:
return EFI_INVALID_PARAMETER;
}
return EFI_SUCCESS;
}
EFI_STATUS
EhciPciIoMemWrite (
IN EFI_PCI_IO_PROTOCOL *This,
IN EFI_PCI_IO_PROTOCOL_WIDTH Width,
IN UINT8 BarIndex,
IN UINT64 Offset,
IN UINTN Count,
IN OUT VOID *Buffer
)
{
UINT32 i;
if ((UINT32)Width >= EfiPciIoWidthMaximum) {
return EFI_INVALID_PARAMETER;
}
if (Buffer == NULL) {
return EFI_INVALID_PARAMETER;
}
Width = Width & 0x03;
//
// Loop for each iteration and move the data
//
switch (Width) {
case EfiPciWidthUint8:
for (i = 0; i < Count; i++){
MmioWrite8(mUsbMemBase + Offset + i, *((UINT8 *)Buffer + i));
}
break;
case EfiPciWidthUint16:
for (i = 0; i < Count; i++){
MmioWrite16(mUsbMemBase + Offset + i * 2, *((UINT16 *)Buffer + i));
}
break;
case EfiPciWidthUint32:
for (i = 0; i < Count; i++){
MmioWrite32(mUsbMemBase + Offset + i * 4, *((UINT32 *)Buffer + i));
}
break;
case EfiPciWidthUint64:
for (i = 0; i < Count; i++){
MmioWrite64(mUsbMemBase + Offset + i * 8, *((UINT64 *)Buffer + i));
}
break;
default:
return EFI_INVALID_PARAMETER;
}
return EFI_SUCCESS;
}
EFI_STATUS
EhciPciIoIoRead (
IN EFI_PCI_IO_PROTOCOL *This,
IN EFI_PCI_IO_PROTOCOL_WIDTH Width,
IN UINT8 BarIndex,
IN UINT64 Offset,
IN UINTN Count,
IN OUT VOID *Buffer
)
{
ASSERT (FALSE);
return EFI_UNSUPPORTED;
}
EFI_STATUS
EhciPciIoIoWrite (
IN EFI_PCI_IO_PROTOCOL *This,
IN EFI_PCI_IO_PROTOCOL_WIDTH Width,
IN UINT8 BarIndex,
IN UINT64 Offset,
IN UINTN Count,
IN OUT VOID *Buffer
)
{
ASSERT (FALSE);
return EFI_UNSUPPORTED;
}
EFI_STATUS
EhciPciIoPciRead (
IN EFI_PCI_IO_PROTOCOL *This,
IN EFI_PCI_IO_PROTOCOL_WIDTH Width,
IN UINT32 Offset,
IN UINTN Count,
IN OUT VOID *Buffer
)
{
UINT32 i;
UINT8 *DataPtr;
Width = Width & 0x03;
if (Offset < sizeof (EHCI_PCI_CONFIG) / sizeof (UINT8)){
DataPtr = (UINT8 *)(&mEhciPciConfig) + Offset;
switch (Width) {
case EfiPciWidthUint8:
for (i = 0; i < Count; i++){
*((UINT8 *)Buffer + i)= *(DataPtr + i);
}
break;
case EfiPciWidthUint16:
for (i = 0; i < Count; i++){
*((UINT16 *)Buffer + i)= *((UINT16 *)DataPtr + i);
}
break;
case EfiPciWidthUint32:
for (i = 0; i < Count; i++){
*(UINT32 *)(Buffer + i)= *((UINT32 *)DataPtr + i);
}
break;
case EfiPciWidthUint64:
for (i = 0; i < Count; i++){
*(UINT64 *)(Buffer + i)= *((UINT64 *)DataPtr + i);
}
break;
default:
return EFI_INVALID_PARAMETER;
}
} else {
switch (Width) {
case EfiPciWidthUint8:
*(UINT8 *)Buffer = 0xFF;
break;
case EfiPciWidthUint16:
*(UINT16 *)Buffer = 0xFFFF;
break;
case EfiPciWidthUint32:
*(UINT32 *)Buffer = 0xFFFFFFFF;
break;
case EfiPciWidthUint64:
*(UINT64 *)Buffer = 0xFFFFFFFFFFFFFFFF;
break;
default:
return EFI_INVALID_PARAMETER;
}
}
return EFI_SUCCESS;
}
EFI_STATUS
EhciPciIoPciWrite (
IN EFI_PCI_IO_PROTOCOL *This,
IN EFI_PCI_IO_PROTOCOL_WIDTH Width,
IN UINT32 Offset,
IN UINTN Count,
IN OUT VOID *Buffer
)
{
return EFI_SUCCESS;
}
EFI_STATUS
EhciPciIoCopyMem (
IN EFI_PCI_IO_PROTOCOL *This,
IN EFI_PCI_IO_PROTOCOL_WIDTH Width,
IN UINT8 DestBarIndex,
IN UINT64 DestOffset,
IN UINT8 SrcBarIndex,
IN UINT64 SrcOffset,
IN UINTN Count
)
{
ASSERT (FALSE);
return EFI_UNSUPPORTED;
}
EFI_STATUS
EhciPciIoMap (
IN EFI_PCI_IO_PROTOCOL *This,
IN EFI_PCI_IO_PROTOCOL_OPERATION Operation,
IN VOID *HostAddress,
IN OUT UINTN *NumberOfBytes,
OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
OUT VOID **Mapping
)
{
EFI_STATUS Status;
MEM_MAP_INFO_INSTANCE *Map;
VOID *Buffer;
EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor;
if (HostAddress == NULL || NumberOfBytes == NULL || DeviceAddress == NULL || Mapping == NULL) {
return EFI_INVALID_PARAMETER;
}
if ((UINT32)Operation >= EfiPciIoOperationMaximum) {
return EFI_INVALID_PARAMETER;
}
*DeviceAddress = ConvertToPhysicalAddress (HostAddress);
// Remember range so we can flush on the other side
Map = AllocatePool (sizeof (MEM_MAP_INFO_INSTANCE));
if (Map == NULL) {
return EFI_OUT_OF_RESOURCES;
}
*Mapping = Map;
if ((((UINTN)HostAddress & (EFI_PAGE_SIZE - 1)) != 0) ||
((*NumberOfBytes % EFI_PAGE_SIZE) != 0)) {
// Get the cacheability of the region
Status = gDS->GetMemorySpaceDescriptor (*DeviceAddress, &GcdDescriptor);
if (EFI_ERROR(Status)) {
return Status;
}
// If the mapped buffer is not an uncached buffer
if ( (GcdDescriptor.Attributes != EFI_MEMORY_WC) &&
(GcdDescriptor.Attributes != EFI_MEMORY_UC) )
{
//
// If the buffer does not fill entire cache lines we must double buffer into
// uncached memory. Device (PCI) address becomes uncached page.
//
Map->DoubleBuffer = TRUE;
Buffer = UncachedAllocatePages(EFI_SIZE_TO_PAGES (*NumberOfBytes));
if (Buffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
CopyMem (Buffer, HostAddress, *NumberOfBytes);
*DeviceAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Buffer;
} else {
Map->DoubleBuffer = FALSE;
}
} else {
Map->DoubleBuffer = FALSE;
// Flush the Data Cache (should not have any effect if the memory region is uncached)
gCpu->FlushDataCache (gCpu, *DeviceAddress, *NumberOfBytes, EfiCpuFlushTypeWriteBackInvalidate);
Status = gDS->SetMemorySpaceAttributes (*DeviceAddress & ~(BASE_4KB - 1), ALIGN_VALUE (*NumberOfBytes, BASE_4KB), EFI_MEMORY_WC);
if (EFI_ERROR (Status)) {
DEBUG((EFI_D_ERROR, "[%a]:[%dL] SetMemorySpaceAttributes Fail. %r\n", __FUNCTION__, __LINE__, Status));
}
}
Map->HostAddress = (UINTN)HostAddress;
Map->DeviceAddress = *DeviceAddress;
Map->NumberOfBytes = *NumberOfBytes;
Map->Operation = Operation;
return EFI_SUCCESS;
}
EFI_STATUS
EhciPciIoUnmap (
IN EFI_PCI_IO_PROTOCOL *This,
IN VOID *Mapping
)
{
MEM_MAP_INFO_INSTANCE *Map;
if (Mapping == NULL) {
return EFI_INVALID_PARAMETER;
}
Map = (MEM_MAP_INFO_INSTANCE *)Mapping;
if (Map->DoubleBuffer) {
if ((Map->Operation == EfiPciIoOperationBusMasterWrite) || (Map->Operation == EfiPciIoOperationBusMasterCommonBuffer)) {
CopyMem ((VOID *)(UINTN)Map->HostAddress, (VOID *)(UINTN)Map->DeviceAddress, Map->NumberOfBytes);
}
if((VOID *)(UINTN)Map->DeviceAddress != NULL) {
UncachedFreePages ((VOID *)(UINTN)Map->DeviceAddress, EFI_SIZE_TO_PAGES (Map->NumberOfBytes));
}
} else {
if (Map->Operation == EfiPciIoOperationBusMasterWrite) {
//
// Make sure we read buffer from uncached memory and not the cache
//
gCpu->FlushDataCache (gCpu, Map->HostAddress, Map->NumberOfBytes, EfiCpuFlushTypeInvalidate);
}
}
FreePool (Map);
return EFI_SUCCESS;
}
EFI_STATUS
EhciPciIoAllocateBuffer (
IN EFI_PCI_IO_PROTOCOL *This,
IN EFI_ALLOCATE_TYPE Type,
IN EFI_MEMORY_TYPE MemoryType,
IN UINTN Pages,
OUT VOID **HostAddress,
IN UINT64 Attributes
)
{
UINT32 HcCapParams;
if (Attributes &
(~(EFI_PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE |
EFI_PCI_ATTRIBUTE_MEMORY_CACHED ))) {
return EFI_UNSUPPORTED;
}
if (HostAddress == NULL) {
return EFI_INVALID_PARAMETER;
}
if (MemoryType == EfiBootServicesData) {
HcCapParams = MmioRead32(mUsbMemBase + EHC_HCCPARAMS_OFFSET);
if ((BOOLEAN)(((HcCapParams) & (HCCP_64BIT)) == (HCCP_64BIT))){
*HostAddress = UncachedAllocatePages(Pages);
} else {
// TODO: We need support allocating UC memory below 4GB strictly
*HostAddress = UncachedAllocatePages(Pages);
}
}else{
return EFI_INVALID_PARAMETER;
}
return EFI_SUCCESS;
}
EFI_STATUS
EhciPciIoFreeBuffer (
IN EFI_PCI_IO_PROTOCOL *This,
IN UINTN Pages,
IN VOID *HostAddress
)
{
UncachedFreePages (HostAddress, Pages);
return EFI_SUCCESS;
}
EFI_STATUS
EhciPciIoFlush (
IN EFI_PCI_IO_PROTOCOL *This
)
{
return EFI_SUCCESS;
}
EFI_STATUS
EhciPciIoGetLocation (
IN EFI_PCI_IO_PROTOCOL *This,
OUT UINTN *SegmentNumber,
OUT UINTN *BusNumber,
OUT UINTN *DeviceNumber,
OUT UINTN *FunctionNumber
)
{
*SegmentNumber = mSegmentNumber;
*BusNumber = mBusNumber;
*DeviceNumber = mDeviceNumber;
*FunctionNumber = mFunctionNumber;
return EFI_SUCCESS;
}
EFI_STATUS
EhciPciIoAttributes (
IN EFI_PCI_IO_PROTOCOL *This,
IN EFI_PCI_IO_PROTOCOL_ATTRIBUTE_OPERATION Operation,
IN UINT64 Attributes,
OUT UINT64 *Result OPTIONAL
)
{
if (Result != NULL) {
*Result = 0;
}
return EFI_SUCCESS;
}
EFI_STATUS
EhciPciIoGetBarAttributes (
IN EFI_PCI_IO_PROTOCOL *This,
IN UINT8 BarIndex,
OUT UINT64 *Supports, OPTIONAL
OUT VOID **Resources OPTIONAL
)
{
ASSERT (FALSE);
return EFI_UNSUPPORTED;
}
EFI_STATUS
EhciPciIoSetBarAttributes (
IN EFI_PCI_IO_PROTOCOL *This,
IN UINT64 Attributes,
IN UINT8 BarIndex,
IN OUT UINT64 *Offset,
IN OUT UINT64 *Length
)
{
ASSERT (FALSE);
return EFI_UNSUPPORTED;
}
//
// Pci Io Protocol Interface
//
EFI_PCI_IO_PROTOCOL mEhciPciIoInterface = {
EhciPciIoPollMem,
EhciPciIoPollIo,
{
EhciPciIoMemRead,
EhciPciIoMemWrite
},
{
EhciPciIoIoRead,
EhciPciIoIoWrite
},
{
EhciPciIoPciRead,
EhciPciIoPciWrite
},
EhciPciIoCopyMem,
EhciPciIoMap,
EhciPciIoUnmap,
EhciPciIoAllocateBuffer,
EhciPciIoFreeBuffer,
EhciPciIoFlush,
EhciPciIoGetLocation,
EhciPciIoAttributes,
EhciPciIoGetBarAttributes,
EhciPciIoSetBarAttributes,
0,
NULL
};
EFI_STATUS
EFIAPI
EhciVirtualPciIoInitialize (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_HANDLE Handle;
EFI_DEV_PATH EndNode;
EFI_DEV_PATH Node;
EFI_DEVICE_PATH_PROTOCOL *DevicePath = NULL;
mUsbMemBase = PlatformGetEhciBase ();
DEBUG ((EFI_D_ERROR, "mUsbMemBase: 0x%x\n", mUsbMemBase));
// Get the Cpu protocol for later use
Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&gCpu);
//
// Install the pciio protocol, device path protocol
//
Handle = NULL;
Status = gBS->InstallMultipleProtocolInterfaces (
&Handle,
&gEfiPciIoProtocolGuid,
&mEhciPciIoInterface,
NULL
);
if (EFI_ERROR (Status)) {
return Status;
}
(void)ZeroMem (&Node, sizeof (Node));
Node.DevPath.Type = HARDWARE_DEVICE_PATH;
Node.DevPath.SubType = HW_PCI_DP;
(void)SetDevicePathNodeLength (&Node.DevPath, sizeof (PCI_DEVICE_PATH));
// Make USB controller device path different from built-in SATA controller
Node.Pci.Function = 1;
Node.Pci.Device = 0;
SetDevicePathEndNode (&EndNode.DevPath);
DevicePath = AppendDevicePathNode (&EndNode.DevPath, &Node.DevPath);
Status = gBS->InstallProtocolInterface (
&Handle,
&gEfiDevicePathProtocolGuid,
EFI_NATIVE_INTERFACE,
DevicePath
);
if(EFI_ERROR(Status))
{
DEBUG((EFI_D_ERROR, "[%a]:[%dL] InstallProtocolInterface fail. %r\n", __FUNCTION__, __LINE__, Status));
}
return EFI_SUCCESS;
}