Subject: patch to support the EISA bus on the Indigo2

From: "Andrew R. Baker" <[email-masked]>
Date: Thu, 26 Aug 1999 16:17:46 -0500 (CDT)

Here is my initial patch for adding EISA support on the Indigo2.  It has
allowed me to get an ISA Etherlink III running in my Indigo2 with a couple
of changes to the driver.  Several things have yet to be done:  I have not
looked into DMA support;  Interrupts on the EISA bus are not reported via
/proc;  "make config" won't allow you to select any (E)ISA bus network
drivers;  I have not looked at irq probing either.  Also, remember that
many drivers will not work on the Indigo2 because they were written with
only little endian architectures in mind.

Things I want some input on:

1)  Should the #ifdefs for making the support optional be put somewhere
      else, it looks clunky in "indy_int.c".  I thought about putting it
      in i8259.h by creating stubs if support is not supposed to compiled
      in.

2)  Where in the /proc/interrupts output should the (E)ISA interrupts go?
      They have a range from 0-15.  They could be interleaved with the
      current set with an [EISA] tag.  Any opinions?

-Andrew

P.S.  This goes against the 2.2 branch of the CVS tree.
--- linux/include/asm-mips/i8259.h.orig	Thu Aug 26 15:29:50 1999
+++ linux/include/asm-mips/i8259.h	Wed Aug 25 17:34:46 1999
@@ -0,0 +1,26 @@
+/* 
+ * sgieisa.h: Defines for the (E)ISA bus on the SGI Indigo2
+ *
+ * Copyright (C) 1999 Andrew R. Baker ([email-masked]) 
+ *
+ */
+#ifndef _MIPS_SGIEISA_H
+#define _MIPS_SGIEISA_H
+
+#ifdef CONFIG_SGI_EISA
+extern void i8259_disable_irq(unsigned int);
+extern void i8259_enable_irq(unsigned int);
+extern int i8259_request_irq(unsigned int irq,
+			     void (*handler)(int, void *, struct pt_regs *),
+			     unsigned long flags,
+			     const char *device,
+			     void *dev_id);
+
+extern void i8259_free_irq(unsigned int irq, void *dev_id);
+
+extern void i8259_do_irq(int, struct pt_regs *);
+
+extern void i8259_init(void);
+#endif /* CONFIG_SGI_EISA */
+
+#endif /* !(_MIPS_SGIEISA_H) */
--- linux/include/asm-mips/sgieisa.h.orig	Thu Aug 26 15:25:15 1999
+++ linux/include/asm-mips/sgieisa.h	Thu Aug 26 16:07:36 1999
@@ -0,0 +1,32 @@
+/* 
+ * sgieisa.h: Defines for the (E)ISA bus on the SGI Indigo2
+ *
+ * Copyright (C) 1999 Andrew R. Baker ([email-masked]) 
+ *
+ */
+#ifndef _MIPS_SGIEISA_H
+#define _MIPS_SGIEISA_H
+
+#define SGI_EISA_BASE 0x00080000
+
+/* EIU registers */
+#define SGI_EIU_BASE 0x0009ffc0
+
+struct sgi_eiu_regs {
+	volatile unsigned long mode;
+	volatile unsigned long status;
+	volatile unsigned long prempt;
+	volatile unsigned long quiet;
+};
+
+
+#define SGI_EISA_NMI_BASE 0x00080461
+
+struct sgi_eisa_nmi_regs { 
+	volatile unsigned char reset;
+	volatile unsigned char port;
+};
+
+extern void sgi_eisa_init(void);
+
+#endif /* !(_MIPS_SGIEISA_H) */
--- linux/include/asm-mips/sgint23.h.orig	Thu Aug 26 15:29:12 1999
+++ linux/include/asm-mips/sgint23.h	Thu Aug 26 15:28:54 1999
@@ -2,7 +2,9 @@
  * sgint23.h: Defines for the SGI INT2 and INT3 chipsets.
  *
  * Copyright (C) 1996 David S. Miller ([email-masked])
- * Copyright (C) 1999 Andrew R. Baker ([email-masked]) - INT2 corrections
+ * Copyright (C) 1999 Andrew R. Baker ([email-masked]) 
+ * 		- INT2 corrections
+ * 		- EISA additions
  */
 #ifndef _MIPS_SGINT23_H
 #define _MIPS_SGINT23_H
@@ -12,13 +14,14 @@
  * that particular IRQ on an SGI machine.  Add new 'spaces' as new
  * IRQ hardware is supported.
  */
-#define SGINT_LOCAL0   0   /* INDY has 8 local0 irq levels */
-#define SGINT_LOCAL1   8   /* INDY has 8 local1 irq levels */
-#define SGINT_LOCAL2   16  /* INDY has 8 local2 vectored irq levels */
-#define SGINT_LOCAL3   24  /* INDY has 8 local3 vectored irq levels */
-#define SGINT_GIO      32  /* INDY has 9 GIO irq levels */
-#define SGINT_HPCDMA   41  /* INDY has 11 HPCDMA irq _sources_ */
-#define SGINT_END      52  /* End of 'spaces' */
+#define SGINT_EISA     0   /* Indigo2 has EISA interrupts 0-15 */  
+#define SGINT_LOCAL0   16  /* INDY has 8 local0 irq levels */
+#define SGINT_LOCAL1   24  /* INDY has 8 local1 irq levels */
+#define SGINT_LOCAL2   32  /* INDY has 8 local2 vectored irq levels */
+#define SGINT_LOCAL3   40  /* INDY has 8 local3 vectored irq levels */
+#define SGINT_GIO      48  /* INDY has 9 GIO irq levels */
+#define SGINT_HPCDMA   57  /* INDY has 11 HPCDMA irq _sources_ */
+#define SGINT_END      68  /* End of 'spaces' */
 
 /* Individual interrupt definitions for the INDY and Indigo2
  */
--- linux/init/main.c.orig	Thu Aug 26 15:44:48 1999
+++ linux/init/main.c	Tue Aug 24 12:25:46 1999
@@ -97,6 +97,10 @@
 extern void ecard_init(void);
 #endif
 
+#ifdef CONFIG_SGI_EISA
+extern void sgi_eisa_init(void);
+#endif
+
 extern void smp_setup(char *str, int *ints);
 #ifdef __i386__
 extern void ioapic_pirq_setup(char *str, int *ints);
@@ -878,6 +882,7 @@
 };
 
 static struct kernel_param raw_params[] __initdata = {
+	{ "OSLoadOptions=", root_dev_setup },
 	{ "root=", root_dev_setup },
 #ifdef CONFIG_ROOT_NFS
 	{ "nfsroot=", nfs_root_setup },
@@ -1303,6 +1308,10 @@
 #endif
 #ifdef CONFIG_TC
 	tc_init();
+#endif
+	
+#if defined(CONFIG_SGI_EISA)
+	sgi_eisa_init();
 #endif
 
 	/* Networking initialization needs a process context */ 
--- linux/arch/mips/config.in.orig	Thu Aug 26 15:43:07 1999
+++ linux/arch/mips/config.in	Thu Aug 26 15:43:17 1999
@@ -70,6 +70,9 @@
   fi
   bool '   Backward-compatible /proc/pci' CONFIG_PCI_OLD_PROC
 fi
+if [ "$CONFIG_SGI" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
+	bool 'SGI (E)ISA support (experimental)' CONFIG_SGI_EISA
+fi
 
 if [ "$CONFIG_DECSTATION" = "y" ]; then
   define_bool CONFIG_CPU_LITTLE_ENDIAN y
--- linux/arch/mips/sgi/kernel/Makefile.orig	Thu Aug 26 15:40:58 1999
+++ linux/arch/mips/sgi/kernel/Makefile	Thu Aug 26 15:40:40 1999
@@ -15,8 +15,13 @@
 
 OBJS  = indy_mc.o indy_sc.o indy_hpc.o indy_int.o indy_rtc.o \
         system.o indy_timer.o indyIRQ.o reset.o setup.o time.o
+
 ifdef CONFIG_SGI_PROM_CONSOLE
 OBJS += promcon.o
+endif
+
+ifdef CONFIG_SGI_EISA
+OBJS +=	eisa.o i8259.o
 endif
 
 all: sgikern.a
--- linux/arch/mips/sgi/kernel/indy_mc.c.orig	Thu Aug 26 15:37:05 1999
+++ linux/arch/mips/sgi/kernel/indy_mc.c	Thu Aug 26 16:09:08 1999
@@ -17,6 +17,8 @@
 
 /* #define DEBUG_SGIMC */
 
+extern int sgi_eisa_bus;
+
 struct sgimc_misc_ctrl *mcmisc_regs;
 unsigned long *rpsscounter;
 struct sgimc_dma_ctrl *dmactrlregs;
@@ -58,16 +60,16 @@
 	printk("MC: SGI memory controller Revision %d\n",
 	       (int) mcmisc_regs->systemid & SGIMC_SYSID_MASKREV);
 
-#if 0 /* XXX Until I figure out what this bit really indicates XXX */
+	/* XXX Until I figure out what this bit really indicates XXX */
+	/* it should indicate whether we have an (E)ISA bus or not -Andrew */
 	/* XXX Is this systemid bit reliable? */
 	if(mcmisc_regs->systemid & SGIMC_SYSID_EPRESENT) {
-		EISA_bus = 1;
+		sgi_eisa_bus = 1;
 		printk("with EISA\n");
 	} else {
-		EISA_bus = 0;
+		sgi_eisa_bus = 0;
 		printk("no EISA\n");
 	}
-#endif
 
 #ifdef DEBUG_SGIMC
 	prom_printf("sgimc_init: memconfig0<%s> mconfig1<%s>\n",
--- linux/arch/mips/sgi/kernel/indy_int.c.orig	Thu Aug 26 15:38:41 1999
+++ linux/arch/mips/sgi/kernel/indy_int.c	Wed Aug 25 17:23:27 1999
@@ -8,6 +8,7 @@
  * Copyright (C) 1999 Andrew R. Baker ([email-masked]) 
  *                    - Indigo2 changes
  *                    - Interrupt handling fixes
+ *                    - EISA support
  */
 #include <linux/config.h>
 #include <linux/init.h>
@@ -40,6 +41,10 @@
 #include <asm/sgialib.h>
 #include <asm/gdb-stub.h>
 
+#ifdef CONFIG_SGI_EISA
+  #include <asm/sgieisa.h>
+#endif
+
 /* #define DEBUG_SGINT */
 
 struct sgi_int2_regs *sgi_i2regs;
@@ -146,14 +151,20 @@
 		printk("whee, invalid irq_nr %d\n", irq_nr);
 		panic("IRQ, you lose...");
 	}
-	if(n >= SGINT_LOCAL0 && n < SGINT_GIO) {
+	if(n >= SGINT_EISA && n < SGINT_LOCAL0) {
+#ifdef CONFIG_SGI_EISA
+		i8259_disable_irq(n - SGINT_EISA);
+#else
+		printk("No (E)ISA support.");
+#endif
+	} else if(n >= SGINT_LOCAL0 && n < SGINT_GIO) {
 		disable_local_irq(n - SGINT_LOCAL0);
 	} else if(n >= SGINT_GIO && n < SGINT_HPCDMA) {
 		disable_gio_irq(n - SGINT_GIO);
 	} else if(n >= SGINT_HPCDMA && n < SGINT_END) {
 		disable_hpcdma_irq(n - SGINT_HPCDMA);
 	} else {
-		panic("how did I get here?");
+		panic("how did I get here? (%i)", irq_nr);
 	}
 }
 
@@ -164,14 +175,20 @@
 		printk("whee, invalid irq_nr %d\n", irq_nr);
 		panic("IRQ, you lose...");
 	}
-	if(n >= SGINT_LOCAL0 && n < SGINT_GIO) {
+	if(n >= SGINT_EISA && n < SGINT_LOCAL0) {
+#ifdef CONFIG_SGI_EISA
+		i8259_enable_irq(n - SGINT_EISA);
+#else
+		printk("No (E)ISA support.\n");
+#endif
+	} if(n >= SGINT_LOCAL0 && n < SGINT_GIO) {
 		enable_local_irq(n - SGINT_LOCAL0);
 	} else if(n >= SGINT_GIO && n < SGINT_HPCDMA) {
 		enable_gio_irq(n - SGINT_GIO);
 	} else if(n >= SGINT_HPCDMA && n < SGINT_END) {
 		enable_hpcdma_irq(n - SGINT_HPCDMA);
 	} else {
-		panic("how did I get here?");
+		panic("how did I get here? (%i)", irq_nr);
 	}
 }
 
@@ -355,6 +372,7 @@
 	kfree(action);
 }
 
+
 int request_irq(unsigned int irq, 
 		void (*handler)(int, void *, struct pt_regs *),
 		unsigned long irqflags, 
@@ -368,7 +386,16 @@
 		return -EINVAL;
 	if (!handler)
 		return -EINVAL;
-
+	
+	if((irq >= SGINT_EISA) && (irq < SGINT_LOCAL0))
+#ifdef CONFIG_SGI_EISA
+		return i8259_request_irq(irq, handler, irqflags, devname, dev_id);
+#else
+	{
+		printk("No (E)ISA support.");
+		return -EINVAL;
+	}
+#endif
 	if((irq >= SGINT_LOCAL0) && (irq < SGINT_GIO))
 		return request_local_irq(irq, handler, irqflags, devname, dev_id);
 
@@ -397,6 +424,14 @@
 
 	if (irq >= SGINT_END) {
 		printk("Trying to free IRQ%d\n",irq);
+		return;
+	}
+	if((irq >= SGINT_EISA) && (irq <SGINT_LOCAL0)) {
+#ifdef CONFIG_SGI_EISA
+		i8259_free_irq(irq, dev_id);
+#else
+		printk("No (E)ISA support.\n");
+#endif
 		return;
 	}
 	if((irq >= SGINT_LOCAL0) && (irq < SGINT_GIO)) {
--- linux/arch/mips/sgi/kernel/i8259.c.orig	Thu Aug 26 15:36:30 1999
+++ linux/arch/mips/sgi/kernel/i8259.c	Thu Aug 26 15:48:33 1999
@@ -0,0 +1,253 @@
+/*
+ *
+ * i8259.c:  Generic support for the i8259 PIC
+ *
+ * Code copied from arch/mips/kernel/irq.c with minor changes.
+ *
+ *  This should really go into some architecture independent place
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/malloc.h>
+#include <linux/random.h>
+
+#include <asm/io.h>
+#include <asm/ptrace.h>
+#include <asm/sgint23.h>
+#include <asm/i8259.h>
+
+/*
+ * This contains the irq mask for both 8259A irq controllers, it's an
+ * int so we can deal with the third PIC in some systems like the RM300.
+ */
+
+static unsigned int cached_irq_mask = 0xfffb;
+
+#define __byte(x,y) (((unsigned char *)&(y))[x])
+//#define __word(x,y) (((unsigned short *)&(y))[x])
+//#define __long(x,y) (((unsigned int *)&(y))[x])
+
+/* These are for big endian machines */
+#define cached_21       (__byte(3,cached_irq_mask))
+#define cached_A1       (__byte(2,cached_irq_mask))
+
+static struct irqaction *i8259_irq_action[16] = {
+	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static inline void i8259_mask_irq(unsigned int irq)
+{
+	cached_irq_mask |= 1 << irq;
+	if (irq & 8) {
+		outb(cached_A1, 0xa1);
+	} else {
+		outb(cached_21, 0x21);
+	}
+}
+
+static inline void i8259_unmask_irq(unsigned int irq)
+{
+        cached_irq_mask &= ~(1 << irq);
+        if (irq & 8) {
+		outb(cached_A1, 0xa1);
+        } else {
+		outb(cached_21, 0x21);
+	}
+}
+
+
+int i8259_setup_irq(int irq, struct irqaction * new)
+{
+	int shared = 0;
+	struct irqaction *old, **p;
+	unsigned long flags;
+
+	p = i8259_irq_action + irq;
+	if ((old = *p) != NULL) {
+		/* Can't share interrupts unless both agree to */
+		if (!(old->flags & new->flags & SA_SHIRQ))
+			return -EBUSY;
+
+		/* Can't share interrupts unless both are the same type */
+		if ((old->flags ^ new->flags) & SA_INTERRUPT)
+			return -EBUSY;
+	
+		/* add new interrupt at end of irq queue */
+		do {
+			p = & old->next;
+			old = *p;
+		} while (old);
+		shared = 1;
+	}
+
+	if (new->flags & SA_SAMPLE_RANDOM)
+		rand_initialize_irq(irq);
+
+	save_and_cli(flags);
+	*p = new;
+
+	if (!shared) {
+		i8259_unmask_irq(irq);
+	}
+	restore_flags(flags);
+	return 0;
+}
+
+
+/* enable and disable for EISA irqs */
+
+void i8259_disable_irq(unsigned int irq_nr)
+{
+	unsigned long flags;
+
+	save_and_cli(flags);
+	i8259_mask_irq(irq_nr);
+	restore_flags(flags);
+}
+
+void i8259_enable_irq(unsigned int irq_nr)
+{
+	unsigned long flags;
+
+	save_and_cli(flags);
+	i8259_unmask_irq(irq_nr);
+	restore_flags(flags);
+}
+
+
+int i8259_request_irq(unsigned int irq, 
+		      void (*handler)(int, void *, struct pt_regs *),
+                      unsigned long flags, 
+		      const char *device, 
+		      void *dev_id)
+{
+	int retval;
+	struct irqaction *action;
+	
+	/* We only have 0-15 for (E)ISA irqs */
+	if (irq >= 16)
+		return -EINVAL;
+	if (!handler)
+		return -EINVAL;
+
+	action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);
+	if (!action)
+		return -ENOMEM;
+
+	action->handler = handler;
+	action->flags = flags;
+	action->mask = 0;
+	action->name = device;
+	action->next = NULL;
+	action->dev_id = dev_id;
+
+	retval = i8259_setup_irq(irq, action);
+
+	if (retval)
+		kfree(action);
+	return retval;
+}
+
+void i8259_free_irq(unsigned int irq, void *dev_id)
+{
+	struct irqaction *action, **p;
+	unsigned long flags;
+
+	if ( irq > 15) { 
+		printk("Trying to free (E)ISA IRQ%d\n", irq);
+		return;
+	}
+	for (p = irq + i8259_irq_action; (action = *p) != NULL; p = &action->next) {
+		if (action->dev_id != dev_id)
+			continue;
+
+		/* Found it - now free it */
+		save_and_cli(flags);
+		*p = action->next;
+		if (!irq[i8259_irq_action])
+			i8259_mask_irq(irq);
+		restore_flags(flags);
+		kfree(action);
+		return;
+	}
+	printk("Trying to free free (E)ISA IRQ%d\n", irq);
+}
+
+static inline void i8259_mask_and_ack_irq(int irq)
+{
+	cached_irq_mask |= 1 << irq;
+	if (irq & 8) {
+		inb(0xa1);
+		outb(cached_A1, 0xa1);
+		outb(0x62, 0x20);               /* Specific EOI to cascade */
+		outb(0x20, 0xa0);
+	} else {
+		inb(0x21);
+		outb(cached_21, 0x21);
+		outb(0x20, 0x20);
+	}
+}
+
+
+asmlinkage void i8259_do_irq(int irq, struct pt_regs *regs)
+{
+	struct irqaction *action;
+	int do_random, cpu;
+
+	cpu = smp_processor_id();
+	hardirq_enter(cpu);
+
+	if (irq >= 16)
+		goto out;
+
+	i8259_mask_and_ack_irq(irq);
+
+	kstat.irqs[cpu][irq]++;
+
+	action = *(irq + i8259_irq_action);
+	if (!action)
+		goto out;
+
+	if (!(action->flags & SA_INTERRUPT))
+		__sti();
+	action = *(irq + i8259_irq_action);
+	do_random = 0;
+	do {
+		do_random |= action->flags;
+		action->handler(irq, action->dev_id, regs);
+		action = action->next;
+	} while (action);
+	if (do_random & SA_SAMPLE_RANDOM)
+		add_interrupt_randomness(irq);
+	__cli();
+	i8259_unmask_irq (irq);
+
+out:
+	hardirq_exit(cpu);
+}	
+
+__initfunc(void i8259_init(void))
+{
+	/* Init master interrupt controller */
+	outb(0x11, (0x20)); /* Start init sequence */
+	outb(0x00, (0x21)); /* Vector base */
+	outb(0x04, (0x21)); /* edge tiggered, Cascade (slave) on IRQ2 */
+	outb(0x01, (0x21)); /* Select 8086 mode */
+	outb(0xff, (0x21)); /* Mask all */
+
+	/* Init slave interrupt controller */
+	outb(0x11, (0xa0)); /* Start init sequence */
+	outb(0x08, (0xa1)); /* Vector base */
+	outb(0x02, (0xa1)); /* edge triggered, Cascade (slave) on IRQ2 */
+	outb(0x01, (0xa1)); /* Select 8086 mode */
+	outb(0xff, (0xa1)); /* Mask all */
+
+	outb(cached_A1, (0xa1));
+	outb(cached_21, (0x21));
+}
--- linux/arch/mips/sgi/kernel/eisa.c.orig	Thu Aug 26 15:36:24 1999
+++ linux/arch/mips/sgi/kernel/eisa.c	Thu Aug 26 16:12:04 1999
@@ -0,0 +1,70 @@
+/*
+ *
+ * eisa.c:  Support for the (E)ISA bus found on the Indigo2
+ *
+ * Copyright (C) 1999 Andrew R. Baker ([email-masked])
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/malloc.h>
+#include <linux/random.h>
+
+#include <asm/io.h>
+#include <asm/ptrace.h>
+#include <asm/sgint23.h>
+#include <asm/sgieisa.h>
+#include <asm/i8259.h>
+
+struct sgi_eiu_regs *sgi_eiu;
+int sgi_eisa_bus = 0;
+
+static void eisa_int(int irq, void *dev_id, struct pt_regs *regs)
+{
+	/* which (E)ISA interrupt did we get? */
+	int eisa_irq = inb(0x10004);
+	/* find out which EISA irq we got and call i8259_do_irq */
+	i8259_do_irq(eisa_irq, regs);
+}
+
+void eisa_reset(void)
+{
+	int i;
+	outb(0x1, 0x461);
+	/* wait */
+	for (i = 0 ; i < 1000 ; i++)
+		;
+	outb(0x0, 0x461);
+}
+
+
+__initfunc(void sgi_eisa_init(void))
+{
+	if(sgi_eisa_bus) 
+	{
+		printk("Initializing the (E)ISA subsystem.\n");
+
+		/* setup the EIU */
+		sgi_eiu = (struct sgi_eiu_regs *) (KSEG1 + SGI_EIU_BASE);
+		sgi_eiu->prempt = 0xffff;
+		sgi_eiu->quiet = 0x1;
+		sgi_eiu->mode = 0x40f3c07f;
+
+		/* set the I/O base */
+		mips_io_port_base = KSEG1 | SGI_EISA_BASE;
+
+		/* reset the bus */
+		eisa_reset();
+	
+		/* setup the i8259 interrupt controllers */
+		i8259_init();
+		
+		/* register the EISA bus interrupt handler */
+		request_irq(SGI_EISA_IRQ, eisa_int, 0, "EISA Bus", NULL);
+	}
+}