/*++ Copyright (c) 2005 - 2012, 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. Module Name: PcatPciRootBridgeIo.c Abstract: EFI PC AT PCI Root Bridge Io Protocol Revision History --*/ #include "PcatPciRootBridge.h" #include <IndustryStandard/Pci.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 // PLABEL mSalProcPlabel; CALL_SAL_PROC mGlobalSalProc; 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 ((UINT32)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; }