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

📄 io_apic.c

📁 底层驱动开发
💻 C
📖 第 1 页 / 共 5 页
字号:
	v = apic_read(APIC_ICR);	printk(KERN_DEBUG "... APIC ICR: %08x\n", v);	v = apic_read(APIC_ICR2);	printk(KERN_DEBUG "... APIC ICR2: %08x\n", v);	v = apic_read(APIC_LVTT);	printk(KERN_DEBUG "... APIC LVTT: %08x\n", v);	if (maxlvt > 3) {                       /* PC is LVT#4. */		v = apic_read(APIC_LVTPC);		printk(KERN_DEBUG "... APIC LVTPC: %08x\n", v);	}	v = apic_read(APIC_LVT0);	printk(KERN_DEBUG "... APIC LVT0: %08x\n", v);	v = apic_read(APIC_LVT1);	printk(KERN_DEBUG "... APIC LVT1: %08x\n", v);	if (maxlvt > 2) {			/* ERR is LVT#3. */		v = apic_read(APIC_LVTERR);		printk(KERN_DEBUG "... APIC LVTERR: %08x\n", v);	}	v = apic_read(APIC_TMICT);	printk(KERN_DEBUG "... APIC TMICT: %08x\n", v);	v = apic_read(APIC_TMCCT);	printk(KERN_DEBUG "... APIC TMCCT: %08x\n", v);	v = apic_read(APIC_TDCR);	printk(KERN_DEBUG "... APIC TDCR: %08x\n", v);	printk("\n");}void print_all_local_APICs (void){	on_each_cpu(print_local_APIC, NULL, 1, 1);}void /*__init*/ print_PIC(void){	unsigned int v;	unsigned long flags;	if (apic_verbosity == APIC_QUIET)		return;	printk(KERN_DEBUG "\nprinting PIC contents\n");	spin_lock_irqsave(&i8259A_lock, flags);	v = inb(0xa1) << 8 | inb(0x21);	printk(KERN_DEBUG "... PIC  IMR: %04x\n", v);	v = inb(0xa0) << 8 | inb(0x20);	printk(KERN_DEBUG "... PIC  IRR: %04x\n", v);	outb(0x0b,0xa0);	outb(0x0b,0x20);	v = inb(0xa0) << 8 | inb(0x20);	outb(0x0a,0xa0);	outb(0x0a,0x20);	spin_unlock_irqrestore(&i8259A_lock, flags);	printk(KERN_DEBUG "... PIC  ISR: %04x\n", v);	v = inb(0x4d1) << 8 | inb(0x4d0);	printk(KERN_DEBUG "... PIC ELCR: %04x\n", v);}#endif  /*  0  */static void __init enable_IO_APIC(void){	union IO_APIC_reg_01 reg_01;	int i;	unsigned long flags;	for (i = 0; i < PIN_MAP_SIZE; i++) {		irq_2_pin[i].pin = -1;		irq_2_pin[i].next = 0;	}	if (!pirqs_enabled)		for (i = 0; i < MAX_PIRQS; i++)			pirq_entries[i] = -1;	/*	 * The number of IO-APIC IRQ registers (== #pins):	 */	for (i = 0; i < nr_ioapics; i++) {		spin_lock_irqsave(&ioapic_lock, flags);		reg_01.raw = io_apic_read(i, 1);		spin_unlock_irqrestore(&ioapic_lock, flags);		nr_ioapic_registers[i] = reg_01.bits.entries+1;	}	/*	 * Do not trust the IO-APIC being empty at bootup	 */	clear_IO_APIC();}/* * Not an __init, needed by the reboot code */void disable_IO_APIC(void){	int pin;	/*	 * Clear the IO-APIC before rebooting:	 */	clear_IO_APIC();	/*	 * If the i8259 is routed through an IOAPIC	 * Put that IOAPIC in virtual wire mode	 * so legacy interrupts can be delivered.	 */	pin = find_isa_irq_pin(0, mp_ExtINT);	if (pin != -1) {		struct IO_APIC_route_entry entry;		unsigned long flags;		memset(&entry, 0, sizeof(entry));		entry.mask            = 0; /* Enabled */		entry.trigger         = 0; /* Edge */		entry.irr             = 0;		entry.polarity        = 0; /* High */		entry.delivery_status = 0;		entry.dest_mode       = 0; /* Physical */		entry.delivery_mode   = 7; /* ExtInt */		entry.vector          = 0;		entry.dest.physical.physical_dest = 0;		/*		 * Add it to the IO-APIC irq-routing table:		 */		spin_lock_irqsave(&ioapic_lock, flags);		io_apic_write(0, 0x11+2*pin, *(((int *)&entry)+1));		io_apic_write(0, 0x10+2*pin, *(((int *)&entry)+0));		spin_unlock_irqrestore(&ioapic_lock, flags);	}	disconnect_bsp_APIC(pin != -1);}/* * function to set the IO-APIC physical IDs based on the * values stored in the MPC table. * * by Matt Domsch <Matt_Domsch@dell.com>  Tue Dec 21 12:25:05 CST 1999 */#ifndef CONFIG_X86_NUMAQstatic void __init setup_ioapic_ids_from_mpc(void){	union IO_APIC_reg_00 reg_00;	physid_mask_t phys_id_present_map;	int apic;	int i;	unsigned char old_id;	unsigned long flags;	/*	 * Don't check I/O APIC IDs for xAPIC systems.  They have	 * no meaning without the serial APIC bus.	 */	if (!(boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && boot_cpu_data.x86 < 15))		return;	/*	 * This is broken; anything with a real cpu count has to	 * circumvent this idiocy regardless.	 */	phys_id_present_map = ioapic_phys_id_map(phys_cpu_present_map);	/*	 * Set the IOAPIC ID to the value stored in the MPC table.	 */	for (apic = 0; apic < nr_ioapics; apic++) {		/* Read the register 0 value */		spin_lock_irqsave(&ioapic_lock, flags);		reg_00.raw = io_apic_read(apic, 0);		spin_unlock_irqrestore(&ioapic_lock, flags);				old_id = mp_ioapics[apic].mpc_apicid;		if (mp_ioapics[apic].mpc_apicid >= get_physical_broadcast()) {			printk(KERN_ERR "BIOS bug, IO-APIC#%d ID is %d in the MPC table!...\n",				apic, mp_ioapics[apic].mpc_apicid);			printk(KERN_ERR "... fixing up to %d. (tell your hw vendor)\n",				reg_00.bits.ID);			mp_ioapics[apic].mpc_apicid = reg_00.bits.ID;		}		/*		 * Sanity check, is the ID really free? Every APIC in a		 * system must have a unique ID or we get lots of nice		 * 'stuck on smp_invalidate_needed IPI wait' messages.		 */		if (check_apicid_used(phys_id_present_map,					mp_ioapics[apic].mpc_apicid)) {			printk(KERN_ERR "BIOS bug, IO-APIC#%d ID %d is already used!...\n",				apic, mp_ioapics[apic].mpc_apicid);			for (i = 0; i < get_physical_broadcast(); i++)				if (!physid_isset(i, phys_id_present_map))					break;			if (i >= get_physical_broadcast())				panic("Max APIC ID exceeded!\n");			printk(KERN_ERR "... fixing up to %d. (tell your hw vendor)\n",				i);			physid_set(i, phys_id_present_map);			mp_ioapics[apic].mpc_apicid = i;		} else {			physid_mask_t tmp;			tmp = apicid_to_cpu_present(mp_ioapics[apic].mpc_apicid);			apic_printk(APIC_VERBOSE, "Setting %d in the "					"phys_id_present_map\n",					mp_ioapics[apic].mpc_apicid);			physids_or(phys_id_present_map, phys_id_present_map, tmp);		}		/*		 * We need to adjust the IRQ routing table		 * if the ID changed.		 */		if (old_id != mp_ioapics[apic].mpc_apicid)			for (i = 0; i < mp_irq_entries; i++)				if (mp_irqs[i].mpc_dstapic == old_id)					mp_irqs[i].mpc_dstapic						= mp_ioapics[apic].mpc_apicid;		/*		 * Read the right value from the MPC table and		 * write it into the ID register.	 	 */		apic_printk(APIC_VERBOSE, KERN_INFO			"...changing IO-APIC physical APIC ID to %d ...",			mp_ioapics[apic].mpc_apicid);		reg_00.bits.ID = mp_ioapics[apic].mpc_apicid;		spin_lock_irqsave(&ioapic_lock, flags);		io_apic_write(apic, 0, reg_00.raw);		spin_unlock_irqrestore(&ioapic_lock, flags);		/*		 * Sanity check		 */		spin_lock_irqsave(&ioapic_lock, flags);		reg_00.raw = io_apic_read(apic, 0);		spin_unlock_irqrestore(&ioapic_lock, flags);		if (reg_00.bits.ID != mp_ioapics[apic].mpc_apicid)			printk("could not set ID!\n");		else			apic_printk(APIC_VERBOSE, " ok.\n");	}}#elsestatic void __init setup_ioapic_ids_from_mpc(void) { }#endif/* * There is a nasty bug in some older SMP boards, their mptable lies * about the timer IRQ. We do the following to work around the situation: * *	- timer IRQ defaults to IO-APIC IRQ *	- if this function detects that timer IRQs are defunct, then we fall *	  back to ISA timer IRQs */static int __init timer_irq_works(void){	unsigned long t1 = jiffies;	local_irq_enable();	/* Let ten ticks pass... */	mdelay((10 * 1000) / HZ);	/*	 * Expect a few ticks at least, to be sure some possible	 * glue logic does not lock up after one or two first	 * ticks in a non-ExtINT mode.  Also the local APIC	 * might have cached one ExtINT interrupt.  Finally, at	 * least one tick may be lost due to delays.	 */	if (jiffies - t1 > 4)		return 1;	return 0;}/* * In the SMP+IOAPIC case it might happen that there are an unspecified * number of pending IRQ events unhandled. These cases are very rare, * so we 'resend' these IRQs via IPIs, to the same CPU. It's much * better to do it this way as thus we do not have to be aware of * 'pending' interrupts in the IRQ path, except at this point. *//* * Edge triggered needs to resend any interrupt * that was delayed but this is now handled in the device * independent code. *//* * Starting up a edge-triggered IO-APIC interrupt is * nasty - we need to make sure that we get the edge. * If it is already asserted for some reason, we need * return 1 to indicate that is was pending. * * This is not complete - we should be able to fake * an edge even if it isn't on the 8259A... */static unsigned int startup_edge_ioapic_irq(unsigned int irq){	int was_pending = 0;	unsigned long flags;	spin_lock_irqsave(&ioapic_lock, flags);	if (irq < 16) {		disable_8259A_irq(irq);		if (i8259A_irq_pending(irq))			was_pending = 1;	}	__unmask_IO_APIC_irq(irq);	spin_unlock_irqrestore(&ioapic_lock, flags);	return was_pending;}/* * Once we have recorded IRQ_PENDING already, we can mask the * interrupt for real. This prevents IRQ storms from unhandled * devices. */static void ack_edge_ioapic_irq(unsigned int irq){	move_irq(irq);	if ((irq_desc[irq].status & (IRQ_PENDING | IRQ_DISABLED))					== (IRQ_PENDING | IRQ_DISABLED))		mask_IO_APIC_irq(irq);	ack_APIC_irq();}/* * Level triggered interrupts can just be masked, * and shutting down and starting up the interrupt * is the same as enabling and disabling them -- except * with a startup need to return a "was pending" value. * * Level triggered interrupts are special because we * do not touch any IO-APIC register while handling * them. We ack the APIC in the end-IRQ handler, not * in the start-IRQ-handler. Protection against reentrance * from the same interrupt is still provided, both by the * generic IRQ layer and by the fact that an unacked local * APIC does not accept IRQs. */static unsigned int startup_level_ioapic_irq (unsigned int irq){	unmask_IO_APIC_irq(irq);	return 0; /* don't check for pending */}static void end_level_ioapic_irq (unsigned int irq){	unsigned long v;	int i;	move_irq(irq);/* * It appears there is an erratum which affects at least version 0x11 * of I/O APIC (that's the 82093AA and cores integrated into various * chipsets).  Under certain conditions a level-triggered interrupt is * erroneously delivered as edge-triggered one but the respective IRR * bit gets set nevertheless.  As a result the I/O unit expects an EOI * message but it will never arrive and further interrupts are blocked * from the source.  The exact reason is so far unknown, but the * phenomenon was observed when two consecutive interrupt requests * from a given source get delivered to the same CPU and the source is * temporarily disabled in between. * * A workaround is to simulate an EOI message manually.  We achieve it * by setting the trigger mode to edge and then to level when the edge * trigger mode gets detected in the TMR of a local APIC for a * level-triggered interrupt.  We mask the source for the time of the * operation to prevent an edge-triggered interrupt escaping meanwhile. * The idea is from Manfred Spraul.  --macro */	i = IO_APIC_VECTOR(irq);	v = apic_read(APIC_TMR + ((i & ~0x1f) >> 1));	ack_APIC_irq();	if (!(v & (1 << (i & 0x1f)))) {		atomic_inc(&irq_mis_count);		spin_lock(&ioapic_lock);		__mask_and_edge_IO_APIC_irq(irq);		__unmask_and_level_IO_APIC_irq(irq);		spin_unlock(&ioapic_lock);	}}#ifdef CONFIG_PCI_MSIstatic unsigned int startup_edge_ioapic_vector(unsigned int vector){	int irq = vector_to_irq(vector);	return startup_edge_ioapic_irq(irq);}static void ack_edge_ioapic_vector(unsigned int vector){	int irq = vector_to_irq(vector);	move_irq(vector);	ack_edge_ioapic_irq(irq);}static unsigned int startup_level_ioapic_vector (unsigned int vector){	int irq = vector_to_irq(vector);	return startup_level_ioapic_irq (irq);}static void end_level_ioapic_vector (unsigned int vector){	int irq = vector_to_irq(vector);	move_irq(vector);	end_level_ioapic_irq(irq);}static void mask_IO_APIC_vector (unsigned int vector){	int irq = vector_to_irq(vector);	mask_IO_APIC_irq(irq);}static void unmask_IO_APIC_vector (unsigned int vector){	int irq = vector_to_irq(vector);	unmask_IO_APIC_irq(irq);}#ifdef CONFIG_SMPstatic void set_ioapic_affinity_vector (unsigned int vector,					cpumask_t cpu_mask){	int irq = vector_to_irq(vector);	set_native_irq_info(vector, cpu_mask);	set_ioapic_affinity_irq(irq, cpu_mask);}#endif#endif/* * Level and edge triggered IO-APIC interrupts need different handling, * so we use two separate IRQ descriptors. Edge triggered IRQs can be * handled with the level-triggered descriptor, but that one has slightly * more overhead. Level-triggered interrupts cannot be handled with the * edge-triggered handler, without risking IRQ storms and other ugly * races. */static struct hw_interrupt_type ioapic_edge_type __read_mostly = {	.typename 	= "IO-APIC-edge",	.startup 	= startup_edge_ioapic,	.shutdown 	= shutdown_edge_ioapic,	.enable 	= enable_edge_ioapic,	.disable 	= disable_edge_ioapic,	.ack 		= ack_edge_ioapic,	.end 		= end_edge_ioapic,#ifdef CONFIG_SMP	.set_affinity 	= set_ioapic_affinity,#endif};static struct hw_interrupt_type ioapic_level_type __read_mostly = {	.typename 	= "IO-APIC-level",	.startup 	= startup_level_ioapic,	.shutdown 	= shutdown_level_ioapic,	.enable 	= enable_level_ioapic,	.disable 	= disable_level_ioapic,	.ack 		= mask_and_ack_level_ioapic,	.end 		= end_level_ioapic,#ifdef CONFIG_SMP	.set_affinity 	= set_ioapic_affinity,#endif};static inline void init_IO_APIC_traps(void){	int irq;	/*	 * NOTE! The local APIC isn't very good at handling	 * multiple interrupts at the same interrupt level.	 * As the interrupt level is determined by taking the	 * vector number and shifting that right by 4, we	 * want to spread these out a bit so that they don't	 * all fall in the same interrupt level.	 *	 * Also, we've got to be careful not to trash gate	 * 0x80, because int 0x80 is hm, kind of importantish. ;)

⌨️ 快捷键说明

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