patch-2.3.20 linux/arch/ppc/amiga/amiints.c
Next file: linux/arch/ppc/amiga/bootinfo.c
Previous file: linux/arch/ppc/amiga/Makefile
Back to the patch index
Back to the overall index
- Lines: 272
- Date:
Thu Oct 7 10:17:08 1999
- Orig file:
v2.3.19/linux/arch/ppc/amiga/amiints.c
- Orig date:
Tue Aug 31 17:29:12 1999
diff -u --recursive --new-file v2.3.19/linux/arch/ppc/amiga/amiints.c linux/arch/ppc/amiga/amiints.c
@@ -1,7 +1,3 @@
-/* Rename a few functions. */
-#define amiga_request_irq request_irq
-#define amiga_free_irq free_irq
-
/*
* linux/arch/m68k/amiga/amiints.c -- Amiga Linux interrupt handling code
*
@@ -23,6 +19,20 @@
* called again.
* The whole interrupt handling for CIAs is moved to cia.c
* /Roman Zippel
+ *
+ * 07/08/99: rewamp of the interrupt handling - we now have two types of
+ * interrupts, normal and fast handlers, fast handlers being
+ * marked with SA_INTERRUPT and runs with all other interrupts
+ * disabled. Normal interrupts disable their own source but
+ * run with all other interrupt sources enabled.
+ * PORTS and EXTER interrupts are always shared even if the
+ * drivers do not explicitly mark this when calling
+ * request_irq which they really should do.
+ * This is similar to the way interrupts are handled on all
+ * other architectures and makes a ton of sense besides
+ * having the advantage of making it easier to share
+ * drivers.
+ * /Jes
*/
#include <linux/config.h>
@@ -90,7 +100,7 @@
} else {
ami_irq_list[i] = new_irq_node();
ami_irq_list[i]->handler = ami_badint;
- ami_irq_list[i]->flags = IRQ_FLG_STD;
+ ami_irq_list[i]->flags = 0;
ami_irq_list[i]->dev_id = NULL;
ami_irq_list[i]->devname = NULL;
ami_irq_list[i]->next = NULL;
@@ -108,7 +118,7 @@
custom.intreq = 0x7fff;
#ifdef CONFIG_APUS
- /* Clear any inter-CPU interrupt requests. Circumvents bug in
+ /* Clear any inter-CPU interupt requests. Circumvents bug in
Blizzard IPL emulation HW (or so it appears). */
APUS_WRITE(APUS_INT_LVL, INTLVL_SETRESET | INTLVL_MASK);
@@ -124,7 +134,7 @@
cia_init_IRQ(&ciab_base);
}
-static inline void amiga_insert_irq(irq_node_t **list, irq_node_t *node)
+static inline int amiga_insert_irq(irq_node_t **list, irq_node_t *node)
{
unsigned long flags;
irq_node_t *cur;
@@ -138,19 +148,18 @@
cur = *list;
- if (node->flags & IRQ_FLG_FAST) {
- node->flags &= ~IRQ_FLG_SLOW;
- while (cur && cur->flags & IRQ_FLG_FAST) {
- list = &cur->next;
- cur = cur->next;
- }
- } else if (node->flags & IRQ_FLG_SLOW) {
- while (cur) {
+ if (node->flags & SA_INTERRUPT) {
+ if (node->flags & SA_SHIRQ)
+ return -EBUSY;
+ /*
+ * There should never be more than one
+ */
+ while (cur && cur->flags & SA_INTERRUPT) {
list = &cur->next;
cur = cur->next;
}
} else {
- while (cur && !(cur->flags & IRQ_FLG_SLOW)) {
+ while (cur) {
list = &cur->next;
cur = cur->next;
}
@@ -160,6 +169,7 @@
*list = node;
restore_flags(flags);
+ return 0;
}
static inline void amiga_delete_irq(irq_node_t **list, void *dev_id)
@@ -189,13 +199,16 @@
* If the addition was successful, it returns 0.
*/
-int amiga_request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
+int amiga_request_irq(unsigned int irq,
+ void (*handler)(int, void *, struct pt_regs *),
unsigned long flags, const char *devname, void *dev_id)
{
irq_node_t *node;
+ int error = 0;
if (irq >= AMI_IRQS) {
- printk ("%s: Unknown IRQ %d from %s\n", __FUNCTION__, irq, devname);
+ printk ("%s: Unknown IRQ %d from %s\n", __FUNCTION__,
+ irq, devname);
return -ENXIO;
}
@@ -211,6 +224,11 @@
return cia_request_irq(&ciaa_base, irq - IRQ_AMIGA_CIAA,
handler, flags, devname, dev_id);
+ /*
+ * IRQ_AMIGA_PORTS & IRQ_AMIGA_EXTER defaults to shared,
+ * we could add a check here for the SA_SHIRQ flag but all drivers
+ * should be aware of sharing anyway.
+ */
if (ami_servers[irq]) {
if (!(node = new_irq_node()))
return -ENOMEM;
@@ -219,20 +237,8 @@
node->dev_id = dev_id;
node->devname = devname;
node->next = NULL;
- amiga_insert_irq(&ami_irq_list[irq], node);
+ error = amiga_insert_irq(&ami_irq_list[irq], node);
} else {
- if (!(ami_irq_list[irq]->flags & IRQ_FLG_STD)) {
- if (ami_irq_list[irq]->flags & IRQ_FLG_LOCK) {
- printk("%s: IRQ %d from %s is not replaceable\n",
- __FUNCTION__, irq, ami_irq_list[irq]->devname);
- return -EBUSY;
- }
- if (!(flags & IRQ_FLG_REPLACE)) {
- printk("%s: %s can't replace IRQ %d from %s\n",
- __FUNCTION__, devname, irq, ami_irq_list[irq]->devname);
- return -EBUSY;
- }
- }
ami_irq_list[irq]->handler = handler;
ami_irq_list[irq]->flags = flags;
ami_irq_list[irq]->dev_id = dev_id;
@@ -243,7 +249,7 @@
if (irq < IRQ_AMIGA_PORTS && !ami_ablecount[irq])
custom.intena = IF_SETCLR | ami_intena_vals[irq];
- return 0;
+ return error;
}
void amiga_free_irq(unsigned int irq, void *dev_id)
@@ -276,7 +282,7 @@
printk("%s: removing probably wrong IRQ %d from %s\n",
__FUNCTION__, irq, ami_irq_list[irq]->devname);
ami_irq_list[irq]->handler = ami_badint;
- ami_irq_list[irq]->flags = IRQ_FLG_STD;
+ ami_irq_list[irq]->flags = 0;
ami_irq_list[irq]->dev_id = NULL;
ami_irq_list[irq]->devname = NULL;
custom.intena = ami_intena_vals[irq];
@@ -366,45 +372,58 @@
void amiga_do_irq_list(int irq, struct pt_regs *fp, struct irq_server *server)
{
irq_node_t *node, *slow_nodes;
- unsigned short flags;
+ unsigned short flags, intena;
kstat.irqs[0][SYS_IRQS + irq]++;
if (server->count++)
server->reentrance = 1;
- /* serve first fast and normal handlers */
- for (node = ami_irq_list[irq];
- node && (!(node->flags & IRQ_FLG_SLOW));
- node = node->next)
- node->handler(irq, node->dev_id, fp);
- custom.intreq = ami_intena_vals[irq];
+
+ intena = ami_intena_vals[irq];
+ custom.intreq = intena;
+
+ /* serve first fast handlers - there can only be one of these */
+ node = ami_irq_list[irq];
+
+ /*
+ * Timer interrupts show up like this
+ */
if (!node) {
server->count--;
return;
}
-#ifdef CONFIG_APUS
- APUS_WRITE(APUS_IPL_EMU, IPLEMU_SETRESET | IPLEMU_DISABLEINT);
- APUS_WRITE(APUS_IPL_EMU, IPLEMU_IPLMASK);
- APUS_WRITE(APUS_IPL_EMU, (IPLEMU_SETRESET
- | (~(fp->mq) & IPLEMU_IPLMASK)));
- APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT);
-#else
+
+ if (node && (node->flags & SA_INTERRUPT)) {
+ save_flags(flags);
+ cli();
+ node->handler(irq, node->dev_id, fp);
+ restore_flags(flags);
+
+ server->count--;
+ return;
+ }
+
+ /*
+ * Disable the interrupt source in question and reenable all
+ * other interrupts. No interrupt handler should ever touch
+ * the intena flags directly!
+ */
+ custom.intena = intena;
save_flags(flags);
- restore_flags((flags & ~0x0700) | (fp->sr & 0x0700));
-#endif
- /* if slow handlers exists, serve them now */
+ sti();
+
slow_nodes = node;
for (;;) {
for (; node; node = node->next)
node->handler(irq, node->dev_id, fp);
- /* if reentrance occurred, serve slow handlers again */
- custom.intena = ami_intena_vals[irq];
+
if (!server->reentrance) {
server->count--;
- custom.intena = IF_SETCLR | ami_intena_vals[irq];
+ restore_flags(flags);
+ custom.intena = IF_SETCLR | intena;
return;
}
+
server->reentrance = 0;
- custom.intena = IF_SETCLR | ami_intena_vals[irq];
node = slow_nodes;
}
}
@@ -522,24 +541,13 @@
for (i = 0; i < AMI_STD_IRQS; i++) {
if (!(node = ami_irq_list[i]))
continue;
- if (node->flags & IRQ_FLG_STD)
- continue;
len += sprintf(buf+len, "ami %2d: %10u ", i,
kstat.irqs[0][SYS_IRQS + i]);
do {
- if (ami_servers[i]) {
- if (node->flags & IRQ_FLG_FAST)
- len += sprintf(buf+len, "F ");
- else if (node->flags & IRQ_FLG_SLOW)
- len += sprintf(buf+len, "S ");
- else
- len += sprintf(buf+len, " ");
- } else {
- if (node->flags & IRQ_FLG_LOCK)
- len += sprintf(buf+len, "L ");
- else
- len += sprintf(buf+len, " ");
- }
+ if (node->flags & SA_INTERRUPT)
+ len += sprintf(buf+len, "F ");
+ else
+ len += sprintf(buf+len, " ");
len += sprintf(buf+len, "%s\n", node->devname);
if ((node = node->next))
len += sprintf(buf+len, " ");
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)