2008-04-17 07:48:13 +02:00
|
|
|
/*++
|
|
|
|
|
|
|
|
Copyright (c) 2005, Intel Corporation
|
|
|
|
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.
|
|
|
|
|
|
|
|
Module Name:
|
|
|
|
PcatPciRootBridgeIo.c
|
|
|
|
|
|
|
|
Abstract:
|
|
|
|
|
|
|
|
EFI PC AT PCI Root Bridge Io Protocol
|
|
|
|
|
|
|
|
Revision History
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
#include "PcatPciRootBridge.h"
|
|
|
|
#include "pci22.h"
|
|
|
|
#include "SalProc.h"
|
|
|
|
|
|
|
|
#include EFI_GUID_DEFINITION (SalSystemTable)
|
|
|
|
|
|
|
|
//
|
|
|
|
// Might be good to put this in an include file, but people may start
|
|
|
|
// using it! They should always access the EFI abstraction that is
|
|
|
|
// contained in this file. Just a little information hiding.
|
|
|
|
//
|
|
|
|
#define PORT_TO_MEM(_Port) ( ((_Port) & 0xffffffffffff0000) | (((_Port) & 0xfffc) << 10) | ((_Port) & 0x0fff) )
|
|
|
|
|
|
|
|
//
|
|
|
|
// Macro's with casts make this much easier to use and read.
|
|
|
|
//
|
|
|
|
#define PORT_TO_MEM8(_Port) (*(UINT8 *)(PORT_TO_MEM(_Port)))
|
|
|
|
#define PORT_TO_MEM16(_Port) (*(UINT16 *)(PORT_TO_MEM(_Port)))
|
|
|
|
#define PORT_TO_MEM32(_Port) (*(UINT32 *)(PORT_TO_MEM(_Port)))
|
|
|
|
|
|
|
|
#define EFI_PCI_ADDRESS_IA64(_seg, _bus,_dev,_func,_reg) \
|
|
|
|
( (UINT64) ( (((UINTN)_seg) << 24) + (((UINTN)_bus) << 16) + (((UINTN)_dev) << 11) + (((UINTN)_func) << 8) + ((UINTN)_reg)) )
|
|
|
|
|
|
|
|
//
|
|
|
|
// Local variables for performing SAL Proc calls
|
|
|
|
//
|
2008-10-30 07:17:19 +01:00
|
|
|
PLABEL mSalProcPlabel;
|
|
|
|
CALL_SAL_PROC mGlobalSalProc;
|
2008-04-17 07:48:13 +02:00
|
|
|
|
|
|
|
EFI_STATUS
|
|
|
|
PcatRootBridgeIoIoRead (
|
|
|
|
IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *This,
|
|
|
|
IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH Width,
|
|
|
|
IN UINT64 UserAddress,
|
|
|
|
IN UINTN Count,
|
|
|
|
IN OUT VOID *UserBuffer
|
|
|
|
)
|
|
|
|
{
|
|
|
|
PCAT_PCI_ROOT_BRIDGE_INSTANCE *PrivateData;
|
|
|
|
UINTN InStride;
|
|
|
|
UINTN OutStride;
|
|
|
|
UINTN AlignMask;
|
|
|
|
UINTN Address;
|
|
|
|
PTR Buffer;
|
|
|
|
UINT16 Data16;
|
|
|
|
UINT32 Data32;
|
|
|
|
|
|
|
|
|
|
|
|
if ( UserBuffer == NULL ) {
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
PrivateData = DRIVER_INSTANCE_FROM_PCI_ROOT_BRIDGE_IO_THIS(This);
|
|
|
|
|
|
|
|
Address = (UINTN) UserAddress;
|
|
|
|
Buffer.buf = (UINT8 *)UserBuffer;
|
|
|
|
|
|
|
|
if ( Address < PrivateData->IoBase || Address > PrivateData->IoLimit ) {
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Width < 0 || Width >= EfiPciWidthMaximum) {
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((Width & 0x03) == EfiPciWidthUint64) {
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlignMask = (1 << (Width & 0x03)) - 1;
|
|
|
|
if ( Address & AlignMask ) {
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
InStride = 1 << (Width & 0x03);
|
|
|
|
OutStride = InStride;
|
|
|
|
if (Width >=EfiPciWidthFifoUint8 && Width <= EfiPciWidthFifoUint64) {
|
|
|
|
InStride = 0;
|
|
|
|
}
|
|
|
|
if (Width >=EfiPciWidthFillUint8 && Width <= EfiPciWidthFillUint64) {
|
|
|
|
OutStride = 0;
|
|
|
|
}
|
|
|
|
Width = Width & 0x03;
|
|
|
|
|
|
|
|
Address += PrivateData->PhysicalIoBase;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Loop for each iteration and move the data
|
|
|
|
//
|
|
|
|
|
|
|
|
switch (Width) {
|
|
|
|
case EfiPciWidthUint8:
|
|
|
|
for (; Count > 0; Count--, Buffer.buf += OutStride, Address += InStride) {
|
|
|
|
MEMORY_FENCE();
|
|
|
|
*Buffer.ui8 = PORT_TO_MEM8(Address);
|
|
|
|
MEMORY_FENCE();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EfiPciWidthUint16:
|
|
|
|
for (; Count > 0; Count--, Buffer.buf += OutStride, Address += InStride) {
|
|
|
|
MEMORY_FENCE();
|
|
|
|
if (Buffer.ui & 0x1) {
|
|
|
|
Data16 = PORT_TO_MEM16(Address);
|
|
|
|
*Buffer.ui8 = (UINT8)(Data16 & 0xff);
|
|
|
|
*(Buffer.ui8+1) = (UINT8)((Data16 >> 8) & 0xff);
|
|
|
|
} else {
|
|
|
|
*Buffer.ui16 = PORT_TO_MEM16(Address);
|
|
|
|
}
|
|
|
|
MEMORY_FENCE();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EfiPciWidthUint32:
|
|
|
|
for (; Count > 0; Count--, Buffer.buf += OutStride, Address += InStride) {
|
|
|
|
MEMORY_FENCE();
|
|
|
|
if (Buffer.ui & 0x3) {
|
|
|
|
Data32 = PORT_TO_MEM32(Address);
|
|
|
|
*Buffer.ui8 = (UINT8)(Data32 & 0xff);
|
|
|
|
*(Buffer.ui8+1) = (UINT8)((Data32 >> 8) & 0xff);
|
|
|
|
*(Buffer.ui8+2) = (UINT8)((Data32 >> 16) & 0xff);
|
|
|
|
*(Buffer.ui8+3) = (UINT8)((Data32 >> 24) & 0xff);
|
|
|
|
} else {
|
|
|
|
*Buffer.ui32 = PORT_TO_MEM32(Address);
|
|
|
|
}
|
|
|
|
MEMORY_FENCE();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
EFI_STATUS
|
|
|
|
PcatRootBridgeIoIoWrite (
|
|
|
|
IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *This,
|
|
|
|
IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH Width,
|
|
|
|
IN UINT64 UserAddress,
|
|
|
|
IN UINTN Count,
|
|
|
|
IN OUT VOID *UserBuffer
|
|
|
|
)
|
|
|
|
{
|
|
|
|
PCAT_PCI_ROOT_BRIDGE_INSTANCE *PrivateData;
|
|
|
|
UINTN InStride;
|
|
|
|
UINTN OutStride;
|
|
|
|
UINTN AlignMask;
|
|
|
|
UINTN Address;
|
|
|
|
PTR Buffer;
|
|
|
|
UINT16 Data16;
|
|
|
|
UINT32 Data32;
|
|
|
|
|
|
|
|
if ( UserBuffer == NULL ) {
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
PrivateData = DRIVER_INSTANCE_FROM_PCI_ROOT_BRIDGE_IO_THIS(This);
|
|
|
|
|
|
|
|
Address = (UINTN) UserAddress;
|
|
|
|
Buffer.buf = (UINT8 *)UserBuffer;
|
|
|
|
|
|
|
|
if ( Address < PrivateData->IoBase || Address > PrivateData->IoLimit ) {
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Width < 0 || Width >= EfiPciWidthMaximum) {
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((Width & 0x03) == EfiPciWidthUint64) {
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlignMask = (1 << (Width & 0x03)) - 1;
|
|
|
|
if ( Address & AlignMask ) {
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
InStride = 1 << (Width & 0x03);
|
|
|
|
OutStride = InStride;
|
|
|
|
if (Width >=EfiPciWidthFifoUint8 && Width <= EfiPciWidthFifoUint64) {
|
|
|
|
InStride = 0;
|
|
|
|
}
|
|
|
|
if (Width >=EfiPciWidthFillUint8 && Width <= EfiPciWidthFillUint64) {
|
|
|
|
OutStride = 0;
|
|
|
|
}
|
|
|
|
Width = Width & 0x03;
|
|
|
|
|
|
|
|
Address += PrivateData->PhysicalIoBase;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Loop for each iteration and move the data
|
|
|
|
//
|
|
|
|
|
|
|
|
switch (Width) {
|
|
|
|
case EfiPciWidthUint8:
|
|
|
|
for (; Count > 0; Count--, Buffer.buf += OutStride, Address += InStride) {
|
|
|
|
MEMORY_FENCE();
|
|
|
|
PORT_TO_MEM8(Address) = *Buffer.ui8;
|
|
|
|
MEMORY_FENCE();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EfiPciWidthUint16:
|
|
|
|
for (; Count > 0; Count--, Buffer.buf += OutStride, Address += InStride) {
|
|
|
|
MEMORY_FENCE();
|
|
|
|
if (Buffer.ui & 0x1) {
|
|
|
|
Data16 = *Buffer.ui8;
|
|
|
|
Data16 = Data16 | (*(Buffer.ui8+1) << 8);
|
|
|
|
PORT_TO_MEM16(Address) = Data16;
|
|
|
|
} else {
|
|
|
|
PORT_TO_MEM16(Address) = *Buffer.ui16;
|
|
|
|
}
|
|
|
|
MEMORY_FENCE();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case EfiPciWidthUint32:
|
|
|
|
for (; Count > 0; Count--, Buffer.buf += OutStride, Address += InStride) {
|
|
|
|
MEMORY_FENCE();
|
|
|
|
if (Buffer.ui & 0x3) {
|
|
|
|
Data32 = *Buffer.ui8;
|
|
|
|
Data32 = Data32 | (*(Buffer.ui8+1) << 8);
|
|
|
|
Data32 = Data32 | (*(Buffer.ui8+2) << 16);
|
|
|
|
Data32 = Data32 | (*(Buffer.ui8+3) << 24);
|
|
|
|
PORT_TO_MEM32(Address) = Data32;
|
|
|
|
} else {
|
|
|
|
PORT_TO_MEM32(Address) = *Buffer.ui32;
|
|
|
|
}
|
|
|
|
MEMORY_FENCE();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
EFI_STATUS
|
|
|
|
PcatRootBridgeIoGetIoPortMapping (
|
|
|
|
OUT EFI_PHYSICAL_ADDRESS *IoPortMapping,
|
|
|
|
OUT EFI_PHYSICAL_ADDRESS *MemoryPortMapping
|
|
|
|
)
|
|
|
|
/*++
|
|
|
|
|
|
|
|
Get the IO Port Map from the SAL System Table.
|
|
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
SAL_SYSTEM_TABLE_ASCENDING_ORDER *SalSystemTable;
|
|
|
|
SAL_ST_MEMORY_DESCRIPTOR_ENTRY *SalMemDesc;
|
|
|
|
EFI_STATUS Status;
|
|
|
|
|
|
|
|
//
|
|
|
|
// On all Itanium architectures, bit 63 is the I/O bit for performming Memory Mapped I/O operations
|
|
|
|
//
|
|
|
|
*MemoryPortMapping = 0x8000000000000000;
|
|
|
|
|
|
|
|
Status = EfiLibGetSystemConfigurationTable(&gEfiSalSystemTableGuid, &SalSystemTable);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
|
|
return EFI_NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// BugBug: Add code to test checksum on the Sal System Table
|
|
|
|
//
|
|
|
|
if (SalSystemTable->Entry0.Type != 0) {
|
|
|
|
return EFI_UNSUPPORTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
mSalProcPlabel.ProcEntryPoint = SalSystemTable->Entry0.SalProcEntry;
|
|
|
|
mSalProcPlabel.GP = SalSystemTable->Entry0.GlobalDataPointer;
|
|
|
|
mGlobalSalProc = (CALL_SAL_PROC)&mSalProcPlabel.ProcEntryPoint;
|
|
|
|
|
|
|
|
//
|
|
|
|
// The SalSystemTable pointer includes the Type 0 entry.
|
|
|
|
// The SalMemDesc is Type 1 so it comes next.
|
|
|
|
//
|
|
|
|
SalMemDesc = (SAL_ST_MEMORY_DESCRIPTOR_ENTRY *)(SalSystemTable + 1);
|
|
|
|
while (SalMemDesc->Type == SAL_ST_MEMORY_DESCRIPTOR) {
|
|
|
|
if (SalMemDesc->MemoryType == SAL_IO_PORT_MAPPING) {
|
|
|
|
*IoPortMapping = SalMemDesc->PhysicalMemoryAddress;
|
|
|
|
*IoPortMapping |= 0x8000000000000000;
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
SalMemDesc++;
|
|
|
|
}
|
|
|
|
return EFI_UNSUPPORTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
EFI_STATUS
|
|
|
|
PcatRootBridgeIoPciRW (
|
|
|
|
IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *This,
|
|
|
|
IN BOOLEAN Write,
|
|
|
|
IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH Width,
|
|
|
|
IN UINT64 UserAddress,
|
|
|
|
IN UINTN Count,
|
|
|
|
IN OUT UINT8 *UserBuffer
|
|
|
|
)
|
|
|
|
{
|
|
|
|
PCAT_PCI_ROOT_BRIDGE_INSTANCE *PrivateData;
|
|
|
|
UINTN AlignMask;
|
|
|
|
UINTN InStride;
|
|
|
|
UINTN OutStride;
|
|
|
|
UINT64 Address;
|
|
|
|
DEFIO_PCI_ADDR *Defio;
|
|
|
|
PTR Buffer;
|
|
|
|
UINT32 Data32;
|
|
|
|
UINT16 Data16;
|
|
|
|
rArg Return;
|
|
|
|
|
|
|
|
if (Width < 0 || Width >= EfiPciWidthMaximum) {
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((Width & 0x03) == EfiPciWidthUint64) {
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlignMask = (1 << (Width & 0x03)) - 1;
|
|
|
|
if ( UserAddress & AlignMask ) {
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
InStride = 1 << (Width & 0x03);
|
|
|
|
OutStride = InStride;
|
|
|
|
if (Width >=EfiPciWidthFifoUint8 && Width <= EfiPciWidthFifoUint64) {
|
|
|
|
InStride = 0;
|
|
|
|
}
|
|
|
|
if (Width >=EfiPciWidthFillUint8 && Width <= EfiPciWidthFillUint64) {
|
|
|
|
OutStride = 0;
|
|
|
|
}
|
|
|
|
Width = Width & 0x03;
|
|
|
|
|
|
|
|
Defio = (DEFIO_PCI_ADDR *)&UserAddress;
|
|
|
|
|
|
|
|
if ((Defio->Function > PCI_MAX_FUNC) || (Defio->Device > PCI_MAX_DEVICE)) {
|
|
|
|
return EFI_UNSUPPORTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
Buffer.buf = (UINT8 *)UserBuffer;
|
|
|
|
|
|
|
|
PrivateData = DRIVER_INSTANCE_FROM_PCI_ROOT_BRIDGE_IO_THIS(This);
|
|
|
|
|
|
|
|
Address = EFI_PCI_ADDRESS_IA64(
|
|
|
|
This->SegmentNumber,
|
|
|
|
Defio->Bus,
|
|
|
|
Defio->Device,
|
|
|
|
Defio->Function,
|
|
|
|
Defio->Register
|
|
|
|
);
|
|
|
|
|
|
|
|
//
|
|
|
|
// PCI Config access are all 32-bit alligned, but by accessing the
|
|
|
|
// CONFIG_DATA_REGISTER (0xcfc) with different widths more cycle types
|
|
|
|
// are possible on PCI.
|
|
|
|
//
|
|
|
|
// SalProc takes care of reading the proper register depending on stride
|
|
|
|
//
|
|
|
|
|
|
|
|
EfiAcquireLock(&PrivateData->PciLock);
|
|
|
|
|
|
|
|
while (Count) {
|
|
|
|
|
|
|
|
if(Write) {
|
|
|
|
|
|
|
|
if (Buffer.ui & 0x3) {
|
|
|
|
Data32 = (*(Buffer.ui8+0) << 0);
|
|
|
|
Data32 |= (*(Buffer.ui8+1) << 8);
|
|
|
|
Data32 |= (*(Buffer.ui8+2) << 16);
|
|
|
|
Data32 |= (*(Buffer.ui8+3) << 24);
|
|
|
|
} else {
|
|
|
|
Data32 = *Buffer.ui32;
|
|
|
|
}
|
|
|
|
|
|
|
|
Return.p0 = -3;
|
|
|
|
Return = mGlobalSalProc((UINT64) SAL_PCI_CONFIG_WRITE,
|
|
|
|
Address, 1 << Width, Data32, 0, 0, 0, 0);
|
|
|
|
|
|
|
|
if(Return.p0) {
|
|
|
|
EfiReleaseLock(&PrivateData->PciLock);
|
|
|
|
return EFI_UNSUPPORTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
Return.p0 = -3;
|
|
|
|
Return = mGlobalSalProc((UINT64) SAL_PCI_CONFIG_READ,
|
|
|
|
Address, 1 << Width, 0, 0, 0, 0, 0);
|
|
|
|
|
|
|
|
if(Return.p0) {
|
|
|
|
EfiReleaseLock(&PrivateData->PciLock);
|
|
|
|
return EFI_UNSUPPORTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (Width) {
|
|
|
|
case EfiPciWidthUint8:
|
|
|
|
*Buffer.ui8 = (UINT8)Return.p1;
|
|
|
|
break;
|
|
|
|
case EfiPciWidthUint16:
|
|
|
|
if (Buffer.ui & 0x1) {
|
|
|
|
Data16 = (UINT16)Return.p1;
|
|
|
|
*(Buffer.ui8 + 0) = Data16 & 0xff;
|
|
|
|
*(Buffer.ui8 + 1) = (Data16 >> 8) & 0xff;
|
|
|
|
} else {
|
|
|
|
*Buffer.ui16 = (UINT16)Return.p1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case EfiPciWidthUint32:
|
|
|
|
if (Buffer.ui & 0x3) {
|
|
|
|
Data32 = (UINT32)Return.p1;
|
|
|
|
*(Buffer.ui8 + 0) = (UINT8)(Data32 & 0xff);
|
|
|
|
*(Buffer.ui8 + 1) = (UINT8)((Data32 >> 8) & 0xff);
|
|
|
|
*(Buffer.ui8 + 2) = (UINT8)((Data32 >> 16) & 0xff);
|
|
|
|
*(Buffer.ui8 + 3) = (UINT8)((Data32 >> 24) & 0xff);
|
|
|
|
} else {
|
|
|
|
*Buffer.ui32 = (UINT32)Return.p1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Address += InStride;
|
|
|
|
Buffer.buf += OutStride;
|
|
|
|
Count -= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
EfiReleaseLock(&PrivateData->PciLock);
|
|
|
|
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
EFI_STATUS
|
|
|
|
ScanPciRootBridgeForRoms(
|
|
|
|
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *IoDev
|
|
|
|
)
|
|
|
|
|
|
|
|
{
|
|
|
|
return EFI_UNSUPPORTED;
|
|
|
|
}
|