patch-2.4.0-test12 linux/arch/i386/kernel/pci-irq.c

Next file: linux/arch/i386/kernel/process.c
Previous file: linux/arch/i386/kernel/mtrr.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test11/linux/arch/i386/kernel/pci-irq.c linux/arch/i386/kernel/pci-irq.c
@@ -176,7 +176,6 @@
 	pci_read_config_byte(router, reg, &x);
 	x = (x & 0xe0) | val;	/* clear the level->edge transform */
 	pci_write_config_byte(router, reg, x);
-	eisa_set_level_irq(irq);
 }
 
 static int pirq_ali_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
@@ -196,7 +195,6 @@
 			pirq_ali_ide_interrupt(router, 0x75, val, irq);
 			break;
 		}
-		eisa_set_level_irq(irq);
 		return 1;
 	}
 	return 0;
@@ -279,6 +277,54 @@
 	return 1;
 }
 
+static int pirq_sis_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
+{
+	u8 x;
+	int reg = 0x41 + (pirq - 'A') ;
+
+	pci_read_config_byte(router, reg, &x);
+	return (x & 0x80) ? 0 : (x & 0x0f);
+}
+
+static int pirq_sis_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
+{
+	u8 x;
+	int reg = 0x41 + (pirq - 'A') ;
+
+	pci_read_config_byte(router, reg, &x);
+	x = (pirq & 0x20) ? 0 : (irq & 0x0f);
+	pci_write_config_byte(router, reg, x);
+
+	return 1;
+}
+
+/*
+ * VLSI: nibble offset 0x74 - educated guess due to routing table and
+ *       config space of VLSI 82C534 PCI-bridge/router (1004:0102)
+ *       Tested on HP OmniBook 800 covering PIRQ 1, 2, 4, 8 for onboard
+ *       devices, PIRQ 3 for non-pci(!) soundchip and (untested) PIRQ 6
+ *       for the busbridge to the docking station.
+ */
+
+static int pirq_vlsi_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
+{
+	if (pirq > 8) {
+		printk("VLSI router pirq escape (%d)\n", pirq);
+		return 0;
+	}
+	return read_config_nybble(router, 0x74, pirq-1);
+}
+
+static int pirq_vlsi_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
+{
+	if (pirq > 8) {
+		printk("VLSI router pirq escape (%d)\n", pirq);
+		return 0;
+	}
+	write_config_nybble(router, 0x74, pirq-1, irq);
+	return 1;
+}
+
 #ifdef CONFIG_PCI_BIOS
 
 static int pirq_bios_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
@@ -309,7 +355,8 @@
 	{ "OPTI", PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C700, pirq_opti_get, pirq_opti_set },
 
 	{ "NatSemi", PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5520, pirq_cyrix_get, pirq_cyrix_set },
-
+	{ "SIS", PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, pirq_sis_get, pirq_sis_set },
+	{ "VLSI 82C534", PCI_VENDOR_ID_VLSI, PCI_DEVICE_ID_VLSI_82C534, pirq_vlsi_get, pirq_vlsi_set },
 	{ "default", 0, 0, NULL, NULL }
 };
 
@@ -355,7 +402,7 @@
 		pirq_router_dev->slot_name);
 }
 
-static struct irq_info *pirq_get_info(struct pci_dev *dev, int pin)
+static struct irq_info *pirq_get_info(struct pci_dev *dev)
 {
 	struct irq_routing_table *rt = pirq_table;
 	int entries = (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info);
@@ -373,25 +420,28 @@
 
 static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
 {
+	u8 pin;
 	struct irq_info *info;
-	int i, pirq, pin, newirq;
+	int i, pirq, newirq;
 	int irq = 0;
 	u32 mask;
 	struct irq_router *r = pirq_router;
-	struct pci_dev *dev2, *d;
+	struct pci_dev *dev2;
 	char *msg = NULL;
 
 	if (!pirq_table)
 		return 0;
 
 	/* Find IRQ routing entry */
-	pin = pci_get_interrupt_pin(dev, &d);
-	if (pin < 0) {
+	pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
+	if (!pin) {
 		DBG(" -> no interrupt pin\n");
 		return 0;
 	}
-	DBG("IRQ for %s(%d) via %s", dev->slot_name, pin, d->slot_name);
-	info = pirq_get_info(d, pin);
+	pin = pin - 1;
+	
+	DBG("IRQ for %s:%d", dev->slot_name, pin);
+	info = pirq_get_info(dev);
 	if (!info) {
 		DBG(" -> not found in routing table\n");
 		return 0;
@@ -405,9 +455,12 @@
 	DBG(" -> PIRQ %02x, mask %04x, excl %04x", pirq, mask, pirq_table->exclusive_irqs);
 	mask &= pcibios_irq_mask;
 
-	/* Find the best IRQ to assign */
-	newirq = 0;
-	if (assign) {
+	/*
+	 * Find the best IRQ to assign: use the one
+	 * reported by the device if possible.
+	 */
+	newirq = dev->irq;
+	if (!newirq && assign) {
 		for (i = 0; i < 16; i++) {
 			if (!(mask & (1 << i)))
 				continue;
@@ -417,16 +470,22 @@
 				newirq = i;
 			}
 		}
-		DBG(" -> newirq=%d", newirq);
 	}
+	DBG(" -> newirq=%d", newirq);
 
 	/* Try to get current IRQ */
-	if (r->get && (irq = r->get(pirq_router_dev, d, pirq))) {
+	if (r->get && (irq = r->get(pirq_router_dev, dev, pirq))) {
 		DBG(" -> got IRQ %d\n", irq);
 		msg = "Found";
+		/* We refuse to override the dev->irq information. Give a warning! */
+	    	if (dev->irq && dev->irq != irq) {
+	    		printk("IRQ routing conflict in pirq table! Try 'pci=autoirq'\n");
+	    		return 0;
+	    	}
 	} else if (newirq && r->set && (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) {
 		DBG(" -> assigning IRQ %d", newirq);
-		if (r->set(pirq_router_dev, d, pirq, newirq)) {
+		if (r->set(pirq_router_dev, dev, pirq, newirq)) {
+			eisa_set_level_irq(newirq);
 			DBG(" ... OK\n");
 			msg = "Assigned";
 			irq = newirq;
@@ -445,9 +504,14 @@
 
 	/* Update IRQ for all devices with the same pirq value */
 	pci_for_each_dev(dev2) {
-		if ((pin = pci_get_interrupt_pin(dev2, &d)) >= 0 &&
-		    (info = pirq_get_info(d, pin)) &&
-		    info->irq[pin].link == pirq) {
+		pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin);
+		if (!pin)
+			continue;
+		pin--;
+		info = pirq_get_info(dev2);
+		if (!info)
+			continue;
+		if (info->irq[pin].link == pirq) {
 			dev2->irq = irq;
 			pirq_penalty[irq]++;
 			if (dev != dev2)
@@ -556,19 +620,17 @@
 
 void pcibios_enable_irq(struct pci_dev *dev)
 {
-	if (!dev->irq) {
-		u8 pin;
-		pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
-		if (pin && !pcibios_lookup_irq(dev, 1)) {
-			char *msg;
-			if (io_apic_assign_pci_irqs)
-				msg = " Probably buggy MP table.";
-			else if (pci_probe & PCI_BIOS_IRQ_SCAN)
-				msg = "";
-			else
-				msg = " Please try using pci=biosirq.";
-			printk(KERN_WARNING "PCI: No IRQ known for interrupt pin %c of device %s.%s\n",
-			       'A' + pin - 1, dev->slot_name, msg);
-		}
+	u8 pin;
+	pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
+	if (pin && !pcibios_lookup_irq(dev, 1) && !dev->irq) {
+		char *msg;
+		if (io_apic_assign_pci_irqs)
+			msg = " Probably buggy MP table.";
+		else if (pci_probe & PCI_BIOS_IRQ_SCAN)
+			msg = "";
+		else
+			msg = " Please try using pci=biosirq.";
+		printk(KERN_WARNING "PCI: No IRQ known for interrupt pin %c of device %s.%s\n",
+		       'A' + pin - 1, dev->slot_name, msg);
 	}
 }

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