windows-nt/Source/XPSP1/NT/base/busdrv/pci/device.c
2020-09-26 16:20:57 +08:00

651 lines
16 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1997-2000 Microsoft Corporation
Module Name:
device.c
Abstract:
This module contains functions associated with enumerating
ordinary (PCI Header type 0) devices.
Author:
Peter Johnston (peterj) 09-Mar-1997
Revision History:
--*/
#include "pcip.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, Device_MassageHeaderForLimitsDetermination)
#pragma alloc_text(PAGE, Device_RestoreCurrent)
#pragma alloc_text(PAGE, Device_SaveLimits)
#pragma alloc_text(PAGE, Device_SaveCurrentSettings)
#pragma alloc_text(PAGE, Device_GetAdditionalResourceDescriptors)
#endif
VOID
Device_MassageHeaderForLimitsDetermination(
IN PPCI_CONFIGURABLE_OBJECT This
)
/*++
Description:
The limits for a device are determined by writing ones to the
Base Address Registers (BARs) and examining what the hardware does
to them. For example, if a device requires 256 bytes of space,
writing 0xffffffff to the BAR which configures this requirement
tells the device to begin decoding its 256 bytes at that address.
Clearly this is impossible, at most one byte can be configured
at that address. The hardware will lower the address by clearing
the least significant bits until the range requirement can be met.
In this case, we would get 0xffffff00 back from the register when
it is next read.
Arguments:
This - Pointer to a PCI driver "configurable" object. This
object contains configuration data for the function
currently being configured.
Return Value:
The Working configuration has been modified so that all range
fields have been set to their maximum possible values.
The Current configuration has been modified such that writing it
to the hardware will restore the hardware to it's current (disabled)
state.
--*/
{
ULONG index;
index = 0;
//
// PCI IDE controllers operating in legacy mode implement
// the first 4 BARs but don't actually use them,... nor are
// they initialized correctly, and sometimes, nothing will
// change if we change them,... but we can't read them to determine
// if they are implemented or not,... so,...
//
if (PCI_IS_LEGACY_IDE_CONTROLLER(This->PdoExtension)) {
//
// If both interfaces are in native mode and the BARs behave
// normally. If both are in legacy mode then we should skip
// the first 4 BARs. Any other combination is meaningless so
// we skip the BARs and let PCIIDE take the system out when
// its turn comes.
//
index = 4;
}
do {
This->Working->u.type0.BaseAddresses[index] = 0xffffffff;
index++;
} while (index < PCI_TYPE0_ADDRESSES);
//
// Set the ROM to its maximum as well,... and disable it.
//
This->Working->u.type0.ROMBaseAddress =
0xffffffff & PCI_ADDRESS_ROM_ADDRESS_MASK;
return;
}
VOID
Device_RestoreCurrent(
IN PPCI_CONFIGURABLE_OBJECT This
)
/*++
Description:
Restore any type specific fields in the original copy of config
space. In the case of type 0 devices, there are none.
Arguments:
This - Pointer to a PCI driver "configurable" object. This
object contains configuration data for the function
currently being configured.
Return Value:
None.
--*/
{
return;
}
VOID
Device_SaveLimits(
IN PPCI_CONFIGURABLE_OBJECT This
)
/*++
Description:
Fill in the Limit structure with a IO_RESOURCE_REQUIREMENT
for each implemented BAR.
Arguments:
This - Pointer to a PCI driver "configurable" object. This
object contains configuration data for the function
currently being configured.
Return Value:
None.
--*/
{
ULONG index;
PIO_RESOURCE_DESCRIPTOR descriptor;
PULONG bar = This->Working->u.type0.BaseAddresses;
//
// PCI IDE controllers operating in legacy mode implement
// the first 4 BARs but don't actually use them,... nor are
// they initialized correctly, and sometimes, nothing will
// change if we change them,... but we can't read them to determine
// if they are implemented or not,... so,...
//
if (PCI_IS_LEGACY_IDE_CONTROLLER(This->PdoExtension)) {
//
// If both interfaces are in native mode and the BARs behave
// normally. If both are in legacy mode then we should skip
// the first 4 BARs. Any other combination is meaningless so
// we skip the BARs and let PCIIDE take the system out when
// its turn comes.
//
//
// Set the limit to zero in the first 4 bars so we will
// 'detect' the bars as unimplemented.
//
for (index = 0; index < 4; index++) {
bar[index] = 0;
}
}
#if defined(PCI_S3_HACKS)
//
// Check for S3 868 and 968. These cards report memory
// requirements of 32MB but decode 64MB. Gross huh?
//
#if defined(PCIIDE_HACKS)
//
// Ok, it looks gross but turning the above and below into
// an elseif seems marginally more efficient. plj.
//
else
#endif
if (This->PdoExtension->VendorId == 0x5333) {
USHORT deviceId = This->PdoExtension->DeviceId;
if ((deviceId == 0x88f0) || (deviceId == 0x8880)) {
for (index = 0; index < PCI_TYPE0_ADDRESSES; index++) {
//
// Check for memory requirement of 32MB and
// change it to 64MB.
//
if (bar[index] == 0xfe000000) {
bar[index] = 0xfc000000;
PciDebugPrint(
PciDbgObnoxious,
"PCI - Adjusted broken S3 requirement from 32MB to 64MB\n"
);
}
}
}
}
#endif
#if defined(PCI_CIRRUS_54XX_HACK)
//
// This device reports an IO requirement of 0x400 ports, in
// the second BAR. What it really wants is access to the VGA
// registers (3b0 thru 3bb and 3c0 thru 3df). It will actually
// allow them to move but (a) the driver doesn't understand this
// and the device no longer sees the VGA registers, ie vga.sys
// won't work any more, (b) if the device is under a bridge and
// the ISA bit is set, we can't satisfy the requirement,.......
// however, if we left it where it was, it will work under the
// bridge as long as the bridge has the VGA bit set.
//
// Basically, Cirrus tried to make the VGA registers moveable
// which is a noble thing, unfortunately the implementation
// requires a bunch of software knowledge that across all the
// drivers involved, we just don't have.
//
// Solution? Delete the requirement.
//
if ((This->PdoExtension->VendorId == 0x1013) &&
(This->PdoExtension->DeviceId == 0x00a0)) {
//
// If the second requirement is IO for length 0x400,
// currently unassigned, don't report it at all.
//
if ((bar[1] & 0xffff) == 0x0000fc01) {
//
// Only do this if the device does not have a valid
// current setting in this BAR.
//
if (This->Current->u.type0.BaseAddresses[1] == 1) {
bar[1] = 0;
#if DBG
PciDebugPrint(
PciDbgObnoxious,
"PCI - Ignored Cirrus GD54xx broken IO requirement (400 ports)\n"
);
} else {
PciDebugPrint(
PciDbgInformative,
"PCI - Cirrus GD54xx 400 port IO requirement has a valid setting (%08x)\n",
This->Current->u.type0.BaseAddresses[1]
);
#endif
}
#if DBG
} else {
//
// The device doesn't look like we expected it to, complain.
// (unless 0 in which case we assume cirrus already fixed it).
//
if (bar[1] != 0) {
PciDebugPrint(
PciDbgInformative,
"PCI - Warning Cirrus Adapter 101300a0 has unexpected resource requirement (%08x)\n",
bar[1]
);
}
#endif
}
}
#endif
descriptor = This->PdoExtension->Resources->Limit;
//
// Create an IO_RESOURCE_DESCRIPTOR for each implemented
// resource supported by this function.
//
for (index = 0; index < PCI_TYPE0_ADDRESSES; index++) {
if (PciCreateIoDescriptorFromBarLimit(descriptor, bar, FALSE)) {
//
// This base address register is 64 bit, skip one.
//
ASSERT((index+1) < PCI_TYPE0_ADDRESSES);
index++;
bar++;
//
// Null descriptor in place holder.
//
descriptor++;
descriptor->Type = CmResourceTypeNull;
}
descriptor++;
bar++;
}
//
// Do the same for the ROM
//
PciCreateIoDescriptorFromBarLimit(descriptor,
&This->Working->u.type0.ROMBaseAddress,
TRUE);
}
VOID
Device_SaveCurrentSettings(
IN PPCI_CONFIGURABLE_OBJECT This
)
/*++
Description:
Fill in the Current array in the PDO extension with the current
settings for each implemented BAR.
Arguments:
This - Pointer to a PCI driver "configurable" object. This
object contains configuration data for the function
currently being configured.
Return Value:
None.
--*/
{
ULONG index;
PCM_PARTIAL_RESOURCE_DESCRIPTOR partial;
PIO_RESOURCE_DESCRIPTOR ioResourceDescriptor;
PULONG baseAddress = This->Current->u.type0.BaseAddresses;
ULONG bar;
ULONG addressMask;
BOOLEAN nonZeroBars = FALSE;
partial = This->PdoExtension->Resources->Current;
ioResourceDescriptor = This->PdoExtension->Resources->Limit;
//
// Create an CM_PARTIAL_RESOURCE_DESCRIPTOR for each implemented
// resource supported by this function.
//
// Note: SaveLimits must have been called before SaveCurrentSettings
// so we can tell which BARs are implemented.
//
// Note: The following loop runs one extra time to get the ROM.
//
for (index = 0;
index <= PCI_TYPE0_ADDRESSES;
index++, partial++, ioResourceDescriptor++) {
partial->Type = ioResourceDescriptor->Type;
bar = *baseAddress++;
//
// If this BAR is not implemented, no further processing for
// this partial descriptor.
//
if (partial->Type == CmResourceTypeNull) {
continue;
}
//
// Copy the length from the limits descriptor, then we
// actually need to do a little processing to figure out
// the current limits.
//
partial->Flags = ioResourceDescriptor->Flags;
partial->ShareDisposition = ioResourceDescriptor->ShareDisposition;
partial->u.Generic.Length = ioResourceDescriptor->u.Generic.Length;
partial->u.Generic.Start.HighPart = 0;
if (index == PCI_TYPE0_ADDRESSES) {
bar = This->Current->u.type0.ROMBaseAddress;
addressMask = PCI_ADDRESS_ROM_ADDRESS_MASK;
//
// If the ROM Enabled bit is clear, don't record
// a current setting for this ROM BAR.
//
if ((bar & PCI_ROMADDRESS_ENABLED) == 0) {
partial->Type = CmResourceTypeNull;
continue;
}
} else if (bar & PCI_ADDRESS_IO_SPACE) {
ASSERT(partial->Type == CmResourceTypePort);
addressMask = PCI_ADDRESS_IO_ADDRESS_MASK;
} else {
ASSERT(partial->Type == CmResourceTypeMemory);
addressMask = PCI_ADDRESS_MEMORY_ADDRESS_MASK;
if ((bar & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_64BIT) {
//
// This is a 64 bit PCI device. Get the high 32 bits
// from the next BAR.
//
partial->u.Generic.Start.HighPart = *baseAddress;
} else if ((bar & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_20BIT) {
//
// This device must locate below 1MB, the BAR shouldn't
// have any top bits set but this isn't clear from the
// spec. Enforce it by clearing the top bits.
//
addressMask &= 0x000fffff;
}
}
partial->u.Generic.Start.LowPart = bar & addressMask;
if (partial->u.Generic.Start.QuadPart == 0) {
//
// No current setting if the value is current setting
// is 0.
//
partial->Type = CmResourceTypeNull;
continue;
}
nonZeroBars = TRUE;
}
//
// Save type 0 specific data in the PDO.
//
This->PdoExtension->SubsystemVendorId =
This->Current->u.type0.SubVendorID;
This->PdoExtension->SubsystemId =
This->Current->u.type0.SubSystemID;
}
VOID
Device_ChangeResourceSettings(
IN PPCI_PDO_EXTENSION PdoExtension,
IN PPCI_COMMON_CONFIG CommonConfig
)
/*++
Description:
Reconfigure each BAR using the settings from the Current array
in the PDO extension. All we actually do here is write the new
settings into the memory pointed to by CommonConfig, the actual
write to the hardware is done elsewhere.
Note: Possibly not all BARs will be changed, at least one has
changed or this routine would not have been called.
Arguments:
PdoExtension A pointer to the PDO extension for this device.
CurrentConfig A pointer to the current contents of PCI config
space.
Return Value:
None.
--*/
{
ULONG index;
PCM_PARTIAL_RESOURCE_DESCRIPTOR partial;
PULONG baseAddress;
ULONG bar;
ULONG lowPart;
if (PdoExtension->Resources == NULL) {
//
// Nothing to play with.
//
return;
}
partial = PdoExtension->Resources->Current;
baseAddress = CommonConfig->u.type0.BaseAddresses;
for (index = 0;
index <= PCI_TYPE0_ADDRESSES;
index++, partial++, baseAddress++) {
//
// If this BAR is not implemented, no further processing for
// this partial descriptor.
//
if (partial->Type == CmResourceTypeNull) {
continue;
}
lowPart = partial->u.Generic.Start.LowPart;
bar = *baseAddress;
if (index == PCI_TYPE0_ADDRESSES) {
ASSERT(partial->Type == CmResourceTypeMemory);
bar = CommonConfig->u.type0.ROMBaseAddress;
bar &= ~PCI_ADDRESS_ROM_ADDRESS_MASK;
bar |= (lowPart & PCI_ADDRESS_ROM_ADDRESS_MASK);
CommonConfig->u.type0.ROMBaseAddress = bar;
} else if (bar & PCI_ADDRESS_IO_SPACE) {
ASSERT(partial->Type == CmResourceTypePort);
*baseAddress = lowPart;
} else {
ASSERT(partial->Type == CmResourceTypeMemory);
*baseAddress = lowPart;
if ((bar & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_64BIT) {
//
// This is a 64 bit address. Need to set the upper
// 32 bits in the next BAR.
//
baseAddress++;
*baseAddress = partial->u.Generic.Start.HighPart;
//
// We need to skip the next partial entry and descrement
// the loop count because we consumed a BAR here.
//
index++;
partial++;
#if DBG
} else if ((bar & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_20BIT) {
//
// This device must locate below 1MB, make sure we're
// configuring it that way.
//
ASSERT((lowPart & 0xfff00000) == 0);
#endif
}
}
}
}
VOID
Device_GetAdditionalResourceDescriptors(
IN PPCI_PDO_EXTENSION PdoExtension,
IN PPCI_COMMON_CONFIG CommonConfig,
IN PIO_RESOURCE_DESCRIPTOR Resource
)
{
//
// Type 0 (devices) don't require resources not adequately
// described in the BARs.
//
return;
}
NTSTATUS
Device_ResetDevice(
IN PPCI_PDO_EXTENSION PdoExtension,
IN PPCI_COMMON_CONFIG CommonConfig
)
{
return STATUS_SUCCESS;
}