Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame^] | 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 <asm/errno.h> |
| 26 | #include "ints.h" |
| 27 | |
| 28 | /* Each ipl has a linked list of interrupt service routines. |
| 29 | * Service routines are added via hp300_request_irq() and removed |
| 30 | * via hp300_free_irq(). The device driver should set IRQ_FLG_FAST |
| 31 | * if it needs to be serviced early (eg FIFOless UARTs); this will |
| 32 | * cause it to be added at the front of the queue rather than |
| 33 | * the back. |
| 34 | * Currently IRQ_FLG_SLOW and flags=0 are treated identically; if |
| 35 | * we needed three levels of priority we could distinguish them |
| 36 | * but this strikes me as mildly ugly... |
| 37 | */ |
| 38 | |
| 39 | /* we start with no entries in any list */ |
| 40 | static irq_node_t *hp300_irq_list[HP300_NUM_IRQS]; |
| 41 | |
| 42 | static spinlock_t irqlist_lock; |
| 43 | |
| 44 | /* This handler receives all interrupts, dispatching them to the registered handlers */ |
| 45 | static irqreturn_t hp300_int_handler(int irq, void *dev_id, struct pt_regs *fp) |
| 46 | { |
| 47 | irq_node_t *t; |
| 48 | /* We just give every handler on the chain an opportunity to handle |
| 49 | * the interrupt, in priority order. |
| 50 | */ |
| 51 | for(t = hp300_irq_list[irq]; t; t=t->next) |
| 52 | t->handler(irq, t->dev_id, fp); |
| 53 | /* We could put in some accounting routines, checks for stray interrupts, |
| 54 | * etc, in here. Note that currently we can't tell whether or not |
| 55 | * a handler handles the interrupt, though. |
| 56 | */ |
| 57 | return IRQ_HANDLED; |
| 58 | } |
| 59 | |
| 60 | static irqreturn_t hp300_badint(int irq, void *dev_id, struct pt_regs *fp) |
| 61 | { |
| 62 | num_spurious += 1; |
| 63 | return IRQ_NONE; |
| 64 | } |
| 65 | |
| 66 | irqreturn_t (*hp300_default_handler[SYS_IRQS])(int, void *, struct pt_regs *) = { |
| 67 | [0] = hp300_badint, |
| 68 | [1] = hp300_int_handler, |
| 69 | [2] = hp300_int_handler, |
| 70 | [3] = hp300_int_handler, |
| 71 | [4] = hp300_int_handler, |
| 72 | [5] = hp300_int_handler, |
| 73 | [6] = hp300_int_handler, |
| 74 | [7] = hp300_int_handler |
| 75 | }; |
| 76 | |
| 77 | /* dev_id had better be unique to each handler because it's the only way we have |
| 78 | * to distinguish handlers when removing them... |
| 79 | * |
| 80 | * It would be pretty easy to support IRQ_FLG_LOCK (handler is not replacable) |
| 81 | * and IRQ_FLG_REPLACE (handler replaces existing one with this dev_id) |
| 82 | * if we wanted to. IRQ_FLG_FAST is needed for devices where interrupt latency |
| 83 | * matters (eg the dreaded FIFOless UART...) |
| 84 | */ |
| 85 | int hp300_request_irq(unsigned int irq, |
| 86 | irqreturn_t (*handler) (int, void *, struct pt_regs *), |
| 87 | unsigned long flags, const char *devname, void *dev_id) |
| 88 | { |
| 89 | irq_node_t *t, *n = new_irq_node(); |
| 90 | |
| 91 | if (!n) /* oops, no free nodes */ |
| 92 | return -ENOMEM; |
| 93 | |
| 94 | spin_lock_irqsave(&irqlist_lock, flags); |
| 95 | |
| 96 | if (!hp300_irq_list[irq]) { |
| 97 | /* no list yet */ |
| 98 | hp300_irq_list[irq] = n; |
| 99 | n->next = NULL; |
| 100 | } else if (flags & IRQ_FLG_FAST) { |
| 101 | /* insert at head of list */ |
| 102 | n->next = hp300_irq_list[irq]; |
| 103 | hp300_irq_list[irq] = n; |
| 104 | } else { |
| 105 | /* insert at end of list */ |
| 106 | for(t = hp300_irq_list[irq]; t->next; t = t->next) |
| 107 | /* do nothing */; |
| 108 | n->next = NULL; |
| 109 | t->next = n; |
| 110 | } |
| 111 | |
| 112 | /* Fill in n appropriately */ |
| 113 | n->handler = handler; |
| 114 | n->flags = flags; |
| 115 | n->dev_id = dev_id; |
| 116 | n->devname = devname; |
| 117 | spin_unlock_irqrestore(&irqlist_lock, flags); |
| 118 | return 0; |
| 119 | } |
| 120 | |
| 121 | void hp300_free_irq(unsigned int irq, void *dev_id) |
| 122 | { |
| 123 | irq_node_t *t; |
| 124 | unsigned long flags; |
| 125 | |
| 126 | spin_lock_irqsave(&irqlist_lock, flags); |
| 127 | |
| 128 | t = hp300_irq_list[irq]; |
| 129 | if (!t) /* no handlers at all for that IRQ */ |
| 130 | { |
| 131 | printk(KERN_ERR "hp300_free_irq: attempt to remove nonexistent handler for IRQ %d\n", irq); |
| 132 | spin_unlock_irqrestore(&irqlist_lock, flags); |
| 133 | return; |
| 134 | } |
| 135 | |
| 136 | if (t->dev_id == dev_id) |
| 137 | { /* removing first handler on chain */ |
| 138 | t->flags = IRQ_FLG_STD; /* we probably don't really need these */ |
| 139 | t->dev_id = NULL; |
| 140 | t->devname = NULL; |
| 141 | t->handler = NULL; /* frees this irq_node_t */ |
| 142 | hp300_irq_list[irq] = t->next; |
| 143 | spin_unlock_irqrestore(&irqlist_lock, flags); |
| 144 | return; |
| 145 | } |
| 146 | |
| 147 | /* OK, must be removing from middle of the chain */ |
| 148 | |
| 149 | for (t = hp300_irq_list[irq]; t->next && t->next->dev_id != dev_id; t = t->next) |
| 150 | /* do nothing */; |
| 151 | if (!t->next) |
| 152 | { |
| 153 | printk(KERN_ERR "hp300_free_irq: attempt to remove nonexistent handler for IRQ %d\n", irq); |
| 154 | spin_unlock_irqrestore(&irqlist_lock, flags); |
| 155 | return; |
| 156 | } |
| 157 | /* remove the entry after t: */ |
| 158 | t->next->flags = IRQ_FLG_STD; |
| 159 | t->next->dev_id = NULL; |
| 160 | t->next->devname = NULL; |
| 161 | t->next->handler = NULL; |
| 162 | t->next = t->next->next; |
| 163 | |
| 164 | spin_unlock_irqrestore(&irqlist_lock, flags); |
| 165 | } |
| 166 | |
| 167 | int show_hp300_interrupts(struct seq_file *p, void *v) |
| 168 | { |
| 169 | return 0; |
| 170 | } |
| 171 | |
| 172 | void __init hp300_init_IRQ(void) |
| 173 | { |
| 174 | spin_lock_init(&irqlist_lock); |
| 175 | } |