[parisc-linux-cvs] 2.4.9-pa79, greatly improved EISA support patch

Helge Deller deller@gmx.de
Thu, 8 Nov 2001 00:54:45 +0100


--------------Boundary-00=_9RFGM72Y64QGTZP80OU4
Content-Type: text/plain;
  charset="iso-8859-1"
Content-Transfer-Encoding: 8bit

On Thursday 08 November 2001 00:51, Helge Deller wrote:
> CVSROOT:	/var/cvs
> Module name:	linux
> Changes by:	deller	01/11/07 16:51:31
>
> Modified files:
> 	.              : Makefile
> 	drivers/gsc    : eisa.c
> 	arch/parisc/kernel: irq.c
> 	arch/parisc/lib: io.c
>
> Log message:
> - 2.4.9-pa79
> - greatly improved EISA support, tested with an old ISA NE2000 NIC on irq 10 :-)
>
> This patch was contributed by Daniel Engstrom <5116@telia.com>,
> Thanks a lot Daniel!



--------------Boundary-00=_9RFGM72Y64QGTZP80OU4
Content-Type: text/plain;
  charset="iso-8859-1";
  name="diff"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="diff"

Index: Makefile
===================================================================
RCS file: /var/cvs/linux/Makefile,v
retrieving revision 1.190
diff -u -p -r1.190 Makefile
--- Makefile	2001/11/05 07:42:14	1.190
+++ Makefile	2001/11/07 23:43:58
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 9
-EXTRAVERSION = -pa78
+EXTRAVERSION = -pa79
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
Index: drivers/gsc/eisa.c
===================================================================
RCS file: /var/cvs/linux/drivers/gsc/eisa.c,v
retrieving revision 1.10
diff -u -p -r1.10 eisa.c
--- drivers/gsc/eisa.c	2001/11/05 07:42:15	1.10
+++ drivers/gsc/eisa.c	2001/11/07 23:44:09
@@ -7,12 +7,24 @@
  * 2 of the License, or (at your option) any later version.
  *
  * Copyright (c) 2001 Matthew Wilcox for Hewlett Packard
+ * Copyright (c) 2001 Daniel Engstrom <5116@telia.com>
  *
  * There are two distinct EISA adapters.  Mongoose is found in machines
  * before the 712; then the Wax ASIC is used.  To complicate matters, the
  * Wax ASIC also includes a PS/2 and RS-232 controller, but those are
  * dealt with elsewhere; this file is concerned only with the EISA portions
  * of Wax.
+ * 
+ * 
+ * HINT:
+ * -----
+ * To allow an ISA card to work properly in the EISA slot you need to
+ * set an edge trigger level. This may be done on the palo command line 
+ * by adding the kernel parameter "eisa_irq_edge=n,n2,[...]]", with 
+ * n and n2 as the irq levels you want to use.
+ * 
+ * Example: "eisa-irq_edge=10,11" allows ISA cards to operate at 
+ * irq levels 10 and 11.
  */
 
 #include <linux/init.h>
@@ -27,11 +39,20 @@
 #include <asm/gsc.h>
 #include <asm/hardware.h>
 #include <asm/processor.h>
+#include <asm/delay.h>
 
+#if 0
+#define EISA_DBG(msg, arg... ) printk(KERN_DEBUG "eisa: " msg , ## arg )
+#else
+#define EISA_DBG(msg, arg... )  
+#endif
+
+static spinlock_t eisa_irq_lock = SPIN_LOCK_UNLOCKED;
+
 /* We can only have one EISA adapter in the system because neither
  * implementation can be flexed.
  */
-struct eisa_ba {
+static struct eisa_ba {
 	struct pci_hba_data	hba;
 } eisa_dev;
 
@@ -57,14 +78,14 @@ unsigned char eisa_in8(unsigned short po
 unsigned short eisa_in16(unsigned short port)
 {
 	if (EISA_bus)
-		return le16_to_cpu(gsc_readw(eisa_permute(port)));
+		return gsc_readw(eisa_permute(port));
 	return 0xffff;
 }
 
 unsigned int eisa_in32(unsigned short port)
 {
 	if (EISA_bus)
-		return le32_to_cpu(gsc_readl(eisa_permute(port)));
+		return gsc_readl(eisa_permute(port));
 	return 0xffffffff;
 }
 
@@ -76,54 +97,211 @@ void eisa_out8(unsigned char data, unsig
 
 void eisa_out16(unsigned short data, unsigned short port)
 {
-	if (EISA_bus)
-		gsc_writew(cpu_to_le16(data), eisa_permute(port));
+	if (EISA_bus)	
+		gsc_writew(data, eisa_permute(port));
 }
 
 void eisa_out32(unsigned int data, unsigned short port)
 {
 	if (EISA_bus)
-		gsc_writel(cpu_to_le32(data), eisa_permute(port));
+		gsc_writel(data, eisa_permute(port));
 }
 
 /* Interrupt handling */
 
+/* cached interrupt mask registers */
+static int master_mask;
+static int slave_mask;
+
+/* the trig level can be set with the
+ * eisa_irq_edge=n,n,n commanlnine parameter 
+ * We should really read this from the EEPROM 
+ * in the furure. 
+ */
+/* irq 13,8,2,1,0 must be edge */
+static unsigned int eisa_irq_level=0xdef8; /* these are to be level-trigged */
+
+
+/* called by free irq */
 static void eisa_disable_irq(void *irq_dev, int irq)
 {
-	return;
+	unsigned long flags;
+
+	EISA_DBG("disable irq %d\n", irq);
+	/* just mask for now */
+	spin_lock_irqsave(&eisa_irq_lock, flags);
+        if (irq & 8) {
+		slave_mask |= (1 << (irq&7));
+		eisa_out8(slave_mask, 0xa1);
+	} else {
+		master_mask |= (1 << (irq&7));
+		eisa_out8(master_mask, 0x21);
+	}
+	spin_unlock_irqrestore(&eisa_irq_lock, flags);
+	EISA_DBG("pic0 mask %02x\n", eisa_in8(0x21));
+	EISA_DBG("pic1 mask %02x\n", eisa_in8(0xa1));
 }
 
+/* called by request irq */
 static void eisa_enable_irq(void *irq_dev, int irq)
 {
-	return;
+	unsigned long flags;
+	EISA_DBG("enable irq %d\n", irq);
+		
+	spin_lock_irqsave(&eisa_irq_lock, flags);
+        if (irq & 8) {
+		slave_mask &= ~(1 << (irq&7));
+		eisa_out8(slave_mask, 0xa1);
+	} else {
+		master_mask &= ~(1 << (irq&7));
+		eisa_out8(master_mask, 0x21);
+	}
+	spin_unlock_irqrestore(&eisa_irq_lock, flags);
+	EISA_DBG("pic0 mask %02x\n", eisa_in8(0x21));
+	EISA_DBG("pic1 mask %02x\n", eisa_in8(0xa1));
 }
 
 static void eisa_mask_irq(void *irq_dev, int irq)
 {
-	return;
+	unsigned long flags;
+	EISA_DBG("mask irq %d\n", irq);
+	
+        /* mask irq */
+	spin_lock_irqsave(&eisa_irq_lock, flags);
+	if (irq & 8) {
+		slave_mask |= (1 << (irq&7));
+		eisa_out8(slave_mask, 0xa1);
+	} else {
+		master_mask |= (1 << (irq&7));
+		eisa_out8(master_mask, 0x21);
+	}
+	spin_unlock_irqrestore(&eisa_irq_lock, flags);
 }
 
 static void eisa_unmask_irq(void *irq_dev, int irq)
 {
-	return;
+	unsigned long flags;
+	EISA_DBG("unmask irq %d\n", irq);
+        
+	/* unmask */
+	spin_lock_irqsave(&eisa_irq_lock, flags);
+	if (irq & 8) {
+		slave_mask &= ~(1 << (irq&7));
+		eisa_out8(slave_mask, 0xa1);
+	} else {
+		master_mask &= ~(1 << (irq&7));
+		eisa_out8(master_mask, 0x21);
+	}
+	spin_unlock_irqrestore(&eisa_irq_lock, flags);
 }
 
-struct irqaction action[IRQ_PER_REGION];
+static struct irqaction action[IRQ_PER_REGION];
 
-#define EISA_IRQ_REGION 0 /* Compatibility */
-
+/* EISA needs to be fixed at IRQ region  #0 (EISA_IRQ_REGION) */
 static struct irq_region eisa_irq_region = {
-	{ eisa_disable_irq, eisa_enable_irq, eisa_mask_irq, eisa_unmask_irq },
-	{ NULL, "EISA", EISA_IRQ_REGION },
-	action,
+	ops:	{ eisa_disable_irq, eisa_enable_irq, eisa_mask_irq, eisa_unmask_irq },
+	data:	{ name: "EISA", irqbase: 0 },
+	action:	action,
 };
 
 static void eisa_irq(int _, void *intr_dev, struct pt_regs *regs)
 {
 	extern void do_irq(struct irqaction *a, int i, struct pt_regs *p);
-	int irq = gsc_readb(0xfc01f000) & 0xf; /* EISA supports 16 irqs */
+	int irq = gsc_readb(0xfc01f000); /* EISA supports 16 irqs */
+	unsigned long flags;
+        
+	spin_lock_irqsave(&eisa_irq_lock, flags);
+	/* read IRR command */
+	eisa_out8(0x0a, 0x20);
+	eisa_out8(0x0a, 0xa0);
+
+	EISA_DBG("irq IAR %02x 8259-1 irr %02x 8259-2 irr %02x\n",
+		   irq, eisa_in8(0x20), eisa_in8(0xa0));
+   
+	/* read ISR command */
+	eisa_out8(0x0a, 0x20);
+	eisa_out8(0x0a, 0xa0);
+	EISA_DBG("irq 8259-1 isr %02x imr %02x 8259-2 isr %02x imr %02x\n",
+		 eisa_in8(0x20), eisa_in8(0x21), eisa_in8(0xa0), eisa_in8(0xa1));
+	
+	irq &= 0xf;
+	
+	/* mask irq and write eoi */
+	if (irq & 8) {
+		slave_mask |= (1 << (irq&7));
+		eisa_out8(slave_mask, 0xa1);
+		eisa_out8(0x60 | (irq&7),0xa0);/* 'Specific EOI' to slave */
+		eisa_out8(0x62,0x20);	/* 'Specific EOI' to master-IRQ2 */
+		
+	} else {
+		master_mask |= (1 << (irq&7));
+		eisa_out8(master_mask, 0x21);
+		eisa_out8(0x60|irq,0x20);	/* 'Specific EOI' to master */
+	}
+	spin_unlock_irqrestore(&eisa_irq_lock, flags);
 
+   
 	do_irq(&eisa_irq_region.action[irq], EISA_IRQ_REGION + irq, regs);
+   
+	spin_lock_irqsave(&eisa_irq_lock, flags);
+	/* unmask */
+        if (irq & 8) {
+		slave_mask &= ~(1 << (irq&7));
+		eisa_out8(slave_mask, 0xa1);
+	} else {
+		master_mask &= ~(1 << (irq&7));
+		eisa_out8(master_mask, 0x21);
+	}
+	spin_unlock_irqrestore(&eisa_irq_lock, flags);
+}
+
+static void dummy_irq2_handler(int _, void *dev, struct pt_regs *regs)
+{
+	printk(KERN_ALERT "eisa: uhh, irq2?\n");
+}
+
+static void init_eisa_pic(void)
+{
+	unsigned long flags;
+	
+	spin_lock_irqsave(&eisa_irq_lock, flags);
+
+	eisa_out8(0xff, 0x21); /* mask during init */
+	eisa_out8(0xff, 0xa1); /* mask during init */
+	
+	/* master pic */
+	eisa_out8(0x11,0x20); /* ICW1 */   
+	eisa_out8(0x00,0x21); /* ICW2 */   
+	eisa_out8(0x04,0x21); /* ICW3 */   
+	eisa_out8(0x01,0x21); /* ICW4 */   
+	eisa_out8(0x40,0x20); /* OCW2 */   
+	
+	/* slave pic */
+	eisa_out8(0x11,0xa0); /* ICW1 */   
+	eisa_out8(0x08,0xa1); /* ICW2 */   
+        eisa_out8(0x02,0xa1); /* ICW3 */   
+	eisa_out8(0x01,0xa1); /* ICW4 */   
+	eisa_out8(0x40,0xa0); /* OCW2 */   
+        
+	udelay(100);
+	
+	slave_mask = 0xff; 
+	master_mask = 0xfb; 
+	eisa_out8(slave_mask, 0xa1); /* OCW1 */
+	eisa_out8(master_mask, 0x21); /* OCW1 */
+	
+	/* setup trig level */
+	EISA_DBG("EISA edge/level %04x\n", eisa_irq_level);
+	
+	eisa_out8(eisa_irq_level&0xff, 0x4d0); /* Set all irq's to edge  */
+	eisa_out8((eisa_irq_level >> 8) & 0xff, 0x4d1); 
+	
+	EISA_DBG("pic0 mask %02x\n", eisa_in8(0x21));
+	EISA_DBG("pic1 mask %02x\n", eisa_in8(0xa1));
+	EISA_DBG("pic0 edge/level %02x\n", eisa_in8(0x4d0));
+	EISA_DBG("pic1 edge/level %02x\n", eisa_in8(0x4d1));
+	
+	spin_unlock_irqrestore(&eisa_irq_lock, flags);
 }
 
 /* Device initialisation */
@@ -134,9 +312,10 @@ static int __devinit eisa_probe(struct p
 {
 	int result;
 	char *name = is_mongoose(dev) ? "Mongoose" : "Wax";
-
-	printk("%s EISA Adapter found at 0x%08lx\n", name, dev->hpa);
 
+	printk(KERN_INFO "%s EISA Adapter found at 0x%08lx\n", 
+		name, dev->hpa);
+   
 	eisa_dev.hba.lmmio_space.name = "EISA";
 	eisa_dev.hba.lmmio_space.start = (unsigned long) 0xfffffffffc000000;
 	eisa_dev.hba.lmmio_space.end = (unsigned long) 0xffffffffffbfffff;
@@ -157,20 +336,23 @@ static int __devinit eisa_probe(struct p
 	}
 	pcibios_register_hba(&eisa_dev.hba);
 
-	if (!dev->irq) {
-		printk(KERN_ERR "EISA: failed to claim IRQ\n");
-		return -ENODEV;
-	}
-
-	result = request_irq(dev->irq, eisa_irq, 0, "EISA", NULL);
+	init_eisa_pic();
+	
+	result = request_irq(dev->irq, eisa_irq, SA_SHIRQ, "EISA", NULL);
 	if (result) {
 		printk(KERN_ERR "EISA: request_irq failed!\n");
 		return result;
 	}
+	
+	/* Reserve IRQ2 */
+	action[2].handler = dummy_irq2_handler;
+	action[2].name = "cascade";
+	
+	eisa_irq_region.data.dev = dev;
 	irq_region[0] = &eisa_irq_region;
-
+	
 	EISA_bus = 1;
-
+	
 	return 0;
 }
 
@@ -192,3 +374,36 @@ void __init eisa_init(void)
 {
 	register_parisc_driver(&eisa_driver);
 }
+
+
+static int __init eisa_irq_setup(char *str)
+{
+	char *cur = str;
+	int val;
+
+	EISA_DBG("IRQ setup\n");
+	while (cur != NULL) {
+		char *pe;
+		
+		val = (int) simple_strtoul(cur, &pe, 0);
+		if (val > 15 || val < 0) {
+			printk(KERN_ERR "eisa: EISA irq value are 0-15\n");
+			continue;
+		}
+		if (val == 2) { 
+			val = 9;
+		}
+		eisa_irq_level &= ~(1<<val); /* clear the corresponding bit */
+		EISA_DBG("setting IRQ %d to edge-triggered mode\n", val);
+		
+		if ((cur = strchr(cur, ','))) {
+			cur++;
+		} else {
+			break;
+		}
+	}
+	return 1;
+}
+
+__setup("eisa_irq_edge=", eisa_irq_setup);
+
Index: arch/parisc/kernel/irq.c
===================================================================
RCS file: /var/cvs/linux/arch/parisc/kernel/irq.c,v
retrieving revision 1.49
diff -u -p -r1.49 irq.c
--- arch/parisc/kernel/irq.c	2001/11/05 07:42:15	1.49
+++ arch/parisc/kernel/irq.c	2001/11/07 23:44:35
@@ -116,6 +116,7 @@ void mask_irq(int irq)
 	struct irq_region *region;
 
 	DBG_IRQ(irq, ("mask_irq(%d) %d+%d\n", irq, IRQ_REGION(irq), IRQ_OFFSET(irq)));
+	irq = irq_cannonicalize(irq);
 	region = irq_region[IRQ_REGION(irq)];
 	if (region->ops.mask_irq)
 		region->ops.mask_irq(region->data.dev, IRQ_OFFSET(irq));
@@ -126,6 +127,7 @@ void unmask_irq(int irq)
 	struct irq_region *region;
 
 	DBG_IRQ(irq, ("unmask_irq(%d) %d+%d\n", irq, IRQ_REGION(irq), IRQ_OFFSET(irq)));
+	irq = irq_cannonicalize(irq);
 	region = irq_region[IRQ_REGION(irq)];
 	if (region->ops.unmask_irq)
 		region->ops.unmask_irq(region->data.dev, IRQ_OFFSET(irq));
@@ -136,6 +138,7 @@ void disable_irq(int irq)
 	struct irq_region *region;
 
 	DBG_IRQ(irq, ("disable_irq(%d) %d+%d\n", irq, IRQ_REGION(irq), IRQ_OFFSET(irq)));
+	irq = irq_cannonicalize(irq);
 	region = irq_region[IRQ_REGION(irq)];
 	if (region->ops.disable_irq)
 		region->ops.disable_irq(region->data.dev, IRQ_OFFSET(irq));
@@ -148,6 +151,7 @@ void enable_irq(int irq)
 	struct irq_region *region;
 
 	DBG_IRQ(irq, ("enable_irq(%d) %d+%d\n", irq, IRQ_REGION(irq), IRQ_OFFSET(irq)));
+	irq = irq_cannonicalize(irq);
 	region = irq_region[IRQ_REGION(irq)];
 
 	if (region->ops.enable_irq)
@@ -530,6 +534,7 @@ int request_irq(unsigned int irq,
 	printk(KERN_INFO "request_irq(%d, %p, 0x%lx, %s, %p)\n",irq, handler, irqflags, devname, dev_id);
 #endif
 
+	irq = irq_cannonicalize(irq);
 	/* request_irq()/free_irq() may not be called from interrupt context. */
 	if (in_interrupt())
 		BUG();
@@ -589,6 +594,8 @@ void free_irq(unsigned int irq, void *de
 	struct irqaction *action, **p;
 
 	/* See comments in request_irq() about interrupt context */
+	irq = irq_cannonicalize(irq);
+	
 	if (in_interrupt()) BUG();
 
 	spin_lock(&irq_lock);
@@ -667,7 +674,7 @@ unsigned long probe_irq_on(void)
 	 */
 	for (i = EISA_MAX_IRQS-1; i > 0; i--) {
 		struct irqaction *action;
-
+		
 		spin_lock_irq(&irq_lock);
 		action = region->action + i;
 		if (!action->handler) {
Index: arch/parisc/lib/io.c
===================================================================
RCS file: /var/cvs/linux/arch/parisc/lib/io.c,v
retrieving revision 1.5
diff -u -p -r1.5 io.c
--- arch/parisc/lib/io.c	2001/08/24 14:45:05	1.5
+++ arch/parisc/lib/io.c	2001/11/07 23:44:49
@@ -160,32 +160,67 @@ void insb (unsigned long port, void *dst
  */
 void insw (unsigned long port, void *dst, unsigned long count)
 {
-	if (((unsigned long)dst) & 0x3) {
-		if (((unsigned long)dst) & 0x1) {
-			BUG();
+	unsigned int l = 0, l2;
+	
+	if (!count)
+		return;
+	
+	switch (((unsigned long) dst) & 0x3)
+	{
+	 case 0x00:			/* Buffer 32-bit aligned */
+		while (count>=2) {
+			
+			count -= 2;
+			l = inw(port) << 16;
+			l |= inw(port);
+			*(unsigned int *) dst = l;
+			((unsigned int *) dst)++;
 		}
-		if (!count)
-			return;
-		count--;
-		*(unsigned short* ) dst = inw(port);
+		if (count) {
+			*(unsigned short *) dst = inw(port);
+		}
+		break;
+	
+	 case 0x02:			/* Buffer 16-bit aligned */
+		*(unsigned short *) dst = inw(port);
 		((unsigned short *) dst)++;
-	}
-
-	while (count >= 2) {
-		unsigned int w;
-		count -= 2;
-		w = inw(port) << 16;
-		w |= inw(port);
-		*(unsigned int *) dst = w;
-		((unsigned int *) dst)++;
-	}
-
-	if (count) {
-		*(unsigned short*) dst = inw(port);
+		count--;
+		while (count>=2) {
+			
+			count -= 2;
+			l = inw(port) << 16;
+			l |= inw(port);
+			*(unsigned int *) dst = l;
+			((unsigned int *) dst)++;
+		}
+		if (count) {
+			*(unsigned short *) dst = inw(port);
+		}
+		break;
+		
+	 case 0x01:			/* Buffer 8-bit aligned */
+	 case 0x03:
+		/* I don't bother with 32bit transfers
+		 * in this case, 16bit will have to do -- DE */
+		--count;
+		
+		l = inw(port);
+		*(unsigned char *) dst = l >> 8;
+		((unsigned char *) dst)++;
+		while (count--)
+		{
+			l2 = inw(port);
+			*(unsigned short *) dst = (l & 0xff) << 8 | (l2 >> 8);
+			((unsigned short *) dst)++;
+			l = l2;
+		}
+		*(unsigned char *) dst = l & 0xff;
+		break;
 	}
 }
 
 
+
 /*
  * Read COUNT 32-bit words from port PORT into memory starting at
  * SRC. Now works with any alignment in SRC. Performance is important,
@@ -286,27 +321,63 @@ void outsb(unsigned long port, const voi
  */
 void outsw (unsigned long port, const void *src, unsigned long count)
 {
-	if (((unsigned long)src) & 0x3) {
-		if (((unsigned long)src) & 0x1) {
-			/* unaligned input buffer */
-			BUG();
+	unsigned int l = 0, l2;
+	
+	if (!count)
+		return;
+	
+	switch (((unsigned long) src) & 0x3)
+	{
+	 case 0x00:			/* Buffer 32-bit aligned */
+		while (count>=2) {
+			count -= 2;
+			l = *(unsigned int *) src;
+			((unsigned int *) src)++;
+			outw(l >> 16, port);
+			outw(l & 0xffff, port);
 		}
+		if (count) {
+			outw(*(unsigned short*)src, port);
+		}
+		break;
+	
+	 case 0x02:			/* Buffer 16-bit aligned */
+		
 		outw(*(unsigned short*)src, port);
 		((unsigned short *) src)++;
-		--count;
-	}
-
-	while (count >= 2) {
-		unsigned int w;
-		count -= 2;
-		w = *(unsigned int *) src;
-		((unsigned int *) src)++;
-		outw(w >> 16, port);
-		outw(w & 0xff, port);
-	}
-
-	if (count) {
-		outw(*(unsigned short *) src, port);
+		count--;
+		
+		while (count>=2) {
+			count -= 2;
+			l = *(unsigned int *) src;
+			((unsigned int *) src)++;
+			outw(l >> 16, port);
+			outw(l & 0xffff, port);
+		}
+		if (count) {
+			outw(*(unsigned short*)src, port);
+		}
+		break;
+		
+	 case 0x01:			/* Buffer 8-bit aligned */	
+		/* I don't bother with 32bit transfers
+		 * in this case, 16bit will have to do -- DE */
+		
+		l  = *(unsigned char *) src << 8;
+		((unsigned char *) src)++;
+		count--;
+		while (count)
+		{
+			count--;
+			l2 = *(unsigned short *) src;
+			((unsigned short *) src)++;
+			outw(l | l2 >> 8, port);
+		        l = l2 << 8;
+		}
+		l2 = *(unsigned char *) src;
+		outw (l | l2>>8, port);
+		break;
+	
 	}
 }
 

--------------Boundary-00=_9RFGM72Y64QGTZP80OU4--