1 /****************************************************************************
3 * SciTech OS Portability Manager Library
5 * ========================================================================
7 * The contents of this file are subject to the SciTech MGL Public
8 * License Version 1.0 (the "License"); you may not use this file
9 * except in compliance with the License. You may obtain a copy of
10 * the License at http://www.scitechsoft.com/mgl-license.txt
12 * Software distributed under the License is distributed on an
13 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * rights and limitations under the License.
17 * The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc.
19 * The Initial Developer of the Original Code is SciTech Software, Inc.
20 * All Rights Reserved.
22 * ========================================================================
27 * Description: Module for interfacing to the PCI bus and configuration
30 ****************************************************************************/
34 #if !defined(__WIN32_VXD__) && !defined(__NT_DRIVER__)
38 /*---------------------- Macros and type definitions ----------------------*/
42 /* Length of the memory mapping for the PCI BIOS */
44 #define BIOS_LIMIT (128 * 1024L - 1)
46 /* Macros for accessing the PCI BIOS functions from 32-bit protected mode */
48 #define BIOS32_SIGNATURE (((ulong)'_' << 0) + ((ulong)'3' << 8) + ((ulong)'2' << 16) + ((ulong)'_' << 24))
49 #define PCI_SIGNATURE (((ulong)'P' << 0) + ((ulong)'C' << 8) + ((ulong)'I' << 16) + ((ulong)' ' << 24))
50 #define PCI_SERVICE (((ulong)'$' << 0) + ((ulong)'P' << 8) + ((ulong)'C' << 16) + ((ulong)'I' << 24))
51 #define PCI_BIOS_PRESENT 0xB101
52 #define FIND_PCI_DEVICE 0xB102
53 #define FIND_PCI_CLASS 0xB103
54 #define GENERATE_SPECIAL 0xB106
55 #define READ_CONFIG_BYTE 0xB108
56 #define READ_CONFIG_WORD 0xB109
57 #define READ_CONFIG_DWORD 0xB10A
58 #define WRITE_CONFIG_BYTE 0xB10B
59 #define WRITE_CONFIG_WORD 0xB10C
60 #define WRITE_CONFIG_DWORD 0xB10D
61 #define GET_IRQ_ROUTING_OPT 0xB10E
62 #define SET_PCI_IRQ 0xB10F
64 /* This is the standard structure used to identify the entry point to the
65 * BIOS32 Service Directory, as documented in PCI 2.1 BIOS Specicition.
70 ulong signature; /* _32_ */
71 ulong entry; /* 32 bit physical address */
72 uchar revision; /* Revision level, 0 */
73 uchar length; /* Length in paragraphs should be 01 */
74 uchar checksum; /* All bytes must add up to zero */
75 uchar reserved[5]; /* Must be zero */
80 /* Structure for a far pointer to call the PCI BIOS services with */
87 /* Macros to copy a structure that includes dwSize members */
89 #define COPY_STRUCTURE(d,s) memcpy(d,s,MIN((s)->dwSize,(d)->dwSize))
93 /*--------------------------- Global variables ----------------------------*/
95 static uchar *BIOSImage = NULL; /* BIOS image mapping */
96 static int PCIBIOSVersion = -1;/* PCI BIOS version */
97 static PCIBIOS_entry PCIEntry; /* PCI services entry point */
98 static ulong PCIPhysEntry = 0; /* Physical address */
100 /*----------------------------- Implementation ----------------------------*/
102 /* External assembler helper functions */
104 uchar _ASMAPI _BIOS32_service(ulong service,ulong function,ulong *physBase,ulong *length,ulong *serviceOffset,PCIBIOS_entry entry);
105 ushort _ASMAPI _PCIBIOS_isPresent(ulong i_eax,ulong *o_edx,ushort *o_ax,uchar *o_cl,PCIBIOS_entry entry);
106 ulong _ASMAPI _PCIBIOS_service(ulong r_eax,ulong r_ebx,ulong r_edi,ulong r_ecx,PCIBIOS_entry entry);
107 int _ASMAPI _PCIBIOS_getRouting(PCIRoutingOptionsBuffer *buf,PCIBIOS_entry entry);
108 ibool _ASMAPI _PCIBIOS_setIRQ(int busDev,int intPin,int IRQ,PCIBIOS_entry entry);
109 ulong _ASMAPI _PCIBIOS_specialCycle(int bus,ulong data,PCIBIOS_entry entry);
110 ushort _ASMAPI _PCI_getCS(void);
112 /****************************************************************************
114 This functions returns the physical address of the PCI BIOS entry point.
115 ****************************************************************************/
116 ulong _ASMAPI PCIBIOS_getEntry(void)
117 { return PCIPhysEntry; }
119 /****************************************************************************
121 hwType - Place to store the PCI hardware access mechanism flags
122 lastBus - Place to store the index of the last PCI bus in the system
125 Version number of the PCI BIOS found.
128 This function determines if the PCI BIOS is present in the system, and if
129 so returns the information returned by the PCI BIOS detect function.
130 ****************************************************************************/
131 static int PCIBIOS_detect(
139 PCIBIOS_entry BIOSEntry = {0};
142 ulong physBase,length,offset;
144 /* Bail if we have already detected no BIOS is present */
145 if (PCIBIOSVersion == 0)
148 /* First scan the memory from 0xE0000 to 0xFFFFF looking for the
149 * BIOS32 service directory, so we can determine if we can call it
150 * from 32-bit protected mode.
152 if (PCIBIOSVersion == -1) {
154 BIOSImage = PM_mapPhysicalAddr(0xE0000,BIOS_LIMIT,false);
157 BIOSEnd = BIOSImage + 0x20000;
158 for (BIOSDir = (PCI_bios32*)BIOSImage; BIOSDir < (PCI_bios32*)BIOSEnd; BIOSDir++) {
162 if (BIOSDir->fields.signature != BIOS32_SIGNATURE)
164 length = BIOSDir->fields.length * 16;
167 for (sum = i = 0; i < length ; i++)
168 sum += BIOSDir->chars[i];
171 BIOSEntry.address = (ulong)BIOSImage + (BIOSDir->fields.entry - 0xE0000);
172 BIOSEntry.segment = _PCI_getCS();
176 /* If we found the BIOS32 directory, call it to get the address of the
179 if (BIOSEntry.address == 0)
181 if (_BIOS32_service(PCI_SERVICE,0,&physBase,&length,&offset,BIOSEntry) != 0)
183 PCIPhysEntry = physBase + offset;
184 PCIEntry.address = (ulong)BIOSImage + (PCIPhysEntry - 0xE0000);
185 PCIEntry.segment = _PCI_getCS();
188 /* We found the BIOS entry, so now do the version check */
189 version = _PCIBIOS_isPresent(PCI_BIOS_PRESENT,&signature,&stat,lastBus,PCIEntry);
190 if (version > 0 && ((stat >> 8) == 0) && signature == PCI_SIGNATURE) {
191 *hwType = stat & 0xFF;
192 return PCIBIOSVersion = version;
197 /****************************************************************************
199 info - Array of PCIDeviceInfo structures to check against
200 index - Index of the current device to check
203 True if the device is a duplicate, false if not.
206 This function goes through the list of all devices preceeding the newly
207 found device in the info structure, and checks that the device is not a
208 duplicate of a previous device. Some devices incorrectly enumerate
209 themselves at different function addresses so we check here to exclude
211 ****************************************************************************/
212 static ibool CheckDuplicate(
216 /* Ignore devices with a vendor ID of 0 */
217 if (info->VendorID == 0)
220 /* NOTE: We only check against the current device on
221 * the bus to ensure that we do not exclude
222 * multiple controllers of the same device ID.
224 if (info->slot.p.Bus == prev->slot.p.Bus &&
225 info->slot.p.Device == prev->slot.p.Device &&
226 info->DeviceID == prev->DeviceID)
231 /****************************************************************************
233 info - Array of PCIDeviceInfo structures to fill in
234 maxDevices - Maximum number of of devices to enumerate into array
237 Number of PCI devices found and enumerated on the PCI bus, 0 if not PCI.
240 Function to enumerate all available devices on the PCI bus into an array
241 of configuration information blocks.
242 ****************************************************************************/
243 static int PCI_enumerateMech1(
244 PCIDeviceInfo info[])
246 int bus,device,function,i,numFound = 0;
248 PCIslot slot = {{0,0,0,0,0,0,1}};
249 PCIDeviceInfo pci,prev = {0};
251 /* Try PCI access mechanism 1 */
252 PM_outpb(0xCFB,0x01);
253 tmp = PM_inpd(0xCF8);
254 PM_outpd(0xCF8,slot.i);
255 if ((PM_inpd(0xCF8) == slot.i) && (PM_inpd(0xCFC) != 0xFFFFFFFFUL)) {
256 /* PCI access mechanism 1 - the preferred mechanism */
257 for (bus = 0; bus < 8; bus++) {
259 for (device = 0; device < 32; device++) {
260 slot.p.Device = device;
261 for (function = 0; function < 8; function++) {
262 slot.p.Function = function;
264 PM_outpd(0xCF8,slot.i);
265 if (PM_inpd(0xCFC) != 0xFFFFFFFFUL) {
266 memset(&pci,0,sizeof(pci));
267 pci.dwSize = sizeof(pci);
270 lp = (ulong*)&(pci.VendorID);
271 for (i = 0; i < NUM_PCI_REG; i++, lp++) {
273 PM_outpd(0xCF8,slot.i);
274 *lp = PM_inpd(0xCFC);
276 if (!CheckDuplicate(&pci,&prev)) {
278 COPY_STRUCTURE(&info[numFound],&pci);
287 /* Disable PCI config cycle on exit */
293 /* No hardware access mechanism 1 found */
297 /****************************************************************************
299 info - Array of PCIDeviceInfo structures to fill in
300 maxDevices - Maximum number of of devices to enumerate into array
303 Number of PCI devices found and enumerated on the PCI bus, 0 if not PCI.
306 Function to enumerate all available devices on the PCI bus into an array
307 of configuration information blocks.
308 ****************************************************************************/
309 static int PCI_enumerateMech2(
310 PCIDeviceInfo info[])
312 int bus,device,function,i,numFound = 0;
315 PCIslot slot = {{0,0,0,0,0,0,1}};
316 PCIDeviceInfo pci,prev = {0};
318 /* Try PCI access mechanism 2 */
319 PM_outpb(0xCFB,0x00);
320 PM_outpb(0xCF8,0x00);
321 PM_outpb(0xCFA,0x00);
322 if (PM_inpb(0xCF8) == 0x00 && PM_inpb(0xCFB) == 0x00) {
323 /* PCI access mechanism 2 - the older mechanism for legacy busses */
324 for (bus = 0; bus < 2; bus++) {
326 PM_outpb(0xCFA,(uchar)bus);
327 for (device = 0; device < 16; device++) {
328 slot.p.Device = device;
329 deviceIO = 0xC000 + (device << 8);
330 for (function = 0; function < 8; function++) {
331 slot.p.Function = function;
333 PM_outpb(0xCF8,(uchar)((function << 1) | 0x10));
334 if (PM_inpd(deviceIO) != 0xFFFFFFFFUL) {
335 memset(&pci,0,sizeof(pci));
336 pci.dwSize = sizeof(pci);
339 lp = (ulong*)&(pci.VendorID);
340 for (i = 0; i < NUM_PCI_REG; i++, lp++) {
342 *lp = PM_inpd(deviceIO + (i << 2));
344 if (!CheckDuplicate(&pci,&prev)) {
346 COPY_STRUCTURE(&info[numFound],&pci);
355 /* Disable PCI config cycle on exit */
360 /* No hardware access mechanism 2 found */
364 /****************************************************************************
366 This functions reads a configuration dword via the PCI BIOS.
367 ****************************************************************************/
368 static ulong PCIBIOS_readDWORD(
372 return (ulong)_PCIBIOS_service(READ_CONFIG_DWORD,slot >> 8,index,0,PCIEntry);
375 /****************************************************************************
377 info - Array of PCIDeviceInfo structures to fill in
378 maxDevices - Maximum number of of devices to enumerate into array
381 Number of PCI devices found and enumerated on the PCI bus, 0 if not PCI.
384 Function to enumerate all available devices on the PCI bus into an array
385 of configuration information blocks.
386 ****************************************************************************/
387 static int PCI_enumerateBIOS(
388 PCIDeviceInfo info[])
390 uchar hwType,lastBus;
391 int bus,device,function,i,numFound = 0;
393 PCIslot slot = {{0,0,0,0,0,0,1}};
394 PCIDeviceInfo pci,prev = {0};
396 if (PCIBIOS_detect(&hwType,&lastBus)) {
397 /* PCI BIOS access - the ultimate fallback */
398 for (bus = 0; bus <= lastBus; bus++) {
400 for (device = 0; device < 32; device++) {
401 slot.p.Device = device;
402 for (function = 0; function < 8; function++) {
403 slot.p.Function = function;
404 if (PCIBIOS_readDWORD(0,slot.i) != 0xFFFFFFFFUL) {
405 memset(&pci,0,sizeof(pci));
406 pci.dwSize = sizeof(pci);
409 lp = (ulong*)&(pci.VendorID);
410 for (i = 0; i < NUM_PCI_REG; i++, lp++)
411 *lp = PCIBIOS_readDWORD(i << 2,slot.i);
412 if (!CheckDuplicate(&pci,&prev)) {
414 COPY_STRUCTURE(&info[numFound],&pci);
424 /* Return number of devices found */
428 /****************************************************************************
430 info - Array of PCIDeviceInfo structures to fill in
431 maxDevices - Maximum number of of devices to enumerate into array
434 Number of PCI devices found and enumerated on the PCI bus, 0 if not PCI.
437 Function to enumerate all available devices on the PCI bus into an array
438 of configuration information blocks.
439 ****************************************************************************/
440 int _ASMAPI PCI_enumerate(
441 PCIDeviceInfo info[])
445 /* First try via the direct access mechanisms which are faster if we
446 * have them (nearly always). The BIOS is used as a fallback, and for
447 * stuff we can't do directly.
449 if ((numFound = PCI_enumerateMech1(info)) == 0) {
450 if ((numFound = PCI_enumerateMech2(info)) == 0) {
451 if ((numFound = PCI_enumerateBIOS(info)) == 0)
458 /****************************************************************************
460 info - Array of PCIDeviceInfo structures to fill in
461 maxDevices - Maximum number of of devices to enumerate into array
464 Number of PCI devices found and enumerated on the PCI bus, 0 if not PCI.
467 Function to enumerate all available devices on the PCI bus into an array
468 of configuration information blocks.
469 ****************************************************************************/
470 int _ASMAPI PCI_getNumDevices(void)
472 return PCI_enumerate(NULL);
475 /****************************************************************************
477 bar - Base address to measure
478 pci - PCI device to access
481 Size of the PCI base address in bytes
484 This function measures the size of the PCI base address register in bytes,
485 by writing all F's to the register, and reading the value back. The size
486 of the base address is determines by the bits that are hardwired to zero's.
487 ****************************************************************************/
488 ulong _ASMAPI PCI_findBARSize(
494 base = PCI_accessReg(bar,0,PCI_READ_DWORD,pci);
495 if (base && !(base & 0x1)) {
496 /* For some strange reason some devices don't properly decode
497 * their base address registers (Intel PCI/PCI bridges!), and
498 * we read completely bogus values. We check for that here
499 * and clear out those BAR's.
501 * We check for that here because at least the low 12 bits
502 * of the address range must be zeros, since the page size
503 * on IA32 processors is always 4Kb.
505 if ((base & 0xFFF) == 0) {
506 PCI_accessReg(bar,0xFFFFFFFF,PCI_WRITE_DWORD,pci);
507 size = PCI_accessReg(bar,0,PCI_READ_DWORD,pci) & ~0xFF;
509 PCI_accessReg(bar,base,PCI_WRITE_DWORD,pci);
512 pci->slot.p.Register = 0;
516 /****************************************************************************
518 index - DWORD index of the register to access
519 value - Value to write to the register for write access
520 func - Function to implement
523 The value read from the register for read operations
526 The function code are defined as follows
535 ****************************************************************************/
536 ulong _ASMAPI PCI_accessReg(
544 if (info->mech1 == 2) {
545 /* Use PCI BIOS access since we dont have direct hardware access */
548 return (uchar)_PCIBIOS_service(READ_CONFIG_BYTE,info->slot.i >> 8,index,0,PCIEntry);
550 return (ushort)_PCIBIOS_service(READ_CONFIG_WORD,info->slot.i >> 8,index,0,PCIEntry);
552 return (ulong)_PCIBIOS_service(READ_CONFIG_DWORD,info->slot.i >> 8,index,0,PCIEntry);
554 _PCIBIOS_service(WRITE_CONFIG_BYTE,info->slot.i >> 8,index,value,PCIEntry);
557 _PCIBIOS_service(WRITE_CONFIG_WORD,info->slot.i >> 8,index,value,PCIEntry);
559 case PCI_WRITE_DWORD:
560 _PCIBIOS_service(WRITE_CONFIG_DWORD,info->slot.i >> 8,index,value,PCIEntry);
565 /* Use direct hardware access mechanisms */
567 /* PCI access mechanism 1 */
568 iobase = 0xCFC + (index & 3);
569 info->slot.p.Register = index >> 2;
570 PM_outpd(0xCF8,info->slot.i);
573 /* PCI access mechanism 2 */
574 PM_outpb(0xCF8,(uchar)((info->slot.p.Function << 1) | 0x10));
575 PM_outpb(0xCFA,(uchar)info->slot.p.Bus);
576 iobase = 0xC000 + (info->slot.p.Device << 8) + index;
581 case PCI_READ_DWORD: value = PM_inpd(iobase); break;
582 case PCI_WRITE_BYTE: PM_outpb(iobase,(uchar)value); break;
583 case PCI_WRITE_WORD: PM_outpw(iobase,(ushort)value); break;
584 case PCI_WRITE_DWORD: PM_outpd(iobase,(ulong)value); break;
591 /****************************************************************************
593 numDevices - Number of devices to query info for
596 0 on success, -1 on error, number of devices to enumerate if numDevices = 0
599 This function reads the PCI routing information. If you pass a value of
600 0 for numDevices, this function will return with the number of devices
601 needed in the routing buffer that will be filled in by the BIOS.
602 ****************************************************************************/
603 ibool _ASMAPI PCI_getIRQRoutingOptions(
605 PCIRouteInfo *buffer)
607 PCIRoutingOptionsBuffer buf;
611 buf.BufferSize = numDevices * sizeof(PCIRouteInfo);
612 buf.DataBuffer = buffer;
613 if ((ret = _PCIBIOS_getRouting(&buf,PCIEntry)) == 0x89)
614 return buf.BufferSize / sizeof(PCIRouteInfo);
620 /* We currently only support this via the PCI BIOS functions */
624 /****************************************************************************
626 info - PCI device information for the specified device
627 intPin - Value to store in the PCI InterruptPin register
628 IRQ - New ISA IRQ to map the PCI interrupt to (0-15)
631 True on success, or false if this function failed.
634 This function changes the PCI IRQ routing for the specified device to the
635 desired PCI interrupt and the desired ISA bus compatible IRQ. This function
636 may not be supported by the PCI BIOS, in which case this function will
638 ****************************************************************************/
639 ibool _ASMAPI PCI_setHardwareIRQ(
645 if (_PCIBIOS_setIRQ(info->slot.i >> 8,intPin,IRQ,PCIEntry)) {
646 info->u.type0.InterruptPin = intPin;
647 info->u.type0.InterruptLine = IRQ;
653 /* We currently only support this via the PCI BIOS functions */
657 /****************************************************************************
659 bus - Bus number to generate the special cycle for
660 specialCycleData - Data to send for the special cyle
663 This function generates a special cycle on the specified bus using with
665 ****************************************************************************/
666 void _ASMAPI PCI_generateSpecialCyle(
668 ulong specialCycleData)
671 _PCIBIOS_specialCycle(bus,specialCycleData,PCIEntry);
672 /* We currently only support this via the PCI BIOS functions */
675 /****************************************************************************
677 info - PCI device information block for device to access
678 index - Index of register to start reading from
679 dst - Place to store the values read from configuration space
680 count - Count of bytes to read from configuration space
683 This function is used to read a block of PCI configuration space registers
684 from the configuration space into the passed in data block. This function
685 will properly handle reading non-DWORD aligned data from the configuration
687 ****************************************************************************/
688 void _ASMAPI PCI_readRegBlock(
697 int startCount = (index & 3);
698 int middleCount = (count - startCount) >> 2;
699 int endCount = count - middleCount * 4 - startCount;
701 for (i = 0,pb = dst; i < startCount; i++, index++) {
702 *pb++ = (uchar)PCI_accessReg(index,0,PCI_READ_BYTE,info);
704 for (i = 0,pd = (ulong*)pb; i < middleCount; i++, index += 4) {
705 *pd++ = (ulong)PCI_accessReg(index,0,PCI_READ_DWORD,info);
707 for (i = 0,pb = (uchar*)pd; i < endCount; i++, index++) {
708 *pb++ = (uchar)PCI_accessReg(index,0,PCI_READ_BYTE,info);
712 /****************************************************************************
714 info - PCI device information block for device to access
715 index - Index of register to start reading from
716 dst - Place to store the values read from configuration space
717 count - Count of bytes to read from configuration space
720 This function is used to write a block of PCI configuration space registers
721 to the configuration space from the passed in data block. This function
722 will properly handle writing non-DWORD aligned data to the configuration
724 ****************************************************************************/
725 void _ASMAPI PCI_writeRegBlock(
734 int startCount = (index & 3);
735 int middleCount = (count - startCount) >> 2;
736 int endCount = count - middleCount * 4 - startCount;
738 for (i = 0,pb = src; i < startCount; i++, index++) {
739 PCI_accessReg(index,*pb++,PCI_WRITE_BYTE,info);
741 for (i = 0,pd = (ulong*)pb; i < middleCount; i++, index += 4) {
742 PCI_accessReg(index,*pd++,PCI_WRITE_DWORD,info);
744 for (i = 0,pb = (uchar*)pd; i < endCount; i++, index++) {
745 PCI_accessReg(index,*pb++,PCI_WRITE_BYTE,info);