patch-2.4.27 linux-2.4.27/arch/sh64/kernel/pci_sh5.c

Next file: linux-2.4.27/arch/sh64/kernel/time.c
Previous file: linux-2.4.27/arch/sh64/kernel/head.S
Back to the patch index
Back to the overall index

diff -urN linux-2.4.26/arch/sh64/kernel/pci_sh5.c linux-2.4.27/arch/sh64/kernel/pci_sh5.c
@@ -320,43 +320,70 @@
 {
 	int result = -1;
 
-	if (dev->bus->number == 0) {
-	        switch ((slot + (pin-1)) & 3) {
-	        case 0:
-	          result = IRQ_INTA;
-		  break;
-                case 1:
-	          result = IRQ_INTB;
-		  break;
-                case 2:
-	          result = IRQ_INTC;
-		  break;
-		case 3:
-		  result = IRQ_INTD;
-		  break;
-                }
-        }
-
-	if (dev->bus->number == 2) {
-	        switch((slot + (pin-1)) & 3) {
-	        case 0:
-	          result = IRQ_P2INTA;
-		  break;
-                case 1:
-	          result = IRQ_P2INTB;
-		  break;
-                case 2:
-	          result = IRQ_P2INTC;
-		  break;
-                case 3:
-	          result = IRQ_P2INTD;
-		  break;
-                }
-        }
-
-	dprintk("map_cayman_irq for dev %d on bus %d slot %d, pin is %d : irq=%d\n",
-	       dev->devfn,dev->bus->number,slot,pin,result);
+	/* The complication here is that the PCI IRQ lines from the Cayman's 2
+	   5V slots get into the CPU via a different path from the IRQ lines
+	   from the 3 3.3V slots.  Thus, we have to detect whether the card's
+	   interrupts go via the 5V or 3.3V path, i.e. the 'bridge swizzling'
+	   at the point where we cross from 5V to 3.3V is not the normal case.
+
+	   The added complication is that we don't know that the 5V slots are
+	   always bus 2, because a card containing a PCI-PCI bridge may be
+	   plugged into a 3.3V slot, and this changes the bus numbering.
+
+	   Also, the Cayman has an intermediate PCI bus that goes a custom
+	   expansion board header (and to the secondary bridge).  This bus has
+	   never been used in practice.
+
+	   The 1ary onboard PCI-PCI bridge is device 3 on bus 0
+	   The 2ary onboard PCI-PCI bridge is device 0 on the 2ary bus of the 1ary bridge.
+	   */
+
+	struct slot_pin {
+		int slot;
+		int pin;
+	} path[4];
+	int i=0;
+	int base;
+
+	while (dev->bus->number > 0) {
+
+		slot = path[i].slot = PCI_SLOT(dev->devfn);
+		pin = path[i].pin = bridge_swizzle(pin, slot);
+		dev = dev->bus->self;
+		i++;
+		if (i > 3) panic("PCI path to root bus too long!\n");
+	}
 
+	slot = PCI_SLOT(dev->devfn);
+	/* This is the slot on bus 0 through which the device is eventually
+	   reachable. */
+
+	/* Now work back up. */
+	if ((slot < 3) || (i == 0)) {
+		/* Bus 0 (incl. PCI-PCI bridge itself) : perform the final
+		   swizzle now. */
+		result = IRQ_INTA + bridge_swizzle(pin, slot) - 1;
+	} else {
+		i--;
+		slot = path[i].slot;
+		pin  = path[i].pin;
+		if (slot > 0) {
+			panic("PCI expansion bus device found - not handled!\n");
+		} else {
+			if (i > 0) {
+				/* 5V slots */
+				i--;
+				slot = path[i].slot;
+				pin  = path[i].pin;
+				/* 'pin' was swizzled earlier wrt slot, don't do it again. */
+				result = IRQ_P2INTA + (pin - 1);
+			} else {
+				/* IRQ for 2ary PCI-PCI bridge : unused */
+				result = -1;
+			}
+		}
+	}
+	
 	return result;
 }
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)