⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ip27-irq.c

📁 该文件是rt_linux
💻 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. * Copyright (C) 1999 - 2001 Kanoj Sarcar */#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/slab.h>#include <linux/random.h>#include <linux/smp_lock.h>#include <linux/kernel_stat.h>#include <linux/delay.h>#include <asm/bitops.h>#include <asm/bootinfo.h>#include <asm/io.h>#include <asm/mipsregs.h>#include <asm/system.h>#include <asm/irq.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/addrs.h>#include <asm/sn/agent.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/* These should die */unsigned char bus_to_wid[256];	/* widget id for linux pci bus */unsigned char bus_to_nid[256];	/* nasid for linux pci bus */unsigned char num_bridges;	/* number of bridges in the system *//* * 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 void do_IRQ(int irq, struct pt_regs *regs);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);/* * There is a single intpend register per node, and we want to have * distinct levels for intercpu intrs for both cpus A and B on a node. */int node_level_to_irq[MAX_COMPACT_NODES][PERNODE_LEVELS];/* * 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]static inline int alloc_level(cpuid_t cpunum, int irq){	cnodeid_t nodenum = CPUID_TO_COMPACT_NODEID(cpunum);	int j = LEAST_LEVEL + 3;	/* resched & crosscall entries taken */	while (++j < PERNODE_LEVELS) {		if (node_level_to_irq[nodenum][j] == -1) {			node_level_to_irq[nodenum][j] = irq;			return j;		}	}	printk("Cpu %ld flooded with devices\n", cpunum);	while(1);	return -1;}static inline int find_level(cpuid_t *cpunum, int irq){	int j;	cnodeid_t nodenum = INVALID_CNODEID;	while (++nodenum < MAX_COMPACT_NODES) {		j = LEAST_LEVEL + 3;	/* resched & crosscall entries taken */		while (++j < PERNODE_LEVELS)			if (node_level_to_irq[nodenum][j] == irq) {				*cpunum = 0;	/* XXX Fixme */				return(j);			}	}	printk("Could not identify cpu/level for irq %d\n", irq);	while(1);	return(-1);}/* * Find first bit set */static int ms1bit(unsigned long x){	int b = 0, s;	s = 16; if (x >> 16 == 0) s = 0; b += s; x >>= s;	s =  8; if (x >>  8 == 0) s = 0; b += s; x >>= s;	s =  4; if (x >>  4 == 0) s = 0; b += s; x >>= s;	s =  2; if (x >>  2 == 0) s = 0; b += s; x >>= s;	s =  1; if (x >>  1 == 0) s = 0; b += s;	return b;}/* * 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 = LEVEL_TO_IRQ(thiscpu, swlevel);				do_IRQ(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 startup_bridge_irq(unsigned int irq){	bridgereg_t device;	bridge_t *bridge;	int pin, swlevel;	cpuid_t cpu;	nasid_t master = NASID_FROM_PCI_IRQ(irq);	if (irq < BASE_PCI_IRQ)		return 0;        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 = alloc_level(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 shutdown_bridge_irq(unsigned int irq){	bridge_t *bridge;	int pin, swlevel;	cpuid_t cpu;	if (irq < BASE_PCI_IRQ)		return 0;	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 = find_level(&cpu, irq);	intr_disconnect_level(cpu, swlevel);	LEVEL_TO_IRQ(cpu, swlevel) = -1;	bridge->b_int_enable &= ~(1 << pin);	bridge->b_widget.w_tflush;                      /* Flush */	return 0;       /* Never anything pending.  */}static inline void enable_bridge_irq(unsigned int irq){	/* All the braindamage happens magically for us in ip27_do_irq */}static void disable_bridge_irq(unsigned int irq){	/* All the braindamage happens magically for us in ip27_do_irq */}static void mask_and_ack_bridge_irq(unsigned int irq){	/* All the braindamage happens magically for us in ip27_do_irq */}static void end_bridge_irq (unsigned int irq){	if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))		enable_bridge_irq(irq);}static struct hw_interrupt_type bridge_irq_type = {	"bridge",	startup_bridge_irq,	shutdown_bridge_irq,	enable_bridge_irq,	disable_bridge_irq,	mask_and_ack_bridge_irq,	end_bridge_irq};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));}void __init init_IRQ(void){	int i;	set_except_vector(0, ip27_irq);	/*	 * Right now the bridge irq is our kitchen sink interrupt type	 */	for (i = 0; i <= NR_IRQS; i++) {		irq_desc[i].status	= IRQ_DISABLED;		irq_desc[i].action	= 0;		irq_desc[i].depth	= 1;		irq_desc[i].handler	= &bridge_irq_type;	}}/* * Get values that vary depending on which CPU and bit we're operating on. */static hub_intmasks_t *intr_get_ptrs(cpuid_t cpu, int bit, int *new_bit,				hubreg_t **intpend_masks, int *ip){	hub_intmasks_t *hub_intmasks;	hub_intmasks = &cpu_data[cpu].p_intmasks;	if (bit < N_INTPEND_BITS) {		*intpend_masks = hub_intmasks->intpend0_masks;		*ip = 0;		*new_bit = bit;	} else {		*intpend_masks = hub_intmasks->intpend1_masks;		*ip = 1;		*new_bit = bit - N_INTPEND_BITS;	}	return hub_intmasks;}int intr_connect_level(int cpu, int bit){	int ip;	int slice = cputoslice(cpu);	volatile hubreg_t *mask_reg;	hubreg_t *intpend_masks;	nasid_t nasid = COMPACT_TO_NASID_NODEID(cputocnode(cpu));	(void)intr_get_ptrs(cpu, bit, &bit, &intpend_masks, &ip);	/* Make sure it's not already pending when we connect it. */	REMOTE_HUB_CLR_INTR(nasid, bit + ip * N_INTPEND_BITS);	intpend_masks[0] |= (1ULL << (u64)bit);	if (ip == 0) {		mask_reg = REMOTE_HUB_ADDR(nasid, PI_INT_MASK0_A +				PI_INT_MASK_OFFSET * slice);	} else {		mask_reg = REMOTE_HUB_ADDR(nasid, PI_INT_MASK1_A +				PI_INT_MASK_OFFSET * slice);	}	HUB_S(mask_reg, intpend_masks[0]);	return(0);}int intr_disconnect_level(int cpu, int bit){	int ip;	int slice = cputoslice(cpu);	volatile hubreg_t *mask_reg;	hubreg_t *intpend_masks;	nasid_t nasid = COMPACT_TO_NASID_NODEID(cputocnode(cpu));	(void)intr_get_ptrs(cpu, bit, &bit, &intpend_masks, &ip);	intpend_masks[0] &= ~(1ULL << (u64)bit);	if (ip == 0) {		mask_reg = REMOTE_HUB_ADDR(nasid, PI_INT_MASK0_A +				PI_INT_MASK_OFFSET * slice);	} else {		mask_reg = REMOTE_HUB_ADDR(nasid, PI_INT_MASK1_A +				PI_INT_MASK_OFFSET * slice);	}	HUB_S(mask_reg, intpend_masks[0]);	return(0);}void handle_resched_intr(int irq, void *dev_id, struct pt_regs *regs){	/* Nothing, the return from intr will work for us */}#ifdef CONFIG_SMPvoid core_send_ipi(int destid, unsigned int action){	int irq;#if (CPUS_PER_NODE == 2)	switch (action) {		case SMP_RESCHEDULE_YOURSELF:			irq = CPU_RESCHED_A_IRQ;			break;		case SMP_CALL_FUNCTION:			irq = CPU_CALL_A_IRQ;			break;		default:			panic("sendintr");	}	irq += cputoslice(destid);	/*	 * Convert the compact hub number to the NASID to get the correct	 * part of the address space.  Then set the interrupt bit associated	 * with the CPU we want to send the interrupt to.	 */	REMOTE_HUB_SEND_INTR(COMPACT_TO_NASID_NODEID(cputocnode(destid)),			FAST_IRQ_TO_LEVEL(irq));#else	<< Bomb!  Must redefine this for more than 2 CPUS. >>#endif}#endifextern void smp_call_function_interrupt(void);void install_cpuintr(int cpu){#ifdef CONFIG_SMP#if (CPUS_PER_NODE == 2)	static int done = 0;	/*	 * This is a hack till we have a pernode irqlist. Currently,	 * just have the master cpu set up the handlers for the per	 * cpu irqs.	 */	if (done == 0) {		int j;		if (request_irq(CPU_RESCHED_A_IRQ, handle_resched_intr,							0, "resched", 0))			panic("intercpu intr unconnectible");		if (request_irq(CPU_RESCHED_B_IRQ, handle_resched_intr,							0, "resched", 0))			panic("intercpu intr unconnectible");		if (request_irq(CPU_CALL_A_IRQ, smp_call_function_interrupt,							0, "callfunc", 0))			panic("intercpu intr unconnectible");		if (request_irq(CPU_CALL_B_IRQ, smp_call_function_interrupt,							0, "callfunc", 0))			panic("intercpu intr unconnectible");		for (j = 0; j < PERNODE_LEVELS; j++)			LEVEL_TO_IRQ(0, j) = -1;		LEVEL_TO_IRQ(0, FAST_IRQ_TO_LEVEL(CPU_RESCHED_A_IRQ)) =							CPU_RESCHED_A_IRQ;		LEVEL_TO_IRQ(0, FAST_IRQ_TO_LEVEL(CPU_RESCHED_B_IRQ)) =							CPU_RESCHED_B_IRQ;		LEVEL_TO_IRQ(0, FAST_IRQ_TO_LEVEL(CPU_CALL_A_IRQ)) =							CPU_CALL_A_IRQ;		LEVEL_TO_IRQ(0, FAST_IRQ_TO_LEVEL(CPU_CALL_B_IRQ)) =							CPU_CALL_B_IRQ;		for (j = 1; j < MAX_COMPACT_NODES; j++)			memcpy(&node_level_to_irq[j][0],			&node_level_to_irq[0][0],			sizeof(node_level_to_irq[0][0])*PERNODE_LEVELS);		done = 1;	}	intr_connect_level(cpu, FAST_IRQ_TO_LEVEL(CPU_RESCHED_A_IRQ +							cputoslice(cpu)));	intr_connect_level(cpu, FAST_IRQ_TO_LEVEL(CPU_CALL_A_IRQ +							cputoslice(cpu)));#else /* CPUS_PER_NODE */#error Must redefine this for more than 2 CPUS.#endif /* CPUS_PER_NODE */#endif /* CONFIG_SMP */}void install_tlbintr(int cpu){#if 0	int intr_bit = N_INTPEND_BITS + TLB_INTR_A + cputoslice(cpu);	intr_connect_level(cpu, intr_bit);#endif}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -