summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--linux/dev/arch/i386/kernel/irq.c492
-rw-r--r--linux/dev/arch/i386/kernel/setup.c15
2 files changed, 507 insertions, 0 deletions
diff --git a/linux/dev/arch/i386/kernel/irq.c b/linux/dev/arch/i386/kernel/irq.c
new file mode 100644
index 0000000..1543234
--- /dev/null
+++ b/linux/dev/arch/i386/kernel/irq.c
@@ -0,0 +1,492 @@
+/*
+ * Linux IRQ management.
+ * Copyright (C) 1995 Shantanu Goel.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * linux/arch/i386/kernel/irq.c
+ *
+ * Copyright (C) 1992 Linus Torvalds
+ */
+
+#include <sys/types.h>
+#include <mach/mach_types.h>
+#include <mach/vm_param.h>
+#include <kern/assert.h>
+
+#include <i386/spl.h>
+#include <i386/pic.h>
+#include <i386/pit.h>
+
+#define MACH_INCLUDE
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/delay.h>
+#include <linux/kernel_stat.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+
+extern int linux_timer_intr (void);
+extern spl_t splhigh (void);
+extern spl_t spl0 (void);
+extern void form_pic_mask (void);
+
+/*
+ * XXX Move this into more suitable place...
+ * Set if the machine has an EISA bus.
+ */
+int EISA_bus = 0;
+
+/*
+ * Priority at which a Linux handler should be called.
+ * This is used at the time of an IRQ allocation. It is
+ * set by emulation routines for each class of device.
+ */
+spl_t linux_intr_pri;
+
+/*
+ * Flag indicating an interrupt is being handled.
+ */
+unsigned long intr_count = 0;
+
+/*
+ * List of Linux interrupt handlers.
+ */
+struct linux_action
+{
+ void (*handler) (int, void *, struct pt_regs *);
+ void *dev_id;
+ struct linux_action *next;
+ unsigned long flags;
+};
+
+static struct linux_action *irq_action[16] =
+{
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL
+};
+
+extern spl_t curr_ipl;
+extern int curr_pic_mask;
+extern int pic_mask[];
+
+extern int intnull (), prtnull ();
+
+/*
+ * Generic interrupt handler for Linux devices.
+ * Set up a fake `struct pt_regs' then call the real handler.
+ */
+static int
+linux_intr (int irq)
+{
+ struct pt_regs regs;
+ struct linux_action *action = *(irq_action + irq);
+
+ kstat.interrupts[irq]++;
+ intr_count++;
+
+ while (action)
+ {
+ action->handler (irq, action->dev_id, &regs);
+ action = action->next;
+ }
+
+ intr_count--;
+
+ /* Not used. by OKUJI Yoshinori. */
+ return 0;
+}
+
+/*
+ * Mask an IRQ.
+ */
+static inline void
+mask_irq (unsigned int irq_nr)
+{
+ int i;
+
+ for (i = 0; i < intpri[irq_nr]; i++)
+ pic_mask[i] |= 1 << irq_nr;
+
+ if (curr_pic_mask != pic_mask[curr_ipl])
+ {
+ curr_pic_mask = pic_mask[curr_ipl];
+ if (irq_nr < 8)
+ outb (curr_pic_mask & 0xff, PIC_MASTER_OCW);
+ else
+ outb (curr_pic_mask >> 8, PIC_SLAVE_OCW);
+ }
+}
+
+/*
+ * Unmask an IRQ.
+ */
+static inline void
+unmask_irq (unsigned int irq_nr)
+{
+ int mask, i;
+
+ mask = 1 << irq_nr;
+ if (irq_nr >= 8)
+ mask |= 1 << 2;
+
+ for (i = 0; i < intpri[irq_nr]; i++)
+ pic_mask[i] &= ~mask;
+
+ if (curr_pic_mask != pic_mask[curr_ipl])
+ {
+ curr_pic_mask = pic_mask[curr_ipl];
+ if (irq_nr < 8)
+ outb (curr_pic_mask & 0xff, PIC_MASTER_OCW);
+ else
+ outb (curr_pic_mask >> 8, PIC_SLAVE_OCW);
+ }
+}
+
+void
+disable_irq (unsigned int irq_nr)
+{
+ unsigned long flags;
+
+ assert (irq_nr < NR_IRQS);
+
+ save_flags (flags);
+ cli ();
+ mask_irq (irq_nr);
+ restore_flags (flags);
+}
+
+void
+enable_irq (unsigned int irq_nr)
+{
+ unsigned long flags;
+
+ assert (irq_nr < NR_IRQS);
+
+ save_flags (flags);
+ cli ();
+ unmask_irq (irq_nr);
+ restore_flags (flags);
+}
+
+/*
+ * Default interrupt handler for Linux.
+ */
+int
+linux_bad_intr (int irq)
+{
+ mask_irq (irq);
+ return 0;
+}
+
+static int
+setup_x86_irq (int irq, struct linux_action *new)
+{
+ int shared = 0;
+ struct linux_action *old, **p;
+ unsigned long flags;
+
+ p = irq_action + irq;
+ if ((old = *p) != NULL)
+ {
+ /* Can't share interrupts unless both agree to */
+ if (!(old->flags & new->flags & SA_SHIRQ))
+ return (-LINUX_EBUSY);
+
+ /* Can't share interrupts unless both are same type */
+ if ((old->flags ^ new->flags) & SA_INTERRUPT)
+ return (-LINUX_EBUSY);
+
+ /* add new interrupt at end of irq queue */
+ do
+ {
+ p = &old->next;
+ old = *p;
+ }
+ while (old);
+ shared = 1;
+ }
+
+ save_flags (flags);
+ cli ();
+ *p = new;
+
+ if (!shared)
+ {
+ ivect[irq] = linux_intr;
+ iunit[irq] = irq;
+ intpri[irq] = linux_intr_pri;
+ unmask_irq (irq);
+ }
+ restore_flags (flags);
+ return 0;
+}
+
+/*
+ * Attach a handler to an IRQ.
+ */
+int
+request_irq (unsigned int irq, void (*handler) (int, void *, struct pt_regs *),
+ unsigned long flags, const char *device, void *dev_id)
+{
+ struct linux_action *action;
+ int retval;
+
+ assert (irq < 16);
+
+ if (!handler)
+ return -LINUX_EINVAL;
+
+ /*
+ * Hmm... Should I use `kalloc()' ?
+ * By OKUJI Yoshinori.
+ */
+ action = (struct linux_action *)
+ linux_kmalloc (sizeof (struct linux_action), GFP_KERNEL);
+ if (action == NULL)
+ return -LINUX_ENOMEM;
+
+ action->handler = handler;
+ action->next = NULL;
+ action->dev_id = dev_id;
+ action->flags = flags;
+
+ retval = setup_x86_irq (irq, action);
+ if (retval)
+ linux_kfree (action);
+
+ return retval;
+}
+
+/*
+ * Deallocate an irq.
+ */
+void
+free_irq (unsigned int irq, void *dev_id)
+{
+ struct linux_action *action, **p;
+ unsigned long flags;
+
+ if (irq > 15)
+ panic ("free_irq: bad irq number");
+
+ for (p = irq_action + irq; (action = *p) != NULL; p = &action->next)
+ {
+ if (action->dev_id != dev_id)
+ continue;
+
+ save_flags (flags);
+ cli ();
+ *p = action->next;
+ if (!irq_action[irq])
+ {
+ mask_irq (irq);
+ ivect[irq] = linux_bad_intr;
+ iunit[irq] = irq;
+ intpri[irq] = SPL0;
+ }
+ restore_flags (flags);
+ linux_kfree (action);
+ return;
+ }
+
+ panic ("free_irq: bad irq number");
+}
+
+/*
+ * Set for an irq probe.
+ */
+unsigned long
+probe_irq_on (void)
+{
+ unsigned i, irqs = 0;
+ unsigned long delay;
+
+ assert (curr_ipl == 0);
+
+ /*
+ * Allocate all available IRQs.
+ */
+ for (i = 15; i > 0; i--)
+ {
+ if (!irq_action[i] && ivect[i] == linux_bad_intr)
+ {
+ intpri[i] = linux_intr_pri;
+ enable_irq (i);
+ irqs |= 1 << i;
+ }
+ }
+
+ /*
+ * Wait for spurious interrupts to mask themselves out.
+ */
+ for (delay = jiffies + HZ / 10; delay > jiffies;)
+ ;
+
+ return (irqs & ~curr_pic_mask);
+}
+
+/*
+ * Return the result of an irq probe.
+ */
+int
+probe_irq_off (unsigned long irqs)
+{
+ unsigned int i;
+
+ assert (curr_ipl == 0);
+
+ irqs &= curr_pic_mask;
+
+ /*
+ * Disable unnecessary IRQs.
+ */
+ for (i = 15; i > 0; i--)
+ {
+ if (!irq_action[i] && ivect[i] == linux_bad_intr)
+ {
+ disable_irq (i);
+ intpri[i] = SPL0;
+ }
+ }
+
+ /*
+ * Return IRQ number.
+ */
+ if (!irqs)
+ return 0;
+ i = ffz (~irqs);
+ if (irqs != (irqs & (1 << i)))
+ i = -i;
+ return i;
+}
+
+/*
+ * Reserve IRQs used by Mach drivers.
+ * Must be called before Linux IRQ detection, after Mach IRQ detection.
+ */
+static void
+reserve_mach_irqs (void)
+{
+ unsigned int i;
+
+ for (i = 0; i < 16; i++)
+ {
+ if (ivect[i] != prtnull && ivect[i] != intnull)
+ /* Set non-NULL value. */
+ irq_action[i] = (struct linux_action *) -1;
+ }
+}
+
+static int (*old_clock_handler) ();
+static int old_clock_pri;
+
+void
+init_IRQ (void)
+{
+ int i;
+ char *p;
+ int latch = (CLKNUM + hz / 2) / hz;
+
+ /*
+ * Ensure interrupts are disabled.
+ */
+ (void) splhigh ();
+
+ /*
+ * Program counter 0 of 8253 to interrupt hz times per second.
+ */
+ outb_p (PIT_C0 | PIT_SQUAREMODE | PIT_READMODE, PITCTL_PORT);
+ outb_p (latch && 0xff, PITCTR0_PORT);
+ outb (latch >> 8, PITCTR0_PORT);
+
+ /*
+ * Install our clock interrupt handler.
+ */
+ old_clock_handler = ivect[0];
+ old_clock_pri = intpri[0];
+ ivect[0] = linux_timer_intr;
+ intpri[0] = SPLHI;
+
+ reserve_mach_irqs ();
+
+ for (i = 1; i < 16; i++)
+ {
+ /*
+ * irq2 and irq13 should be igonored.
+ */
+ if (i == 2 || i == 13)
+ continue;
+ if (ivect[i] == prtnull || ivect[i] == intnull)
+ {
+ ivect[i] = linux_bad_intr;
+ iunit[i] = i;
+ intpri[i] = SPL0;
+ }
+ }
+
+ form_pic_mask ();
+
+ /*
+ * Enable interrupts.
+ */
+ (void) spl0 ();
+
+ /*
+ * Check if the machine has an EISA bus.
+ */
+ p = (char *) 0x0FFFD9;
+ if (*p++ == 'E' && *p++ == 'I' && *p++ == 'S' && *p == 'A')
+ EISA_bus = 1;
+
+ /*
+ * Permanently allocate standard device ports.
+ */
+ request_region (0x00, 0x20, "dma1");
+ request_region (0x20, 0x20, "pic1");
+ request_region (0x40, 0x20, "timer");
+ request_region (0x70, 0x10, "rtc");
+ request_region (0x80, 0x20, "dma page reg");
+ request_region (0xa0, 0x20, "pic2");
+ request_region (0xc0, 0x20, "dma2");
+ request_region (0xf0, 0x10, "npu");
+}
+
+void
+restore_IRQ (void)
+{
+ /*
+ * Disable interrupts.
+ */
+ (void) splhigh ();
+
+ /*
+ * Restore clock interrupt handler.
+ */
+ ivect[0] = old_clock_handler;
+ intpri[0] = old_clock_pri;
+ form_pic_mask ();
+}
+
diff --git a/linux/dev/arch/i386/kernel/setup.c b/linux/dev/arch/i386/kernel/setup.c
new file mode 100644
index 0000000..7fba6d8
--- /dev/null
+++ b/linux/dev/arch/i386/kernel/setup.c
@@ -0,0 +1,15 @@
+#include <device-drivers.h>
+
+char x86 =
+#if defined(CONFIG_M386)
+3;
+#elif defined(CONFIG_M486)
+4;
+#elif defined(CONFIG_M586)
+5;
+#elif defined(CONFIG_M686)
+6;
+#else
+#error "CPU type is undefined!"
+#endif
+