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

📄 ml_sn_intr.c

📁 上传linux-jx2410的源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* $Id: ml_SN_intr.c,v 1.1.1.1 2004/02/04 12:55:33 laputa Exp $ * * 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. * * Copyright (C) 1992 - 1997, 2000 Silicon Graphics, Inc. * Copyright (C) 2000 by Alan Mayer *//* * intr.c- *	This file contains all of the routines necessary to set up and *	handle interrupts on an IP27 board. */#ident  "$Revision: 1.1.1.1 $"#include <linux/types.h>#include <linux/config.h>#include <linux/slab.h>#include <asm/smp.h>#include <asm/sn/sgi.h>#include <asm/sn/iograph.h>#include <asm/sn/invent.h>#include <asm/sn/hcl.h>#include <asm/sn/labelcl.h>#include <asm/sn/nodemask.h>#include <asm/sn/sn_private.h>#include <asm/sn/klconfig.h>#include <asm/sn/synergy.h>#include <asm/sn/sn_cpuid.h>#include <asm/sn/pci/pciio.h>#include <asm/sn/pci/pcibr.h>#include <asm/sn/xtalk/xtalk.h>#include <asm/sn/pci/pcibr_private.h>#include <asm/sn/intr.h>#if DEBUG_INTR_TSTAMP_DEBUG#include <sys/debug.h>#include <sys/idbg.h>#include <sys/inst.h>void do_splx_log(int, int);void spldebug_log_event(int);#endif// FIXME - BRINGUP#ifdef CONFIG_SMPextern unsigned long cpu_online_map;#endif#define cpu_allows_intr(cpu)	(1)// If I understand what's going on with this, 32 should work.// physmem_maxradius seems to be the maximum number of router// hops to get from one end of the system to the other.  With// a maximally configured machine, with the dumbest possible// topology, we would make 32 router hops.  For what we're using// it for, the dumbest possible should suffice.#define physmem_maxradius()	32#define SUBNODE_ANY -1extern int	nmied;extern int	hub_intr_wakeup_cnt;extern synergy_da_t	*Synergy_da_indr[];extern cpuid_t         master_procid;extern cnodeid_t master_node_get(devfs_handle_t vhdl);extern snia_error_intr_handler(int irq, void *devid, struct pt_regs *pt_regs);#define INTR_LOCK(vecblk) \     (s = mutex_spinlock(&(vecblk)->vector_lock))#define INTR_UNLOCK(vecblk) \      mutex_spinunlock(&(vecblk)->vector_lock, s)/* * REACT/Pro *//*  * Find first bit set  * Used outside this file also  */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);}/* ARGSUSED */voidintr_stray(void *lvl){    PRINT_WARNING("Stray Interrupt - level %ld to cpu %d", (long)lvl, cpuid());}#if defined(DEBUG)/* Infrastructure  to gather the device - target cpu mapping info */#define MAX_DEVICES	1000	/* Reasonable large number . Need not be 				 * the exact maximum # devices possible.				 */#define MAX_NAME	100	typedef struct {	dev_t		dev;	/* device */	cpuid_t		cpuid;	/* target cpu */	cnodeid_t	cnodeid;/* node on which the target cpu is present */	int		bit;	/* intr bit reserved */	char		intr_name[MAX_NAME]; /* name of the interrupt */} intr_dev_targ_map_t;intr_dev_targ_map_t 	intr_dev_targ_map[MAX_DEVICES];uint64_t		intr_dev_targ_map_size;spinlock_t		intr_dev_targ_map_lock;/* Print out the device - target cpu mapping. * This routine is used only in the idbg command * "intrmap"  */voidintr_dev_targ_map_print(cnodeid_t cnodeid){	int  i,j,size = 0;	int  print_flag = 0,verbose = 0;		char node_name[10];		if (cnodeid != CNODEID_NONE) {		nodepda_t 	*npda;		npda = NODEPDA(cnodeid);		for (j=0; j<NUM_SUBNODES; j++) {			qprintf("\n SUBNODE %d\n INT_PEND0: ", j);			for(i = 0 ; i < N_INTPEND_BITS ; i++)				qprintf("%d",SNPDA(npda,j)->intr_dispatch0.info[i].ii_flags);			qprintf("\n INT_PEND1: ");			for(i = 0 ; i < N_INTPEND_BITS ; i++)				qprintf("%d",SNPDA(npda,j)->intr_dispatch1.info[i].ii_flags);		}		verbose = 1;	}	qprintf("\n Device - Target Map [Interrupts: %s Node%s]\n\n",		(verbose ? "All" : "Non-hardwired"),		(cnodeid == CNODEID_NONE) ? "s: All" : node_name); 			qprintf("Device\tCpu\tCnode\tIntr_bit\tIntr_name\n");	for (i = 0 ; i < intr_dev_targ_map_size ; i++) {		print_flag = 0;		if (verbose) {			if (cnodeid != CNODEID_NONE) {				if (cnodeid == intr_dev_targ_map[i].cnodeid)					print_flag = 1;			} else {				print_flag = 1;			}		} else {			if (intr_dev_targ_map[i].dev != 0) {				if (cnodeid != CNODEID_NONE) {					if (cnodeid == 					    intr_dev_targ_map[i].cnodeid)						print_flag = 1;				} else {					print_flag = 1;				}			}		}		if (print_flag) {			size++;			qprintf("%d\t%d\t%d\t%d\t%s\n",				intr_dev_targ_map[i].dev,				intr_dev_targ_map[i].cpuid,				intr_dev_targ_map[i].cnodeid,				intr_dev_targ_map[i].bit,				intr_dev_targ_map[i].intr_name);		}	}	qprintf("\nTotal : %d\n",size);}#endif /* DEBUG *//* * The spinlocks have already been initialized.  Now initialize the interrupt * vectors.  One processor on each hub does the work. */voidintr_init_vecblk(nodepda_t *npda, cnodeid_t node, int sn){    int			i, ip=0;    intr_vecblk_t	*vecblk;    subnode_pda_t	*snpda;    snpda = SNPDA(npda,sn);    do {	if (ip == 0) {	    vecblk = &snpda->intr_dispatch0;	} else {	    vecblk = &snpda->intr_dispatch1;	}	/* Initialize this vector. */	for (i = 0; i < N_INTPEND_BITS; i++) {		vecblk->vectors[i].iv_func = intr_stray;		vecblk->vectors[i].iv_prefunc = NULL;		vecblk->vectors[i].iv_arg = (void *)(__psint_t)(ip * N_INTPEND_BITS + i);		vecblk->info[i].ii_owner_dev = 0;		strcpy(vecblk->info[i].ii_name, "Unused");		vecblk->info[i].ii_flags = 0;	/* No flags */		vecblk->vectors[i].iv_mustruncpu = -1; /* No CPU yet. */	    }	mutex_spinlock_init(&vecblk->vector_lock);	vecblk->vector_count = 0;    	for (i = 0; i < CPUS_PER_SUBNODE; i++)		vecblk->cpu_count[i] = 0;	vecblk->vector_state = VECTOR_UNINITED;    } while (++ip < 2);}/* * do_intr_reserve_level(cpuid_t cpu, int bit, int resflags, int reserve,  *					devfs_handle_t owner_dev, char *name) *	Internal work routine to reserve or unreserve an interrupt level. *		cpu is the CPU to which the interrupt will be sent. *		bit is the level bit to reserve.  -1 means any level *		resflags should include II_ERRORINT if this is an *			error interrupt, II_THREADED if the interrupt handler *			will be threaded, or 0 otherwise. *		reserve should be set to II_RESERVE or II_UNRESERVE *			to get or clear a reservation. *		owner_dev is the device that "owns" this interrupt, if supplied *		name is a human-readable name for this interrupt, if supplied *	intr_reserve_level returns the bit reserved or -1 to indicate an error */static intdo_intr_reserve_level(cpuid_t cpu, int bit, int resflags, int reserve, 					devfs_handle_t owner_dev, char *name){    intr_vecblk_t	*vecblk;    hub_intmasks_t 	*hub_intmasks;    unsigned long s;    int rv = 0;    int ip;    synergy_da_t	*sda;    int		which_synergy;    cnodeid_t	cnode;    ASSERT(bit < N_INTPEND_BITS * 2);    cnode = cpuid_to_cnodeid(cpu);    which_synergy = cpuid_to_synergy(cpu);    sda = Synergy_da_indr[(cnode * 2) + which_synergy];    hub_intmasks = &sda->s_intmasks;    // hub_intmasks = &pdaindr[cpu].pda->p_intmasks;    // if (pdaindr[cpu].pda == NULL) return -1;    if ((bit < N_INTPEND_BITS) && !(resflags & II_ERRORINT)) {	vecblk = hub_intmasks->dispatch0;	ip = 0;    } else {	ASSERT((bit >= N_INTPEND_BITS) || (bit == -1));	bit -= N_INTPEND_BITS;	/* Get position relative to INT_PEND1 reg. */	vecblk = hub_intmasks->dispatch1;	ip = 1;    }    INTR_LOCK(vecblk);    if (bit <= -1) {	bit = 0;	ASSERT(reserve == II_RESERVE);	/* Choose any available level */	for (; bit < N_INTPEND_BITS; bit++) {	    if (!(vecblk->info[bit].ii_flags & II_RESERVE)) {		rv = bit;		break;	    }	}	/* Return -1 if all interrupt levels int this register are taken. */	if (bit == N_INTPEND_BITS)	    rv = -1;    } else {	/* Reserve a particular level if it's available. */	if ((vecblk->info[bit].ii_flags & II_RESERVE) == reserve) {	    /* Can't (un)reserve a level that's already (un)reserved. */	    rv = -1;	} else {	    rv = bit;	}    }    /* Reserve the level and bump the count. */    if (rv != -1) {	if (reserve) {	    int maxlen = sizeof(vecblk->info[bit].ii_name) - 1;	    int namelen;	    vecblk->info[bit].ii_flags |= (II_RESERVE | resflags);	    vecblk->info[bit].ii_owner_dev = owner_dev;	    /* Copy in the name. */	    namelen = name ? strlen(name) : 0;	    strncpy(vecblk->info[bit].ii_name, name, MIN(namelen, maxlen)); 	    vecblk->info[bit].ii_name[maxlen] = '\0';	    vecblk->vector_count++;	} else {	    vecblk->info[bit].ii_flags = 0;	/* Clear all the flags */	    vecblk->info[bit].ii_owner_dev = 0;	    /* Clear the name. */	    vecblk->info[bit].ii_name[0] = '\0';	    vecblk->vector_count--;	}    }    INTR_UNLOCK(vecblk);#if defined(DEBUG)    if (rv >= 0) {	    int namelen = name ? strlen(name) : 0;	    /* Gather this device - target cpu mapping information	     * in a table which can be used later by the idbg "intrmap"	     * command	     */	    s = mutex_spinlock(&intr_dev_targ_map_lock);	    if (intr_dev_targ_map_size < MAX_DEVICES) {		    intr_dev_targ_map_t	*p;		    p 		= &intr_dev_targ_map[intr_dev_targ_map_size];		    p->dev  	= owner_dev;		    p->cpuid 	= cpu; 		    p->cnodeid 	= cputocnode(cpu); 		    p->bit 	= ip * N_INTPEND_BITS + rv;		    strncpy(p->intr_name,			    name,			    MIN(MAX_NAME,namelen));		    intr_dev_targ_map_size++;	    }	    mutex_spinunlock(&intr_dev_targ_map_lock,s);    }#endif /* DEBUG */    return (((rv == -1) ? rv : (ip * N_INTPEND_BITS) + rv)) ;}/* * WARNING:  This routine should only be called from within ml/SN. *	Reserve an interrupt level. */intintr_reserve_level(cpuid_t cpu, int bit, int resflags, devfs_handle_t owner_dev, char *name){	return(do_intr_reserve_level(cpu, bit, resflags, II_RESERVE, owner_dev, name));}/* * WARNING:  This routine should only be called from within ml/SN. *	Unreserve an interrupt level. */voidintr_unreserve_level(cpuid_t cpu, int bit){	(void)do_intr_reserve_level(cpu, bit, 0, II_UNRESERVE, 0, NULL);}/* * 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,		/* Bit relative to the register */	      hubreg_t **intpend_masks, /* Masks for this register */	      intr_vecblk_t **vecblk,	/* Vecblock for this interrupt */	      int *ip)			/* Which intpend register */{	hub_intmasks_t *hub_intmasks;	synergy_da_t	*sda;	int		which_synergy;	cnodeid_t	cnode;	ASSERT(bit < N_INTPEND_BITS * 2);	cnode = cpuid_to_cnodeid(cpu);	which_synergy = cpuid_to_synergy(cpu);	sda = Synergy_da_indr[(cnode * 2) + which_synergy];	hub_intmasks = &sda->s_intmasks;	// hub_intmasks = &pdaindr[cpu].pda->p_intmasks;	if (bit < N_INTPEND_BITS) {		*intpend_masks = hub_intmasks->intpend0_masks;		*vecblk = hub_intmasks->dispatch0;		*ip = 0;		*new_bit = bit;	} else {		*intpend_masks = hub_intmasks->intpend1_masks;		*vecblk = hub_intmasks->dispatch1;		*ip = 1;		*new_bit = bit - N_INTPEND_BITS;	}	return hub_intmasks;}/* * intr_connect_level(cpuid_t cpu, int bit, ilvl_t intr_swlevel,  *		intr_func_t intr_func, void *intr_arg); *	This is the lowest-level interface to the interrupt code.  It shouldn't *	be called from outside the ml/SN directory. *	intr_connect_level hooks up an interrupt to a particular bit in *	the INT_PEND0/1 masks.  Returns 0 on success. *		cpu is the CPU to which the interrupt will be sent. *		bit is the level bit to connect to *		intr_swlevel tells which software level to use *		intr_func is the interrupt handler *		intr_arg is an arbitrary argument interpreted by the handler *		intr_prefunc is a prologue function, to be called *			with interrupts disabled, to disable *			the interrupt at source.  It is called *			with the same argument.  Should be NULL for *			typical interrupts, which can be masked *			by the infrastructure at the level bit. *	intr_connect_level returns 0 on success or nonzero on an error *//* ARGSUSED */intintr_connect_level(cpuid_t cpu, int bit, ilvl_t intr_swlevel, 		intr_func_t intr_func, void *intr_arg,		intr_func_t intr_prefunc){    intr_vecblk_t	*vecblk;    hubreg_t		*intpend_masks;    int rv = 0;    int ip;    unsigned long s;    ASSERT(bit < N_INTPEND_BITS * 2);    (void)intr_get_ptrs(cpu, bit, &bit, &intpend_masks,				 &vecblk, &ip);    INTR_LOCK(vecblk);    if ((vecblk->info[bit].ii_flags & II_INUSE) ||	(!(vecblk->info[bit].ii_flags & II_RESERVE))) {	/* Can't assign to a level that's in use or isn't reserved. */	rv = -1;    } else {	/* Stuff parameters into vector and info */	vecblk->vectors[bit].iv_func = intr_func;	vecblk->vectors[bit].iv_prefunc = intr_prefunc;	vecblk->vectors[bit].iv_arg = intr_arg;	vecblk->info[bit].ii_flags |= II_INUSE;    }    /* Now stuff the masks if everything's okay. */    if (!rv) {	int lslice;	volatile hubreg_t *mask_reg;	// nasid_t nasid = COMPACT_TO_NASID_NODEID(cputocnode(cpu));	nasid_t nasid = cpuid_to_nasid(cpu);	int	subnode = cpuid_to_subnode(cpu);	/* Make sure it's not already pending when we connect it. */	REMOTE_HUB_PI_CLR_INTR(nasid, subnode, bit + ip * N_INTPEND_BITS);	intpend_masks[0] |= (1ULL << (uint64_t)bit);	lslice = cputolocalslice(cpu);	vecblk->cpu_count[lslice]++;#if SN1	/*	 * On SN1, there are 8 interrupt mask registers per node:	 * 	PI_0 MASK_0 A	 * 	PI_0 MASK_1 A	 * 	PI_0 MASK_0 B	 * 	PI_0 MASK_1 B	 * 	PI_1 MASK_0 A	 * 	PI_1 MASK_1 A	 * 	PI_1 MASK_0 B	 * 	PI_1 MASK_1 B	 */#endif	if (ip == 0) {		mask_reg = REMOTE_HUB_PI_ADDR(nasid, subnode, 		        PI_INT_MASK0_A + PI_INT_MASK_OFFSET * lslice);	} else {		mask_reg = REMOTE_HUB_PI_ADDR(nasid, subnode,			PI_INT_MASK1_A + PI_INT_MASK_OFFSET * lslice);	}	HUB_S(mask_reg, intpend_masks[0]);    }    INTR_UNLOCK(vecblk);    return rv;}/* * intr_disconnect_level(cpuid_t cpu, int bit) * *	This is the lowest-level interface to the interrupt code.  It should *	not be called from outside the ml/SN directory. *	intr_disconnect_level removes a particular bit from an interrupt in * 	the INT_PEND0/1 masks.  Returns 0 on success or nonzero on failure. */intintr_disconnect_level(cpuid_t cpu, int bit){    intr_vecblk_t	*vecblk;    hubreg_t		*intpend_masks;    unsigned long s;    int rv = 0;    int ip;    (void)intr_get_ptrs(cpu, bit, &bit, &intpend_masks,				 &vecblk, &ip);    INTR_LOCK(vecblk);    if ((vecblk->info[bit].ii_flags & (II_RESERVE | II_INUSE)) !=	((II_RESERVE | II_INUSE))) {	/* Can't remove a level that's not in use or isn't reserved. */	rv = -1;    } else {	/* Stuff parameters into vector and info */	vecblk->vectors[bit].iv_func = (intr_func_t)NULL;	vecblk->vectors[bit].iv_prefunc = (intr_func_t)NULL;	vecblk->vectors[bit].iv_arg = 0;	vecblk->info[bit].ii_flags &= ~II_INUSE;#ifdef BASE_ITHRTEAD	vecblk->vectors[bit].iv_mustruncpu = -1; /* No mustrun CPU any more. */#endif    }    /* Now clear the masks if everything's okay. */    if (!rv) {	int lslice;	volatile hubreg_t *mask_reg;	intpend_masks[0] &= ~(1ULL << (uint64_t)bit);	lslice = cputolocalslice(cpu);	vecblk->cpu_count[lslice]--;	mask_reg = REMOTE_HUB_PI_ADDR(COMPACT_TO_NASID_NODEID(cputocnode(cpu)), 				   cpuid_to_subnode(cpu),				   ip == 0 ? PI_INT_MASK0_A : PI_INT_MASK1_A);	mask_reg = (volatile hubreg_t *)((__psunsigned_t)mask_reg +					(PI_INT_MASK_OFFSET * lslice));	*mask_reg = intpend_masks[0];    }    INTR_UNLOCK(vecblk);    return rv;

⌨️ 快捷键说明

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