📄 ml_sn_intr.c
字号:
/* $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 + -