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

📄 pcibr_intr.c

📁 microwindows移植到S3C44B0的源码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * * 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 Silicon Graphics, Inc. All rights reserved. */#include <linux/types.h>#include <linux/config.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);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);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){    pciio_slot_t            slot = pciio_info_slot_get(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 < 8) {	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 (IP35), we do this by writing the appropriate Bridge Force  *	Interrupt register. */voidpcibr_force_interrupt(pcibr_intr_wrap_t wrap){	unsigned	bit;	pcibr_soft_t    pcibr_soft = wrap->iw_soft;	bridge_t       *bridge = pcibr_soft->bs_base;	cpuid_t cpuvertex_to_cpuid(devfs_handle_t vhdl);	bit = wrap->iw_intr;	if (pcibr_soft->bs_xbridge) {	    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 = cpuvertex_to_cpuid(xtalk_intr_cpu_get(xtalk_intr));#if defined(CONFIG_IA64_SGI_SN1)	    REMOTE_CPU_SEND_INTR(cpu, intr_bit);#endif	}}/*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->f_slot;    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;    int                     thread_swlevel;    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;#if DEBUG && INTR_DEBUG    printk("%v: pcibr_intr_alloc\n"	    "%v:%s%s%s%s%s\n",	    owner_dev, pconn_vhdl,	    !(lines & 15) ? " No INTs?" : "",	    lines & 1 ? " INTA" : "",	    lines & 2 ? " INTB" : "",	    lines & 4 ? " INTC" : "",	    lines & 8 ? " INTD" : "");#endif    NEW(pcibr_intr);    if (!pcibr_intr)	return NULL;    if (dev_desc) {	cpuid_t intr_target_from_desc(device_desc_t, int);    } else {	extern int default_intr_pri;	is_threaded = 1; /* PCI interrupts are threaded, by default */	thread_swlevel = default_intr_pri;    }    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_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);    /*     * 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.     */#if DEBUG && INTR_DEBUG    printk("pcibr_int_bits: 0x%X\n", pcibr_int_bits);#endif     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 IP35, addressing constraints on IP35 and Bridge force		 *    us to use a single PI number for all interrupts from a		 *    single Bridge. (IP35-specific code forces this, and we		 *    verify in pcibr_setwidint.)		 */		/*		 * 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);#if DEBUG && INTR_DEBUG		printk("%v: xtalk_intr=0x%X\n", xconn_vhdl, xtalk_intr);#endif		/* 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 */#if DEBUG && INTR_DEBUG		    printk("%v: bridge intr bit %d clears my wrb\n",			    pconn_vhdl, pcibr_int_bit);#endif		} else {		    /* someone else got one allocated first;		     * free the one we just created, and		     * retrieve the one they allocated.		     */		    xtalk_intr_free(xtalk_intr);		    xtalk_intr = *xtalk_intr_p;#if PARANOID		    /* once xtalk_intr is set, we never clear it,		     * so if the CAS fails above, this condition		     * can "never happen" ...		     */		    if (!xtalk_intr) {			printk(KERN_ALERT  				"pcibr_intr_alloc %v: unable to set xtalk interrupt resources",				xconn_vhdl);			/* yes, we leak resources here. */			return 0;		    }#endif		}	    }	    pcibr_intr->bi_ibits |= 1 << pcibr_int_bit;	    NEW(intr_entry);	    intr_entry->il_next = NULL;	    intr_entry->il_intr = pcibr_intr;	    intr_entry->il_wrbf = &(bridge->b_wr_req_buf[pciio_slot].reg);	    intr_list_p = 		&pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_list;#if DEBUG && INTR_DEBUG#if defined(SUPPORT_PRINTING_V_FORMAT)	    printk("0x%x: Bridge bit %d wrap=0x%x\n",		pconn_vhdl, pcibr_int_bit,		pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap);#else	    printk("%v: Bridge bit %d wrap=0x%x\n",		pconn_vhdl, pcibr_int_bit,		pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap);#endif#endif	    if (compare_and_swap_ptr((void **) intr_list_p, NULL, intr_entry)) {		/* we are the first interrupt on this bridge bit.		 */#if DEBUG && INTR_DEBUG		printk("%v INT 0x%x (bridge bit %d) allocated [FIRST]\n",			pconn_vhdl, pcibr_int_bits, pcibr_int_bit);#endif		continue;	    }	    intr_list = *intr_list_p;	    pcibr_intr_p = &intr_list->il_intr;	    if (compare_and_swap_ptr((void **) pcibr_intr_p, NULL, pcibr_intr)) {		/* first entry on list was erased,		 * and we replaced it, so we		 * don't need our intr_entry.		 */		DEL(intr_entry);#if DEBUG && INTR_DEBUG		printk("%v INT 0x%x (bridge bit %d) replaces erased first\n",			pconn_vhdl, pcibr_int_bits, pcibr_int_bit);#endif		continue;	    }	    intr_list_p = &intr_list->il_next;	    if (compare_and_swap_ptr((void **) intr_list_p, NULL, intr_entry)) {		/* we are the new second interrupt on this bit.		 */		pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap.iw_shared = 1;#if DEBUG && INTR_DEBUG		printk("%v INT 0x%x (bridge bit %d) is new SECOND\n",			pconn_vhdl, pcibr_int_bits, pcibr_int_bit);#endif		continue;	    }	    while (1) {		pcibr_intr_p = &intr_list->il_intr;		if (compare_and_swap_ptr((void **) pcibr_intr_p, NULL, pcibr_intr)) {		    /* an entry on list was erased,		     * and we replaced it, so we		     * don't need our intr_entry.		     */		    DEL(intr_entry);#if DEBUG && INTR_DEBUG		    printk("%v INT 0x%x (bridge bit %d) replaces erased Nth\n",			    pconn_vhdl, pcibr_int_bits, pcibr_int_bit);#endif		    break;		}		intr_list_p = &intr_list->il_next;		if (compare_and_swap_ptr((void **) intr_list_p, NULL, intr_entry)) {		    /* entry appended to share list		     */#if DEBUG && INTR_DEBUG		    printk("%v INT 0x%x (bridge bit %d) is new Nth\n",			    pconn_vhdl, pcibr_int_bits, pcibr_int_bit);#endif		    break;		}		/* step to next record in chain		 */		intr_list = *intr_list_p;	    }	}    }#if DEBUG && INTR_DEBUG    printk("%v pcibr_intr_alloc complete\n", pconn_vhdl);

⌨️ 快捷键说明

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