📄 iosapic.c
字号:
case PDC_RET_OK: /* PAT box. Proceed to get the IRT */ /* save the number of entries in the table */ num_entries = pdc_io_num.num; ASSERT(0UL != num_entries); /* ** allocate memory for interrupt routing table ** This interface isn't really right. We are assuming ** the contents of the table are exclusively ** for I/O sapic devices. */ table = IOSAPIC_KALLOC(struct irt_entry, num_entries); if (table == NULL) { printk(KERN_WARNING MODULE_NAME ": read_irt : can not alloc mem for IRT\n"); return 0; } /* get PCI INT routing table */ status = pdc_pat_get_irt( (void *) table, cell_num); DBG(KERN_DEBUG "pdc_pat_get_irt: %ld\n", status); ASSERT(status == PDC_RET_OK); break; case PDC_RET_NE_PROC: /* Not a PAT platform. Try PDC_PCI extensions */ /* ** C3000/J5000 (and similar) platforms with "legacy" PDC ** will return exactly one IRT. ** So if we have one, don't need to get it again. */ if (NULL != irt_cell) break; status = pdc_pci_irt_size( (void *)&pdc_io_num, /* elroy HPA (really a NOP) */ 0); DBG(KERN_WARNING "pdc_pci_irt_size: %ld\n", status); if (PDC_RET_OK != status) { /* Not a "legacy" system with I/O SAPIC either */ return 0; } num_entries = pdc_io_num.num; ASSERT(0UL != num_entries); table = IOSAPIC_KALLOC(struct irt_entry, num_entries); if (table == NULL) { printk(KERN_WARNING MODULE_NAME ": read_irt : can not alloc mem for IRT\n"); return 0; } status = pdc_pci_irt( (void *) &pdc_io_num, (void *) NULL, /* Elroy HPA - not used */ (void *) table); ASSERT(PDC_RET_OK == status); break; default: printk(KERN_WARNING MODULE_NAME ": PDC_PAT_IO call failed with %ld\n", status); break; } /* return interrupt table address */ *irt = table;#ifdef DEBUG_IOSAPIC_IRT { struct irt_entry *p = table; int i; printk(MODULE_NAME " Interrupt Routing Table (cell %ld)\n", cell_num); printk(MODULE_NAME " start = 0x%p num_entries %ld entry_size %d\n", table, num_entries, (int) sizeof(struct irt_entry)); for (i = 0 ; i < num_entries ; i++, p++) { printk(MODULE_NAME " %02x %02x %02x %02x %02x %02x %02x %02x %08x%08x\n", p->entry_type, p->entry_length, p->interrupt_type, p->polarity_trigger, p->src_bus_irq_devno, p->src_bus_id, p->src_seg_id, p->dest_iosapic_intin, ((u32 *) p)[2], ((u32 *) p)[3] ); } }#endif /* DEBUG_IOSAPIC_IRT */ return num_entries;}void __initiosapic_init(void){ /* init global data */ iosapic_lock = SPIN_LOCK_UNLOCKED; iosapic_list = (struct iosapic_info *) NULL; iosapic_count = 0; DBG("iosapic_init()\n"); /* ** get IRT for this cell. */ irt_num_entry = iosapic_load_irt(0L, &irt_cell); if (0 == irt_num_entry) irt_cell = NULL; /* old PDC w/o iosapic */#ifdef IOSAPIC_CALLBACK /* ** When new I/O SAPICs are discovered, this callback ** will get invoked. Implies lba driver will register ** I/O Sapic as a device it "discovered" with faked ** IODC data. */ register_driver(iosapic_driver_for);#endif /* IOSAPIC_CALLBACK */}/*** Return the IRT entry in case we need to look something else up.*/static struct irt_entry *irt_find_irqline(struct iosapic_info *isi, u8 slot, u8 intr_pin){ struct irt_entry *i = irt_cell; int cnt; /* track how many entries we've looked at */ u8 irq_devno = (slot << IRT_DEV_SHIFT) | (intr_pin-1); DBG_IRT("irt_find_irqline() SLOT %d pin %d\n", slot, intr_pin); for (cnt=0; cnt < irt_num_entry; cnt++, i++) { /* ** Validate: entry_type, entry_length, interrupt_type ** ** Difference between validate vs compare is the former ** should print debug info and is not expected to "fail" ** on current platforms. */ if (i->entry_type != IRT_IOSAPIC_TYPE) { DBG_IRT(KERN_WARNING MODULE_NAME ":find_irqline(0x%p): skipping entry %d type %d\n", i, cnt, i->entry_type); continue; } if (i->entry_length != IRT_IOSAPIC_LENGTH) { DBG_IRT(KERN_WARNING MODULE_NAME ":find_irqline(0x%p): skipping entry %d length %d\n", i, cnt, i->entry_length); continue; } if (i->interrupt_type != IRT_VECTORED_INTR) { DBG_IRT(KERN_WARNING MODULE_NAME ":find_irqline(0x%p): skipping entry %d interrupt_type %d\n", i, cnt, i->interrupt_type); continue; } /* ** Compare: dest_iosapic_addr, src_bus_irq_devno */ if (i->dest_iosapic_addr != (u64) ((long) isi->isi_hpa)) continue; if ((i->src_bus_irq_devno & IRT_IRQ_DEVNO_MASK) != irq_devno) continue; /* ** Ignore: src_bus_id and rc_seg_id correlate with ** iosapic_info->isi_hpa on HP platforms. ** If needed, pass in "PFA" (aka config space addr) ** instead of slot. */ /* Found it! */ return i; } printk(KERN_WARNING MODULE_NAME ": 0x%p : no IRT entry for slot %d, pin %d\n", isi->isi_hpa, slot, intr_pin); return NULL;}/*** xlate_pin() supports the skewing of IRQ lines done by subsidiary bridges.** Legacy PDC already does this translation for us and stores it in INTR_LINE.**** PAT PDC needs to basically do what legacy PDC does:** o read PIN** o adjust PIN in case device is "behind" a PPB** (eg 4-port 100BT and SCSI/LAN "Combo Card")** o convert slot/pin to I/O SAPIC input line.**** HP platforms only support:** o one level of skewing for any number of PPBs** o only support PCI-PCI Bridges.*/static struct irt_entry *iosapic_xlate_pin(struct iosapic_info *isi, struct pci_dev *pcidev){ u8 intr_pin, intr_slot; (void) pci_read_config_byte(pcidev, PCI_INTERRUPT_PIN, &intr_pin); DBG_IRT("iosapic_xlate_pin() SLOT %d pin %d\n", PCI_SLOT(pcidev->devfn), intr_pin); if (0 == intr_pin) { /* ** The device does NOT support/use IRQ lines. */ return NULL; } /* Check if pcidev behind a PPB */ if (NULL != pcidev->bus->self) { /* Convert pcidev INTR_PIN into something we ** can lookup in the IRT. */#ifdef PCI_BRIDGE_FUNCS /* ** Proposal #1: ** ** call implementation specific translation function ** This is architecturally "cleaner". HP-UX doesn't ** support other secondary bus types (eg. E/ISA) directly. ** May be needed for other processor (eg IA64) architectures ** or by some ambitous soul who wants to watch TV. */ if (pci_bridge_funcs->xlate_intr_line) { intr_pin = (*pci_bridge_funcs->xlate_intr_line)(pcidev); }#else /* PCI_BRIDGE_FUNCS */ struct pci_bus *p = pcidev->bus; /* ** Proposal #2: ** The "pin" is skewed ((pin + dev - 1) % 4). ** ** This isn't very clean since I/O SAPIC must assume: ** - all platforms only have PCI busses. ** - only PCI-PCI bridge (eg not PCI-EISA, PCI-PCMCIA) ** - IRQ routing is only skewed once regardless of ** the number of PPB's between iosapic and device. ** (Bit3 expansion chassis follows this rule) ** ** Advantage is it's really easy to implement. */ intr_pin = ((intr_pin-1)+PCI_SLOT(pcidev->devfn)) % 4; intr_pin++; /* convert back to INTA-D (1-4) */#endif /* PCI_BRIDGE_FUNCS */ /* ** Locate the host slot the PPB nearest the Host bus ** adapter. */ while (NULL != p->parent->self) p = p->parent; intr_slot = PCI_SLOT(p->self->devfn); } else { intr_slot = PCI_SLOT(pcidev->devfn); } DBG_IRT("iosapic_xlate_pin: bus %d slot %d pin %d\n", pcidev->bus->secondary, intr_slot, intr_pin); return irt_find_irqline(isi, intr_slot, intr_pin);}static voidiosapic_interrupt(int irq, void *dev_id, struct pt_regs * regs){ struct vector_info *vi = (struct vector_info *)dev_id; extern void do_irq(struct irqaction *a, int i, struct pt_regs *p); int irq_num = vi->vi_ios->isi_region->data.irqbase + vi->vi_irqline; DBG("iosapic_interrupt(): irq %d line %d eoi %p\n", irq, vi->vi_irqline, vi->vi_eoi_addr);/* FIXME: Need to mask/unmask? processor IRQ is already masked... */ do_irq(&vi->vi_ios->isi_region->action[vi->vi_irqline], irq_num, regs); /* ** PCI only supports level triggered in order to share IRQ lines. ** I/O SAPIC must always issue EOI. */ IOSAPIC_EOI(vi->vi_eoi_addr, vi->vi_eoi_data);}intiosapic_fixup_irq(void *isi_obj, struct pci_dev *pcidev){ struct iosapic_info *isi = (struct iosapic_info *)isi_obj; struct irt_entry *irte = NULL; /* only used if PAT PDC */ struct vector_info *vi; int isi_line; /* line used by device */ int tmp; if (NULL == isi) { printk(KERN_WARNING MODULE_NAME ": 0x%p hpa not registered\n", isi->isi_hpa); return(-1); } /* lookup IRT entry for isi/slot/pin set */ irte = iosapic_xlate_pin(isi, pcidev); if (NULL == irte) { return(-1); } DBG_IRT("iosapic_fixup_irq(): irte %p %x %x %x %x %x %x %x %x\n", irte, irte->entry_type, irte->entry_length, irte->polarity_trigger, irte->src_bus_irq_devno, irte->src_bus_id, irte->src_seg_id, irte->dest_iosapic_intin, (u32) irte->dest_iosapic_addr); isi_line = irte->dest_iosapic_intin; /* get vector info for this input line */ ASSERT(NULL != isi->isi_vector); vi = &(isi->isi_vector[isi_line]); DBG_IRT("iosapic_fixup_irq: line %d vi 0x%p\n", isi_line, vi); vi->vi_irte = irte; /* Allocate processor IRQ */ vi->vi_txn_irq = txn_alloc_irq();/* XXX/FIXME The txn_alloc_irq() code and related code should be moved** to enable_irq(). That way we only allocate processor IRQ bits** for devices that actually have drivers claiming them.** Right now we assign an IRQ to every PCI device present regardless** of whether it's used or not.*/ if (vi->vi_txn_irq < 0) panic("I/O sapic: couldn't get TXN IRQ\n"); /* enable_irq() will use txn_* to program IRdT */ vi->vi_txn_addr = txn_alloc_addr(vi->vi_txn_irq); vi->vi_txn_data = txn_alloc_data(vi->vi_txn_irq, 8); ASSERT(vi->vi_txn_data < 256); /* matches 8 above */ tmp = request_irq(vi->vi_txn_irq, iosapic_interrupt, 0, "iosapic", vi); ASSERT(tmp == 0); vi->vi_eoi_addr = ((void *) isi->isi_hpa) + IOSAPIC_REG_EOI; vi->vi_eoi_data = cpu_to_le32(vi->vi_irqline); ASSERT(NULL != isi->isi_region); /* ** pcidev->irq still needs to be virtualized. */ pcidev->irq = isi->isi_region->data.irqbase + isi_line; DBG_IRT("iosapic_fixup_irq() %d:%d %x %x line %d irq %d\n", PCI_SLOT(pcidev->devfn), PCI_FUNC(pcidev->devfn), pcidev->vendor, pcidev->device, isi_line, pcidev->irq); return(pcidev->irq);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -