11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * linux/arch/m68k/kernel/ints.c -- Linux/m68k general interrupt handling code 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * This file is subject to the terms and conditions of the GNU General Public 51da177e4SLinus Torvalds * License. See the file COPYING in the main directory of this archive 61da177e4SLinus Torvalds * for more details. 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * 07/03/96: Timer initialization, and thus mach_sched_init(), 91da177e4SLinus Torvalds * removed from request_irq() and moved to init_time(). 101da177e4SLinus Torvalds * We should therefore consider renaming our add_isr() and 111da177e4SLinus Torvalds * remove_isr() to request_irq() and free_irq() 121da177e4SLinus Torvalds * respectively, so they are compliant with the other 131da177e4SLinus Torvalds * architectures. /Jes 141da177e4SLinus Torvalds * 11/07/96: Changed all add_/remove_isr() to request_/free_irq() calls. 151da177e4SLinus Torvalds * Removed irq list support, if any machine needs an irq server 161da177e4SLinus Torvalds * it must implement this itself (as it's already done), instead 171da177e4SLinus Torvalds * only default handler are used with mach_default_handler. 181da177e4SLinus Torvalds * request_irq got some flags different from other architectures: 191da177e4SLinus Torvalds * - IRQ_FLG_REPLACE : Replace an existing handler (the default one 201da177e4SLinus Torvalds * can be replaced without this flag) 211da177e4SLinus Torvalds * - IRQ_FLG_LOCK : handler can't be replaced 221da177e4SLinus Torvalds * There are other machine depending flags, see there 231da177e4SLinus Torvalds * If you want to replace a default handler you should know what 241da177e4SLinus Torvalds * you're doing, since it might handle different other irq sources 251da177e4SLinus Torvalds * which must be served /Roman Zippel 261da177e4SLinus Torvalds */ 271da177e4SLinus Torvalds 281da177e4SLinus Torvalds #include <linux/config.h> 291da177e4SLinus Torvalds #include <linux/module.h> 301da177e4SLinus Torvalds #include <linux/types.h> 311da177e4SLinus Torvalds #include <linux/sched.h> 321da177e4SLinus Torvalds #include <linux/kernel_stat.h> 331da177e4SLinus Torvalds #include <linux/errno.h> 341da177e4SLinus Torvalds #include <linux/init.h> 351da177e4SLinus Torvalds 361da177e4SLinus Torvalds #include <asm/setup.h> 371da177e4SLinus Torvalds #include <asm/system.h> 381da177e4SLinus Torvalds #include <asm/irq.h> 391da177e4SLinus Torvalds #include <asm/traps.h> 401da177e4SLinus Torvalds #include <asm/page.h> 411da177e4SLinus Torvalds #include <asm/machdep.h> 421da177e4SLinus Torvalds 431da177e4SLinus Torvalds #ifdef CONFIG_Q40 441da177e4SLinus Torvalds #include <asm/q40ints.h> 451da177e4SLinus Torvalds #endif 461da177e4SLinus Torvalds 471da177e4SLinus Torvalds /* table for system interrupt handlers */ 48*b5dc7840SRoman Zippel static struct irq_node *irq_list[SYS_IRQS]; 49*b5dc7840SRoman Zippel static struct irq_controller *irq_controller[SYS_IRQS]; 50*b5dc7840SRoman Zippel 51*b5dc7840SRoman Zippel static struct irq_controller auto_irq_controller = { 52*b5dc7840SRoman Zippel .name = "auto", 53*b5dc7840SRoman Zippel .lock = SPIN_LOCK_UNLOCKED, 54*b5dc7840SRoman Zippel .startup = m68k_irq_startup, 55*b5dc7840SRoman Zippel .shutdown = m68k_irq_shutdown, 56*b5dc7840SRoman Zippel }; 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds static const char *default_names[SYS_IRQS] = { 591da177e4SLinus Torvalds [0] = "spurious int", 601da177e4SLinus Torvalds [1] = "int1 handler", 611da177e4SLinus Torvalds [2] = "int2 handler", 621da177e4SLinus Torvalds [3] = "int3 handler", 631da177e4SLinus Torvalds [4] = "int4 handler", 641da177e4SLinus Torvalds [5] = "int5 handler", 651da177e4SLinus Torvalds [6] = "int6 handler", 661da177e4SLinus Torvalds [7] = "int7 handler" 671da177e4SLinus Torvalds }; 681da177e4SLinus Torvalds 691da177e4SLinus Torvalds /* The number of spurious interrupts */ 701da177e4SLinus Torvalds volatile unsigned int num_spurious; 711da177e4SLinus Torvalds 721da177e4SLinus Torvalds #define NUM_IRQ_NODES 100 731da177e4SLinus Torvalds static irq_node_t nodes[NUM_IRQ_NODES]; 741da177e4SLinus Torvalds 751da177e4SLinus Torvalds static void dummy_enable_irq(unsigned int irq); 761da177e4SLinus Torvalds static void dummy_disable_irq(unsigned int irq); 771da177e4SLinus Torvalds static int dummy_request_irq(unsigned int irq, 781da177e4SLinus Torvalds irqreturn_t (*handler) (int, void *, struct pt_regs *), 791da177e4SLinus Torvalds unsigned long flags, const char *devname, void *dev_id); 801da177e4SLinus Torvalds static void dummy_free_irq(unsigned int irq, void *dev_id); 811da177e4SLinus Torvalds 821da177e4SLinus Torvalds void (*enable_irq) (unsigned int) = dummy_enable_irq; 831da177e4SLinus Torvalds void (*disable_irq) (unsigned int) = dummy_disable_irq; 841da177e4SLinus Torvalds 851da177e4SLinus Torvalds int (*mach_request_irq) (unsigned int, irqreturn_t (*)(int, void *, struct pt_regs *), 861da177e4SLinus Torvalds unsigned long, const char *, void *) = dummy_request_irq; 871da177e4SLinus Torvalds void (*mach_free_irq) (unsigned int, void *) = dummy_free_irq; 881da177e4SLinus Torvalds 891da177e4SLinus Torvalds void init_irq_proc(void); 901da177e4SLinus Torvalds 911da177e4SLinus Torvalds /* 921da177e4SLinus Torvalds * void init_IRQ(void) 931da177e4SLinus Torvalds * 941da177e4SLinus Torvalds * Parameters: None 951da177e4SLinus Torvalds * 961da177e4SLinus Torvalds * Returns: Nothing 971da177e4SLinus Torvalds * 981da177e4SLinus Torvalds * This function should be called during kernel startup to initialize 991da177e4SLinus Torvalds * the IRQ handling routines. 1001da177e4SLinus Torvalds */ 1011da177e4SLinus Torvalds 1021da177e4SLinus Torvalds void __init init_IRQ(void) 1031da177e4SLinus Torvalds { 1041da177e4SLinus Torvalds int i; 1051da177e4SLinus Torvalds 1066d2f16a8SRoman Zippel /* assembly irq entry code relies on this... */ 1076d2f16a8SRoman Zippel if (HARDIRQ_MASK != 0x00ff0000) { 1086d2f16a8SRoman Zippel extern void hardirq_mask_is_broken(void); 1096d2f16a8SRoman Zippel hardirq_mask_is_broken(); 1106d2f16a8SRoman Zippel } 1116d2f16a8SRoman Zippel 112*b5dc7840SRoman Zippel for (i = IRQ_AUTO_1; i <= IRQ_AUTO_7; i++) { 113*b5dc7840SRoman Zippel irq_controller[i] = &auto_irq_controller; 114*b5dc7840SRoman Zippel if (mach_default_handler && (*mach_default_handler)[i]) 115*b5dc7840SRoman Zippel cpu_request_irq(i, (*mach_default_handler)[i], 116*b5dc7840SRoman Zippel 0, default_names[i], NULL); 1171da177e4SLinus Torvalds } 1181da177e4SLinus Torvalds 1191da177e4SLinus Torvalds mach_init_IRQ (); 1201da177e4SLinus Torvalds } 1211da177e4SLinus Torvalds 1221da177e4SLinus Torvalds irq_node_t *new_irq_node(void) 1231da177e4SLinus Torvalds { 1241da177e4SLinus Torvalds irq_node_t *node; 1251da177e4SLinus Torvalds short i; 1261da177e4SLinus Torvalds 127*b5dc7840SRoman Zippel for (node = nodes, i = NUM_IRQ_NODES-1; i >= 0; node++, i--) { 128*b5dc7840SRoman Zippel if (!node->handler) { 129*b5dc7840SRoman Zippel memset(node, 0, sizeof(*node)); 1301da177e4SLinus Torvalds return node; 131*b5dc7840SRoman Zippel } 132*b5dc7840SRoman Zippel } 1331da177e4SLinus Torvalds 1341da177e4SLinus Torvalds printk ("new_irq_node: out of nodes\n"); 1351da177e4SLinus Torvalds return NULL; 1361da177e4SLinus Torvalds } 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds /* 1391da177e4SLinus Torvalds * We will keep these functions until I have convinced Linus to move 1401da177e4SLinus Torvalds * the declaration of them from include/linux/sched.h to 1411da177e4SLinus Torvalds * include/asm/irq.h. 1421da177e4SLinus Torvalds */ 1431da177e4SLinus Torvalds int request_irq(unsigned int irq, 1441da177e4SLinus Torvalds irqreturn_t (*handler) (int, void *, struct pt_regs *), 1451da177e4SLinus Torvalds unsigned long flags, const char *devname, void *dev_id) 1461da177e4SLinus Torvalds { 1471da177e4SLinus Torvalds return mach_request_irq(irq, handler, flags, devname, dev_id); 1481da177e4SLinus Torvalds } 1491da177e4SLinus Torvalds 1501da177e4SLinus Torvalds EXPORT_SYMBOL(request_irq); 1511da177e4SLinus Torvalds 1521da177e4SLinus Torvalds void free_irq(unsigned int irq, void *dev_id) 1531da177e4SLinus Torvalds { 1541da177e4SLinus Torvalds mach_free_irq(irq, dev_id); 1551da177e4SLinus Torvalds } 1561da177e4SLinus Torvalds 1571da177e4SLinus Torvalds EXPORT_SYMBOL(free_irq); 1581da177e4SLinus Torvalds 159*b5dc7840SRoman Zippel int setup_irq(unsigned int irq, struct irq_node *node) 160*b5dc7840SRoman Zippel { 161*b5dc7840SRoman Zippel struct irq_controller *contr; 162*b5dc7840SRoman Zippel struct irq_node **prev; 163*b5dc7840SRoman Zippel unsigned long flags; 164*b5dc7840SRoman Zippel 165*b5dc7840SRoman Zippel if (irq >= SYS_IRQS || !(contr = irq_controller[irq])) { 166*b5dc7840SRoman Zippel printk("%s: Incorrect IRQ %d from %s\n", 167*b5dc7840SRoman Zippel __FUNCTION__, irq, node->devname); 168*b5dc7840SRoman Zippel return -ENXIO; 169*b5dc7840SRoman Zippel } 170*b5dc7840SRoman Zippel 171*b5dc7840SRoman Zippel spin_lock_irqsave(&contr->lock, flags); 172*b5dc7840SRoman Zippel 173*b5dc7840SRoman Zippel prev = irq_list + irq; 174*b5dc7840SRoman Zippel if (*prev) { 175*b5dc7840SRoman Zippel /* Can't share interrupts unless both agree to */ 176*b5dc7840SRoman Zippel if (!((*prev)->flags & node->flags & SA_SHIRQ)) { 177*b5dc7840SRoman Zippel spin_unlock_irqrestore(&contr->lock, flags); 178*b5dc7840SRoman Zippel return -EBUSY; 179*b5dc7840SRoman Zippel } 180*b5dc7840SRoman Zippel while (*prev) 181*b5dc7840SRoman Zippel prev = &(*prev)->next; 182*b5dc7840SRoman Zippel } 183*b5dc7840SRoman Zippel 184*b5dc7840SRoman Zippel if (!irq_list[irq]) { 185*b5dc7840SRoman Zippel if (contr->startup) 186*b5dc7840SRoman Zippel contr->startup(irq); 187*b5dc7840SRoman Zippel else 188*b5dc7840SRoman Zippel contr->enable(irq); 189*b5dc7840SRoman Zippel } 190*b5dc7840SRoman Zippel node->next = NULL; 191*b5dc7840SRoman Zippel *prev = node; 192*b5dc7840SRoman Zippel 193*b5dc7840SRoman Zippel spin_unlock_irqrestore(&contr->lock, flags); 194*b5dc7840SRoman Zippel 195*b5dc7840SRoman Zippel return 0; 196*b5dc7840SRoman Zippel } 197*b5dc7840SRoman Zippel 1981da177e4SLinus Torvalds int cpu_request_irq(unsigned int irq, 1991da177e4SLinus Torvalds irqreturn_t (*handler)(int, void *, struct pt_regs *), 2001da177e4SLinus Torvalds unsigned long flags, const char *devname, void *dev_id) 2011da177e4SLinus Torvalds { 202*b5dc7840SRoman Zippel struct irq_node *node; 203*b5dc7840SRoman Zippel int res; 2041da177e4SLinus Torvalds 205*b5dc7840SRoman Zippel node = new_irq_node(); 206*b5dc7840SRoman Zippel if (!node) 207*b5dc7840SRoman Zippel return -ENOMEM; 2081da177e4SLinus Torvalds 209*b5dc7840SRoman Zippel node->handler = handler; 210*b5dc7840SRoman Zippel node->flags = flags; 211*b5dc7840SRoman Zippel node->dev_id = dev_id; 212*b5dc7840SRoman Zippel node->devname = devname; 213*b5dc7840SRoman Zippel 214*b5dc7840SRoman Zippel res = setup_irq(irq, node); 215*b5dc7840SRoman Zippel if (res) 216*b5dc7840SRoman Zippel node->handler = NULL; 217*b5dc7840SRoman Zippel 218*b5dc7840SRoman Zippel return res; 2191da177e4SLinus Torvalds } 2201da177e4SLinus Torvalds 2211da177e4SLinus Torvalds void cpu_free_irq(unsigned int irq, void *dev_id) 2221da177e4SLinus Torvalds { 223*b5dc7840SRoman Zippel struct irq_controller *contr; 224*b5dc7840SRoman Zippel struct irq_node **p, *node; 225*b5dc7840SRoman Zippel unsigned long flags; 226*b5dc7840SRoman Zippel 227*b5dc7840SRoman Zippel if (irq >= SYS_IRQS || !(contr = irq_controller[irq])) { 2281da177e4SLinus Torvalds printk("%s: Incorrect IRQ %d\n", __FUNCTION__, irq); 2291da177e4SLinus Torvalds return; 2301da177e4SLinus Torvalds } 2311da177e4SLinus Torvalds 232*b5dc7840SRoman Zippel spin_lock_irqsave(&contr->lock, flags); 2331da177e4SLinus Torvalds 234*b5dc7840SRoman Zippel p = irq_list + irq; 235*b5dc7840SRoman Zippel while ((node = *p)) { 236*b5dc7840SRoman Zippel if (node->dev_id == dev_id) 237*b5dc7840SRoman Zippel break; 238*b5dc7840SRoman Zippel p = &node->next; 2391da177e4SLinus Torvalds } 2401da177e4SLinus Torvalds 241*b5dc7840SRoman Zippel if (node) { 242*b5dc7840SRoman Zippel *p = node->next; 243*b5dc7840SRoman Zippel node->handler = NULL; 244*b5dc7840SRoman Zippel } else 245*b5dc7840SRoman Zippel printk("%s: Removing probably wrong IRQ %d\n", 246*b5dc7840SRoman Zippel __FUNCTION__, irq); 247*b5dc7840SRoman Zippel 248*b5dc7840SRoman Zippel if (!irq_list[irq]) 249*b5dc7840SRoman Zippel contr->shutdown(irq); 250*b5dc7840SRoman Zippel 251*b5dc7840SRoman Zippel spin_unlock_irqrestore(&contr->lock, flags); 252*b5dc7840SRoman Zippel } 253*b5dc7840SRoman Zippel 254*b5dc7840SRoman Zippel int m68k_irq_startup(unsigned int irq) 255*b5dc7840SRoman Zippel { 256*b5dc7840SRoman Zippel if (irq <= IRQ_AUTO_7) 257*b5dc7840SRoman Zippel vectors[VEC_SPUR + irq] = auto_inthandler; 258*b5dc7840SRoman Zippel return 0; 259*b5dc7840SRoman Zippel } 260*b5dc7840SRoman Zippel 261*b5dc7840SRoman Zippel void m68k_irq_shutdown(unsigned int irq) 262*b5dc7840SRoman Zippel { 263*b5dc7840SRoman Zippel if (irq <= IRQ_AUTO_7) 264*b5dc7840SRoman Zippel vectors[VEC_SPUR + irq] = bad_inthandler; 265*b5dc7840SRoman Zippel } 266*b5dc7840SRoman Zippel 267*b5dc7840SRoman Zippel 2681da177e4SLinus Torvalds /* 2691da177e4SLinus Torvalds * Do we need these probe functions on the m68k? 2701da177e4SLinus Torvalds * 2711da177e4SLinus Torvalds * ... may be useful with ISA devices 2721da177e4SLinus Torvalds */ 2731da177e4SLinus Torvalds unsigned long probe_irq_on (void) 2741da177e4SLinus Torvalds { 2751da177e4SLinus Torvalds #ifdef CONFIG_Q40 2761da177e4SLinus Torvalds if (MACH_IS_Q40) 2771da177e4SLinus Torvalds return q40_probe_irq_on(); 2781da177e4SLinus Torvalds #endif 2791da177e4SLinus Torvalds return 0; 2801da177e4SLinus Torvalds } 2811da177e4SLinus Torvalds 2821da177e4SLinus Torvalds EXPORT_SYMBOL(probe_irq_on); 2831da177e4SLinus Torvalds 2841da177e4SLinus Torvalds int probe_irq_off (unsigned long irqs) 2851da177e4SLinus Torvalds { 2861da177e4SLinus Torvalds #ifdef CONFIG_Q40 2871da177e4SLinus Torvalds if (MACH_IS_Q40) 2881da177e4SLinus Torvalds return q40_probe_irq_off(irqs); 2891da177e4SLinus Torvalds #endif 2901da177e4SLinus Torvalds return 0; 2911da177e4SLinus Torvalds } 2921da177e4SLinus Torvalds 2931da177e4SLinus Torvalds EXPORT_SYMBOL(probe_irq_off); 2941da177e4SLinus Torvalds 2951da177e4SLinus Torvalds static void dummy_enable_irq(unsigned int irq) 2961da177e4SLinus Torvalds { 2971da177e4SLinus Torvalds printk("calling uninitialized enable_irq()\n"); 2981da177e4SLinus Torvalds } 2991da177e4SLinus Torvalds 3001da177e4SLinus Torvalds static void dummy_disable_irq(unsigned int irq) 3011da177e4SLinus Torvalds { 3021da177e4SLinus Torvalds printk("calling uninitialized disable_irq()\n"); 3031da177e4SLinus Torvalds } 3041da177e4SLinus Torvalds 3051da177e4SLinus Torvalds static int dummy_request_irq(unsigned int irq, 3061da177e4SLinus Torvalds irqreturn_t (*handler) (int, void *, struct pt_regs *), 3071da177e4SLinus Torvalds unsigned long flags, const char *devname, void *dev_id) 3081da177e4SLinus Torvalds { 3091da177e4SLinus Torvalds printk("calling uninitialized request_irq()\n"); 3101da177e4SLinus Torvalds return 0; 3111da177e4SLinus Torvalds } 3121da177e4SLinus Torvalds 3131da177e4SLinus Torvalds static void dummy_free_irq(unsigned int irq, void *dev_id) 3141da177e4SLinus Torvalds { 3151da177e4SLinus Torvalds printk("calling uninitialized disable_irq()\n"); 3161da177e4SLinus Torvalds } 3171da177e4SLinus Torvalds 31892445eaaSRoman Zippel asmlinkage void m68k_handle_int(unsigned int irq, struct pt_regs *regs) 3191da177e4SLinus Torvalds { 320*b5dc7840SRoman Zippel struct irq_node *node; 321*b5dc7840SRoman Zippel 32292445eaaSRoman Zippel kstat_cpu(0).irqs[irq]++; 323*b5dc7840SRoman Zippel node = irq_list[irq]; 324*b5dc7840SRoman Zippel do { 325*b5dc7840SRoman Zippel node->handler(irq, node->dev_id, regs); 326*b5dc7840SRoman Zippel node = node->next; 327*b5dc7840SRoman Zippel } while (node); 3281da177e4SLinus Torvalds } 32992445eaaSRoman Zippel 33092445eaaSRoman Zippel asmlinkage void handle_badint(struct pt_regs *regs) 33192445eaaSRoman Zippel { 33292445eaaSRoman Zippel kstat_cpu(0).irqs[0]++; 33392445eaaSRoman Zippel printk("unexpected interrupt from %u\n", regs->vector); 3341da177e4SLinus Torvalds } 3351da177e4SLinus Torvalds 3361da177e4SLinus Torvalds int show_interrupts(struct seq_file *p, void *v) 3371da177e4SLinus Torvalds { 338*b5dc7840SRoman Zippel struct irq_controller *contr; 339*b5dc7840SRoman Zippel struct irq_node *node; 3401da177e4SLinus Torvalds int i = *(loff_t *) v; 3411da177e4SLinus Torvalds 3421da177e4SLinus Torvalds /* autovector interrupts */ 343*b5dc7840SRoman Zippel if (i < SYS_IRQS && irq_list[i]) { 344*b5dc7840SRoman Zippel contr = irq_controller[i]; 345*b5dc7840SRoman Zippel node = irq_list[i]; 346*b5dc7840SRoman Zippel seq_printf(p, "%s %u: %10u %s", contr->name, i, kstat_cpu(0).irqs[i], node->devname); 347*b5dc7840SRoman Zippel while ((node = node->next)) 348*b5dc7840SRoman Zippel seq_printf(p, ", %s", node->devname); 349*b5dc7840SRoman Zippel seq_puts(p, "\n"); 3501da177e4SLinus Torvalds } else if (i == SYS_IRQS) 3511da177e4SLinus Torvalds mach_get_irq_list(p, v); 3521da177e4SLinus Torvalds return 0; 3531da177e4SLinus Torvalds } 3541da177e4SLinus Torvalds 3551da177e4SLinus Torvalds void init_irq_proc(void) 3561da177e4SLinus Torvalds { 3571da177e4SLinus Torvalds /* Insert /proc/irq driver here */ 3581da177e4SLinus Torvalds } 3591da177e4SLinus Torvalds 360