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

📄 pcibr_intr.c

📁 一个2.4.21版本的嵌入式linux内核
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * * 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) 2001-2002 Silicon Graphics, Inc. All rights reserved. */#include <linux/types.h>#include <linux/slab.h>#include <linux/module.h>#include <asm/sn/sgi.h>#include <asm/sn/sn_cpuid.h>#include <asm/sn/addrs.h>#include <asm/sn/arch.h>#include <asm/sn/iograph.h>#include <asm/sn/invent.h>#include <asm/sn/hcl.h>#include <asm/sn/labelcl.h>#include <asm/sn/xtalk/xwidget.h>#include <asm/sn/pci/bridge.h>#include <asm/sn/pci/pciio.h>#include <asm/sn/pci/pcibr.h>#include <asm/sn/pci/pcibr_private.h>#include <asm/sn/pci/pci_defs.h>#include <asm/sn/prio.h>#include <asm/sn/xtalk/xbow.h>#include <asm/sn/ioc3.h>#include <asm/sn/eeprom.h>#include <asm/sn/io.h>#include <asm/sn/sn_private.h>#ifdef __ia64#define rmallocmap atemapalloc#define rmfreemap atemapfree#define rmfree atefree#define rmalloc atealloc#endifunsigned		pcibr_intr_bits(pciio_info_t info, pciio_intr_line_t lines, int nslots);pcibr_intr_t            pcibr_intr_alloc(devfs_handle_t, device_desc_t, pciio_intr_line_t, devfs_handle_t);void                    pcibr_intr_free(pcibr_intr_t);void              pcibr_setpciint(xtalk_intr_t);int                     pcibr_intr_connect(pcibr_intr_t, intr_func_t, intr_arg_t);void                    pcibr_intr_disconnect(pcibr_intr_t);devfs_handle_t            pcibr_intr_cpu_get(pcibr_intr_t);void                    pcibr_xintr_preset(void *, int, xwidgetnum_t, iopaddr_t, xtalk_intr_vector_t);void                    pcibr_intr_func(intr_arg_t);extern pcibr_info_t      pcibr_info_get(devfs_handle_t);/* ===================================================================== *    INTERRUPT MANAGEMENT */unsignedpcibr_intr_bits(pciio_info_t info,		pciio_intr_line_t lines, int nslots){    pciio_slot_t            slot = PCIBR_INFO_SLOT_GET_INT(info);    unsigned		    bbits = 0;    /*     * Currently favored mapping from PCI     * slot number and INTA/B/C/D to Bridge     * PCI Interrupt Bit Number:     *     *     SLOT     A B C D     *      0       0 4 0 4     *      1       1 5 1 5     *      2       2 6 2 6     *      3       3 7 3 7     *      4       4 0 4 0     *      5       5 1 5 1     *      6       6 2 6 2     *      7       7 3 7 3     */    if (slot < nslots) {	if (lines & (PCIIO_INTR_LINE_A| PCIIO_INTR_LINE_C))	    bbits |= 1 << slot;	if (lines & (PCIIO_INTR_LINE_B| PCIIO_INTR_LINE_D))	    bbits |= 1 << (slot ^ 4);    }    return bbits;}/* *	Get the next wrapper pointer queued in the interrupt circular buffer. */pcibr_intr_wrap_tpcibr_wrap_get(pcibr_intr_cbuf_t cbuf){    pcibr_intr_wrap_t	wrap;	if (cbuf->ib_in == cbuf->ib_out)	    PRINT_PANIC( "pcibr intr circular buffer empty, cbuf=0x%p, ib_in=ib_out=%d\n",		(void *)cbuf, cbuf->ib_out);	wrap = cbuf->ib_cbuf[cbuf->ib_out++];	cbuf->ib_out = cbuf->ib_out % IBUFSIZE;	return(wrap);}/*  *	Queue a wrapper pointer in the interrupt circular buffer. */voidpcibr_wrap_put(pcibr_intr_wrap_t wrap, pcibr_intr_cbuf_t cbuf){	int	in;	int	s;	/*	 * Multiple CPUs could be executing this code simultaneously	 * if a handler has registered multiple interrupt lines and	 * the interrupts are directed to different CPUs.	 */	s = mutex_spinlock(&cbuf->ib_lock);	in = (cbuf->ib_in + 1) % IBUFSIZE;	if (in == cbuf->ib_out) 	    PRINT_PANIC( "pcibr intr circular buffer full, cbuf=0x%p, ib_in=%d\n",		(void *)cbuf, cbuf->ib_in);	cbuf->ib_cbuf[cbuf->ib_in] = wrap;	cbuf->ib_in = in;	mutex_spinunlock(&cbuf->ib_lock, s);	return;}/* *	There are end cases where a deadlock can occur if interrupt  *	processing completes and the Bridge b_int_status bit is still set. * *	One scenerio is if a second PCI interrupt occurs within 60ns of *	the previous interrupt being cleared. In this case the Bridge *	does not detect the transition, the Bridge b_int_status bit *	remains set, and because no transition was detected no interrupt *	packet is sent to the Hub/Heart. * *	A second scenerio is possible when a b_int_status bit is being *	shared by multiple devices: *						Device #1 generates interrupt *						Bridge b_int_status bit set *						Device #2 generates interrupt *		interrupt processing begins *		  ISR for device #1 runs and *			clears interrupt *						Device #1 generates interrupt *		  ISR for device #2 runs and *			clears interrupt *						(b_int_status bit still set) *		interrupt processing completes *		   *	Interrupt processing is now complete, but an interrupt is still *	outstanding for Device #1. But because there was no transition of *	the b_int_status bit, no interrupt packet will be generated and *	a deadlock will occur. * *	To avoid these deadlock situations, this function is used *	to check if a specific Bridge b_int_status bit is set, and if so, *	cause the setting of the corresponding interrupt bit. * *	On a XBridge (SN1), we do this by writing the appropriate Bridge Force  *	Interrupt register. On SN0, or SN1 with an older Bridge, the Bridge  *	Force Interrupt register does not exist, so we write the Hub  *	INT_PEND_MOD register directly. Likewise for Octane, where we write the  *	Heart Set Interrupt Status register directly. */voidpcibr_force_interrupt(pcibr_intr_wrap_t wrap){#ifdef PIC_LATER	unsigned	bit;	pcibr_soft_t    pcibr_soft = wrap->iw_soft;	bridge_t       *bridge = pcibr_soft->bs_base;	bit = wrap->iw_ibit;	PCIBR_DEBUG((PCIBR_DEBUG_INTR, pcibr_soft->bs_vhdl,		    "pcibr_force_interrupt: bit=0x%x\n", bit));	if (IS_XBRIDGE_OR_PIC_SOFT(pcibr_soft)) {	    bridge->b_force_pin[bit].intr = 1;	} else if ((1 << bit) & *wrap->iw_stat) {	    cpuid_t	    cpu;	    unsigned        intr_bit;	    xtalk_intr_t    xtalk_intr =				pcibr_soft->bs_intr[bit].bsi_xtalk_intr;	    intr_bit = (short) xtalk_intr_vector_get(xtalk_intr);	    cpu = xtalk_intr_cpuid_get(xtalk_intr);	    REMOTE_CPU_SEND_INTR(cpu, intr_bit);	}#endif	/* PIC_LATER */}/*ARGSUSED */pcibr_intr_tpcibr_intr_alloc(devfs_handle_t pconn_vhdl,		 device_desc_t dev_desc,		 pciio_intr_line_t lines,		 devfs_handle_t owner_dev){    pcibr_info_t            pcibr_info = pcibr_info_get(pconn_vhdl);    pciio_slot_t            pciio_slot = PCIBR_INFO_SLOT_GET_INT(pcibr_info);    pcibr_soft_t            pcibr_soft = (pcibr_soft_t) pcibr_info->f_mfast;    devfs_handle_t            xconn_vhdl = pcibr_soft->bs_conn;    bridge_t               *bridge = pcibr_soft->bs_base;    int                     is_threaded = 0;    xtalk_intr_t           *xtalk_intr_p;    pcibr_intr_t           *pcibr_intr_p;    pcibr_intr_list_t      *intr_list_p;    unsigned                pcibr_int_bits;    unsigned                pcibr_int_bit;    xtalk_intr_t            xtalk_intr = (xtalk_intr_t)0;    hub_intr_t		    hub_intr;    pcibr_intr_t            pcibr_intr;    pcibr_intr_list_t       intr_entry;    pcibr_intr_list_t       intr_list;    bridgereg_t             int_dev;    PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_INTR_ALLOC, pconn_vhdl,    		"pcibr_intr_alloc: %s%s%s%s%s\n",		!(lines & 15) ? " No INTs?" : "",		lines & 1 ? " INTA" : "",		lines & 2 ? " INTB" : "",		lines & 4 ? " INTC" : "",		lines & 8 ? " INTD" : ""));    NEW(pcibr_intr);    if (!pcibr_intr)	return NULL;    pcibr_intr->bi_dev = pconn_vhdl;    pcibr_intr->bi_lines = lines;    pcibr_intr->bi_soft = pcibr_soft;    pcibr_intr->bi_ibits = 0;		/* bits will be added below */    pcibr_intr->bi_func = 0;            /* unset until connect */    pcibr_intr->bi_arg = 0;             /* unset until connect */    pcibr_intr->bi_flags = is_threaded ? 0 : PCIIO_INTR_NOTHREAD;    pcibr_intr->bi_mustruncpu = CPU_NONE;    pcibr_intr->bi_ibuf.ib_in = 0;    pcibr_intr->bi_ibuf.ib_out = 0;    mutex_spinlock_init(&pcibr_intr->bi_ibuf.ib_lock);    pcibr_int_bits = pcibr_soft->bs_intr_bits((pciio_info_t)pcibr_info, lines, 		PCIBR_NUM_SLOTS(pcibr_soft));    /*     * For each PCI interrupt line requested, figure     * out which Bridge PCI Interrupt Line it maps     * to, and make sure there are xtalk resources     * allocated for it.     */    PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_INTR_ALLOC, pconn_vhdl,		"pcibr_intr_alloc: pcibr_int_bits: 0x%x\n", pcibr_int_bits));    for (pcibr_int_bit = 0; pcibr_int_bit < 8; pcibr_int_bit ++) {	if (pcibr_int_bits & (1 << pcibr_int_bit)) {	    xtalk_intr_p = &pcibr_soft->bs_intr[pcibr_int_bit].bsi_xtalk_intr;	    xtalk_intr = *xtalk_intr_p;	    if (xtalk_intr == NULL) {		/*		 * This xtalk_intr_alloc is constrained for two reasons:		 * 1) Normal interrupts and error interrupts need to be delivered		 *    through a single xtalk target widget so that there aren't any		 *    ordering problems with DMA, completion interrupts, and error		 *    interrupts. (Use of xconn_vhdl forces this.)		 *		 * 2) On SN1, addressing constraints on SN1 and Bridge force		 *    us to use a single PI number for all interrupts from a		 *    single Bridge. (SN1-specific code forces this).		 */		/*		 * All code dealing with threaded PCI interrupt handlers		 * is located at the pcibr level. Because of this,		 * we always want the lower layers (hub/heart_intr_alloc, 		 * intr_level_connect) to treat us as non-threaded so we		 * don't set up a duplicate threaded environment. We make		 * this happen by calling a special xtalk interface.		 */		xtalk_intr = xtalk_intr_alloc_nothd(xconn_vhdl, dev_desc, 			owner_dev);		PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_INTR_ALLOC, pconn_vhdl,			    "pcibr_intr_alloc: xtalk_intr=0x%x\n", xtalk_intr));		/* both an assert and a runtime check on this:		 * we need to check in non-DEBUG kernels, and		 * the ASSERT gets us more information when		 * we use DEBUG kernels.		 */		ASSERT(xtalk_intr != NULL);		if (xtalk_intr == NULL) {		    /* it is quite possible that our		     * xtalk_intr_alloc failed because		     * someone else got there first,		     * and we can find their results		     * in xtalk_intr_p.		     */		    if (!*xtalk_intr_p) {#ifdef SUPPORT_PRINTING_V_FORMAT			printk(KERN_ALERT  				"pcibr_intr_alloc %v: unable to get xtalk interrupt resources",				xconn_vhdl);#else			printk(KERN_ALERT  				"pcibr_intr_alloc 0x%p: unable to get xtalk interrupt resources",				(void *)xconn_vhdl);#endif			/* yes, we leak resources here. */			return 0;		    }		} else if (compare_and_swap_ptr((void **) xtalk_intr_p, NULL, xtalk_intr)) {		    /*		     * now tell the bridge which slot is		     * using this interrupt line.		     */		    int_dev = bridge->b_int_device;		    int_dev &= ~BRIDGE_INT_DEV_MASK(pcibr_int_bit);		    int_dev |= pciio_slot << BRIDGE_INT_DEV_SHFT(pcibr_int_bit);		    bridge->b_int_device = int_dev;	/* XXXMP */		    PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_INTR_ALLOC, pconn_vhdl,

⌨️ 快捷键说明

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