📄 old-irq.c
字号:
/* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Code to handle x86 style IRQs plus some generic interrupt stuff. * * Copyright (C) 1992 Linus Torvalds * Copyright (C) 1994 - 2001 Ralf Baechle * * Old rotten IRQ code. To be killed as soon as everybody had converted or * in 2.5.0, whatever comes first. */#include <linux/config.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/kernel_stat.h>#include <linux/module.h>#include <linux/signal.h>#include <linux/sched.h>#include <linux/types.h>#include <linux/interrupt.h>#include <linux/ioport.h>#include <linux/timex.h>#include <linux/slab.h>#include <linux/random.h>#include <asm/bitops.h>#include <asm/bootinfo.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/mipsregs.h>#include <asm/system.h>#include <asm/nile4.h>/* * The board specific setup routine sets irq_setup to point to a board * specific setup routine. */void (*irq_setup)(void);/* * Linux has a controller-independent x86 interrupt architecture. * every controller has a 'controller-template', that is used * by the main code to do the right thing. Each driver-visible * interrupt source is transparently wired to the apropriate * controller. Thus drivers need not be aware of the * interrupt-controller. * * Various interrupt controllers we handle: 8259 PIC, SMP IO-APIC, * PIIX4's internal 8259 PIC and SGI's Visual Workstation Cobalt (IO-)APIC. * (IO-APICs assumed to be messaging to Pentium local-APICs) * * the code is designed to be easily extended with new/different * interrupt controllers, without having to do assembly magic. *//* * This contains the irq mask for both 8259A irq controllers, it's an * int so we can deal with the third PIC in some systems like the RM300. * (XXX This is broken for big endian.) */static unsigned int cached_irq_mask = 0xffff;#define __byte(x,y) (((unsigned char *)&(y))[x])#define __word(x,y) (((unsigned short *)&(y))[x])#define __long(x,y) (((unsigned int *)&(y))[x])#define cached_21 (__byte(0,cached_irq_mask))#define cached_A1 (__byte(1,cached_irq_mask))unsigned long spurious_count = 0;/* * (un)mask_irq, disable_irq() and enable_irq() only handle (E)ISA and * PCI devices. Other onboard hardware needs specific routines. */static inline void mask_irq(unsigned int irq){ cached_irq_mask |= 1 << irq; if (irq & 8) { outb(cached_A1, 0xa1); } else { outb(cached_21, 0x21); }}static inline void unmask_irq(unsigned int irq){ cached_irq_mask &= ~(1 << irq); if (irq & 8) { outb(cached_A1, 0xa1); } else { outb(cached_21, 0x21); }}void i8259_disable_irq(unsigned int irq_nr){ unsigned long flags; save_and_cli(flags); mask_irq(irq_nr); restore_flags(flags);}void i8259_enable_irq(unsigned int irq_nr){ unsigned long flags; save_and_cli(flags); unmask_irq(irq_nr); restore_flags(flags);}static struct irqaction *irq_action[NR_IRQS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};int get_irq_list(char *buf){ int i, len = 0; struct irqaction * action; for (i = 0 ; i < 32 ; i++) { action = irq_action[i]; if (!action) continue; len += sprintf(buf+len, "%2d: %8d %c %s", i, kstat.irqs[0][i], (action->flags & SA_INTERRUPT) ? '+' : ' ', action->name); for (action=action->next; action; action = action->next) { len += sprintf(buf+len, ",%s %s", (action->flags & SA_INTERRUPT) ? " +" : "", action->name); } len += sprintf(buf+len, "\n"); } return len;}static inline void i8259_mask_and_ack_irq(int irq){ cached_irq_mask |= 1 << irq; if (irq & 8) { inb(0xa1); outb(cached_A1, 0xa1); outb(0x62, 0x20); /* Specific EOI to cascade */ outb(0x20, 0xa0); } else { inb(0x21); outb(cached_21, 0x21); outb(0x20, 0x20); }}asmlinkage void i8259_do_irq(int irq, struct pt_regs *regs){ struct irqaction *action; int do_random, cpu; cpu = smp_processor_id(); irq_enter(cpu, irq); if (irq >= 16) goto out; i8259_mask_and_ack_irq(irq); kstat.irqs[cpu][irq]++; action = *(irq + irq_action); if (!action) goto out; if (!(action->flags & SA_INTERRUPT)) __sti(); action = *(irq + irq_action); do_random = 0; do { do_random |= action->flags; action->handler(irq, action->dev_id, regs); action = action->next; } while (action); if (do_random & SA_SAMPLE_RANDOM) add_interrupt_randomness(irq); __cli(); unmask_irq (irq);out: irq_exit(cpu, irq);}/* * do_IRQ handles IRQ's that have been installed without the * SA_INTERRUPT flag: it uses the full signal-handling return * and runs with other interrupts enabled. All relatively slow * IRQ's should use this format: notably the keyboard/timer * routines. */asmlinkage void do_IRQ(int irq, struct pt_regs * regs){ struct irqaction *action; int do_random, cpu; cpu = smp_processor_id(); irq_enter(cpu, irq); kstat.irqs[cpu][irq]++; action = *(irq + irq_action); if (action) { if (!(action->flags & SA_INTERRUPT)) __sti(); action = *(irq + irq_action); do_random = 0; do { do_random |= action->flags; action->handler(irq, action->dev_id, regs); action = action->next; } while (action); if (do_random & SA_SAMPLE_RANDOM) add_interrupt_randomness(irq); __cli(); } irq_exit(cpu, irq); if (softirq_pending(cpu)) do_softirq(); /* unmasking and bottom half handling is done magically for us. */}int i8259_setup_irq(int irq, struct irqaction * new){ int shared = 0; struct irqaction *old, **p; unsigned long flags; p = irq_action + irq; if ((old = *p) != NULL) { /* Can't share interrupts unless both agree to */ if (!(old->flags & new->flags & SA_SHIRQ)) return -EBUSY; /* Can't share interrupts unless both are same type */ if ((old->flags ^ new->flags) & SA_INTERRUPT) return -EBUSY; /* add new interrupt at end of irq queue */ do { p = &old->next; old = *p; } while (old); shared = 1; } if (new->flags & SA_SAMPLE_RANDOM) rand_initialize_irq(irq); save_and_cli(flags); *p = new; if (!shared) { if (is_i8259_irq(irq)) unmask_irq(irq);#if (defined(CONFIG_DDB5074) || defined(CONFIG_DDB5476)) else nile4_enable_irq(irq_to_nile4(irq));#endif } restore_flags(flags); return 0;}/* * Request_interrupt and free_interrupt ``sort of'' handle interrupts of * non i8259 devices. They will have to be replaced by architecture * specific variants. For now we still use this as broken as it is because * it used to work ... */int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long irqflags, const char * devname, void *dev_id){ int retval; struct irqaction * action; if (irq >= 32) return -EINVAL; if (!handler) return -EINVAL; action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL); if (!action) return -ENOMEM; action->handler = handler; action->flags = irqflags; action->mask = 0; action->name = devname; action->next = NULL; action->dev_id = dev_id; retval = i8259_setup_irq(irq, action); if (retval) kfree(action); return retval;} void free_irq(unsigned int irq, void *dev_id){ struct irqaction * action, **p; unsigned long flags; if (irq > 31) { printk("Trying to free IRQ%d\n",irq); return; } for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) { if (action->dev_id != dev_id) continue; /* Found it - now free it */ save_and_cli(flags); *p = action->next; if (!irq[irq_action]) mask_irq(irq); restore_flags(flags); kfree(action); return; } printk("Trying to free free IRQ%d\n",irq);}unsigned long probe_irq_on (void){ unsigned int i, irqs = 0; unsigned long delay; /* first, enable any unassigned (E)ISA irqs */ for (i = 15; i > 0; i--) { if (!irq_action[i]) { i8259_enable_irq(i); irqs |= (1 << i); } } /* wait for spurious interrupts to mask themselves out again */ for (delay = jiffies + HZ/10; time_before(jiffies, delay); ) /* about 100ms delay */; /* now filter out any obviously spurious interrupts */ return irqs & ~cached_irq_mask;}int probe_irq_off (unsigned long irqs){ unsigned int i;#ifdef DEBUG printk("probe_irq_off: irqs=0x%04x irqmask=0x%04x\n", irqs, irqmask);#endif irqs &= cached_irq_mask; if (!irqs) return 0; i = ffz(~irqs); if (irqs != (irqs & (1 << i))) i = -i; return i;}void __init i8259_init(void){ /* Init master interrupt controller */ outb(0x11, 0x20); /* Start init sequence */ outb(0x00, 0x21); /* Vector base */ outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */ outb(0x01, 0x21); /* Select 8086 mode */ outb(0xff, 0x21); /* Mask all */ /* Init slave interrupt controller */ outb(0x11, 0xa0); /* Start init sequence */ outb(0x08, 0xa1); /* Vector base */ outb(0x02, 0xa1); /* edge triggered, Cascade (slave) on IRQ2 */ outb(0x01, 0xa1); /* Select 8086 mode */ outb(0xff, 0xa1); /* Mask all */ outb(cached_A1, 0xa1); outb(cached_21, 0x21);}void __init init_IRQ(void){ /* i8259_init(); */ irq_setup();}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -