iosapic.c

来自「linux 内核源代码」· C语言 代码 · 共 943 行 · 第 1/2 页

C
943
字号
*/static struct irt_entry *iosapic_xlate_pin(struct iosapic_info *isi, struct pci_dev *pcidev){	u8 intr_pin, intr_slot;	pci_read_config_byte(pcidev, PCI_INTERRUPT_PIN, &intr_pin);	DBG_IRT("iosapic_xlate_pin(%s) SLOT %d pin %d\n",		pcidev->slot_name, PCI_SLOT(pcidev->devfn), intr_pin);	if (intr_pin == 0) {		/* 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 void iosapic_rd_irt_entry(struct vector_info *vi , u32 *dp0, u32 *dp1){	struct iosapic_info *isp = vi->iosapic;	u8 idx = vi->irqline;	*dp0 = iosapic_read(isp->addr, IOSAPIC_IRDT_ENTRY(idx));	*dp1 = iosapic_read(isp->addr, IOSAPIC_IRDT_ENTRY_HI(idx));}static void iosapic_wr_irt_entry(struct vector_info *vi, u32 dp0, u32 dp1){	struct iosapic_info *isp = vi->iosapic;	DBG_IRT("iosapic_wr_irt_entry(): irq %d hpa %lx 0x%x 0x%x\n",		vi->irqline, isp->isi_hpa, dp0, dp1);	iosapic_write(isp->addr, IOSAPIC_IRDT_ENTRY(vi->irqline), dp0);	/* Read the window register to flush the writes down to HW  */	dp0 = readl(isp->addr+IOSAPIC_REG_WINDOW);	iosapic_write(isp->addr, IOSAPIC_IRDT_ENTRY_HI(vi->irqline), dp1);	/* Read the window register to flush the writes down to HW  */	dp1 = readl(isp->addr+IOSAPIC_REG_WINDOW);}/*** set_irt prepares the data (dp0, dp1) according to the vector_info** and target cpu (id_eid).  dp0/dp1 are then used to program I/O SAPIC** IRdT for the given "vector" (aka IRQ line).*/static voidiosapic_set_irt_data( struct vector_info *vi, u32 *dp0, u32 *dp1){	u32 mode = 0;	struct irt_entry *p = vi->irte;	if ((p->polarity_trigger & IRT_PO_MASK) == IRT_ACTIVE_LO)		mode |= IOSAPIC_IRDT_PO_LOW;	if (((p->polarity_trigger >> IRT_EL_SHIFT) & IRT_EL_MASK) == IRT_LEVEL_TRIG)		mode |= IOSAPIC_IRDT_LEVEL_TRIG;	/*	** IA64 REVISIT	** PA doesn't support EXTINT or LPRIO bits.	*/	*dp0 = mode | (u32) vi->txn_data;	/*	** Extracting id_eid isn't a real clean way of getting it.	** But the encoding is the same for both PA and IA64 platforms.	*/	if (is_pdc_pat()) {		/*		** PAT PDC just hands it to us "right".		** txn_addr comes from cpu_data[x].txn_addr.		*/		*dp1 = (u32) (vi->txn_addr);	} else {		/* 		** eg if base_addr == 0xfffa0000),		**    we want to get 0xa0ff0000.		**		** eid	0x0ff00000 -> 0x00ff0000		** id	0x000ff000 -> 0xff000000		*/		*dp1 = (((u32)vi->txn_addr & 0x0ff00000) >> 4) |			(((u32)vi->txn_addr & 0x000ff000) << 12);	}	DBG_IRT("iosapic_set_irt_data(): 0x%x 0x%x\n", *dp0, *dp1);}static struct vector_info *iosapic_get_vector(unsigned int irq){	return irq_desc[irq].chip_data;}static void iosapic_disable_irq(unsigned int irq){	unsigned long flags;	struct vector_info *vi = iosapic_get_vector(irq);	u32 d0, d1;	spin_lock_irqsave(&iosapic_lock, flags);	iosapic_rd_irt_entry(vi, &d0, &d1);	d0 |= IOSAPIC_IRDT_ENABLE;	iosapic_wr_irt_entry(vi, d0, d1);	spin_unlock_irqrestore(&iosapic_lock, flags);}static void iosapic_enable_irq(unsigned int irq){	struct vector_info *vi = iosapic_get_vector(irq);	u32 d0, d1;	/* data is initialized by fixup_irq */	WARN_ON(vi->txn_irq  == 0);	iosapic_set_irt_data(vi, &d0, &d1);	iosapic_wr_irt_entry(vi, d0, d1);#ifdef DEBUG_IOSAPIC_IRT{	u32 *t = (u32 *) ((ulong) vi->eoi_addr & ~0xffUL);	printk("iosapic_enable_irq(): regs %p", vi->eoi_addr);	for ( ; t < vi->eoi_addr; t++)		printk(" %x", readl(t));	printk("\n");}printk("iosapic_enable_irq(): sel ");{	struct iosapic_info *isp = vi->iosapic;	for (d0=0x10; d0<0x1e; d0++) {		d1 = iosapic_read(isp->addr, d0);		printk(" %x", d1);	}}printk("\n");#endif	/*	 * Issuing I/O SAPIC an EOI causes an interrupt IFF IRQ line is	 * asserted.  IRQ generally should not be asserted when a driver	 * enables their IRQ. It can lead to "interesting" race conditions	 * in the driver initialization sequence.	 */	DBG(KERN_DEBUG "enable_irq(%d): eoi(%p, 0x%x)\n", irq,			vi->eoi_addr, vi->eoi_data);	iosapic_eoi(vi->eoi_addr, vi->eoi_data);}/* * PARISC only supports PCI devices below I/O SAPIC. * PCI only supports level triggered in order to share IRQ lines. * ergo I/O SAPIC must always issue EOI on parisc. * * i386/ia64 support ISA devices and have to deal with * edge-triggered interrupts too. */static void iosapic_end_irq(unsigned int irq){	struct vector_info *vi = iosapic_get_vector(irq);	DBG(KERN_DEBUG "end_irq(%d): eoi(%p, 0x%x)\n", irq,			vi->eoi_addr, vi->eoi_data);	iosapic_eoi(vi->eoi_addr, vi->eoi_data);	cpu_end_irq(irq);}static unsigned int iosapic_startup_irq(unsigned int irq){	iosapic_enable_irq(irq);	return 0;}#ifdef CONFIG_SMPstatic void iosapic_set_affinity_irq(unsigned int irq, cpumask_t dest){	struct vector_info *vi = iosapic_get_vector(irq);	u32 d0, d1, dummy_d0;	unsigned long flags;	if (cpu_check_affinity(irq, &dest))		return;	vi->txn_addr = txn_affinity_addr(irq, first_cpu(dest));	spin_lock_irqsave(&iosapic_lock, flags);	/* d1 contains the destination CPU, so only want to set that	 * entry */	iosapic_rd_irt_entry(vi, &d0, &d1);	iosapic_set_irt_data(vi, &dummy_d0, &d1);	iosapic_wr_irt_entry(vi, d0, d1);	spin_unlock_irqrestore(&iosapic_lock, flags);}#endifstatic struct hw_interrupt_type iosapic_interrupt_type = {	.typename =	"IO-SAPIC-level",	.startup =	iosapic_startup_irq,	.shutdown =	iosapic_disable_irq,	.enable =	iosapic_enable_irq,	.disable =	iosapic_disable_irq,	.ack =		cpu_ack_irq,	.end =		iosapic_end_irq,#ifdef CONFIG_SMP	.set_affinity =	iosapic_set_affinity_irq,#endif};int iosapic_fixup_irq(void *isi_obj, struct pci_dev *pcidev){	struct iosapic_info *isi = isi_obj;	struct irt_entry *irte = NULL;  /* only used if PAT PDC */	struct vector_info *vi;	int isi_line;	/* line used by device */	if (!isi) {		printk(KERN_WARNING MODULE_NAME ": hpa not registered for %s\n",			pci_name(pcidev));		return -1;	}#ifdef CONFIG_SUPERIO	/*	 * HACK ALERT! (non-compliant PCI device support)	 *	 * All SuckyIO interrupts are routed through the PIC's on function 1.	 * But SuckyIO OHCI USB controller gets an IRT entry anyway because	 * it advertises INT D for INT_PIN.  Use that IRT entry to get the	 * SuckyIO interrupt routing for PICs on function 1 (*BLEECCHH*).	 */	if (is_superio_device(pcidev)) {		/* We must call superio_fixup_irq() to register the pdev */		pcidev->irq = superio_fixup_irq(pcidev);		/* Don't return if need to program the IOSAPIC's IRT... */		if (PCI_FUNC(pcidev->devfn) != SUPERIO_USB_FN)			return pcidev->irq;	}#endif /* CONFIG_SUPERIO */	/* lookup IRT entry for isi/slot/pin set */	irte = iosapic_xlate_pin(isi, pcidev);	if (!irte) {		printk("iosapic: no IRTE for %s (IRQ not connected?)\n",				pci_name(pcidev));		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 */	vi = isi->isi_vector + isi_line;	DBG_IRT("iosapic_fixup_irq:  line %d vi 0x%p\n", isi_line, vi);	/* If this IRQ line has already been setup, skip it */	if (vi->irte)		goto out;	vi->irte = irte;	/*	 * Allocate processor 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.	 */	vi->txn_irq = txn_alloc_irq(8);	if (vi->txn_irq < 0)		panic("I/O sapic: couldn't get TXN IRQ\n");	/* enable_irq() will use txn_* to program IRdT */	vi->txn_addr = txn_alloc_addr(vi->txn_irq);	vi->txn_data = txn_alloc_data(vi->txn_irq);	vi->eoi_addr = isi->addr + IOSAPIC_REG_EOI;	vi->eoi_data = cpu_to_le32(vi->txn_data);	cpu_claim_irq(vi->txn_irq, &iosapic_interrupt_type, vi); out:	pcidev->irq = vi->txn_irq;	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;}/*** squirrel away the I/O Sapic Version*/static unsigned intiosapic_rd_version(struct iosapic_info *isi){	return iosapic_read(isi->addr, IOSAPIC_REG_VERSION);}/*** iosapic_register() is called by "drivers" with an integrated I/O SAPIC.** Caller must be certain they have an I/O SAPIC and know its MMIO address.****	o allocate iosapic_info and add it to the list**	o read iosapic version and squirrel that away**	o read size of IRdT.**	o allocate and initialize isi_vector[]**	o allocate irq region*/void *iosapic_register(unsigned long hpa){	struct iosapic_info *isi = NULL;	struct irt_entry *irte = irt_cell;	struct vector_info *vip;	int cnt;	/* track how many entries we've looked at */	/*	 * Astro based platforms can only support PCI OLARD if they implement	 * PAT PDC.  Legacy PDC omits LBAs with no PCI devices from the IRT.	 * Search the IRT and ignore iosapic's which aren't in the IRT.	 */	for (cnt=0; cnt < irt_num_entry; cnt++, irte++) {		WARN_ON(IRT_IOSAPIC_TYPE != irte->entry_type);		if (COMPARE_IRTE_ADDR(irte, hpa))			break;	}	if (cnt >= irt_num_entry) {		DBG("iosapic_register() ignoring 0x%lx (NOT FOUND)\n", hpa);		return NULL;	}	isi = kzalloc(sizeof(struct iosapic_info), GFP_KERNEL);	if (!isi) {		BUG();		return NULL;	}	isi->addr = ioremap_nocache(hpa, 4096);	isi->isi_hpa = hpa;	isi->isi_version = iosapic_rd_version(isi);	isi->isi_num_vectors = IOSAPIC_IRDT_MAX_ENTRY(isi->isi_version) + 1;	vip = isi->isi_vector = (struct vector_info *)		kzalloc(sizeof(struct vector_info) * isi->isi_num_vectors, GFP_KERNEL);	if (vip == NULL) {		kfree(isi);		return NULL;	}	for (cnt=0; cnt < isi->isi_num_vectors; cnt++, vip++) {		vip->irqline = (unsigned char) cnt;		vip->iosapic = isi;	}	return isi;}#ifdef DEBUG_IOSAPICstatic voidiosapic_prt_irt(void *irt, long num_entry){	unsigned int i, *irp = (unsigned int *) irt;	printk(KERN_DEBUG MODULE_NAME ": Interrupt Routing Table (%lx entries)\n", num_entry);	for (i=0; i<num_entry; i++, irp += 4) {		printk(KERN_DEBUG "%p : %2d %.8x %.8x %.8x %.8x\n",					irp, i, irp[0], irp[1], irp[2], irp[3]);	}}static voidiosapic_prt_vi(struct vector_info *vi){	printk(KERN_DEBUG MODULE_NAME ": vector_info[%d] is at %p\n", vi->irqline, vi);	printk(KERN_DEBUG "\t\tstatus:	 %.4x\n", vi->status);	printk(KERN_DEBUG "\t\ttxn_irq:  %d\n",  vi->txn_irq);	printk(KERN_DEBUG "\t\ttxn_addr: %lx\n", vi->txn_addr);	printk(KERN_DEBUG "\t\ttxn_data: %lx\n", vi->txn_data);	printk(KERN_DEBUG "\t\teoi_addr: %p\n",  vi->eoi_addr);	printk(KERN_DEBUG "\t\teoi_data: %x\n",  vi->eoi_data);}static voidiosapic_prt_isi(struct iosapic_info *isi){	printk(KERN_DEBUG MODULE_NAME ": io_sapic_info at %p\n", isi);	printk(KERN_DEBUG "\t\tisi_hpa:       %lx\n", isi->isi_hpa);	printk(KERN_DEBUG "\t\tisi_status:    %x\n", isi->isi_status);	printk(KERN_DEBUG "\t\tisi_version:   %x\n", isi->isi_version);	printk(KERN_DEBUG "\t\tisi_vector:    %p\n", isi->isi_vector);}#endif /* DEBUG_IOSAPIC */

⌨️ 快捷键说明

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