diff options
Diffstat (limited to 'c/src/lib/libbsp/powerpc/shared/pci/pci.c')
-rw-r--r-- | c/src/lib/libbsp/powerpc/shared/pci/pci.c | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/c/src/lib/libbsp/powerpc/shared/pci/pci.c b/c/src/lib/libbsp/powerpc/shared/pci/pci.c index a6d615cea6..0661a8a60e 100644 --- a/c/src/lib/libbsp/powerpc/shared/pci/pci.c +++ b/c/src/lib/libbsp/powerpc/shared/pci/pci.c @@ -216,6 +216,320 @@ const pci_config_access_functions pci_direct_functions = { }; + + + + + + + + + + + +#define PRINT_MSG() \ + printk("pci : Device %d:%02x routed to interrupt_line %d\n", pbus, pslot, int_name ) + + +/* +** Validate a test interrupt name and print a warning if its not one of +** the names defined in the routing record. +*/ +static int test_intname( struct _int_map *row, int pbus, int pslot, int int_pin, int int_name ) +{ + int j,k; + int _nopin= -1, _noname= -1; + + for(j=0; row->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:%02x supplied a bogus interrupt_pin %d\n", pbus, pslot, int_pin ); + return -1; + } + else + { + if( _noname ) + printk("pci : Device %d:%02x supplied a suspicious interrupt_line %d, using it anyway\n", pbus, pslot, int_name ); + } + return 0; +} + + + + + +struct pcibridge +{ + int bus,slot; +}; + + +static int FindPCIbridge( int mybus, struct pcibridge *pb ) +{ + int pbus, pslot; + unsigned8 bussec, buspri; + unsigned16 devid, vendorid, dclass; + + for(pbus=0; pbus< BusCountPCI(); 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); + +#if 0 + printk("pci : Found bridge at %d:%d, mybus %d, pribus %d, secbus %d ", pbus, pslot, mybus, buspri, bussec ); +#endif + if( bussec == mybus ) + { +#if 0 + printk("match\n"); +#endif + /* found our nearest bridge going towards the root */ + pb->bus = pbus; + pb->slot = pslot; + + return 0; + } +#if 0 + printk("no match\n"); +#endif + } + + + } + } + return -1; +} + + + + + + + + +void FixupPCI( struct _int_map *bspmap, int (*swizzler)(int,int) ) +{ + unsigned char cvalue; + unsigned16 devid; + int ismatch, i, j, pbus, pslot, int_pin, int_name; + + /* + ** 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< BusCountPCI(); 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_INTERRUPT_PIN, &cvalue); + int_pin = cvalue; + + pci_read_config_byte( pbus, pslot, 0, PCI_INTERRUPT_LINE, &cvalue); + int_name = cvalue; + +/* printk("pci : device %d:%02x devid %04x, intpin %d, intline %d\n", pbus, pslot, devid, int_pin, int_name ); */ + + 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:%d w/ swizzled int pin %i to an interrupt_line.\n", pbus, pslot, int_pin ); + } + else + { + PRINT_MSG(); + pci_write_config_byte(pbus,pslot,0,PCI_INTERRUPT_LINE,(cvalue= int_name, cvalue)); + } + } + else + { + test_intname( &bspmap[i],pbus,pslot,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:%d w/ swizzled int pin %i to an interrupt_line.\n", pbus, pslot, int_pin ); + } + else + { + PRINT_MSG(); + pci_write_config_byte(pbus,pslot,0,PCI_INTERRUPT_LINE,(cvalue= int_name, cvalue)); + } + } + else + { + test_intname(&bspmap[i],pbus,pslot,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:%d with an int routing table entry\n", pbus, pslot ); + } + + + } + } + } +} + + + + + + + + + + /* * This routine determines the maximum bus number in the system */ @@ -228,6 +542,7 @@ void InitializePCI() unsigned int ulClass, ulDeviceID; detect_host_bridge(); + /* * Scan PCI bus 0 looking for PCI-PCI bridges */ |