/** * @file * * This file was based on the powerpc. */ /* * COPYRIGHT (c) 1989-2012. * On-Line Applications Research Corporation (OAR). * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.org/license/LICENSE. */ #include #include #include #include #include #include /* * DEFINES */ #undef SHOW_PCI_SETTING // #define DEBUG_PCI 1 /* allow for overriding these definitions */ #ifndef PCI_CONFIG_ADDR #define PCI_CONFIG_ADDR 0xcf8 #endif #ifndef PCI_CONFIG_DATA #define PCI_CONFIG_DATA 0xcfc #endif /* define a shortcut */ #define pci BSP_pci_configuration #ifndef PCI_CONFIG_ADDR_VAL #define PCI_CONFIG_ADDR_VAL(bus, slot, funcion, offset) \ (0x80000000|((bus)<<16)|(PCI_DEVFN((slot),(function))<<8)|(((offset)&~3))) #endif #ifdef DEBUG_PCI #define JPRINTK(fmt, ...) printk("%s: " fmt, __FUNCTION__, ##__VA_ARGS__) #else #define JPRINTK(fmt, ...) #endif #ifndef PCI_CONFIG_WR_ADDR #define PCI_CONFIG_WR_ADDR( addr, val ) out_le32((uint32_t)(addr), (val)) #endif /* Bit encode for PCI_CONFIG_HEADER_TYPE register */ #define PCI_CONFIG_SET_ADDR(addr, bus, slot,function,offset) \ PCI_CONFIG_WR_ADDR( \ (addr), \ PCI_CONFIG_ADDR_VAL((bus), (slot), (function), (offset))\ ) #define PRINT_MSG() \ printk("pci : Device %d:0x%02x:%d routed to interrupt_line %d\n", \ pbus, pslot, pfun, int_name ) /* * STRUCTURES */ /* * PROTOTYPES */ void print_bars( unsigned char slot, unsigned char func ); int direct_pci_read_config_byte( unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, uint8_t *val ); int direct_pci_read_config_word( unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, uint16_t *val ); int direct_pci_read_config_dword( unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, uint32_t *val ); int direct_pci_write_config_byte( unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, uint8_t val ); int direct_pci_write_config_word( unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, uint16_t val ); int direct_pci_write_config_dword( unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, uint32_t val ); int test_intname( const struct _int_map *row, int pbus, int pslot, int pfun, int int_pin, int int_name ); void pci_memory_enable( unsigned char bus, unsigned char slot, unsigned char function ); void pci_io_enable( unsigned char bus, unsigned char slot, unsigned char function ); void pci_busmaster_enable( unsigned char bus, unsigned char slot, unsigned char function ); /* * GLOBALS */ unsigned char ucMaxPCIBus; const pci_config_access_functions pci_indirect_functions = { indirect_pci_read_config_byte, indirect_pci_read_config_word, indirect_pci_read_config_dword, indirect_pci_write_config_byte, indirect_pci_write_config_word, indirect_pci_write_config_dword }; rtems_pci_config_t BSP_pci_configuration = { (volatile unsigned char*)PCI_CONFIG_ADDR, (volatile unsigned char*)PCI_CONFIG_DATA, &pci_indirect_functions }; const pci_config_access_functions pci_direct_functions = { direct_pci_read_config_byte, direct_pci_read_config_word, direct_pci_read_config_dword, direct_pci_write_config_byte, direct_pci_write_config_word, direct_pci_write_config_dword }; /* * PCI specific accesses. Note these are made on 32 bit * boundries. */ void pci_out_32(uint32_t base, uint32_t addr, uint32_t val) { volatile uint32_t *ptr; ptr = (volatile uint32_t *) (base + addr); *ptr = val; JPRINTK( "%p data: 0x%x\n", ptr, val); } void pci_out_le32(uint32_t base, uint32_t addr, uint32_t val) { volatile uint32_t *ptr; uint32_t data = 0; ptr = (volatile uint32_t *) (base + addr); rtems_uint32_to_little_endian( val, (uint8_t *) &data); *ptr = data; JPRINTK( "%p data: 0x%x\n", ptr, data); } uint8_t pci_in_8( uint32_t base, uint32_t addr ) { volatile uint32_t *ptr; uint8_t val; uint32_t data; data = addr/4; ptr = (volatile uint32_t *) (base + (data*4)); data = *ptr; switch ( addr%4 ) { case 0: val = (data & 0x000000ff) >> 0; break; case 1: val = (data & 0x0000ff00) >> 8; break; case 2: val = (data & 0x00ff0000) >> 16; break; case 3: val = (data & 0xff000000) >> 24; break; } JPRINTK( "0x%x data: 0x%x raw: 0x%x\n", ptr, val, data); return val; } int16_t pci_in_le16( uint32_t base, uint32_t addr ) { volatile uint32_t *ptr; uint16_t val; uint16_t rval; uint32_t data; data = addr/4; ptr = (volatile uint32_t *) (base + (data*4)); data = *ptr; if ( addr%4 == 0 ) val = data & 0xffff; else val = (data>>16) & 0xffff; rval = rtems_uint16_from_little_endian( (uint8_t *) &val); JPRINTK( "0x%x data: 0x%x raw: 0x%x\n", ptr, rval, data); return rval; } int16_t pci_in_16( uint32_t base, uint32_t addr ) { volatile uint32_t *ptr; uint16_t val; uint32_t data; data = addr/4; ptr = (volatile uint32_t *) (base + (data*4)); data = *ptr; if ( addr%4 == 0 ) val = data & 0xffff; else val = (data>>16) & 0xffff; JPRINTK( "0x%x data: 0x%x raw: 0x%x\n", ptr, val, data); return val; } uint32_t pci_in_32( uint32_t base, uint32_t addr ) { volatile uint32_t *ptr; uint32_t val; ptr = (volatile uint32_t *) (base + addr); val = *ptr; JPRINTK( "0x%x data: 0x%x raw: 0x%x\n", ptr, val, val); return val; } uint32_t pci_in_le32( uint32_t base, uint32_t addr ) { volatile uint32_t *ptr; uint32_t val; uint32_t rval; ptr = (volatile uint32_t *) (base + addr); val = *ptr; rval = rtems_uint32_from_little_endian( (uint8_t *) &val); JPRINTK( "0x%x data: 0x%x raw: 0x%x\n", ptr, rval, val); return rval; } void pci_out_8( uint32_t base, uint32_t addr, uint8_t val ) { volatile uint32_t *ptr; ptr = (volatile uint32_t *) (base + addr); JPRINTK("Address: %p\n", ptr); *ptr = val; JPRINTK( "%p data: 0x%x\n", ptr, val); } void pci_out_le16( uint32_t base, uint32_t addr, uint16_t val ) { volatile uint32_t *ptr; uint32_t out_data; uint32_t data; ptr = (volatile uint32_t *) (base + (addr & ~0x3)); data = *ptr; if ( addr%4 == 0 ) out_data = (data & 0xffff0000) | val; else out_data = ((val << 16)&0xffff0000) | (data & 0xffff); rtems_uint32_to_little_endian( out_data, (uint8_t *) &data); *ptr = data; JPRINTK( "0x%x data: 0x%x\n", ptr, data); } void pci_out_16( uint32_t base, uint32_t addr, uint16_t val ) { volatile uint32_t *ptr; uint32_t out_data; uint32_t data; ptr = (volatile uint32_t *) (base + (addr & ~0x3)); data = *ptr; if ( addr%4 == 0 ) out_data = (data & 0xffff0000) | val; else out_data = ((val << 16)&0xffff0000) | (data & 0xffff); *ptr = out_data; JPRINTK( "0x%x data: 0x%x\n", ptr, out_data); } /* * INDIRECT PCI CONFIGURATION ACCESSES */ int indirect_pci_read_config_byte( unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, uint8_t *val ) { JPRINTK("==>\n"); PCI_CONFIG_SET_ADDR(pci.pci_config_addr, bus, slot, function, offset); *val = in_8((uint32_t) (pci.pci_config_data + (offset&3)) ); JPRINTK("\n\n"); return PCIBIOS_SUCCESSFUL; } int indirect_pci_read_config_word( unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, uint16_t *val ) { JPRINTK("==>\n"); *val = 0xffff; if (offset&1) return PCIBIOS_BAD_REGISTER_NUMBER; PCI_CONFIG_SET_ADDR(pci.pci_config_addr, bus, slot, function, offset); *val = in_16((uint32_t)(pci.pci_config_data + (offset&3))); JPRINTK("\n\n"); return PCIBIOS_SUCCESSFUL; } int indirect_pci_read_config_dword( unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, uint32_t *val ) { uint32_t v; JPRINTK("==>\n"); *val = 0xffffffff; if (offset&3) return PCIBIOS_BAD_REGISTER_NUMBER; PCI_CONFIG_SET_ADDR(pci.pci_config_addr, bus, slot, function, offset); v = in_32( (uint32_t) pci.pci_config_data ); *val = v; if ( offset == 0x0b ) JPRINTK( "%x:%x %x ==> 0x%08x, 0x%08x\n", bus, slot, function, v, *val ); JPRINTK("\n\n"); return PCIBIOS_SUCCESSFUL; } int indirect_pci_write_config_byte( unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, uint8_t val ) { JPRINTK("==>\n"); PCI_CONFIG_SET_ADDR(pci.pci_config_addr, bus, slot, function, offset); out_8( (uint32_t) (pci.pci_config_data + (offset&3)), val); JPRINTK("\n\n"); return PCIBIOS_SUCCESSFUL; } int indirect_pci_write_config_word( unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, uint16_t val ) { JPRINTK("==>\n"); if (offset&1) return PCIBIOS_BAD_REGISTER_NUMBER; PCI_CONFIG_SET_ADDR(pci.pci_config_addr, bus, slot, function, offset); out_16((uint32_t)(pci.pci_config_data + (offset&3)), val); JPRINTK("\n\n"); return PCIBIOS_SUCCESSFUL; } int indirect_pci_write_config_dword( unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, uint32_t val ) { if (offset&3) return PCIBIOS_BAD_REGISTER_NUMBER; JPRINTK("==>\n"); /* * The Base Address Registers get accessed big endian while the * other registers are little endian. */ PCI_CONFIG_SET_ADDR(pci.pci_config_addr, bus, slot, function, offset); if ( bus == 0 && slot == 0x0b && (offset >= PCI_BASE_ADDRESS_0 && offset <= PCI_BASE_ADDRESS_5) ) { out_32((uint32_t)pci.pci_config_data, val); } else { out_le32((uint32_t)pci.pci_config_data, val); } JPRINTK("\n\n"); return PCIBIOS_SUCCESSFUL; } /* * DIRECT CONFIGUREATION ACCESSES. */ int direct_pci_read_config_byte( unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, uint8_t *val ) { if (bus != 0 || (1<\n"); *val=in_8((uint32_t) (pci.pci_config_data + ((1<\n"); *val=in_le16((uint32_t) (pci.pci_config_data + ((1<\n"); *val=in_le32((uint32_t)(pci.pci_config_data + ((1<\n"); out_8((uint32_t) (pci.pci_config_data + ((1<\n"); out_le16((uint32_t)(pci.pci_config_data + ((1<\n"); out_le32((uint32_t) (pci.pci_config_data + ((1<pin_route[j].pin > -1; j++) { if ( row->pin_route[j].pin == int_pin ) { _nopin = 0; for (k=0; k<4 && row->pin_route[j].int_name[k] > -1; k++ ) { if ( row->pin_route[j].int_name[k] == int_name ) { _noname=0; break; } } break; } } if( _nopin ) { printk( "pci : Device %d:0x%02x:%d supplied a bogus interrupt_pin %d\n", pbus, pslot, pfun, int_pin ); return -1; } else { if( _noname ) { unsigned char v = row->pin_route[j].int_name[0]; printk( "pci : Device %d:0x%02x:%d supplied a suspicious interrupt_line %d, ", pbus, pslot, pfun, int_name ); if ((row->opts & PCI_FIXUP_OPT_OVERRIDE_NAME) && 255 != (v = row->pin_route[j].int_name[0]) ) { printk("OVERRIDING with %d from fixup table\n", v); pci_write_config_byte(pbus,pslot,pfun,PCI_INTERRUPT_LINE,v); } else { printk("using it anyway\n"); } } } return 0; } int FindPCIbridge( int mybus, struct pcibridge *pb ) { int pbus, pslot; uint8_t bussec, buspri; uint16_t devid, vendorid, dclass; for(pbus=0; pbus< pci_bus_count(); pbus++) { for(pslot=0; pslot< PCI_MAX_DEVICES; pslot++) { pci_read_config_word(pbus, pslot, 0, PCI_DEVICE_ID, &devid); if ( devid == 0xffff ) continue; pci_read_config_word(pbus, pslot, 0, PCI_DEVICE_ID, &vendorid); if ( vendorid == 0xffff ) continue; pci_read_config_word(pbus, pslot, 0, PCI_CLASS_DEVICE, &dclass); if ( dclass == PCI_CLASS_BRIDGE_PCI ) { pci_read_config_byte(pbus, pslot, 0, PCI_PRIMARY_BUS, &buspri); pci_read_config_byte(pbus, pslot, 0, PCI_SECONDARY_BUS, &bussec); #ifdef SHOW_PCI_SETTING JPRINTK( "pci : Found bridge at %d:0x%02x, mybus %d, pribus %d, secbus %d ", pbus, pslot, mybus, buspri, bussec ); #endif if ( bussec == mybus ) { #ifdef SHOW_PCI_SETTING JPRINTK("match\n"); #endif /* found our nearest bridge going towards the root */ pb->bus = pbus; pb->slot = pslot; return 0; } #ifdef SHOW_PCI_SETTING JPRINTK("no match\n"); #endif } } } return -1; } void FixupPCI( const struct _int_map *bspmap, int (*swizzler)(int,int) ) { unsigned char cvalue; uint16_t devid; int ismatch, i, j, pbus, pslot, pfun, int_pin, int_name, nfuns; /* * If the device has a non-zero INTERRUPT_PIN, assign a bsp-specific * INTERRUPT_NAME if one isn't already in place. Then, drivers can * trivially use INTERRUPT_NAME to hook up with devices. */ for (pbus=0; pbus< pci_bus_count(); pbus++) { for (pslot=0; pslot< PCI_MAX_DEVICES; pslot++) { pci_read_config_word(pbus, pslot, 0, PCI_DEVICE_ID, &devid); if ( devid == 0xffff ) continue; /* got a device */ pci_read_config_byte(pbus, pslot, 0, PCI_HEADER_TYPE, &cvalue); nfuns = cvalue & PCI_HEADER_TYPE_MULTI_FUNCTION ? PCI_MAX_FUNCTIONS : 1; for (pfun=0; pfun< nfuns; pfun++) { pci_read_config_word(pbus, pslot, pfun, PCI_DEVICE_ID, &devid); if( devid == 0xffff ) continue; pci_read_config_byte( pbus, pslot, pfun, PCI_INTERRUPT_PIN, &cvalue); int_pin = cvalue; pci_read_config_byte( pbus, pslot, pfun, PCI_INTERRUPT_LINE, &cvalue); int_name = cvalue; #ifdef SHOW_PCI_SETTING { unsigned short cmd,stat; unsigned char lat, seclat, csize; pci_read_config_word(pbus,pslot,pfun,PCI_COMMAND, &cmd ); pci_read_config_word(pbus,pslot,pfun,PCI_STATUS, &stat ); pci_read_config_byte(pbus,pslot,pfun,PCI_LATENCY_TIMER, &lat ); pci_read_config_byte(pbus,pslot,pfun,PCI_SEC_LATENCY_TIMER, &seclat); pci_read_config_byte(pbus,pslot,pfun,PCI_CACHE_LINE_SIZE, &csize ); JPRINTK( "pci : device %d:0x%02x:%d cmd %04X, stat %04X, latency %d, " " sec_latency %d, clsize %d\n", pbus, pslot, pfun, cmd, stat, lat, seclat, csize ); } #endif if ( int_pin > 0 ) { ismatch = 0; /* * first run thru the bspmap table and see if we have an * explicit configuration */ for (i=0; bspmap[i].bus > -1; i++) { if ( bspmap[i].bus == pbus && bspmap[i].slot == pslot ) { ismatch = -1; /* we have a record in the table that gives specific * pins and interrupts for devices in this slot */ if ( int_name == 255 ) { /* find the vector associated with whatever pin the * device gives us */ for ( int_name=-1, j=0; bspmap[i].pin_route[j].pin > -1; j++ ){ if ( bspmap[i].pin_route[j].pin == int_pin ) { int_name = bspmap[i].pin_route[j].int_name[0]; break; } } if ( int_name == -1 ) { printk( "pci : Unable to resolve device %d:0x%02x:%d w/ " "swizzled int pin %i to an interrupt_line.\n", pbus, pslot, pfun, int_pin ); } else { PRINT_MSG(); pci_write_config_byte( pbus, pslot, pfun, PCI_INTERRUPT_LINE,(cvalue= int_name, cvalue) ); } } else { test_intname( &bspmap[i],pbus,pslot,pfun,int_pin,int_name); } break; } } if ( !ismatch ) { /* * no match, which means we're on a bus someplace. Work * backwards from it to one of our defined busses, * swizzling thru each bridge on the way. */ /* keep pbus, pslot pointed to the device being * configured while we track down the bridges using * tbus,tslot. We keep searching the routing table because * we may end up finding our bridge in it */ int tbus= pbus, tslot= pslot; for (;;) { for (i=0; bspmap[i].bus > -1; i++) { if ( bspmap[i].bus == tbus && (bspmap[i].slot == tslot || bspmap[i].slot == -1)) { ismatch = -1; /* found a record for this bus, so swizzle the * int_pin which we then use to find the * interrupt_name. */ if ( int_name == 255 ) { /* * FIXME. I can't believe this little hack * is right. It does not yield an error in * convienently simple situations. */ if ( tbus ) int_pin = (*swizzler)(tslot,int_pin); /* * int_pin points to the interrupt channel * this card ends up delivering interrupts * on. Find the int_name servicing it. */ for (int_name=-1, j=0; bspmap[i].pin_route[j].pin > -1; j++) { if ( bspmap[i].pin_route[j].pin == int_pin ) { int_name = bspmap[i].pin_route[j].int_name[0]; break; } } if ( int_name == -1 ) { printk( "pci : Unable to resolve device %d:0x%02x:%d w/ " "swizzled int pin %i to an interrupt_line.\n", pbus, pslot, pfun, int_pin ); } else { PRINT_MSG(); pci_write_config_byte(pbus,pslot,pfun, PCI_INTERRUPT_LINE,(cvalue=int_name, cvalue)); } } else { test_intname( &bspmap[i], pbus, pslot, pfun, int_pin, int_name ); } goto donesearch; } } if ( !ismatch ) { struct pcibridge pb; /* * Haven't found our bus in the int map, so work * upwards thru the bridges till we find it. */ if ( FindPCIbridge( tbus, &pb )== 0 ) { int_pin = (*swizzler)(tslot,int_pin); /* our next bridge up is on pb.bus, pb.slot- now * instead of pointing to the device we're * trying to configure, we move from bridge to * bridge. */ tbus = pb.bus; tslot = pb.slot; } else { printk( "pci : No bridge from bus %i towards root found\n", tbus ); goto donesearch; } } } } donesearch: if ( !ismatch && int_pin != 0 && int_name == 255 ) { printk( "pci : Unable to match device %d:0x%02x:%d with an int " "routing table entry\n", pbus, pslot, pfun ); } } } } } } void print_bars( unsigned char slot, unsigned char func ) { uint32_t addr; printk( "*** BARs for slot=%d func=%d\n", slot, func ); pci_read_config_dword (0, slot, func, PCI_BASE_ADDRESS_0, &addr); printk("*** PCI DEVICE BAR0: 0x%lx\n", addr); pci_read_config_dword (0, slot, func, PCI_BASE_ADDRESS_1, &addr); printk("*** PCI DEVICE BAR1: 0x%lx\n", addr); pci_read_config_dword (0, slot, func, PCI_BASE_ADDRESS_2, &addr); printk("*** PCI DEVICE BAR2: 0x%lx\n", addr); pci_read_config_dword (0, slot, func, PCI_BASE_ADDRESS_3, &addr); printk("*** PCI DEVICE BAR3: 0x%lx\n", addr); pci_read_config_dword (0, slot, func, PCI_BASE_ADDRESS_4, &addr); printk("*** PCI DEVICE BAR4: 0x%lx\n", addr); pci_read_config_dword (0, slot, func, PCI_BASE_ADDRESS_5, &addr); printk("*** PCI DEVICE BAR5: 0x%lx\n", addr); } void pci_memory_enable( unsigned char bus, unsigned char slot, unsigned char function ) { uint16_t data; pci_read_config_word(0, slot, function, PCI_COMMAND, &data); data |= PCI_COMMAND_MEMORY; pci_write_config_word(0, slot, function, PCI_COMMAND, data ); pci_read_config_word(0, slot, function, PCI_COMMAND, &data); } void pci_io_enable( unsigned char bus, unsigned char slot, unsigned char function ) { uint16_t data; pci_read_config_word(0, slot, function, PCI_COMMAND, &data); data |= PCI_COMMAND_IO; pci_write_config_word(0, slot, function, PCI_COMMAND, data ); } void pci_busmaster_enable( unsigned char bus, unsigned char slot, unsigned char function ) { uint16_t data; pci_read_config_word(0, slot, function, PCI_COMMAND, &data); data |= PCI_COMMAND_MASTER; pci_write_config_word(0, slot, function, PCI_COMMAND, data ); } /* * This routine determines the maximum bus number in the system */ int pci_initialize(void) { unsigned char slot, func, ucNumFuncs; unsigned char ucHeader; uint32_t class; uint32_t device; uint32_t vendor; /* * Initialize GT_PCI0IOREMAP */ pci_out_32( BSP_PCI_BASE_ADDRESS, 0xf0, 0 ); /* * According to Linux and BSD sources, this is needed to cover up a bug * in some versions of the hardware. */ out_le32( PCI_CONFIG_ADDR, 0x80000020 ); out_le32( PCI_CONFIG_DATA, 0x1be00000 ); /* * Scan PCI bus 0 looking for the known Network devices and * initializing the PCI for them. */ for (slot=0;slot> 16; /* This slot/function has a device fitted. */ pci_read_config_dword(0, slot, func, PCI_CLASS_REVISION, &class); class >>= 16; // printk( "FOUND DEVICE 0x%04x/0x%04x class 0x%x\n", // vendor, device, class ); if (class == PCI_CLASS_NETWORK_ETHERNET) { JPRINTK("FOUND ETHERNET\n"); pci_write_config_byte( 0, slot, func, PCI_INTERRUPT_LINE, MALTA_IRQ_ETHERNET ); /* * Rewrite BAR1 for RTL8139 */ if ( vendor == PCI_VENDOR_ID_REALTEK && device == PCI_DEVICE_ID_REALTEK_8139 ) { pci_memory_enable(0, slot, func); pci_io_enable(0, slot, func); pci_busmaster_enable(0, slot, func); // BAR0: IO at 0x0000_1001 pci_write_config_dword(0, slot, func, PCI_BASE_ADDRESS_0, 0x00001001); // BAR1: Memory at 0x1203_1000 pci_write_config_dword(0, slot, func, PCI_BASE_ADDRESS_1, 0x12031000); // print_bars( slot, func ); } else if ( vendor == PCI_VENDOR_ID_AMD && device == PCI_DEVICE_ID_AMD_LANCE ) { print_bars( slot, func ); pci_memory_enable(0, slot, func); pci_io_enable(0, slot, func); pci_busmaster_enable(0, slot, func); // BAR0: IO at 0x0000_1041 pci_write_config_dword(0, slot, func, PCI_BASE_ADDRESS_0, 0x00001041); // BAR1: Memory at 0x1201_1020 pci_write_config_dword(0, slot, func, PCI_BASE_ADDRESS_1, 0x12011020); print_bars( slot, func ); } } } } return PCIB_ERR_SUCCESS; } /* * Return the number of PCI busses in the system */ unsigned char pci_bus_count(void) { return (ucMaxPCIBus+1); }