748 lines
24 KiB
C
748 lines
24 KiB
C
/****************************************************************************
|
|
*
|
|
* SciTech OS Portability Manager Library
|
|
*
|
|
* ========================================================================
|
|
*
|
|
* The contents of this file are subject to the SciTech MGL Public
|
|
* License Version 1.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.scitechsoft.com/mgl-license.txt
|
|
*
|
|
* Software distributed under the License is distributed on an
|
|
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc.
|
|
*
|
|
* The Initial Developer of the Original Code is SciTech Software, Inc.
|
|
* All Rights Reserved.
|
|
*
|
|
* ========================================================================
|
|
*
|
|
* Language: ANSI C
|
|
* Environment: Any
|
|
*
|
|
* Description: Module for interfacing to the PCI bus and configuration
|
|
* space registers.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#include "pmapi.h"
|
|
#include "pcilib.h"
|
|
#if !defined(__WIN32_VXD__) && !defined(__NT_DRIVER__)
|
|
#include <string.h>
|
|
#endif
|
|
|
|
/*---------------------- Macros and type definitions ----------------------*/
|
|
|
|
#pragma pack(1)
|
|
|
|
/* Length of the memory mapping for the PCI BIOS */
|
|
|
|
#define BIOS_LIMIT (128 * 1024L - 1)
|
|
|
|
/* Macros for accessing the PCI BIOS functions from 32-bit protected mode */
|
|
|
|
#define BIOS32_SIGNATURE (((ulong)'_' << 0) + ((ulong)'3' << 8) + ((ulong)'2' << 16) + ((ulong)'_' << 24))
|
|
#define PCI_SIGNATURE (((ulong)'P' << 0) + ((ulong)'C' << 8) + ((ulong)'I' << 16) + ((ulong)' ' << 24))
|
|
#define PCI_SERVICE (((ulong)'$' << 0) + ((ulong)'P' << 8) + ((ulong)'C' << 16) + ((ulong)'I' << 24))
|
|
#define PCI_BIOS_PRESENT 0xB101
|
|
#define FIND_PCI_DEVICE 0xB102
|
|
#define FIND_PCI_CLASS 0xB103
|
|
#define GENERATE_SPECIAL 0xB106
|
|
#define READ_CONFIG_BYTE 0xB108
|
|
#define READ_CONFIG_WORD 0xB109
|
|
#define READ_CONFIG_DWORD 0xB10A
|
|
#define WRITE_CONFIG_BYTE 0xB10B
|
|
#define WRITE_CONFIG_WORD 0xB10C
|
|
#define WRITE_CONFIG_DWORD 0xB10D
|
|
#define GET_IRQ_ROUTING_OPT 0xB10E
|
|
#define SET_PCI_IRQ 0xB10F
|
|
|
|
/* This is the standard structure used to identify the entry point to the
|
|
* BIOS32 Service Directory, as documented in PCI 2.1 BIOS Specicition.
|
|
*/
|
|
|
|
typedef union {
|
|
struct {
|
|
ulong signature; /* _32_ */
|
|
ulong entry; /* 32 bit physical address */
|
|
uchar revision; /* Revision level, 0 */
|
|
uchar length; /* Length in paragraphs should be 01 */
|
|
uchar checksum; /* All bytes must add up to zero */
|
|
uchar reserved[5]; /* Must be zero */
|
|
} fields;
|
|
char chars[16];
|
|
} PCI_bios32;
|
|
|
|
/* Structure for a far pointer to call the PCI BIOS services with */
|
|
|
|
typedef struct {
|
|
ulong address;
|
|
ushort segment;
|
|
} PCIBIOS_entry;
|
|
|
|
/* Macros to copy a structure that includes dwSize members */
|
|
|
|
#define COPY_STRUCTURE(d,s) memcpy(d,s,MIN((s)->dwSize,(d)->dwSize))
|
|
|
|
#pragma pack()
|
|
|
|
/*--------------------------- Global variables ----------------------------*/
|
|
|
|
static uchar *BIOSImage = NULL; /* BIOS image mapping */
|
|
static int PCIBIOSVersion = -1;/* PCI BIOS version */
|
|
static PCIBIOS_entry PCIEntry; /* PCI services entry point */
|
|
static ulong PCIPhysEntry = 0; /* Physical address */
|
|
|
|
/*----------------------------- Implementation ----------------------------*/
|
|
|
|
/* External assembler helper functions */
|
|
|
|
uchar _ASMAPI _BIOS32_service(ulong service,ulong function,ulong *physBase,ulong *length,ulong *serviceOffset,PCIBIOS_entry entry);
|
|
ushort _ASMAPI _PCIBIOS_isPresent(ulong i_eax,ulong *o_edx,ushort *o_ax,uchar *o_cl,PCIBIOS_entry entry);
|
|
ulong _ASMAPI _PCIBIOS_service(ulong r_eax,ulong r_ebx,ulong r_edi,ulong r_ecx,PCIBIOS_entry entry);
|
|
int _ASMAPI _PCIBIOS_getRouting(PCIRoutingOptionsBuffer *buf,PCIBIOS_entry entry);
|
|
ibool _ASMAPI _PCIBIOS_setIRQ(int busDev,int intPin,int IRQ,PCIBIOS_entry entry);
|
|
ulong _ASMAPI _PCIBIOS_specialCycle(int bus,ulong data,PCIBIOS_entry entry);
|
|
ushort _ASMAPI _PCI_getCS(void);
|
|
|
|
/****************************************************************************
|
|
REMARKS:
|
|
This functions returns the physical address of the PCI BIOS entry point.
|
|
****************************************************************************/
|
|
ulong _ASMAPI PCIBIOS_getEntry(void)
|
|
{ return PCIPhysEntry; }
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
hwType - Place to store the PCI hardware access mechanism flags
|
|
lastBus - Place to store the index of the last PCI bus in the system
|
|
|
|
RETURNS:
|
|
Version number of the PCI BIOS found.
|
|
|
|
REMARKS:
|
|
This function determines if the PCI BIOS is present in the system, and if
|
|
so returns the information returned by the PCI BIOS detect function.
|
|
****************************************************************************/
|
|
static int PCIBIOS_detect(
|
|
uchar *hwType,
|
|
uchar *lastBus)
|
|
{
|
|
ulong signature;
|
|
ushort stat,version;
|
|
|
|
#ifndef __16BIT__
|
|
PCIBIOS_entry BIOSEntry = {0};
|
|
uchar *BIOSEnd;
|
|
PCI_bios32 *BIOSDir;
|
|
ulong physBase,length,offset;
|
|
|
|
/* Bail if we have already detected no BIOS is present */
|
|
if (PCIBIOSVersion == 0)
|
|
return 0;
|
|
|
|
/* First scan the memory from 0xE0000 to 0xFFFFF looking for the
|
|
* BIOS32 service directory, so we can determine if we can call it
|
|
* from 32-bit protected mode.
|
|
*/
|
|
if (PCIBIOSVersion == -1) {
|
|
PCIBIOSVersion = 0;
|
|
BIOSImage = PM_mapPhysicalAddr(0xE0000,BIOS_LIMIT,false);
|
|
if (!BIOSImage)
|
|
return 0;
|
|
BIOSEnd = BIOSImage + 0x20000;
|
|
for (BIOSDir = (PCI_bios32*)BIOSImage; BIOSDir < (PCI_bios32*)BIOSEnd; BIOSDir++) {
|
|
uchar sum;
|
|
int i,length;
|
|
|
|
if (BIOSDir->fields.signature != BIOS32_SIGNATURE)
|
|
continue;
|
|
length = BIOSDir->fields.length * 16;
|
|
if (!length)
|
|
continue;
|
|
for (sum = i = 0; i < length ; i++)
|
|
sum += BIOSDir->chars[i];
|
|
if (sum != 0)
|
|
continue;
|
|
BIOSEntry.address = (ulong)BIOSImage + (BIOSDir->fields.entry - 0xE0000);
|
|
BIOSEntry.segment = _PCI_getCS();
|
|
break;
|
|
}
|
|
|
|
/* If we found the BIOS32 directory, call it to get the address of the
|
|
* PCI services.
|
|
*/
|
|
if (BIOSEntry.address == 0)
|
|
return 0;
|
|
if (_BIOS32_service(PCI_SERVICE,0,&physBase,&length,&offset,BIOSEntry) != 0)
|
|
return 0;
|
|
PCIPhysEntry = physBase + offset;
|
|
PCIEntry.address = (ulong)BIOSImage + (PCIPhysEntry - 0xE0000);
|
|
PCIEntry.segment = _PCI_getCS();
|
|
}
|
|
#endif
|
|
/* We found the BIOS entry, so now do the version check */
|
|
version = _PCIBIOS_isPresent(PCI_BIOS_PRESENT,&signature,&stat,lastBus,PCIEntry);
|
|
if (version > 0 && ((stat >> 8) == 0) && signature == PCI_SIGNATURE) {
|
|
*hwType = stat & 0xFF;
|
|
return PCIBIOSVersion = version;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
info - Array of PCIDeviceInfo structures to check against
|
|
index - Index of the current device to check
|
|
|
|
RETURNS:
|
|
True if the device is a duplicate, false if not.
|
|
|
|
REMARKS:
|
|
This function goes through the list of all devices preceeding the newly
|
|
found device in the info structure, and checks that the device is not a
|
|
duplicate of a previous device. Some devices incorrectly enumerate
|
|
themselves at different function addresses so we check here to exclude
|
|
those cases.
|
|
****************************************************************************/
|
|
static ibool CheckDuplicate(
|
|
PCIDeviceInfo *info,
|
|
PCIDeviceInfo *prev)
|
|
{
|
|
/* Ignore devices with a vendor ID of 0 */
|
|
if (info->VendorID == 0)
|
|
return true;
|
|
|
|
/* NOTE: We only check against the current device on
|
|
* the bus to ensure that we do not exclude
|
|
* multiple controllers of the same device ID.
|
|
*/
|
|
if (info->slot.p.Bus == prev->slot.p.Bus &&
|
|
info->slot.p.Device == prev->slot.p.Device &&
|
|
info->DeviceID == prev->DeviceID)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
info - Array of PCIDeviceInfo structures to fill in
|
|
maxDevices - Maximum number of of devices to enumerate into array
|
|
|
|
RETURNS:
|
|
Number of PCI devices found and enumerated on the PCI bus, 0 if not PCI.
|
|
|
|
REMARKS:
|
|
Function to enumerate all available devices on the PCI bus into an array
|
|
of configuration information blocks.
|
|
****************************************************************************/
|
|
static int PCI_enumerateMech1(
|
|
PCIDeviceInfo info[])
|
|
{
|
|
int bus,device,function,i,numFound = 0;
|
|
ulong *lp,tmp;
|
|
PCIslot slot = {{0,0,0,0,0,0,1}};
|
|
PCIDeviceInfo pci,prev = {0};
|
|
|
|
/* Try PCI access mechanism 1 */
|
|
PM_outpb(0xCFB,0x01);
|
|
tmp = PM_inpd(0xCF8);
|
|
PM_outpd(0xCF8,slot.i);
|
|
if ((PM_inpd(0xCF8) == slot.i) && (PM_inpd(0xCFC) != 0xFFFFFFFFUL)) {
|
|
/* PCI access mechanism 1 - the preferred mechanism */
|
|
for (bus = 0; bus < 8; bus++) {
|
|
slot.p.Bus = bus;
|
|
for (device = 0; device < 32; device++) {
|
|
slot.p.Device = device;
|
|
for (function = 0; function < 8; function++) {
|
|
slot.p.Function = function;
|
|
slot.p.Register = 0;
|
|
PM_outpd(0xCF8,slot.i);
|
|
if (PM_inpd(0xCFC) != 0xFFFFFFFFUL) {
|
|
memset(&pci,0,sizeof(pci));
|
|
pci.dwSize = sizeof(pci);
|
|
pci.mech1 = 1;
|
|
pci.slot = slot;
|
|
lp = (ulong*)&(pci.VendorID);
|
|
for (i = 0; i < NUM_PCI_REG; i++, lp++) {
|
|
slot.p.Register = i;
|
|
PM_outpd(0xCF8,slot.i);
|
|
*lp = PM_inpd(0xCFC);
|
|
}
|
|
if (!CheckDuplicate(&pci,&prev)) {
|
|
if (info)
|
|
COPY_STRUCTURE(&info[numFound],&pci);
|
|
++numFound;
|
|
}
|
|
prev = pci;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Disable PCI config cycle on exit */
|
|
PM_outpd(0xCF8,0);
|
|
return numFound;
|
|
}
|
|
PM_outpd(0xCF8,tmp);
|
|
|
|
/* No hardware access mechanism 1 found */
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
info - Array of PCIDeviceInfo structures to fill in
|
|
maxDevices - Maximum number of of devices to enumerate into array
|
|
|
|
RETURNS:
|
|
Number of PCI devices found and enumerated on the PCI bus, 0 if not PCI.
|
|
|
|
REMARKS:
|
|
Function to enumerate all available devices on the PCI bus into an array
|
|
of configuration information blocks.
|
|
****************************************************************************/
|
|
static int PCI_enumerateMech2(
|
|
PCIDeviceInfo info[])
|
|
{
|
|
int bus,device,function,i,numFound = 0;
|
|
ushort deviceIO;
|
|
ulong *lp;
|
|
PCIslot slot = {{0,0,0,0,0,0,1}};
|
|
PCIDeviceInfo pci,prev = {0};
|
|
|
|
/* Try PCI access mechanism 2 */
|
|
PM_outpb(0xCFB,0x00);
|
|
PM_outpb(0xCF8,0x00);
|
|
PM_outpb(0xCFA,0x00);
|
|
if (PM_inpb(0xCF8) == 0x00 && PM_inpb(0xCFB) == 0x00) {
|
|
/* PCI access mechanism 2 - the older mechanism for legacy busses */
|
|
for (bus = 0; bus < 2; bus++) {
|
|
slot.p.Bus = bus;
|
|
PM_outpb(0xCFA,(uchar)bus);
|
|
for (device = 0; device < 16; device++) {
|
|
slot.p.Device = device;
|
|
deviceIO = 0xC000 + (device << 8);
|
|
for (function = 0; function < 8; function++) {
|
|
slot.p.Function = function;
|
|
slot.p.Register = 0;
|
|
PM_outpb(0xCF8,(uchar)((function << 1) | 0x10));
|
|
if (PM_inpd(deviceIO) != 0xFFFFFFFFUL) {
|
|
memset(&pci,0,sizeof(pci));
|
|
pci.dwSize = sizeof(pci);
|
|
pci.mech1 = 0;
|
|
pci.slot = slot;
|
|
lp = (ulong*)&(pci.VendorID);
|
|
for (i = 0; i < NUM_PCI_REG; i++, lp++) {
|
|
slot.p.Register = i;
|
|
*lp = PM_inpd(deviceIO + (i << 2));
|
|
}
|
|
if (!CheckDuplicate(&pci,&prev)) {
|
|
if (info)
|
|
COPY_STRUCTURE(&info[numFound],&pci);
|
|
++numFound;
|
|
}
|
|
prev = pci;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Disable PCI config cycle on exit */
|
|
PM_outpb(0xCF8,0);
|
|
return numFound;
|
|
}
|
|
|
|
/* No hardware access mechanism 2 found */
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
REMARKS:
|
|
This functions reads a configuration dword via the PCI BIOS.
|
|
****************************************************************************/
|
|
static ulong PCIBIOS_readDWORD(
|
|
int index,
|
|
ulong slot)
|
|
{
|
|
return (ulong)_PCIBIOS_service(READ_CONFIG_DWORD,slot >> 8,index,0,PCIEntry);
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
info - Array of PCIDeviceInfo structures to fill in
|
|
maxDevices - Maximum number of of devices to enumerate into array
|
|
|
|
RETURNS:
|
|
Number of PCI devices found and enumerated on the PCI bus, 0 if not PCI.
|
|
|
|
REMARKS:
|
|
Function to enumerate all available devices on the PCI bus into an array
|
|
of configuration information blocks.
|
|
****************************************************************************/
|
|
static int PCI_enumerateBIOS(
|
|
PCIDeviceInfo info[])
|
|
{
|
|
uchar hwType,lastBus;
|
|
int bus,device,function,i,numFound = 0;
|
|
ulong *lp;
|
|
PCIslot slot = {{0,0,0,0,0,0,1}};
|
|
PCIDeviceInfo pci,prev = {0};
|
|
|
|
if (PCIBIOS_detect(&hwType,&lastBus)) {
|
|
/* PCI BIOS access - the ultimate fallback */
|
|
for (bus = 0; bus <= lastBus; bus++) {
|
|
slot.p.Bus = bus;
|
|
for (device = 0; device < 32; device++) {
|
|
slot.p.Device = device;
|
|
for (function = 0; function < 8; function++) {
|
|
slot.p.Function = function;
|
|
if (PCIBIOS_readDWORD(0,slot.i) != 0xFFFFFFFFUL) {
|
|
memset(&pci,0,sizeof(pci));
|
|
pci.dwSize = sizeof(pci);
|
|
pci.mech1 = 2;
|
|
pci.slot = slot;
|
|
lp = (ulong*)&(pci.VendorID);
|
|
for (i = 0; i < NUM_PCI_REG; i++, lp++)
|
|
*lp = PCIBIOS_readDWORD(i << 2,slot.i);
|
|
if (!CheckDuplicate(&pci,&prev)) {
|
|
if (info)
|
|
COPY_STRUCTURE(&info[numFound],&pci);
|
|
++numFound;
|
|
}
|
|
prev = pci;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Return number of devices found */
|
|
return numFound;
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
info - Array of PCIDeviceInfo structures to fill in
|
|
maxDevices - Maximum number of of devices to enumerate into array
|
|
|
|
RETURNS:
|
|
Number of PCI devices found and enumerated on the PCI bus, 0 if not PCI.
|
|
|
|
REMARKS:
|
|
Function to enumerate all available devices on the PCI bus into an array
|
|
of configuration information blocks.
|
|
****************************************************************************/
|
|
int _ASMAPI PCI_enumerate(
|
|
PCIDeviceInfo info[])
|
|
{
|
|
int numFound;
|
|
|
|
/* First try via the direct access mechanisms which are faster if we
|
|
* have them (nearly always). The BIOS is used as a fallback, and for
|
|
* stuff we can't do directly.
|
|
*/
|
|
if ((numFound = PCI_enumerateMech1(info)) == 0) {
|
|
if ((numFound = PCI_enumerateMech2(info)) == 0) {
|
|
if ((numFound = PCI_enumerateBIOS(info)) == 0)
|
|
return 0;
|
|
}
|
|
}
|
|
return numFound;
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
info - Array of PCIDeviceInfo structures to fill in
|
|
maxDevices - Maximum number of of devices to enumerate into array
|
|
|
|
RETURNS:
|
|
Number of PCI devices found and enumerated on the PCI bus, 0 if not PCI.
|
|
|
|
REMARKS:
|
|
Function to enumerate all available devices on the PCI bus into an array
|
|
of configuration information blocks.
|
|
****************************************************************************/
|
|
int _ASMAPI PCI_getNumDevices(void)
|
|
{
|
|
return PCI_enumerate(NULL);
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
bar - Base address to measure
|
|
pci - PCI device to access
|
|
|
|
RETURNS:
|
|
Size of the PCI base address in bytes
|
|
|
|
REMARKS:
|
|
This function measures the size of the PCI base address register in bytes,
|
|
by writing all F's to the register, and reading the value back. The size
|
|
of the base address is determines by the bits that are hardwired to zero's.
|
|
****************************************************************************/
|
|
ulong _ASMAPI PCI_findBARSize(
|
|
int bar,
|
|
PCIDeviceInfo *pci)
|
|
{
|
|
ulong base,size = 0;
|
|
|
|
base = PCI_accessReg(bar,0,PCI_READ_DWORD,pci);
|
|
if (base && !(base & 0x1)) {
|
|
/* For some strange reason some devices don't properly decode
|
|
* their base address registers (Intel PCI/PCI bridges!), and
|
|
* we read completely bogus values. We check for that here
|
|
* and clear out those BAR's.
|
|
*
|
|
* We check for that here because at least the low 12 bits
|
|
* of the address range must be zeros, since the page size
|
|
* on IA32 processors is always 4Kb.
|
|
*/
|
|
if ((base & 0xFFF) == 0) {
|
|
PCI_accessReg(bar,0xFFFFFFFF,PCI_WRITE_DWORD,pci);
|
|
size = PCI_accessReg(bar,0,PCI_READ_DWORD,pci) & ~0xFF;
|
|
size = ~size+1;
|
|
PCI_accessReg(bar,base,PCI_WRITE_DWORD,pci);
|
|
}
|
|
}
|
|
pci->slot.p.Register = 0;
|
|
return size;
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
index - DWORD index of the register to access
|
|
value - Value to write to the register for write access
|
|
func - Function to implement
|
|
|
|
RETURNS:
|
|
The value read from the register for read operations
|
|
|
|
REMARKS:
|
|
The function code are defined as follows
|
|
|
|
code - function
|
|
0 - Read BYTE
|
|
1 - Read WORD
|
|
2 - Read DWORD
|
|
3 - Write BYTE
|
|
4 - Write WORD
|
|
5 - Write DWORD
|
|
****************************************************************************/
|
|
ulong _ASMAPI PCI_accessReg(
|
|
int index,
|
|
ulong value,
|
|
int func,
|
|
PCIDeviceInfo *info)
|
|
{
|
|
int iobase;
|
|
|
|
if (info->mech1 == 2) {
|
|
/* Use PCI BIOS access since we dont have direct hardware access */
|
|
switch (func) {
|
|
case PCI_READ_BYTE:
|
|
return (uchar)_PCIBIOS_service(READ_CONFIG_BYTE,info->slot.i >> 8,index,0,PCIEntry);
|
|
case PCI_READ_WORD:
|
|
return (ushort)_PCIBIOS_service(READ_CONFIG_WORD,info->slot.i >> 8,index,0,PCIEntry);
|
|
case PCI_READ_DWORD:
|
|
return (ulong)_PCIBIOS_service(READ_CONFIG_DWORD,info->slot.i >> 8,index,0,PCIEntry);
|
|
case PCI_WRITE_BYTE:
|
|
_PCIBIOS_service(WRITE_CONFIG_BYTE,info->slot.i >> 8,index,value,PCIEntry);
|
|
break;
|
|
case PCI_WRITE_WORD:
|
|
_PCIBIOS_service(WRITE_CONFIG_WORD,info->slot.i >> 8,index,value,PCIEntry);
|
|
break;
|
|
case PCI_WRITE_DWORD:
|
|
_PCIBIOS_service(WRITE_CONFIG_DWORD,info->slot.i >> 8,index,value,PCIEntry);
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
/* Use direct hardware access mechanisms */
|
|
if (info->mech1) {
|
|
/* PCI access mechanism 1 */
|
|
iobase = 0xCFC + (index & 3);
|
|
info->slot.p.Register = index >> 2;
|
|
PM_outpd(0xCF8,info->slot.i);
|
|
}
|
|
else {
|
|
/* PCI access mechanism 2 */
|
|
PM_outpb(0xCF8,(uchar)((info->slot.p.Function << 1) | 0x10));
|
|
PM_outpb(0xCFA,(uchar)info->slot.p.Bus);
|
|
iobase = 0xC000 + (info->slot.p.Device << 8) + index;
|
|
}
|
|
switch (func) {
|
|
case PCI_READ_BYTE:
|
|
case PCI_READ_WORD:
|
|
case PCI_READ_DWORD: value = PM_inpd(iobase); break;
|
|
case PCI_WRITE_BYTE: PM_outpb(iobase,(uchar)value); break;
|
|
case PCI_WRITE_WORD: PM_outpw(iobase,(ushort)value); break;
|
|
case PCI_WRITE_DWORD: PM_outpd(iobase,(ulong)value); break;
|
|
}
|
|
PM_outpd(0xCF8,0);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
numDevices - Number of devices to query info for
|
|
|
|
RETURNS:
|
|
0 on success, -1 on error, number of devices to enumerate if numDevices = 0
|
|
|
|
REMARKS:
|
|
This function reads the PCI routing information. If you pass a value of
|
|
0 for numDevices, this function will return with the number of devices
|
|
needed in the routing buffer that will be filled in by the BIOS.
|
|
****************************************************************************/
|
|
ibool _ASMAPI PCI_getIRQRoutingOptions(
|
|
int numDevices,
|
|
PCIRouteInfo *buffer)
|
|
{
|
|
PCIRoutingOptionsBuffer buf;
|
|
int ret;
|
|
|
|
if (PCIPhysEntry) {
|
|
buf.BufferSize = numDevices * sizeof(PCIRouteInfo);
|
|
buf.DataBuffer = buffer;
|
|
if ((ret = _PCIBIOS_getRouting(&buf,PCIEntry)) == 0x89)
|
|
return buf.BufferSize / sizeof(PCIRouteInfo);
|
|
if (ret != 0)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
/* We currently only support this via the PCI BIOS functions */
|
|
return -1;
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
info - PCI device information for the specified device
|
|
intPin - Value to store in the PCI InterruptPin register
|
|
IRQ - New ISA IRQ to map the PCI interrupt to (0-15)
|
|
|
|
RETURNS:
|
|
True on success, or false if this function failed.
|
|
|
|
REMARKS:
|
|
This function changes the PCI IRQ routing for the specified device to the
|
|
desired PCI interrupt and the desired ISA bus compatible IRQ. This function
|
|
may not be supported by the PCI BIOS, in which case this function will
|
|
fail.
|
|
****************************************************************************/
|
|
ibool _ASMAPI PCI_setHardwareIRQ(
|
|
PCIDeviceInfo *info,
|
|
uint intPin,
|
|
uint IRQ)
|
|
{
|
|
if (PCIPhysEntry) {
|
|
if (_PCIBIOS_setIRQ(info->slot.i >> 8,intPin,IRQ,PCIEntry)) {
|
|
info->u.type0.InterruptPin = intPin;
|
|
info->u.type0.InterruptLine = IRQ;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* We currently only support this via the PCI BIOS functions */
|
|
return false;
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
bus - Bus number to generate the special cycle for
|
|
specialCycleData - Data to send for the special cyle
|
|
|
|
REMARKS:
|
|
This function generates a special cycle on the specified bus using with
|
|
the specified data.
|
|
****************************************************************************/
|
|
void _ASMAPI PCI_generateSpecialCyle(
|
|
uint bus,
|
|
ulong specialCycleData)
|
|
{
|
|
if (PCIPhysEntry)
|
|
_PCIBIOS_specialCycle(bus,specialCycleData,PCIEntry);
|
|
/* We currently only support this via the PCI BIOS functions */
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
info - PCI device information block for device to access
|
|
index - Index of register to start reading from
|
|
dst - Place to store the values read from configuration space
|
|
count - Count of bytes to read from configuration space
|
|
|
|
REMARKS:
|
|
This function is used to read a block of PCI configuration space registers
|
|
from the configuration space into the passed in data block. This function
|
|
will properly handle reading non-DWORD aligned data from the configuration
|
|
space correctly.
|
|
****************************************************************************/
|
|
void _ASMAPI PCI_readRegBlock(
|
|
PCIDeviceInfo *info,
|
|
int index,
|
|
void *dst,
|
|
int count)
|
|
{
|
|
uchar *pb;
|
|
ulong *pd;
|
|
int i;
|
|
int startCount = (index & 3);
|
|
int middleCount = (count - startCount) >> 2;
|
|
int endCount = count - middleCount * 4 - startCount;
|
|
|
|
for (i = 0,pb = dst; i < startCount; i++, index++) {
|
|
*pb++ = (uchar)PCI_accessReg(index,0,PCI_READ_BYTE,info);
|
|
}
|
|
for (i = 0,pd = (ulong*)pb; i < middleCount; i++, index += 4) {
|
|
*pd++ = (ulong)PCI_accessReg(index,0,PCI_READ_DWORD,info);
|
|
}
|
|
for (i = 0,pb = (uchar*)pd; i < endCount; i++, index++) {
|
|
*pb++ = (uchar)PCI_accessReg(index,0,PCI_READ_BYTE,info);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
info - PCI device information block for device to access
|
|
index - Index of register to start reading from
|
|
dst - Place to store the values read from configuration space
|
|
count - Count of bytes to read from configuration space
|
|
|
|
REMARKS:
|
|
This function is used to write a block of PCI configuration space registers
|
|
to the configuration space from the passed in data block. This function
|
|
will properly handle writing non-DWORD aligned data to the configuration
|
|
space correctly.
|
|
****************************************************************************/
|
|
void _ASMAPI PCI_writeRegBlock(
|
|
PCIDeviceInfo *info,
|
|
int index,
|
|
void *src,
|
|
int count)
|
|
{
|
|
uchar *pb;
|
|
ulong *pd;
|
|
int i;
|
|
int startCount = (index & 3);
|
|
int middleCount = (count - startCount) >> 2;
|
|
int endCount = count - middleCount * 4 - startCount;
|
|
|
|
for (i = 0,pb = src; i < startCount; i++, index++) {
|
|
PCI_accessReg(index,*pb++,PCI_WRITE_BYTE,info);
|
|
}
|
|
for (i = 0,pd = (ulong*)pb; i < middleCount; i++, index += 4) {
|
|
PCI_accessReg(index,*pd++,PCI_WRITE_DWORD,info);
|
|
}
|
|
for (i = 0,pb = (uchar*)pd; i < endCount; i++, index++) {
|
|
PCI_accessReg(index,*pb++,PCI_WRITE_BYTE,info);
|
|
}
|
|
}
|