Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.12 323 lines 8.8 kB view raw
1/* 2 * arch/ppc/amiga/amiints.c -- Amiga Linux interrupt handling code 3 * 4 * This file is subject to the terms and conditions of the GNU General Public 5 * License. See the file COPYING in the main directory of this archive 6 * for more details. 7 * 8 * 11/07/96: rewritten interrupt handling, irq lists are exists now only for 9 * this sources where it makes sense (VERTB/PORTS/EXTER) and you must 10 * be careful that dev_id for this sources is unique since this the 11 * only possibility to distinguish between different handlers for 12 * free_irq. irq lists also have different irq flags: 13 * - IRQ_FLG_FAST: handler is inserted at top of list (after other 14 * fast handlers) 15 * - IRQ_FLG_SLOW: handler is inserted at bottom of list and before 16 * they're executed irq level is set to the previous 17 * one, but handlers don't need to be reentrant, if 18 * reentrance occurred, slow handlers will be just 19 * called again. 20 * The whole interrupt handling for CIAs is moved to cia.c 21 * /Roman Zippel 22 * 23 * 07/08/99: rewamp of the interrupt handling - we now have two types of 24 * interrupts, normal and fast handlers, fast handlers being 25 * marked with SA_INTERRUPT and runs with all other interrupts 26 * disabled. Normal interrupts disable their own source but 27 * run with all other interrupt sources enabled. 28 * PORTS and EXTER interrupts are always shared even if the 29 * drivers do not explicitly mark this when calling 30 * request_irq which they really should do. 31 * This is similar to the way interrupts are handled on all 32 * other architectures and makes a ton of sense besides 33 * having the advantage of making it easier to share 34 * drivers. 35 * /Jes 36 */ 37 38#include <linux/config.h> 39#include <linux/types.h> 40#include <linux/kernel.h> 41#include <linux/sched.h> 42#include <linux/interrupt.h> 43#include <linux/irq.h> 44#include <linux/kernel_stat.h> 45#include <linux/init.h> 46 47#include <asm/system.h> 48#include <asm/irq.h> 49#include <asm/traps.h> 50#include <asm/amigahw.h> 51#include <asm/amigaints.h> 52#include <asm/amipcmcia.h> 53 54#ifdef CONFIG_APUS 55#include <asm/amigappc.h> 56#endif 57 58extern void cia_init_IRQ(struct ciabase *base); 59 60unsigned short ami_intena_vals[AMI_STD_IRQS] = { 61 IF_VERTB, IF_COPER, IF_AUD0, IF_AUD1, IF_AUD2, IF_AUD3, IF_BLIT, 62 IF_DSKSYN, IF_DSKBLK, IF_RBF, IF_TBE, IF_SOFT, IF_PORTS, IF_EXTER 63}; 64static const unsigned char ami_servers[AMI_STD_IRQS] = { 65 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 66}; 67 68static short ami_ablecount[AMI_IRQS]; 69 70static void ami_badint(int irq, void *dev_id, struct pt_regs *fp) 71{ 72/* num_spurious += 1;*/ 73} 74 75/* 76 * void amiga_init_IRQ(void) 77 * 78 * Parameters: None 79 * 80 * Returns: Nothing 81 * 82 * This function should be called during kernel startup to initialize 83 * the amiga IRQ handling routines. 84 */ 85 86__init 87void amiga_init_IRQ(void) 88{ 89 int i; 90 91 for (i = 0; i < AMI_IRQS; i++) 92 ami_ablecount[i] = 0; 93 94 /* turn off PCMCIA interrupts */ 95 if (AMIGAHW_PRESENT(PCMCIA)) 96 gayle.inten = GAYLE_IRQ_IDE; 97 98 /* turn off all interrupts... */ 99 custom.intena = 0x7fff; 100 custom.intreq = 0x7fff; 101 102#ifdef CONFIG_APUS 103 /* Clear any inter-CPU interrupt requests. Circumvents bug in 104 Blizzard IPL emulation HW (or so it appears). */ 105 APUS_WRITE(APUS_INT_LVL, INTLVL_SETRESET | INTLVL_MASK); 106 107 /* Init IPL emulation. */ 108 APUS_WRITE(APUS_REG_INT, REGINT_INTMASTER | REGINT_ENABLEIPL); 109 APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT); 110 APUS_WRITE(APUS_IPL_EMU, IPLEMU_SETRESET | IPLEMU_IPLMASK); 111#endif 112 /* ... and enable the master interrupt bit */ 113 custom.intena = IF_SETCLR | IF_INTEN; 114 115 cia_init_IRQ(&ciaa_base); 116 cia_init_IRQ(&ciab_base); 117} 118 119/* 120 * Enable/disable a particular machine specific interrupt source. 121 * Note that this may affect other interrupts in case of a shared interrupt. 122 * This function should only be called for a _very_ short time to change some 123 * internal data, that may not be changed by the interrupt at the same time. 124 * ami_(enable|disable)_irq calls may also be nested. 125 */ 126 127void amiga_enable_irq(unsigned int irq) 128{ 129 if (irq >= AMI_IRQS) { 130 printk("%s: Unknown IRQ %d\n", __FUNCTION__, irq); 131 return; 132 } 133 134 ami_ablecount[irq]--; 135 if (ami_ablecount[irq]<0) 136 ami_ablecount[irq]=0; 137 else if (ami_ablecount[irq]) 138 return; 139 140 /* No action for auto-vector interrupts */ 141 if (irq >= IRQ_AMIGA_AUTO){ 142 printk("%s: Trying to enable auto-vector IRQ %i\n", 143 __FUNCTION__, irq - IRQ_AMIGA_AUTO); 144 return; 145 } 146 147 if (irq >= IRQ_AMIGA_CIAA) { 148 cia_set_irq(irq, 0); 149 cia_able_irq(irq, 1); 150 return; 151 } 152 153 /* enable the interrupt */ 154 custom.intena = IF_SETCLR | ami_intena_vals[irq]; 155} 156 157void amiga_disable_irq(unsigned int irq) 158{ 159 if (irq >= AMI_IRQS) { 160 printk("%s: Unknown IRQ %d\n", __FUNCTION__, irq); 161 return; 162 } 163 164 if (ami_ablecount[irq]++) 165 return; 166 167 /* No action for auto-vector interrupts */ 168 if (irq >= IRQ_AMIGA_AUTO) { 169 printk("%s: Trying to disable auto-vector IRQ %i\n", 170 __FUNCTION__, irq - IRQ_AMIGA_AUTO); 171 return; 172 } 173 174 if (irq >= IRQ_AMIGA_CIAA) { 175 cia_able_irq(irq, 0); 176 return; 177 } 178 179 /* disable the interrupt */ 180 custom.intena = ami_intena_vals[irq]; 181} 182 183inline void amiga_do_irq(int irq, struct pt_regs *fp) 184{ 185 irq_desc_t *desc = irq_desc + irq; 186 struct irqaction *action = desc->action; 187 188 kstat_cpu(0).irqs[irq]++; 189 action->handler(irq, action->dev_id, fp); 190} 191 192void amiga_do_irq_list(int irq, struct pt_regs *fp) 193{ 194 irq_desc_t *desc = irq_desc + irq; 195 struct irqaction *action; 196 197 kstat_cpu(0).irqs[irq]++; 198 199 custom.intreq = ami_intena_vals[irq]; 200 201 for (action = desc->action; action; action = action->next) 202 action->handler(irq, action->dev_id, fp); 203} 204 205/* 206 * The builtin Amiga hardware interrupt handlers. 207 */ 208 209static void ami_int1(int irq, void *dev_id, struct pt_regs *fp) 210{ 211 unsigned short ints = custom.intreqr & custom.intenar; 212 213 /* if serial transmit buffer empty, interrupt */ 214 if (ints & IF_TBE) { 215 custom.intreq = IF_TBE; 216 amiga_do_irq(IRQ_AMIGA_TBE, fp); 217 } 218 219 /* if floppy disk transfer complete, interrupt */ 220 if (ints & IF_DSKBLK) { 221 custom.intreq = IF_DSKBLK; 222 amiga_do_irq(IRQ_AMIGA_DSKBLK, fp); 223 } 224 225 /* if software interrupt set, interrupt */ 226 if (ints & IF_SOFT) { 227 custom.intreq = IF_SOFT; 228 amiga_do_irq(IRQ_AMIGA_SOFT, fp); 229 } 230} 231 232static void ami_int3(int irq, void *dev_id, struct pt_regs *fp) 233{ 234 unsigned short ints = custom.intreqr & custom.intenar; 235 236 /* if a blitter interrupt */ 237 if (ints & IF_BLIT) { 238 custom.intreq = IF_BLIT; 239 amiga_do_irq(IRQ_AMIGA_BLIT, fp); 240 } 241 242 /* if a copper interrupt */ 243 if (ints & IF_COPER) { 244 custom.intreq = IF_COPER; 245 amiga_do_irq(IRQ_AMIGA_COPPER, fp); 246 } 247 248 /* if a vertical blank interrupt */ 249 if (ints & IF_VERTB) 250 amiga_do_irq_list(IRQ_AMIGA_VERTB, fp); 251} 252 253static void ami_int4(int irq, void *dev_id, struct pt_regs *fp) 254{ 255 unsigned short ints = custom.intreqr & custom.intenar; 256 257 /* if audio 0 interrupt */ 258 if (ints & IF_AUD0) { 259 custom.intreq = IF_AUD0; 260 amiga_do_irq(IRQ_AMIGA_AUD0, fp); 261 } 262 263 /* if audio 1 interrupt */ 264 if (ints & IF_AUD1) { 265 custom.intreq = IF_AUD1; 266 amiga_do_irq(IRQ_AMIGA_AUD1, fp); 267 } 268 269 /* if audio 2 interrupt */ 270 if (ints & IF_AUD2) { 271 custom.intreq = IF_AUD2; 272 amiga_do_irq(IRQ_AMIGA_AUD2, fp); 273 } 274 275 /* if audio 3 interrupt */ 276 if (ints & IF_AUD3) { 277 custom.intreq = IF_AUD3; 278 amiga_do_irq(IRQ_AMIGA_AUD3, fp); 279 } 280} 281 282static void ami_int5(int irq, void *dev_id, struct pt_regs *fp) 283{ 284 unsigned short ints = custom.intreqr & custom.intenar; 285 286 /* if serial receive buffer full interrupt */ 287 if (ints & IF_RBF) { 288 /* acknowledge of IF_RBF must be done by the serial interrupt */ 289 amiga_do_irq(IRQ_AMIGA_RBF, fp); 290 } 291 292 /* if a disk sync interrupt */ 293 if (ints & IF_DSKSYN) { 294 custom.intreq = IF_DSKSYN; 295 amiga_do_irq(IRQ_AMIGA_DSKSYN, fp); 296 } 297} 298 299static void ami_int7(int irq, void *dev_id, struct pt_regs *fp) 300{ 301 panic ("level 7 interrupt received\n"); 302} 303 304#ifdef CONFIG_APUS 305/* The PPC irq handling links all handlers requested on the same vector 306 and executes them in a loop. Having ami_badint at the end of the chain 307 is a bad idea. */ 308struct irqaction amiga_sys_irqaction[AUTO_IRQS] = { 309 { .handler = ami_badint, .name = "spurious int" }, 310 { .handler = ami_int1, .name = "int1 handler" }, 311 { 0, /* CIAA */ }, 312 { .handler = ami_int3, .name = "int3 handler" }, 313 { .handler = ami_int4, .name = "int4 handler" }, 314 { .handler = ami_int5, .name = "int5 handler" }, 315 { 0, /* CIAB */ }, 316 { .handler = ami_int7, .name = "int7 handler" }, 317}; 318#else 319void (*amiga_default_handler[SYS_IRQS])(int, void *, struct pt_regs *) = { 320 ami_badint, ami_int1, ami_badint, ami_int3, 321 ami_int4, ami_int5, ami_badint, ami_int7 322}; 323#endif