summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Zammit <damien@zamaudio.com>2020-07-09 23:11:44 +0200
committerSamuel Thibault <samuel.thibault@ens-lyon.org>2020-07-09 23:44:05 +0200
commite85ae9d7befbfe7a79235e68fb0423d40b6baf6c (patch)
tree85f361a10d29ed7dc57d12a8d694687bfc8bd063
parent6cd23e1aa4f3557260e0cf75a2de2c142f8d8f48 (diff)
Fix RPC interface
-rw-r--r--device/dev_hdr.h9
-rw-r--r--device/ds_routines.c80
-rw-r--r--device/ds_routines.h3
-rw-r--r--device/intr.c183
-rw-r--r--device/intr.h52
-rw-r--r--doc/mach.texi22
-rw-r--r--i386/Makefrag.am2
-rw-r--r--i386/i386/irq.c66
-rw-r--r--i386/i386/irq.h27
-rw-r--r--i386/i386/pic.c72
-rw-r--r--i386/i386/pic.h2
-rw-r--r--i386/i386at/conf.c8
-rw-r--r--include/device/device.defs22
-rw-r--r--include/device/notify.defs2
-rw-r--r--include/device/notify.h2
-rw-r--r--linux/dev/arch/i386/kernel/irq.c90
-rw-r--r--linux/dev/arch/i386/kernel/irq.c.orig732
17 files changed, 1107 insertions, 267 deletions
diff --git a/device/dev_hdr.h b/device/dev_hdr.h
index ad98e0bb..4bd12c1c 100644
--- a/device/dev_hdr.h
+++ b/device/dev_hdr.h
@@ -146,4 +146,13 @@ extern void dev_set_indirection(
dev_ops_t ops,
int unit);
+/*
+ * compare device name
+ */
+extern boolean_t __attribute__ ((pure))
+name_equal(
+ const char *src,
+ int len,
+ const char *target);
+
#endif /* _DEVICE_DEV_HDR_H_ */
diff --git a/device/ds_routines.c b/device/ds_routines.c
index 1cc38f98..af9c7617 100644
--- a/device/ds_routines.c
+++ b/device/ds_routines.c
@@ -322,44 +322,55 @@ ds_device_map (device_t dev, vm_prot_t prot, vm_offset_t offset,
/* TODO: missing deregister support */
io_return_t
-ds_device_intr_register (ipc_port_t master_port, int line,
- int id, int flags, ipc_port_t receive_port)
+ds_device_intr_register (device_t dev, int id,
+ int flags, ipc_port_t receive_port)
{
-#ifdef MACH_XEN
- return D_INVALID_OPERATION;
-#else /* MACH_XEN */
- io_return_t ret;
+ kern_return_t err;
+ mach_device_t mdev = dev->emul_data;
- /* Open must be called on the master device port. */
- if (master_port != master_device_port)
+ /* Refuse if device is dead or not completely open. */
+ if (dev == DEVICE_NULL)
+ return D_NO_SUCH_DEVICE;
+
+ /* No flag is defined for now */
+ if (flags != 0)
return D_INVALID_OPERATION;
- /* XXX: move to arch-specific */
- if (line < 0 || line >= 16)
+ /* Must be called on the irq device only */
+ if (! name_equal(mdev->dev_ops->d_name, 3, "irq"))
return D_INVALID_OPERATION;
- user_intr_t *user_intr = insert_intr_entry (line, receive_port);
- if (!user_intr)
+ user_intr_t *e = insert_intr_entry (&irqtab, id, receive_port);
+ if (!e)
return D_NO_MEMORY;
+
// TODO The original port should be replaced
// when the same device driver calls it again,
// in order to handle the case that the device driver crashes and restarts.
- ret = install_user_intr_handler (line, flags, user_intr);
-
- if (ret == 0)
- {
- /* If the port is installed successfully, increase its reference by 1.
- * Thus, the port won't be destroyed after its task is terminated. */
- ip_reference (receive_port);
-
- /* For now netdde calls device_intr_enable once after registration. Assume
- * it does so for now. When we move to IRQ acknowledgment convention we will
- * change this. */
- __disable_irq (line);
- }
-
- return ret;
-#endif /* MACH_XEN */
+ err = install_user_intr_handler (&irqtab, id, flags, e);
+ if (err == D_SUCCESS)
+ {
+ /* If the port is installed successfully, increase its reference by 1.
+ * Thus, the port won't be destroyed after its task is terminated. */
+ ip_reference (receive_port);
+ }
+ return err;
+}
+
+kern_return_t
+ds_device_intr_ack (device_t dev, ipc_port_t receive_port)
+{
+ mach_device_t mdev = dev->emul_data;
+
+ /* Refuse if device is dead or not completely open. */
+ if (dev == DEVICE_NULL)
+ return D_NO_SUCH_DEVICE;
+
+ /* Must be called on the irq device only */
+ if (! name_equal(mdev->dev_ops->d_name, 3, "irq"))
+ return D_INVALID_OPERATION;
+
+ return irq_acknowledge(receive_port);
}
boolean_t
@@ -1842,19 +1853,6 @@ device_writev_trap (mach_device_t device, dev_mode_t mode,
return (result);
}
-kern_return_t
-ds_device_intr_enable(ipc_port_t master_port, int line, char status)
-{
-#ifdef MACH_XEN
- return D_INVALID_OPERATION;
-#else /* MACH_XEN */
- if (master_port != master_device_port)
- return D_INVALID_OPERATION;
-
- return user_intr_enable(line, status);
-#endif /* MACH_XEN */
-}
-
struct device_emulation_ops mach_device_emulation_ops =
{
(void*) mach_device_reference,
diff --git a/device/ds_routines.h b/device/ds_routines.h
index e9f115fc..c0543cbc 100644
--- a/device/ds_routines.h
+++ b/device/ds_routines.h
@@ -83,7 +83,4 @@ io_return_t ds_device_writev_trap(
io_buf_vec_t *iovec,
vm_size_t count);
-/* XXX arch-specific */
-extern ipc_port_t intr_rcv_ports[16];
-
#endif /* DS_ROUTINES_H */
diff --git a/device/intr.c b/device/intr.c
index bbbdc92d..56f0c32c 100644
--- a/device/intr.c
+++ b/device/intr.c
@@ -13,121 +13,98 @@
*/
#include <device/intr.h>
-#include <device/ds_routines.h>
-#include <kern/queue.h>
+#include <device/device_types.h>
+#include <device/device_port.h>
+#include <device/notify.h>
#include <kern/printf.h>
#include <machine/spl.h>
+#include <machine/irq.h>
+#include <ipc/ipc_space.h>
#ifndef MACH_XEN
-static boolean_t deliver_intr (int line, ipc_port_t dest_port);
+queue_head_t main_intr_queue;
+static boolean_t deliver_intr (int id, ipc_port_t dst_port);
-static queue_head_t intr_queue;
-/* The total number of unprocessed interrupts. */
-static int tot_num_intr;
-
-static struct intr_entry *
-search_intr (int line, ipc_port_t dest)
+static user_intr_t *
+search_intr (struct irqdev *dev, ipc_port_t dst_port)
{
- struct intr_entry *e;
- queue_iterate (&intr_queue, e, struct intr_entry *, chain)
+ user_intr_t *e;
+ queue_iterate (dev->intr_queue, e, user_intr_t *, chain)
{
- if (e->dest == dest && e->line == line)
+ if (e->dst_port == dst_port)
return e;
}
return NULL;
}
-static struct intr_entry *
-search_intr_line (int line)
+kern_return_t
+irq_acknowledge (ipc_port_t receive_port)
{
- struct intr_entry *e;
- queue_iterate (&intr_queue, e, struct intr_entry *, chain)
- {
- if (e->line == line &&
- (e->dest != MACH_PORT_NULL
- && e->dest->ip_references != 1
- && e->unacked_interrupts))
- return e;
- }
- return NULL;
-}
-
-kern_return_t user_intr_enable (int line, char status)
-{
- struct intr_entry *e;
- kern_return_t ret = D_SUCCESS;
+ user_intr_t *e;
+ kern_return_t ret;
spl_t s = splhigh ();
- /* FIXME: Use search_intr instead once we get the delivery port from ds_device_intr_enable, and get rid of search_intr_line */
- e = search_intr_line (line);
+ e = search_intr (&irqtab, receive_port);
if (!e)
- printf("didn't find user intr for interrupt %d!?\n", line);
- else if (status)
- {
- if (!e->unacked_interrupts)
- ret = D_INVALID_OPERATION;
- else
- e->unacked_interrupts--;
- }
+ printf("didn't find user intr for interrupt !?\n");
else
- {
- e->unacked_interrupts++;
- if (!e->unacked_interrupts)
{
- ret = D_INVALID_OPERATION;
- e->unacked_interrupts--;
+ if (!e->n_unacked)
+ ret = D_INVALID_OPERATION;
+ else
+ e->n_unacked--;
}
- }
splx (s);
if (ret)
return ret;
- if (status)
- /* TODO: better name for generic-to-arch-specific call */
- __enable_irq (line);
- else
- __disable_irq (line);
+ if (irqtab.irqdev_ack)
+ (*(irqtab.irqdev_ack)) (&irqtab, e->id);
+
+ __enable_irq (irqtab.irq[e->id]);
+
return D_SUCCESS;
}
/* This function can only be used in the interrupt handler. */
static void
-queue_intr (int line, user_intr_t *e)
+queue_intr (struct irqdev *dev, int id, user_intr_t *e)
{
/* Until userland has handled the IRQ in the driver, we have to keep it
* disabled. Level-triggered interrupts would keep raising otherwise. */
- __disable_irq (line);
+ __disable_irq (dev->irq[id]);
spl_t s = splhigh ();
- e->unacked_interrupts++;
+ e->n_unacked++;
e->interrupts++;
- tot_num_intr++;
+ dev->tot_num_intr++;
splx (s);
thread_wakeup ((event_t) &intr_thread);
}
-int deliver_user_intr (int line, user_intr_t *intr)
+int
+deliver_user_intr (struct irqdev *dev, int id, user_intr_t *e)
{
/* The reference of the port was increased
* when the port was installed.
* If the reference is 1, it means the port should
* have been destroyed and I destroy it now. */
- if (intr->dest
- && intr->dest->ip_references == 1)
+ if (e->dst_port
+ && e->dst_port->ip_references == 1)
{
- printf ("irq handler %d: release a dead delivery port %p entry %p\n", line, intr->dest, intr);
- ipc_port_release (intr->dest);
- intr->dest = MACH_PORT_NULL;
+ printf ("irq handler [%d]: release a dead delivery port %p entry %p\n", id, e->dst_port, e);
+ ipc_port_release (e->dst_port);
+ e->dst_port = MACH_PORT_NULL;
thread_wakeup ((event_t) &intr_thread);
return 0;
}
else
{
- queue_intr (line, intr);
+ queue_intr (dev, id, e);
return 1;
}
}
@@ -136,37 +113,32 @@ int deliver_user_intr (int line, user_intr_t *intr)
* This entry exists in the queue until
* the corresponding interrupt port is removed.*/
user_intr_t *
-insert_intr_entry (int line, ipc_port_t dest)
+insert_intr_entry (struct irqdev *dev, int id, ipc_port_t dst_port)
{
- struct intr_entry *e, *new, *ret;
+ user_intr_t *e, *new, *ret;
int free = 0;
- new = (struct intr_entry *) kalloc (sizeof (*new));
+ new = (user_intr_t *) kalloc (sizeof (*new));
if (new == NULL)
return NULL;
/* check whether the intr entry has been in the queue. */
spl_t s = splhigh ();
- e = search_intr (line, dest);
+ e = search_intr (dev, dst_port);
if (e)
{
- printf ("the interrupt entry for line %d and port %p has already been inserted\n", line, dest);
+ printf ("the interrupt entry for irq[%d] and port %p has already been inserted\n", id, dst_port);
free = 1;
ret = NULL;
goto out;
}
- printf("irq handler %d: new delivery port %p entry %p\n", line, dest, new);
+ printf("irq handler [%d]: new delivery port %p entry %p\n", id, dst_port, new);
ret = new;
- new->line = line;
- new->dest = dest;
+ new->id = id;
+ new->dst_port = dst_port;
new->interrupts = 0;
- /* For now netdde calls device_intr_enable once after registration. Assume
- * it does so for now. When we move to IRQ acknowledgment convention we will
- * change this. */
- new->unacked_interrupts = 1;
-
- queue_enter (&intr_queue, new, struct intr_entry *, chain);
+ queue_enter (dev->intr_queue, new, user_intr_t *, chain);
out:
splx (s);
if (free)
@@ -177,10 +149,10 @@ out:
void
intr_thread (void)
{
- struct intr_entry *e;
- int line;
- ipc_port_t dest;
- queue_init (&intr_queue);
+ user_intr_t *e;
+ int id;
+ ipc_port_t dst_port;
+ queue_init (&main_intr_queue);
for (;;)
{
@@ -190,11 +162,11 @@ intr_thread (void)
spl_t s = splhigh ();
/* Check for aborted processes */
- queue_iterate (&intr_queue, e, struct intr_entry *, chain)
+ queue_iterate (&main_intr_queue, e, user_intr_t *, chain)
{
- if ((!e->dest || e->dest->ip_references == 1) && e->unacked_interrupts)
+ if ((!e->dst_port || e->dst_port->ip_references == 1) && e->n_unacked)
{
- printf ("irq handler %d: release dead delivery %d unacked irqs port %p entry %p\n", e->line, e->unacked_interrupts, e->dest, e);
+ printf ("irq handler [%d]: release dead delivery %d unacked irqs port %p entry %p\n", e->id, e->n_unacked, e->dst_port, e);
/* The reference of the port was increased
* when the port was installed.
* If the reference is 1, it means the port should
@@ -202,24 +174,24 @@ intr_thread (void)
* handling can trigger, and we will cleanup later after the Linux
* handler is cleared. */
/* TODO: rather immediately remove from Linux handler */
- while (e->unacked_interrupts)
+ while (e->n_unacked)
{
- __enable_irq(e->line);
- e->unacked_interrupts--;
+ __enable_irq (irqtab.irq[e->id]);
+ e->n_unacked--;
}
}
}
/* Now check for interrupts */
- while (tot_num_intr)
+ while (irqtab.tot_num_intr)
{
int del = 0;
- queue_iterate (&intr_queue, e, struct intr_entry *, chain)
+ queue_iterate (&main_intr_queue, e, user_intr_t *, chain)
{
/* if an entry doesn't have dest port,
* we should remove it. */
- if (e->dest == MACH_PORT_NULL)
+ if (e->dst_port == MACH_PORT_NULL)
{
clear_wait (current_thread (), 0, 0);
del = 1;
@@ -229,13 +201,13 @@ intr_thread (void)
if (e->interrupts)
{
clear_wait (current_thread (), 0, 0);
- line = e->line;
- dest = e->dest;
+ id = e->id;
+ dst_port = e->dst_port;
e->interrupts--;
- tot_num_intr--;
+ irqtab.tot_num_intr--;
splx (s);
- deliver_intr (line, dest);
+ deliver_intr (id, dst_port);
s = splhigh ();
}
}
@@ -243,16 +215,16 @@ intr_thread (void)
/* remove the entry without dest port from the queue and free it. */
if (del)
{
- assert (!queue_empty (&intr_queue));
- queue_remove (&intr_queue, e, struct intr_entry *, chain);
- if (e->unacked_interrupts)
- printf("irq handler %d: still %d unacked irqs in entry %p\n", e->line, e->unacked_interrupts, e);
- while (e->unacked_interrupts)
+ assert (!queue_empty (&main_intr_queue));
+ queue_remove (&main_intr_queue, e, user_intr_t *, chain);
+ if (e->n_unacked)
+ printf("irq handler [%d]: still %d unacked irqs in entry %p\n", e->id, e->n_unacked, e);
+ while (e->n_unacked)
{
- __enable_irq(e->line);
- e->unacked_interrupts--;
+ __enable_irq (irqtab.irq[e->id]);
+ e->n_unacked--;
}
- printf("irq handler %d: removed entry %p\n", e->line, e);
+ printf("irq handler [%d]: removed entry %p\n", e->id, e);
splx (s);
kfree ((vm_offset_t) e, sizeof (*e));
s = splhigh ();
@@ -264,11 +236,11 @@ intr_thread (void)
}
static boolean_t
-deliver_intr (int line, ipc_port_t dest_port)
+deliver_intr (int id, ipc_port_t dst_port)
{
ipc_kmsg_t kmsg;
device_intr_notification_t *n;
- mach_port_t dest = (mach_port_t) dest_port;
+ mach_port_t dest = (mach_port_t) dst_port;
if (dest == MACH_PORT_NULL)
return FALSE;
@@ -299,11 +271,12 @@ deliver_intr (int line, ipc_port_t dest_port)
t->msgt_unused = 0;
n->intr_header.msgh_remote_port = dest;
- n->line = line;
+ n->id = id;
- ipc_port_copy_send (dest_port);
+ ipc_port_copy_send (dst_port);
ipc_mqueue_send_always(kmsg);
return TRUE;
}
+
#endif /* MACH_XEN */
diff --git a/device/intr.h b/device/intr.h
index df282c05..cd3e0bce 100644
--- a/device/intr.h
+++ b/device/intr.h
@@ -15,36 +15,48 @@
#ifndef __INTR_H__
#define __INTR_H__
-#include <device/device_types.h>
+#ifndef MACH_XEN
+
+#include <mach/kern_return.h>
+#include <mach/port.h>
#include <kern/queue.h>
-#include <device/notify.h>
+#include <ipc/ipc_port.h>
+#include <device/conf.h>
+
+#define DEVICE_NOTIFY_MSGH_SEQNO 0
+
+#include <sys/types.h>
-typedef struct intr_entry
-{
+struct irqdev;
+#include <machine/irq.h>
+
+typedef struct {
queue_chain_t chain;
- ipc_port_t dest;
- int line;
- int interrupts; /* The number of interrupts occur since last run of intr_thread. */
- int unacked_interrupts; /* Number of times irqs were disabled for this */
+ int interrupts; /* Number of interrupts occurred since last run of intr_thread */
+ int n_unacked; /* Number of times irqs were disabled for this */
+ ipc_port_t dst_port; /* Notification port */
+ int id; /* Mapping to machine dependent irq_t array elem */
} user_intr_t;
-#define DEVICE_NOTIFY_MSGH_SEQNO 0
-
-int install_user_intr_handler (unsigned int line,
- unsigned long flags,
- user_intr_t *user_intr);
+struct irqdev {
+ char *name;
+ void (*irqdev_ack)(struct irqdev *dev, int id);
-/* Returns 0 if action should be removed */
-int deliver_user_intr (int line, user_intr_t *intr);
+ queue_head_t *intr_queue;
+ int tot_num_intr; /* Total number of unprocessed interrupts */
-user_intr_t *insert_intr_entry (int line, ipc_port_t dest);
+ /* Machine dependent */
+ irq_t irq[NINTR];
+};
-/* TODO: should rather take delivery port */
-kern_return_t user_intr_enable (int line, char status);
+extern queue_head_t main_intr_queue;
+extern int install_user_intr_handler (struct irqdev *dev, int id, unsigned long flags, user_intr_t *e);
+extern int deliver_user_intr (struct irqdev *dev, int id, user_intr_t *e);
+extern user_intr_t *insert_intr_entry (struct irqdev *dev, int id, ipc_port_t receive_port);
void intr_thread (void);
+kern_return_t irq_acknowledge (ipc_port_t receive_port);
-void __disable_irq(unsigned int);
-void __enable_irq(unsigned int);
+#endif /* MACH_XEN */
#endif
diff --git a/doc/mach.texi b/doc/mach.texi
index 91ec96ee..11b0a066 100644
--- a/doc/mach.texi
+++ b/doc/mach.texi
@@ -286,6 +286,7 @@ Device Interface
* Device Map:: Mapping devices into virtual memory.
* Device Status:: Querying and manipulating a device.
* Device Filter:: Filtering packets arriving on a device.
+* Device Interrupt:: Getting hardware interrupt notifications.
Kernel Debugger
@@ -6305,6 +6306,7 @@ All constants and functions in this chapter are defined in
* Device Map:: Mapping devices into virtual memory.
* Device Status:: Querying and manipulating a device.
* Device Filter:: Filtering packets arriving on a device.
+* Device Interrupt:: Getting hardware interrupt notifications.
@end menu
@@ -6699,6 +6701,26 @@ a device port or the device is dead or not completely open.
@end deftypefun
+@node Device Interrupt
+@section Device Interrupt
+
+@deftypefun kern_return_t device_intr_register (@w{device_t @var{device}}, @w{int @var{id}}, @w{int @var{flags}}, @w{mach_port_t @var{receive_port}})
+The function @code{device_intr_register} registers for receiving hardware
+interrupt events through @var{device_intr_notify} notifications. The hardware
+interrupt identifier is specified by @var{id}. @var{flags} must be set to 0. The
+notifications will be sent on the @var{receive_port} send right.
+@code{device_intr_register} is only available on the dedicated @code{irq} device.
+@end deftypefun
+
+@deftypefun kern_return_t device_intr_ack (@w{device_t @var{device}}, @w{mach_port_t @var{receive_port}})
+On a hardware interrupt, the kernel disables the interrupt line before sending
+notifications. To prevent from interrupt losses, the interrupt is kept disabled
+until @code{device_intr_ack} is called to acknowledge the interrupt.
+@var{receive_port} is the send right on which the interrupt notification was
+received.
+@end deftypefun
+
+
@node Kernel Debugger
@chapter Kernel Debugger
diff --git a/i386/Makefrag.am b/i386/Makefrag.am
index f38c0785..59571416 100644
--- a/i386/Makefrag.am
+++ b/i386/Makefrag.am
@@ -102,6 +102,8 @@ libkernel_a_SOURCES += \
i386/i386/io_perm.c \
i386/i386/io_perm.h \
i386/i386/ipl.h \
+ i386/i386/irq.c \
+ i386/i386/irq.h \
i386/i386/ktss.c \
i386/i386/ktss.h \
i386/i386/kttd_interface.c \
diff --git a/i386/i386/irq.c b/i386/i386/irq.c
new file mode 100644
index 00000000..c65d2ea2
--- /dev/null
+++ b/i386/i386/irq.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 1995 Shantanu Goel
+ * Copyright (C) 2020 Free Software Foundation, Inc
+ *
+ * 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.
+ */
+
+#include <i386/irq.h>
+#include <device/intr.h>
+#include <mach/kern_return.h>
+#include <kern/queue.h>
+#include <machine/machspl.h>
+
+extern queue_head_t main_intr_queue;
+
+static void
+irq_eoi (struct irqdev *dev, int id)
+{
+ /* TODO EOI(dev->irq[id]) */
+}
+
+static unsigned int ndisabled_irq[NINTR];
+
+void
+__disable_irq (irq_t irq_nr)
+{
+ assert (irq_nr < NINTR);
+
+ spl_t s = splhigh();
+ ndisabled_irq[irq_nr]++;
+ assert (ndisabled_irq[irq_nr] > 0);
+ if (ndisabled_irq[irq_nr] == 1)
+ mask_irq (irq_nr);
+ splx(s);
+}
+
+void
+__enable_irq (irq_t irq_nr)
+{
+ assert (irq_nr < NINTR);
+
+ spl_t s = splhigh();
+ assert (ndisabled_irq[irq_nr] > 0);
+ ndisabled_irq[irq_nr]--;
+ if (ndisabled_irq[irq_nr] == 0)
+ unmask_irq (irq_nr);
+ splx(s);
+}
+
+struct irqdev irqtab = {
+ "irq", irq_eoi, &main_intr_queue, 0,
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+};
+
diff --git a/i386/i386/irq.h b/i386/i386/irq.h
new file mode 100644
index 00000000..d48a8e92
--- /dev/null
+++ b/i386/i386/irq.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 Free Software Foundation, Inc.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * THE FREE SOFTWARE FOUNDATION ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. THE FREE SOFTWARE FOUNDATION DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ */
+
+#ifndef _I386_IRQ_H
+#define _I386_IRQ_H
+
+#include <i386/pic.h>
+
+typedef unsigned int irq_t;
+
+void __enable_irq (irq_t irq);
+void __disable_irq (irq_t irq);
+
+extern struct irqdev irqtab;
+
+#endif
diff --git a/i386/i386/pic.c b/i386/i386/pic.c
index 0feebc6f..62ed9ed1 100644
--- a/i386/i386/pic.c
+++ b/i386/i386/pic.c
@@ -49,6 +49,24 @@ OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION
WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+/*
+ * 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.
+ */
+
#include <sys/types.h>
#include <kern/printf.h>
#include <i386/ipl.h>
@@ -185,3 +203,57 @@ intnull(int unit_dev)
}
}
+
+/*
+ * Mask a PIC IRQ.
+ */
+inline void
+mask_irq (unsigned int irq_nr)
+{
+ int new_pic_mask = curr_pic_mask | 1 << irq_nr;
+
+ if (curr_pic_mask != new_pic_mask)
+ {
+ curr_pic_mask = new_pic_mask;
+ if (irq_nr < 8)
+ {
+ outb (PIC_MASTER_OCW, curr_pic_mask & 0xff);
+ }
+ else
+ {
+ outb (PIC_SLAVE_OCW, curr_pic_mask >> 8);
+ }
+ }
+}
+
+/*
+ * Unmask a PIC IRQ.
+ */
+inline void
+unmask_irq (unsigned int irq_nr)
+{
+ int mask;
+ int new_pic_mask;
+
+ mask = 1 << irq_nr;
+ if (irq_nr >= 8)
+ {
+ mask |= 1 << 2;
+ }
+
+ new_pic_mask = curr_pic_mask & ~mask;
+
+ if (curr_pic_mask != new_pic_mask)
+ {
+ curr_pic_mask = new_pic_mask;
+ if (irq_nr < 8)
+ {
+ outb (PIC_MASTER_OCW, curr_pic_mask & 0xff);
+ }
+ else
+ {
+ outb (PIC_SLAVE_OCW, curr_pic_mask >> 8);
+ }
+ }
+}
+
diff --git a/i386/i386/pic.h b/i386/i386/pic.h
index f492de5e..6434bf08 100644
--- a/i386/i386/pic.h
+++ b/i386/i386/pic.h
@@ -180,6 +180,8 @@ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
extern void picinit (void);
extern int curr_pic_mask;
extern void intnull(int unit);
+extern inline void mask_irq (unsigned int irq_nr);
+extern inline void unmask_irq (unsigned int irq_nr);
#endif /* __ASSEMBLER__ */
#endif /* _I386_PIC_H_ */
diff --git a/i386/i386at/conf.c b/i386/i386at/conf.c
index fe7c7c09..ca5d0dfb 100644
--- a/i386/i386at/conf.c
+++ b/i386/i386at/conf.c
@@ -68,6 +68,9 @@
#define hypcnname "hyp"
#endif /* MACH_HYP */
+#include <device/intr.h>
+#define irqname "irq"
+
/*
* List of devices - console must be at slot 0
*/
@@ -149,6 +152,11 @@ struct dev_ops dev_name_list[] =
nodev },
#endif /* MACH_HYP */
+ { irqname, nulldev_open, nulldev_close, nulldev_read,
+ nulldev_write,nulldev_getstat,nulldev_setstat, nomap,
+ nodev, nulldev, nulldev_portdeath,0,
+ nodev },
+
};
int dev_name_count = sizeof(dev_name_list)/sizeof(dev_name_list[0]);
diff --git a/include/device/device.defs b/include/device/device.defs
index dca1be4e..ec4b5bf8 100644
--- a/include/device/device.defs
+++ b/include/device/device.defs
@@ -143,22 +143,20 @@ routine device_set_filter(
);
routine device_intr_register(
- master_port : mach_port_t;
- in line : int;
+ device : device_t;
in id : int;
in flags : int;
in receive_port : mach_port_send_t
);
/*
- * enable/disable the specified line.
+ * Acknowledge the specified interrupt notification.
*/
-/* XXX: Naming a function taht can disable something "xxx_enable" is confusing. */
-/* Is the disable part actually used at all? AIUI, the kernel IRQ handler
-should always disable the line; and the userspace driver only has to
-reenable it, after acknowledging and handling the interrupt...
-*/
-routine device_intr_enable(
- master_port : mach_port_t;
- line : int;
- status : char);
+/*
+ * When an IRQ happens and an intr notification is thus sent, the IRQ line
+ * is kept disabled until the notification is acknowledged with this RPC
+ */
+routine device_intr_ack(
+ device : device_t;
+ in receive_port : mach_port_send_t);
+
diff --git a/include/device/notify.defs b/include/device/notify.defs
index ea374d26..7919b339 100644
--- a/include/device/notify.defs
+++ b/include/device/notify.defs
@@ -33,4 +33,4 @@ serverdemux device_intr_notify_server;
simpleroutine device_intr_notify(
notify : notify_port_t;
- name : int);
+ id : int);
diff --git a/include/device/notify.h b/include/device/notify.h
index b6907b03..addf9114 100644
--- a/include/device/notify.h
+++ b/include/device/notify.h
@@ -26,7 +26,7 @@ typedef struct
{
mach_msg_header_t intr_header;
mach_msg_type_t intr_type;
- int line;
+ int id;
} device_intr_notification_t;
#define DEVICE_INTR_NOTIFY 100
diff --git a/linux/dev/arch/i386/kernel/irq.c b/linux/dev/arch/i386/kernel/irq.c
index bc752013..1e911f33 100644
--- a/linux/dev/arch/i386/kernel/irq.c
+++ b/linux/dev/arch/i386/kernel/irq.c
@@ -90,49 +90,6 @@ static struct linux_action *irq_action[16] =
};
/*
- * Mask an IRQ.
- */
-static inline void
-mask_irq (unsigned int irq_nr)
-{
- int new_pic_mask = curr_pic_mask | 1 << irq_nr;
-
- if (curr_pic_mask != new_pic_mask)
- {
- curr_pic_mask = new_pic_mask;
- 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;
- int new_pic_mask;
-
- mask = 1 << irq_nr;
- if (irq_nr >= 8)
- mask |= 1 << 2;
-
- new_pic_mask = curr_pic_mask & ~mask;
-
- if (curr_pic_mask != new_pic_mask)
- {
- curr_pic_mask = new_pic_mask;
- if (irq_nr < 8)
- outb (curr_pic_mask & 0xff, PIC_MASTER_OCW);
- else
- outb (curr_pic_mask >> 8, PIC_SLAVE_OCW);
- }
-}
-
-/*
* Generic interrupt handler for Linux devices.
* Set up a fake `struct pt_regs' then call the real handler.
*/
@@ -157,7 +114,7 @@ linux_intr (int irq)
// the current device. But I don't do it for now.
if (action->user_intr)
{
- if (!deliver_user_intr(irq, action->user_intr))
+ if (!deliver_user_intr(&irqtab, irq, action->user_intr))
{
*prev = action->next;
linux_kfree(action);
@@ -184,43 +141,6 @@ linux_intr (int irq)
intr_count--;
}
-/* Count how many subsystems requested to disable each IRQ */
-static unsigned ndisabled_irq[NR_IRQS];
-
-/* These disable/enable IRQs for real after counting how many subsystems
- * requested that */
-void
-__disable_irq (unsigned int irq_nr)
-{
- unsigned long flags;
-
- assert (irq_nr < NR_IRQS);
-
- save_flags (flags);
- cli ();
- ndisabled_irq[irq_nr]++;
- assert (ndisabled_irq[irq_nr] > 0);
- if (ndisabled_irq[irq_nr] == 1)
- 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 ();
- assert (ndisabled_irq[irq_nr] > 0);
- ndisabled_irq[irq_nr]--;
- if (ndisabled_irq[irq_nr] == 0)
- unmask_irq (irq_nr);
- restore_flags (flags);
-}
-
/* IRQ mask according to Linux drivers */
static unsigned linux_pic_mask;
@@ -300,13 +220,15 @@ setup_x86_irq (int irq, struct linux_action *new)
}
int
-install_user_intr_handler (unsigned int irq, unsigned long flags,
+install_user_intr_handler (struct irqdev *dev, int id, unsigned long flags,
user_intr_t *user_intr)
{
struct linux_action *action;
struct linux_action *old;
int retval;
+ unsigned int irq = dev->irq[id];
+
assert (irq < 16);
/* Test whether the irq handler has been set */
@@ -314,7 +236,7 @@ install_user_intr_handler (unsigned int irq, unsigned long flags,
old = irq_action[irq];
while (old)
{
- if (old->user_intr && old->user_intr->dest == user_intr->dest)
+ if (old->user_intr && old->user_intr->dst_port == user_intr->dst_port)
{
printk ("The interrupt handler has already been installed on line %d", irq);
return linux_to_mach_error (-EAGAIN);
@@ -334,7 +256,7 @@ install_user_intr_handler (unsigned int irq, unsigned long flags,
action->handler = NULL;
action->next = NULL;
action->dev_id = NULL;
- action->flags = flags;
+ action->flags = SA_SHIRQ;
action->user_intr = user_intr;
retval = setup_x86_irq (irq, action);
diff --git a/linux/dev/arch/i386/kernel/irq.c.orig b/linux/dev/arch/i386/kernel/irq.c.orig
new file mode 100644
index 00000000..75f8f812
--- /dev/null
+++ b/linux/dev/arch/i386/kernel/irq.c.orig
@@ -0,0 +1,732 @@
+/*
+ * 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>
+#include <asm/hardirq.h>
+
+#include <linux/dev/glue/glue.h>
+#include <machine/machspl.h>
+
+#if 0
+/* XXX: This is the way it's done in linux 2.2. GNU Mach currently uses intr_count. It should be made using local_{bh/irq}_count instead (through hardirq_enter/exit) for SMP support. */
+unsigned int local_bh_count[NR_CPUS];
+unsigned int local_irq_count[NR_CPUS];
+#endif
+
+/*
+ * XXX Move this into more suitable place...
+ * Set if the machine has an EISA bus.
+ */
+int EISA_bus = 0;
+
+/*
+ * Flag indicating an interrupt is being handled.
+ */
+unsigned int 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
+};
+
+/*
+ * Generic interrupt handler for Linux devices.
+ * Set up a fake `struct pt_regs' then call the real handler.
+ */
+static void
+linux_intr (int irq)
+{
+ struct pt_regs regs;
+ struct linux_action *action = *(irq_action + irq);
+ unsigned long flags;
+
+ kstat.interrupts[irq]++;
+ intr_count++;
+
+ save_flags (flags);
+ if (action && (action->flags & SA_INTERRUPT))
+ cli ();
+
+ while (action)
+ {
+ action->handler (irq, action->dev_id, &regs);
+ action = action->next;
+ }
+
+ restore_flags (flags);
+
+ intr_count--;
+}
+
+/*
+ * Mask an IRQ.
+ */
+static inline void
+mask_irq (unsigned int irq_nr)
+{
+ int new_pic_mask = curr_pic_mask | 1 << irq_nr;
+
+ if (curr_pic_mask != new_pic_mask)
+ {
+ curr_pic_mask = new_pic_mask;
+ 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;
+ int new_pic_mask;
+
+ mask = 1 << irq_nr;
+ if (irq_nr >= 8)
+ mask |= 1 << 2;
+
+ new_pic_mask = curr_pic_mask & ~mask;
+
+ if (curr_pic_mask != new_pic_mask)
+ {
+ curr_pic_mask = new_pic_mask;
+ 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);
+}
+
+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 (-EBUSY);
+
+ /* Can't share interrupts unless both are 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;
+ }
+
+ save_flags (flags);
+ cli ();
+ *p = new;
+
+ if (!shared)
+ {
+ ivect[irq] = linux_intr;
+ iunit[irq] = irq;
+ 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 -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 -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] = intnull;
+ iunit[irq] = irq;
+ }
+ 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] == intnull)
+ {
+ 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] == intnull)
+ {
+ disable_irq (i);
+ }
+ }
+
+ /*
+ * 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 reserved_mach_handler (int line, void *cookie, struct pt_regs *regs)
+{
+ /* These interrupts are actually handled in Mach. */
+ assert (! "reached");
+}
+
+static const struct linux_action reserved_mach =
+ {
+ reserved_mach_handler, NULL, NULL, 0
+ };
+
+static void
+reserve_mach_irqs (void)
+{
+ unsigned int i;
+
+ for (i = 0; i < 16; i++)
+ {
+ if (ivect[i] != intnull)
+ /* This dummy action does not specify SA_SHIRQ, so
+ setup_x86_irq will not try to add a handler to this
+ slot. Therefore, the cast is safe. */
+ irq_action[i] = (struct linux_action *) &reserved_mach;
+ }
+}
+
+#ifdef __SMP__
+unsigned char global_irq_holder = NO_PROC_ID;
+unsigned volatile int global_irq_lock;
+atomic_t global_irq_count;
+
+atomic_t global_bh_count;
+atomic_t global_bh_lock;
+
+/*
+ * "global_cli()" is a special case, in that it can hold the
+ * interrupts disabled for a longish time, and also because
+ * we may be doing TLB invalidates when holding the global
+ * IRQ lock for historical reasons. Thus we may need to check
+ * SMP invalidate events specially by hand here (but not in
+ * any normal spinlocks)
+ */
+#if 0
+/* XXX: check how Mach handles this */
+static inline void check_smp_invalidate(int cpu)
+{
+ if (test_bit(cpu, &smp_invalidate_needed)) {
+ clear_bit(cpu, &smp_invalidate_needed);
+ local_flush_tlb();
+ }
+}
+#endif
+
+static void show(char * str)
+{
+ int i;
+ unsigned long *stack;
+ int cpu = smp_processor_id();
+
+ printk("\n%s, CPU %d:\n", str, cpu);
+ printk("irq: %d [%d %d]\n",
+ atomic_read(&global_irq_count), local_irq_count[0], local_irq_count[1]);
+ printk("bh: %d [%d %d]\n",
+ atomic_read(&global_bh_count), local_bh_count[0], local_bh_count[1]);
+ stack = (unsigned long *) &stack;
+ for (i = 40; i ; i--) {
+ unsigned long x = *++stack;
+ //if (x > (unsigned long) &get_options && x < (unsigned long) &vsprintf) {
+ printk("<[%08lx]> ", x);
+ //}
+ }
+}
+
+#define MAXCOUNT 100000000
+
+static inline void wait_on_bh(void)
+{
+ int count = MAXCOUNT;
+ do {
+ if (!--count) {
+ show("wait_on_bh");
+ count = ~0;
+ }
+ /* nothing .. wait for the other bh's to go away */
+ } while (atomic_read(&global_bh_count) != 0);
+}
+
+/*
+ * I had a lockup scenario where a tight loop doing
+ * spin_unlock()/spin_lock() on CPU#1 was racing with
+ * spin_lock() on CPU#0. CPU#0 should have noticed spin_unlock(), but
+ * apparently the spin_unlock() information did not make it
+ * through to CPU#0 ... nasty, is this by design, do we have to limit
+ * 'memory update oscillation frequency' artificially like here?
+ *
+ * Such 'high frequency update' races can be avoided by careful design, but
+ * some of our major constructs like spinlocks use similar techniques,
+ * it would be nice to clarify this issue. Set this define to 0 if you
+ * want to check whether your system freezes. I suspect the delay done
+ * by SYNC_OTHER_CORES() is in correlation with 'snooping latency', but
+ * i thought that such things are guaranteed by design, since we use
+ * the 'LOCK' prefix.
+ */
+#define SUSPECTED_CPU_OR_CHIPSET_BUG_WORKAROUND 1
+
+#if SUSPECTED_CPU_OR_CHIPSET_BUG_WORKAROUND
+# define SYNC_OTHER_CORES(x) udelay(x+1)
+#else
+/*
+ * We have to allow irqs to arrive between __sti and __cli
+ */
+# define SYNC_OTHER_CORES(x) __asm__ __volatile__ ("nop")
+#endif
+
+static inline void wait_on_irq(int cpu)
+{
+ int count = MAXCOUNT;
+
+ for (;;) {
+
+ /*
+ * Wait until all interrupts are gone. Wait
+ * for bottom half handlers unless we're
+ * already executing in one..
+ */
+ if (!atomic_read(&global_irq_count)) {
+ if (local_bh_count[cpu] || !atomic_read(&global_bh_count))
+ break;
+ }
+
+ /* Duh, we have to loop. Release the lock to avoid deadlocks */
+ clear_bit(0,&global_irq_lock);
+
+ for (;;) {
+ if (!--count) {
+ show("wait_on_irq");
+ count = ~0;
+ }
+ __sti();
+ SYNC_OTHER_CORES(cpu);
+ __cli();
+ //check_smp_invalidate(cpu);
+ if (atomic_read(&global_irq_count))
+ continue;
+ if (global_irq_lock)
+ continue;
+ if (!local_bh_count[cpu] && atomic_read(&global_bh_count))
+ continue;
+ if (!test_and_set_bit(0,&global_irq_lock))
+ break;
+ }
+ }
+}
+
+/*
+ * This is called when we want to synchronize with
+ * bottom half handlers. We need to wait until
+ * no other CPU is executing any bottom half handler.
+ *
+ * Don't wait if we're already running in an interrupt
+ * context or are inside a bh handler.
+ */
+void synchronize_bh(void)
+{
+ if (atomic_read(&global_bh_count) && !in_interrupt())
+ wait_on_bh();
+}
+
+/*
+ * This is called when we want to synchronize with
+ * interrupts. We may for example tell a device to
+ * stop sending interrupts: but to make sure there
+ * are no interrupts that are executing on another
+ * CPU we need to call this function.
+ */
+void synchronize_irq(void)
+{
+ if (atomic_read(&global_irq_count)) {
+ /* Stupid approach */
+ cli();
+ sti();
+ }
+}
+
+static inline void get_irqlock(int cpu)
+{
+ if (test_and_set_bit(0,&global_irq_lock)) {
+ /* do we already hold the lock? */
+ if ((unsigned char) cpu == global_irq_holder)
+ return;
+ /* Uhhuh.. Somebody else got it. Wait.. */
+ do {
+ do {
+ //check_smp_invalidate(cpu);
+ } while (test_bit(0,&global_irq_lock));
+ } while (test_and_set_bit(0,&global_irq_lock));
+ }
+ /*
+ * We also to make sure that nobody else is running
+ * in an interrupt context.
+ */
+ wait_on_irq(cpu);
+
+ /*
+ * Ok, finally..
+ */
+ global_irq_holder = cpu;
+}
+
+#define EFLAGS_IF_SHIFT 9
+
+/*
+ * A global "cli()" while in an interrupt context
+ * turns into just a local cli(). Interrupts
+ * should use spinlocks for the (very unlikely)
+ * case that they ever want to protect against
+ * each other.
+ *
+ * If we already have local interrupts disabled,
+ * this will not turn a local disable into a
+ * global one (problems with spinlocks: this makes
+ * save_flags+cli+sti usable inside a spinlock).
+ */
+void __global_cli(void)
+{
+ unsigned int flags;
+
+ __save_flags(flags);
+ if (flags & (1 << EFLAGS_IF_SHIFT)) {
+ int cpu = smp_processor_id();
+ __cli();
+ if (!local_irq_count[cpu])
+ get_irqlock(cpu);
+ }
+}
+
+void __global_sti(void)
+{
+ int cpu = smp_processor_id();
+
+ if (!local_irq_count[cpu])
+ release_irqlock(cpu);
+ __sti();
+}
+
+/*
+ * SMP flags value to restore to:
+ * 0 - global cli
+ * 1 - global sti
+ * 2 - local cli
+ * 3 - local sti
+ */
+unsigned long __global_save_flags(void)
+{
+ int retval;
+ int local_enabled;
+ unsigned long flags;
+
+ __save_flags(flags);
+ local_enabled = (flags >> EFLAGS_IF_SHIFT) & 1;
+ /* default to local */
+ retval = 2 + local_enabled;
+
+ /* check for global flags if we're not in an interrupt */
+ if (!local_irq_count[smp_processor_id()]) {
+ if (local_enabled)
+ retval = 1;
+ if (global_irq_holder == (unsigned char) smp_processor_id())
+ retval = 0;
+ }
+ return retval;
+}
+
+void __global_restore_flags(unsigned long flags)
+{
+ switch (flags) {
+ case 0:
+ __global_cli();
+ break;
+ case 1:
+ __global_sti();
+ break;
+ case 2:
+ __cli();
+ break;
+ case 3:
+ __sti();
+ break;
+ default:
+ printk("global_restore_flags: %08lx (%08lx)\n",
+ flags, (&flags)[-1]);
+ }
+}
+
+#endif
+
+static void (*old_clock_handler) ();
+
+void
+init_IRQ (void)
+{
+ 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];
+ ivect[0] = linux_timer_intr;
+
+ reserve_mach_irqs ();
+
+ /*
+ * 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;
+}
+