/** @file
Copyright (c) 2004 - 2014, 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 that 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:
PciDevice.c
Abstract:
Platform Initialization Driver.
Revision History
--*/
#include "PlatformDxe.h"
#include "Library/DxeServicesTableLib.h"
#include "PciBus.h"
#include "Guid/PciLanInfo.h"
extern VOID *mPciLanInfo;
extern UINTN mPciLanCount;
extern EFI_HANDLE mImageHandle;
extern SYSTEM_CONFIGURATION mSystemConfiguration;
VOID *mPciRegistration;
#define NCR_VENDOR_ID 0x1000
#define ATI_VENDOR_ID 0x1002
#define INTEL_VENDOR_ID 0x8086
#define ATI_RV423_ID 0x5548
#define ATI_RV423_ID2 0x5d57
#define ATI_RV380_ID 0x3e50
#define ATI_RV370_ID 0x5b60
#define SI_VENDOR_ID 0x1095
#define SI_SISATA_ID 0x3114
#define SI_SIRAID_PCIUNL 0x40
#define INTEL_82573E_IDER 0x108D
typedef struct {
UINT8 ClassCode;
UINT8 SubClassCode;
UINT16 VendorId;
UINT16 DeviceId;
} BAD_DEVICE_TABLE;
BAD_DEVICE_TABLE BadDeviceTable[] = {
{(UINT8)PCI_CLASS_MASS_STORAGE,(UINT8)PCI_CLASS_MASS_STORAGE_SCSI,(UINT16)NCR_VENDOR_ID, (UINT16)0xffff}, // Any NCR cards
{(UINT8)PCI_CLASS_MASS_STORAGE,(UINT8)PCI_CLASS_MASS_STORAGE_IDE,(UINT16)INTEL_VENDOR_ID, (UINT16)INTEL_82573E_IDER}, // Intel i82573E Tekoa GBit Lan IDE-R
{(UINT8)0xff,(UINT8)0xff,(UINT16)0xffff,(UINT16)0xffff}
};
EFI_STATUS
PciBusDriverHook (
)
{
EFI_STATUS Status;
EFI_EVENT FilterEvent;
//
// Register for callback to PCI I/O protocol
//
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
PciBusEvent,
NULL,
&FilterEvent
);
ASSERT_EFI_ERROR(Status);
//
// Register for protocol notifications on this event
//
Status = gBS->RegisterProtocolNotify (
&gEfiPciIoProtocolGuid,
FilterEvent,
&mPciRegistration
);
ASSERT_EFI_ERROR (Status);
return EFI_SUCCESS;
}
VOID
InitBadBars(
IN EFI_PCI_IO_PROTOCOL *PciIo,
IN UINT16 VendorId,
IN UINT16 DeviceId
)
{
EFI_STATUS Status;
PCI_IO_DEVICE *PciIoDevice;
UINT64 BaseAddress = 0;
UINT64 TempBaseAddress = 0;
UINT8 RevId = 0;
UINT32 Bar;
UINT64 IoSize;
UINT64 MemSize;
UINTN MemSizeBits;
PciIoDevice = PCI_IO_DEVICE_FROM_PCI_IO_THIS (PciIo);
switch ( VendorId) {
case ATI_VENDOR_ID:
//
// ATI fix-ups. At this time all ATI cards in BadDeviceTable
// have same problem in that OPROM BAR needs to be increased.
//
Bar = 0x30 ;
//
// Get original BAR address
//
Status = PciIo->Pci.Read (
PciIo,
EfiPciIoWidthUint32,
Bar,
1,
(VOID *) &BaseAddress
);
//
// Find BAR size
//
TempBaseAddress = 0xffffffff;
Status = PciIo->Pci.Write (
PciIo,
EfiPciIoWidthUint32,
Bar,
1,
(VOID *) &TempBaseAddress
);
Status = PciIo->Pci.Read (
PciIo,
EfiPciIoWidthUint32,
Bar,
1,
(VOID *) &TempBaseAddress
);
TempBaseAddress &= 0xfffffffe;
MemSize = 1;
while ((TempBaseAddress & 0x01) == 0) {
TempBaseAddress = TempBaseAddress >> 1;
MemSize = MemSize << 1;
}
//
// Free up allocated memory memory and re-allocate with increased size.
//
Status = gDS->FreeMemorySpace (
BaseAddress,
MemSize
);
//
// Force new alignment
//
MemSize = 0x8000000;
MemSizeBits = 28;
Status = gDS->AllocateMemorySpace (
EfiGcdAllocateAnySearchBottomUp,
EfiGcdMemoryTypeMemoryMappedIo,
MemSizeBits, // Alignment
MemSize,
&BaseAddress,
mImageHandle,
NULL
);
Status = PciIo->Pci.Write (
PciIo,
EfiPciIoWidthUint32,
Bar,
1,
(VOID *) &BaseAddress
);
break;
case NCR_VENDOR_ID:
#define MIN_NCR_IO_SIZE 0x800
#define NCR_GRAN 11 // 2**11 = 0x800
//
// NCR SCSI cards like 8250S lie about IO needed. Assign as least 0x80.
//
for (Bar = 0x10; Bar < 0x28; Bar+= 4) {
Status = PciIo->Pci.Read (
PciIo,
EfiPciIoWidthUint32,
Bar,
1,
(VOID *) &BaseAddress
);
if (BaseAddress && 0x01) {
TempBaseAddress = 0xffffffff;
Status = PciIo->Pci.Write (
PciIo,
EfiPciIoWidthUint32,
Bar,
1,
(VOID *) &TempBaseAddress
);
TempBaseAddress &= 0xfffffffc;
IoSize = 1;
while ((TempBaseAddress & 0x01) == 0) {
TempBaseAddress = TempBaseAddress >> 1;
IoSize = IoSize << 1;
}
if (IoSize < MIN_NCR_IO_SIZE) {
Status = gDS->FreeIoSpace (
BaseAddress,
IoSize
);
Status = gDS->AllocateIoSpace (
EfiGcdAllocateAnySearchTopDown,
EfiGcdIoTypeIo,
NCR_GRAN, // Alignment
MIN_NCR_IO_SIZE,
&BaseAddress,
mImageHandle,
NULL
);
TempBaseAddress = BaseAddress + 1;
Status = PciIo->Pci.Write (
PciIo,
EfiPciIoWidthUint32,
Bar,
1,
(VOID *) &TempBaseAddress
);
}
}
}
break;
case INTEL_VENDOR_ID:
if (DeviceId == INTEL_82573E_IDER) {
//
// Tekoa i82573E IDE-R fix-ups. At this time A2 step and earlier parts do not
// support any BARs except BAR0. Other BARS will actualy map to BAR0 so disable
// them all for Control Blocks and Bus mastering ops as well as Secondary IDE
// Controller.
// All Tekoa A2 or earlier step chips for now.
//
Status = PciIo->Pci.Read (
PciIo,
EfiPciIoWidthUint8,
PCI_REVISION_ID_OFFSET,
1,
&RevId
);
if (RevId <= 0x02) {
for (Bar = 0x14; Bar < 0x24; Bar+= 4) {
//
// Maybe want to clean this up a bit later but for now just clear out the secondary
// Bars don't worry aboyut freeing up thge allocs.
//
TempBaseAddress = 0x0;
Status = PciIo->Pci.Write (
PciIo,
EfiPciIoWidthUint32,
Bar,
1,
(VOID *) &TempBaseAddress
);
} // end for
}
else
{
//
//Tekoa A3 or above:
//Clear bus master base address (PCI register 0x20)
//since Tekoa does not fully support IDE Bus Mastering
//
TempBaseAddress = 0x0;
Status = PciIo->Pci.Write (
PciIo,
EfiPciIoWidthUint32,
0x20,
1,
(VOID *) &TempBaseAddress
);
}
}
break;
default:
break;
}
return;
}
VOID
ProgramPciLatency(
IN EFI_PCI_IO_PROTOCOL *PciIo
)
{
EFI_STATUS Status;
//
// Program Master Latency Timer
//
if (mSystemConfiguration.PciLatency != 0) {
Status = PciIo->Pci.Write (
PciIo,
EfiPciIoWidthUint8,
PCI_LATENCY_TIMER_OFFSET,
1,
&mSystemConfiguration.PciLatency
);
}
return;
}
/**
During S5 shutdown, we need to program PME in all LAN devices.
Here we identify LAN devices and save their bus/dev/func.
**/
VOID
SavePciLanAddress(
IN EFI_PCI_IO_PROTOCOL *PciIo
)
{
EFI_STATUS Status;
UINTN PciSegment,
PciBus,
PciDevice,
PciFunction;
VOID *NewBuffer;
PCI_LAN_INFO *x;
Status = PciIo->GetLocation (
PciIo,
&PciSegment,
&PciBus,
&PciDevice,
&PciFunction
);
if (EFI_ERROR (Status)) {
return;
}
mPciLanCount ++;
Status = gBS->AllocatePool (
EfiBootServicesData,
mPciLanCount * sizeof(PCI_LAN_INFO),
&NewBuffer
);
if (EFI_ERROR (Status)) {
return;
}
if (mPciLanCount > 1) {
//
// copy old data into new, larger buffer
//
gBS->CopyMem (
NewBuffer,
mPciLanInfo,
(mPciLanCount - 1) * sizeof(PCI_LAN_INFO)
);
//
// free the old memory buffer
//
gBS->FreePool (mPciLanInfo);
}
//
// init the new entry
//
x = (PCI_LAN_INFO *)NewBuffer + (mPciLanCount - 1);
x->PciBus = (UINT8)PciBus;
x->PciDevice = (UINT8)PciDevice;
x->PciFunction = (UINT8)PciFunction;
mPciLanInfo = NewBuffer;
return;
}
/**
@param Event the event that is signaled.
@param Context not used here.
**/
VOID
EFIAPI
PciBusEvent (
IN EFI_EVENT Event,
IN VOID* Context
)
{
EFI_STATUS Status;
UINTN BufferSize;
EFI_HANDLE Handle;
EFI_PCI_IO_PROTOCOL *PciIo;
PCI_IO_DEVICE *PciIoDevice;
UINT64 Supports;
UINTN Index;
UINT8 mCacheLineSize = 0x10;
while (TRUE) {
BufferSize = sizeof (EFI_HANDLE);
Status = gBS->LocateHandle (
ByRegisterNotify,
NULL,
mPciRegistration,
&BufferSize,
&Handle
);
if (EFI_ERROR (Status)) {
//
// If no more notification events exist
//
return;
}
Status = gBS->HandleProtocol (
Handle,
&gEfiPciIoProtocolGuid,
(void **)&PciIo
);
PciIoDevice = PCI_IO_DEVICE_FROM_PCI_IO_THIS (PciIo);
//
// Enable I/O for bridge so port 0x80 codes will come out
//
if (PciIoDevice->Pci.Hdr.VendorId == V_PCH_INTEL_VENDOR_ID)
{
Status = PciIo->Attributes(
PciIo,
EfiPciIoAttributeOperationSupported,
0,
&Supports
);
Supports &= EFI_PCI_DEVICE_ENABLE;
Status = PciIo->Attributes (
PciIo,
EfiPciIoAttributeOperationEnable,
Supports,
NULL
);
break;
}
//
// Program PCI Latency Timer
//
ProgramPciLatency(PciIo);
//
// Program Cache Line Size to 64 bytes (0x10 DWORDs)
//
Status = PciIo->Pci.Write (
PciIo,
EfiPciIoWidthUint8,
PCI_CACHELINE_SIZE_OFFSET,
1,
&mCacheLineSize
);
//
// If PCI LAN device, save bus/dev/func info
// so we can program PME during S5 shutdown
//
if (PciIoDevice->Pci.Hdr.ClassCode[2] == PCI_CLASS_NETWORK) {
SavePciLanAddress(PciIo);
break;
}
//
// Workaround for cards with bad BARs
//
Index = 0;
while (BadDeviceTable[Index].ClassCode != 0xff) {
if (BadDeviceTable[Index].DeviceId == 0xffff) {
if ((PciIoDevice->Pci.Hdr.ClassCode[2] == BadDeviceTable[Index].ClassCode) &&
(PciIoDevice->Pci.Hdr.ClassCode[1] == BadDeviceTable[Index].SubClassCode) &&
(PciIoDevice->Pci.Hdr.VendorId == BadDeviceTable[Index].VendorId)) {
InitBadBars(PciIo,BadDeviceTable[Index].VendorId,BadDeviceTable[Index].DeviceId);
}
} else {
if ((PciIoDevice->Pci.Hdr.ClassCode[2] == BadDeviceTable[Index].ClassCode) &&
(PciIoDevice->Pci.Hdr.ClassCode[1] == BadDeviceTable[Index].SubClassCode) &&
(PciIoDevice->Pci.Hdr.VendorId == BadDeviceTable[Index].VendorId) &&
(PciIoDevice->Pci.Hdr.DeviceId == BadDeviceTable[Index].DeviceId)) {
InitBadBars(PciIo,BadDeviceTable[Index].VendorId,BadDeviceTable[Index].DeviceId);
}
}
++Index;
}
break;
}
return;
}