📄 pcibr_intr.c
字号:
/* * * 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/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/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 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);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(vertex_hdl_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) 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; /* * Multiple CPUs could be executing this code simultaneously * if a handler has registered multiple interrupt lines and * the interrupts are directed to different CPUs. */ spin_lock(&cbuf->ib_lock); in = (cbuf->ib_in + 1) % IBUFSIZE; if (in == cbuf->ib_out) 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; spin_unlock(&cbuf->ib_lock); return;}/* * 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; volatile 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) { bwin--; switch (bwin) { case 0: wid_num = ((flush_nasid_list[nasid].iio_itte1) >> 8) & 0xf; break; case 1: wid_num = ((flush_nasid_list[nasid].iio_itte2) >> 8) & 0xf; break; case 2: wid_num = ((flush_nasid_list[nasid].iio_itte3) >> 8) & 0xf; break; case 3: wid_num = ((flush_nasid_list[nasid].iio_itte4) >> 8) & 0xf; break; case 4: wid_num = ((flush_nasid_list[nasid].iio_itte5) >> 8) & 0xf; break; case 5: wid_num = ((flush_nasid_list[nasid].iio_itte6) >> 8) & 0xf; break; case 6: wid_num = ((flush_nasid_list[nasid].iio_itte7) >> 8) & 0xf; break; } } 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. *(bridgereg_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); return;}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; bridge_t *bridge = pcibr_soft->bs_base; 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)); if (IS_XBRIDGE_OR_PIC_SOFT(pcibr_soft)) { bridge->b_force_pin[bit].intr = 1; } } }}/*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; 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; 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -