1/* 2 * linux/arch/m68k/hp300/ints.c 3 * 4 * Copyright (C) 1998 Philip Blundell <philb@gnu.org> 5 * 6 * This file contains the HP300-specific interrupt handling. 7 * We only use the autovector interrupts, and therefore we need to 8 * maintain lists of devices sharing each ipl. 9 * [ipl list code added by Peter Maydell <pmaydell@chiark.greenend.org.uk> 06/1998] 10 */ 11 12#include <linux/kernel.h> 13#include <linux/types.h> 14#include <linux/init.h> 15#include <linux/sched.h> 16#include <linux/kernel_stat.h> 17#include <linux/interrupt.h> 18#include <linux/spinlock.h> 19#include <asm/machdep.h> 20#include <asm/irq.h> 21#include <asm/io.h> 22#include <asm/system.h> 23#include <asm/traps.h> 24#include <asm/ptrace.h> 25#include "ints.h" 26 27/* Each ipl has a linked list of interrupt service routines. 28 * Service routines are added via hp300_request_irq() and removed 29 * via hp300_free_irq(). The device driver should set IRQ_FLG_FAST 30 * if it needs to be serviced early (eg FIFOless UARTs); this will 31 * cause it to be added at the front of the queue rather than 32 * the back. 33 * Currently IRQ_FLG_SLOW and flags=0 are treated identically; if 34 * we needed three levels of priority we could distinguish them 35 * but this strikes me as mildly ugly... 36 */ 37 38/* we start with no entries in any list */ 39static irq_node_t *hp300_irq_list[HP300_NUM_IRQS] = { [0 ... HP300_NUM_IRQS-1] = NULL }; 40 41static spinlock_t irqlist_lock; 42 43/* This handler receives all interrupts, dispatching them to the registered handlers */ 44static void hp300_int_handler(int irq, void *dev_id, struct pt_regs *fp) 45{ 46 irq_node_t *t; 47 /* We just give every handler on the chain an opportunity to handle 48 * the interrupt, in priority order. 49 */ 50 for(t = hp300_irq_list[irq]; t; t=t->next) 51 t->handler(irq, t->dev_id, fp); 52 /* We could put in some accounting routines, checks for stray interrupts, 53 * etc, in here. Note that currently we can't tell whether or not 54 * a handler handles the interrupt, though. 55 */ 56} 57 58void (*hp300_default_handler[SYS_IRQS])(int, void *, struct pt_regs *) = { 59 hp300_int_handler, hp300_int_handler, hp300_int_handler, hp300_int_handler, 60 hp300_int_handler, hp300_int_handler, hp300_int_handler, NULL 61}; 62 63/* dev_id had better be unique to each handler because it's the only way we have 64 * to distinguish handlers when removing them... 65 * 66 * It would be pretty easy to support IRQ_FLG_LOCK (handler is not replacable) 67 * and IRQ_FLG_REPLACE (handler replaces existing one with this dev_id) 68 * if we wanted to. IRQ_FLG_FAST is needed for devices where interrupt latency 69 * matters (eg the dreaded FIFOless UART...) 70 */ 71int hp300_request_irq(unsigned int irq, 72 void (*handler) (int, void *, struct pt_regs *), 73 unsigned long flags, const char *devname, void *dev_id) 74{ 75 irq_node_t *t, *n = new_irq_node(); 76 77 if (!n) /* oops, no free nodes */ 78 return -ENOMEM; 79 80 spin_lock_irqsave(&irqlist_lock, flags); 81 82 if (!hp300_irq_list[irq]) { 83 /* no list yet */ 84 hp300_irq_list[irq] = n; 85 n->next = NULL; 86 } else if (flags & IRQ_FLG_FAST) { 87 /* insert at head of list */ 88 n->next = hp300_irq_list[irq]; 89 hp300_irq_list[irq] = n; 90 } else { 91 /* insert at end of list */ 92 for(t = hp300_irq_list[irq]; t->next; t = t->next) 93 /* do nothing */; 94 n->next = NULL; 95 t->next = n; 96 } 97 98 /* Fill in n appropriately */ 99 n->handler = handler; 100 n->flags = flags; 101 n->dev_id = dev_id; 102 n->devname = devname; 103 spin_unlock_irqrestore(&irqlist_lock, flags); 104 return 0; 105} 106 107void hp300_free_irq(unsigned int irq, void *dev_id) 108{ 109 irq_node_t *t; 110 unsigned long flags; 111 112 spin_lock_irqsave(&irqlist_lock, flags); 113 114 t = hp300_irq_list[irq]; 115 if (!t) /* no handlers at all for that IRQ */ 116 { 117 printk(KERN_ERR "hp300_free_irq: attempt to remove nonexistent handler for IRQ %d\n", irq); 118 spin_unlock_irqrestore(&irqlist_lock, flags); 119 return; 120 } 121 122 if (t->dev_id == dev_id) 123 { /* removing first handler on chain */ 124 t->flags = IRQ_FLG_STD; /* we probably don't really need these */ 125 t->dev_id = NULL; 126 t->devname = NULL; 127 t->handler = NULL; /* frees this irq_node_t */ 128 hp300_irq_list[irq] = t->next; 129 spin_unlock_irqrestore(&irqlist_lock, flags); 130 return; 131 } 132 133 /* OK, must be removing from middle of the chain */ 134 135 for (t = hp300_irq_list[irq]; t->next && t->next->dev_id != dev_id; t = t->next) 136 /* do nothing */; 137 if (!t->next) 138 { 139 printk(KERN_ERR "hp300_free_irq: attempt to remove nonexistent handler for IRQ %d\n", irq); 140 spin_unlock_irqrestore(&irqlist_lock, flags); 141 return; 142 } 143 /* remove the entry after t: */ 144 t->next->flags = IRQ_FLG_STD; 145 t->next->dev_id = NULL; 146 t->next->devname = NULL; 147 t->next->handler = NULL; 148 t->next = t->next->next; 149 150 spin_unlock_irqrestore(&irqlist_lock, flags); 151} 152 153int hp300_get_irq_list(char *buf) 154{ 155 return 0; 156} 157 158void __init hp300_init_IRQ(void) 159{ 160 spin_lock_init(&irqlist_lock); 161} 162