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

Next file: linux/arch/i386/kernel/ptrace.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-test9/linux/arch/i386/kernel/pci-irq.c linux/arch/i386/kernel/pci-irq.c
@@ -125,81 +125,143 @@
 	}
 }
 
+/*
+ * Common IRQ routing practice: nybbles in config space,
+ * offset by some magic constant.
+ */
+static unsigned int read_config_nybble(struct pci_dev *router, unsigned offset, unsigned nr)
+{
+	u8 x;
+	unsigned reg = offset + (nr >> 1);
+
+	pci_read_config_byte(router, reg, &x);
+	return (nr & 1) ? (x >> 4) : (x & 0xf);
+}
+
+static void write_config_nybble(struct pci_dev *router, unsigned offset, unsigned nr, unsigned int val)
+{
+	u8 x;
+	unsigned reg = offset + (nr >> 1);
+
+	pci_read_config_byte(router, reg, &x);
+	x = (nr & 1) ? ((x & 0x0f) | (val << 4)) : ((x & 0xf0) | val);
+	pci_write_config_byte(router, reg, x);
+}
+
+/*
+ * ALI pirq entries are damn ugly, and completely undocumented.
+ * This has been figured out from pirq tables, and it's not a pretty
+ * picture.
+ */
 static int pirq_ali_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
 {
 	static unsigned char irqmap[16] = { 0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 };
+
+	switch (pirq) {
+	case 0x00:
+		return 0;
+	default:
+		return irqmap[read_config_nybble(router, 0x48, pirq-1)];
+	case 0xfe:
+		return irqmap[read_config_nybble(router, 0x44, 0)];
+	case 0xff:
+		return irqmap[read_config_nybble(router, 0x75, 0)];
+	}
+}
+
+static void pirq_ali_ide_interrupt(struct pci_dev *router, unsigned reg, unsigned val, unsigned irq)
+{
 	u8 x;
-	unsigned reg;
 
-	pirq--;
-	reg = 0x48 + (pirq >> 1);
 	pci_read_config_byte(router, reg, &x);
-	return irqmap[(pirq & 1) ? (x >> 4) : (x & 0x0f)];
+	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)
 {
 	static unsigned char irqmap[16] = { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 };
 	unsigned int val = irqmap[irq];
-	pirq--;
+		
 	if (val) {
-		u8 x;
-		unsigned reg = 0x48 + (pirq >> 1);
-		pci_read_config_byte(router, reg, &x);
-		x = (pirq & 1) ? ((x & 0x0f) | (val << 4)) : ((x & 0xf0) | val);
-		pci_write_config_byte(router, reg, x);
+		switch (pirq) {
+		default:
+			write_config_nybble(router, 0x48, pirq-1, val);
+			break;
+		case 0xfe:
+			pirq_ali_ide_interrupt(router, 0x44, val, irq);
+			break;
+		case 0xff:
+			pirq_ali_ide_interrupt(router, 0x75, val, irq);
+			break;
+		}
 		eisa_set_level_irq(irq);
 		return 1;
 	}
 	return 0;
 }
 
+/*
+ * The Intel PIIX4 pirq rules are fairly simple: "pirq" is
+ * just a pointer to the config space. However, something
+ * funny is going on with 0xfe/0xff, and apparently they
+ * should handle IDE irq routing. Ignore them for now.
+ */
 static int pirq_piix_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
 {
 	u8 x;
-	pci_read_config_byte(router, pirq, &x);
-	return (x < 16) ? x : 0;
+
+	switch (pirq) {
+	case 0xfe:
+	case 0xff:
+		return 0;
+	default:
+		pci_read_config_byte(router, pirq, &x);
+		return (x < 16) ? x : 0;
+	}
 }
 
 static int pirq_piix_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
 {
-	pci_write_config_byte(router, pirq, irq);
-	return 1;
+	switch (pirq) {
+	case 0xfe:
+	case 0xff:
+		return 0;
+	default:
+		pci_write_config_byte(router, pirq, irq);
+		return 1;
+	}
 }
 
+/*
+ * The VIA pirq rules are nibble-based, like ALI,
+ * but without the ugly irq number munging or the
+ * strange special cases..
+ */
 static int pirq_via_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
 {
-	u8 x;
-	int reg = 0x55 + (pirq >> 1);
-	pci_read_config_byte(router, reg, &x);
-	return (pirq & 1) ? (x >> 4) : (x & 0x0f);
+	return read_config_nybble(router, 0x55, pirq);
 }
 
 static int pirq_via_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
 {
-	u8 x;
-	int reg = 0x55 + (pirq >> 1);
-	pci_read_config_byte(router, reg, &x);
-	x = (pirq & 1) ? ((x & 0x0f) | (irq << 4)) : ((x & 0xf0) | irq);
-	pci_write_config_byte(router, reg, x);
+	write_config_nybble(router, 0x55, pirq, irq);
 	return 1;
 }
 
+/*
+ * OPTI: high four bits are nibble pointer..
+ * I wonder what the low bits do?
+ */
 static int pirq_opti_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
 {
-	u8 x;
-	int reg = 0xb8 + (pirq >> 5);
-	pci_read_config_byte(router, reg, &x);
-	return (pirq & 0x10) ? (x >> 4) : (x & 0x0f);
+	return read_config_nybble(router, 0xb8, pirq >> 4);
 }
 
 static int pirq_opti_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
 {
-	u8 x;
-	int reg = 0xb8 + (pirq >> 5);
-	pci_read_config_byte(router, reg, &x);
-	x = (pirq & 0x10) ? ((x & 0x0f) | (irq << 4)) : ((x & 0xf0) | irq);
-	pci_write_config_byte(router, reg, x);
+	write_config_nybble(router, 0xb8, pirq >> 4, irq);
 	return 1;
 }
 

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