summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/powerpc/shared/pci/pci.c
diff options
context:
space:
mode:
Diffstat (limited to 'c/src/lib/libbsp/powerpc/shared/pci/pci.c')
-rw-r--r--c/src/lib/libbsp/powerpc/shared/pci/pci.c315
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
*/