📄 ip27-irq.c
字号:
/* * ip27-irq.c: Highlevel interrupt handling for IP27 architecture. * * Copyright (C) 1999, 2000 Ralf Baechle (ralf@gnu.org) * Copyright (C) 1999, 2000 Silicon Graphics, Inc. */#include <linux/config.h>#include <linux/init.h>#include <linux/errno.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/malloc.h>#include <linux/random.h>#include <linux/smp_lock.h>#include <linux/kernel_stat.h>#include <linux/delay.h>#include <linux/irq.h>#include <asm/bitops.h>#include <asm/bootinfo.h>#include <asm/io.h>#include <asm/mipsregs.h>#include <asm/system.h>#include <asm/ptrace.h>#include <asm/processor.h>#include <asm/pci/bridge.h>#include <asm/sn/sn0/hub.h>#include <asm/sn/sn0/ip27.h>#include <asm/sn/arch.h>#include <asm/sn/intr.h>#include <asm/sn/intr_public.h>#undef DEBUG_IRQ#ifdef DEBUG_IRQ#define DBG(x...) printk(x)#else#define DBG(x...)#endif/* * 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. */extern asmlinkage void ip27_irq(void);extern int irq_to_bus[], irq_to_slot[], bus_to_cpu[];int intr_connect_level(int cpu, int bit);int intr_disconnect_level(int cpu, int bit);unsigned long spurious_count = 0;/* * we need to map irq's up to at least bit 7 of the INT_MASK0_A register * since bits 0-6 are pre-allocated for other purposes. */#define IRQ_TO_SWLEVEL(cpu, i) i + 7#define SWLEVEL_TO_IRQ(cpu, s) s - 7/* * use these macros to get the encoded nasid and widget id * from the irq value */#define IRQ_TO_BUS(i) irq_to_bus[(i)]#define IRQ_TO_CPU(i) bus_to_cpu[IRQ_TO_BUS(i)]#define NASID_FROM_PCI_IRQ(i) bus_to_nid[IRQ_TO_BUS(i)]#define WID_FROM_PCI_IRQ(i) bus_to_wid[IRQ_TO_BUS(i)]#define SLOT_FROM_PCI_IRQ(i) irq_to_slot[i]void disable_irq(unsigned int irq_nr){ panic("disable_irq() called ...");}void enable_irq(unsigned int irq_nr){ panic("enable_irq() called ...");}/* This is stupid for an Origin which can have thousands of IRQs ... */static struct irqaction *irq_action[NR_IRQS];int get_irq_list(char *buf){ int i, len = 0; struct irqaction * action; for (i = 0 ; i < NR_IRQS ; 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;}/* * do_IRQ handles all normal device IRQ's (the special SMP cross-CPU interrupts * have their own specific handlers). */static void do_IRQ(cpuid_t thiscpu, int irq, struct pt_regs * regs){ struct irqaction *action; int do_random; irq_enter(thiscpu, irq); kstat.irqs[thiscpu][irq]++; action = *(irq + irq_action); if (action) { if (!(action->flags & SA_INTERRUPT)) __sti(); 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(thiscpu, irq); /* unmasking and bottom half handling is done magically for us. */}/* * Find first bit set */static int ms1bit(unsigned long x){ int b; if (x >> 32) b = 32, x >>= 32; else b = 0; if (x >> 16) b += 16, x >>= 16; if (x >> 8) b += 8, x >>= 8; if (x >> 4) b += 4, x >>= 4; if (x >> 2) b += 2, x >>= 2; return b + (int) (x >> 1);}/* * This code is unnecessarily complex, because we do SA_INTERRUPT * intr enabling. Basically, once we grab the set of intrs we need * to service, we must mask _all_ these interrupts; firstly, to make * sure the same intr does not intr again, causing recursion that * can lead to stack overflow. Secondly, we can not just mask the * one intr we are do_IRQing, because the non-masked intrs in the * first set might intr again, causing multiple servicings of the * same intr. This effect is mostly seen for intercpu intrs. * Kanoj 05.13.00 */void ip27_do_irq(struct pt_regs *regs){ int irq, swlevel; hubreg_t pend0, mask0; cpuid_t thiscpu = smp_processor_id(); int pi_int_mask0 = ((cputoslice(thiscpu) == 0) ? PI_INT_MASK0_A : PI_INT_MASK0_B); /* copied from Irix intpend0() */ while (((pend0 = LOCAL_HUB_L(PI_INT_PEND0)) & (mask0 = LOCAL_HUB_L(pi_int_mask0))) != 0) { pend0 &= mask0; /* Pick intrs we should look at */ if (pend0) { /* Prevent any of the picked intrs from recursing */ LOCAL_HUB_S(pi_int_mask0, mask0 & ~(pend0)); do { swlevel = ms1bit(pend0); LOCAL_HUB_CLR_INTR(swlevel); /* "map" swlevel to irq */ irq = SWLEVEL_TO_IRQ(thiscpu, swlevel); do_IRQ(thiscpu, irq, regs); /* clear bit in pend0 */ pend0 ^= 1ULL << swlevel; } while(pend0); /* Now allow the set of serviced intrs again */ LOCAL_HUB_S(pi_int_mask0, mask0); LOCAL_HUB_L(PI_INT_PEND0); } }}/* Startup one of the (PCI ...) IRQs routes over a bridge. */static unsigned int bridge_startup(unsigned int irq){ bridgereg_t device; bridge_t *bridge; int pin, swlevel; cpuid_t cpu; nasid_t master = NASID_FROM_PCI_IRQ(irq); bridge = (bridge_t *) NODE_SWIN_BASE(master, WID_FROM_PCI_IRQ(irq)); pin = SLOT_FROM_PCI_IRQ(irq); cpu = IRQ_TO_CPU(irq); DBG("bridge_startup(): irq= 0x%x pin=%d\n", irq, pin); /* * "map" irq to a swlevel greater than 6 since the first 6 bits * of INT_PEND0 are taken */ swlevel = IRQ_TO_SWLEVEL(cpu, irq); intr_connect_level(cpu, swlevel); bridge->b_int_addr[pin].addr = (0x20000 | swlevel | (master << 8)); bridge->b_int_enable |= (1 << pin); /* more stuff in int_enable reg */ bridge->b_int_enable |= 0x7ffffe00; /* * XXX This only works if b_int_device is initialized to 0! * We program the bridge to have a 1:1 mapping between devices * (slots) and intr pins. */ device = bridge->b_int_device; device |= (pin << (pin*3)); bridge->b_int_device = device; bridge->b_widget.w_tflush; /* Flush */ return 0; /* Never anything pending. */}/* Shutdown one of the (PCI ...) IRQs routes over a bridge. */static unsigned int bridge_shutdown(unsigned int irq){ bridge_t *bridge; int pin, swlevel; bridge = (bridge_t *) NODE_SWIN_BASE(NASID_FROM_PCI_IRQ(irq), WID_FROM_PCI_IRQ(irq)); DBG("bridge_shutdown: irq 0x%x\n", irq); pin = SLOT_FROM_PCI_IRQ(irq); /* * map irq to a swlevel greater than 6 since the first 6 bits * of INT_PEND0 are taken */ swlevel = IRQ_TO_SWLEVEL(cpu, irq); intr_disconnect_level(smp_processor_id(), swlevel); bridge->b_int_enable &= ~(1 << pin); bridge->b_widget.w_tflush; /* Flush */ return 0; /* Never anything pending. */}void irq_debug(void){ bridge_t *bridge = (bridge_t *) 0x9200000008000000; printk("bridge->b_int_status = 0x%x\n", bridge->b_int_status); printk("bridge->b_int_enable = 0x%x\n", bridge->b_int_enable); printk("PI_INT_PEND0 = 0x%lx\n", LOCAL_HUB_L(PI_INT_PEND0)); printk("PI_INT_MASK0_A = 0x%lx\n", LOCAL_HUB_L(PI_INT_MASK0_A));}int setup_irq(unsigned int irq, struct irqaction *new){ int shared = 0; struct irqaction *old, **p; unsigned long flags; DBG("setup_irq: 0x%x\n", irq); if (irq >= NR_IRQS) { printk("IRQ array overflow %d\n", irq); while(1); } if (new->flags & SA_SAMPLE_RANDOM) rand_initialize_irq(irq); save_and_cli(flags); p = irq_action + irq; if ((old = *p) != NULL) { /* Can't share interrupts unless both agree to */ if (!(old->flags & new->flags & SA_SHIRQ)) { restore_flags(flags); return -EBUSY; } /* Add new interrupt at end of irq queue */ do { p = &old->next; old = *p; } while (old); shared = 1; } *p = new; if ((!shared) && (irq >= BASE_PCI_IRQ)) { bridge_startup(irq); } restore_flags(flags); return 0;}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; DBG("request_irq(): irq= 0x%x\n", irq); if (!handler) return -EINVAL; action = (struct irqaction *)kmalloc(sizeof(*action), 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; DBG("request_irq(): %s devid= 0x%x\n", devname, dev_id); retval = setup_irq(irq, action); DBG("request_irq(): retval= %d\n", retval); if (retval) kfree(action); return retval;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -