pcibr_intr.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 701 行 · 第 1/2 页

C
701
字号
/* * 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-2003 Silicon Graphics, Inc. All rights reserved. */#include <linux/types.h>#include <linux/module.h>#include <asm/sn/sgi.h>#include <asm/sn/arch.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/io.h>#include <asm/sn/sn_private.h>#ifdef __ia64inline intcompare_and_swap_ptr(void **location, void *old_ptr, void *new_ptr){	/* FIXME - compare_and_swap_ptr NOT ATOMIC */	if (*location == old_ptr) {		*location = new_ptr;		return 1;	}	else		return 0;}#endifunsigned int		pcibr_intr_bits(pciio_info_t info, pciio_intr_line_t lines, int nslots);pcibr_intr_t            pcibr_intr_alloc(vertex_hdl_t, device_desc_t, pciio_intr_line_t, vertex_hdl_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);vertex_hdl_t            pcibr_intr_cpu_get(pcibr_intr_t);extern pcibr_info_t      pcibr_info_get(vertex_hdl_t);/* ===================================================================== *    INTERRUPT MANAGEMENT */unsigned intpcibr_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;}/* *	On SN systems there is a race condition between a PIO read response *	and DMA's.  In rare cases, the read response may beat the DMA, causing *	the driver to think that data in memory is complete and meaningful. *	This code eliminates that race. *	This routine is called by the PIO read routines after doing the read. *	This routine then forces a fake interrupt on another line, which *	is logically associated with the slot that the PIO is addressed to. *	(see sn_dma_flush_init() ) *	It then spins while watching the memory location that the interrupt *	is targetted to.  When the interrupt response arrives, we are sure *	that the DMA has landed in memory and it is safe for the driver *	to proceed. */extern struct sn_flush_nasid_entry flush_nasid_list[MAX_NASIDS];voidsn_dma_flush(unsigned long addr){	nasid_t nasid;	int wid_num;	struct sn_flush_device_list *p;	int i,j;	int bwin;	unsigned long flags;	nasid = NASID_GET(addr);	wid_num = SWIN_WIDGETNUM(addr);	bwin = BWIN_WINDOWNUM(addr);	if (flush_nasid_list[nasid].widget_p == NULL) return;	if (bwin > 0) {		unsigned long itte = flush_nasid_list[nasid].iio_itte[bwin];		wid_num = (itte >> IIO_ITTE_WIDGET_SHIFT) &				  IIO_ITTE_WIDGET_MASK;	}	if (flush_nasid_list[nasid].widget_p == NULL) return;	if (flush_nasid_list[nasid].widget_p[wid_num] == NULL) return;	p = &flush_nasid_list[nasid].widget_p[wid_num][0];	/* find a matching BAR */	for (i=0; i<DEV_PER_WIDGET;i++) {		for (j=0; j<PCI_ROM_RESOURCE;j++) {			if (p->bar_list[j].start == 0) break;			if (addr >= p->bar_list[j].start && addr <= p->bar_list[j].end) break;		}		if (j < PCI_ROM_RESOURCE && p->bar_list[j].start != 0) break;		p++;	}	/* if no matching BAR, return without doing anything. */	if (i == DEV_PER_WIDGET) return;	spin_lock_irqsave(&p->flush_lock, flags);	p->flush_addr = 0;	/* force an interrupt. */	*(volatile uint32_t *)(p->force_int_addr) = 1;	/* wait for the interrupt to come back. */	while (p->flush_addr != 0x10f);	/* okay, everything is synched up. */	spin_unlock_irqrestore(&p->flush_lock, flags);}EXPORT_SYMBOL(sn_dma_flush);/* *	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) and PIC (SN2), we do this by writing the appropriate Bridge Force  *	Interrupt register. */voidpcibr_force_interrupt(pcibr_intr_t intr){	unsigned	bit;	unsigned	bits;	pcibr_soft_t    pcibr_soft = intr->bi_soft;	bits = intr->bi_ibits;	for (bit = 0; bit < 8; bit++) {		if (bits & (1 << bit)) {			PCIBR_DEBUG((PCIBR_DEBUG_INTR, pcibr_soft->bs_vhdl,		    		"pcibr_force_interrupt: bit=0x%x\n", bit));			pcireg_force_intr_set(pcibr_soft, bit);		}	}}/*ARGSUSED */pcibr_intr_tpcibr_intr_alloc(vertex_hdl_t pconn_vhdl,		 device_desc_t dev_desc,		 pciio_intr_line_t lines,		 vertex_hdl_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;    vertex_hdl_t            xconn_vhdl = pcibr_soft->bs_conn;    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;    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" : ""));    pcibr_intr = kmalloc(sizeof (*(pcibr_intr)), GFP_KERNEL);    if (!pcibr_intr)	return NULL;    memset(pcibr_intr, 0, sizeof (*(pcibr_intr)));    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;    spin_lock_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%lx\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) {			printk(KERN_ALERT "pcibr_intr_alloc %s: "				"unable to get xtalk interrupt resources",				pcibr_soft->bs_name);			/* 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.		     */		    pcireg_intr_device_bit_clr(pcibr_soft, 			    BRIDGE_INT_DEV_MASK(pcibr_int_bit));		    pcireg_intr_device_bit_set(pcibr_soft, 			    (pciio_slot << BRIDGE_INT_DEV_SHFT(pcibr_int_bit)));		    PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_INTR_ALLOC, pconn_vhdl,		    		"bridge intr bit %d clears my wrb\n",				pcibr_int_bit));		} 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;		}	    }	    pcibr_intr->bi_ibits |= 1 << pcibr_int_bit;	    intr_entry = kmalloc(sizeof (*(intr_entry)), GFP_KERNEL);

⌨️ 快捷键说明

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