io_apic.c

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

C
2,089
字号
	.end = end_lapic_irq,};static void setup_nmi (void){	/* 	 * Dirty trick to enable the NMI watchdog ...	 * We put the 8259A master into AEOI mode and	 * unmask on all local APICs LVT0 as NMI.	 *	 * The idea to use the 8259A in AEOI mode ('8259A Virtual Wire')	 * is from Maciej W. Rozycki - so we do not have to EOI from	 * the NMI handler or the timer interrupt.	 */ 	printk(KERN_INFO "activating NMI Watchdog ...");	enable_NMI_through_LVT0(NULL);	printk(" done.\n");}/* * This looks a bit hackish but it's about the only one way of sending * a few INTA cycles to 8259As and any associated glue logic.  ICR does * not support the ExtINT mode, unfortunately.  We need to send these * cycles as some i82489DX-based boards have glue logic that keeps the * 8259A interrupt line asserted until INTA.  --macro */static inline void unlock_ExtINT_logic(void){	int pin, i;	struct IO_APIC_route_entry entry0, entry1;	unsigned char save_control, save_freq_select;	unsigned long flags;	pin = find_isa_irq_pin(8, mp_INT);	if (pin == -1)		return;	spin_lock_irqsave(&ioapic_lock, flags);	*(((int *)&entry0) + 1) = io_apic_read(0, 0x11 + 2 * pin);	*(((int *)&entry0) + 0) = io_apic_read(0, 0x10 + 2 * pin);	spin_unlock_irqrestore(&ioapic_lock, flags);	clear_IO_APIC_pin(0, pin);	memset(&entry1, 0, sizeof(entry1));	entry1.dest_mode = 0;			/* physical delivery */	entry1.mask = 0;			/* unmask IRQ now */	entry1.dest.physical.physical_dest = hard_smp_processor_id();	entry1.delivery_mode = dest_ExtINT;	entry1.polarity = entry0.polarity;	entry1.trigger = 0;	entry1.vector = 0;	spin_lock_irqsave(&ioapic_lock, flags);	io_apic_write(0, 0x11 + 2 * pin, *(((int *)&entry1) + 1));	io_apic_write(0, 0x10 + 2 * pin, *(((int *)&entry1) + 0));	spin_unlock_irqrestore(&ioapic_lock, flags);	save_control = CMOS_READ(RTC_CONTROL);	save_freq_select = CMOS_READ(RTC_FREQ_SELECT);	CMOS_WRITE((save_freq_select & ~RTC_RATE_SELECT) | 0x6,		   RTC_FREQ_SELECT);	CMOS_WRITE(save_control | RTC_PIE, RTC_CONTROL);	i = 100;	while (i-- > 0) {		mdelay(10);		if ((CMOS_READ(RTC_INTR_FLAGS) & RTC_PF) == RTC_PF)			i -= 10;	}	CMOS_WRITE(save_control, RTC_CONTROL);	CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);	clear_IO_APIC_pin(0, pin);	spin_lock_irqsave(&ioapic_lock, flags);	io_apic_write(0, 0x11 + 2 * pin, *(((int *)&entry0) + 1));	io_apic_write(0, 0x10 + 2 * pin, *(((int *)&entry0) + 0));	spin_unlock_irqrestore(&ioapic_lock, flags);}/* * This code may look a bit paranoid, but it's supposed to cooperate with * a wide range of boards and BIOS bugs.  Fortunately only the timer IRQ * is so screwy.  Thanks to Brian Perkins for testing/hacking this beast * fanatically on his truly buggy board. */static inline void check_timer(void){	int pin1, pin2;	int vector;	/*	 * get/set the timer IRQ vector:	 */	disable_8259A_irq(0);	vector = assign_irq_vector(0);	set_intr_gate(vector, interrupt[0]);	/*	 * Subtle, code in do_timer_interrupt() expects an AEOI	 * mode for the 8259A whenever interrupts are routed	 * through I/O APICs.  Also IRQ0 has to be enabled in	 * the 8259A which implies the virtual wire has to be	 * disabled in the local APIC.	 */	apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT);	init_8259A(1);	enable_8259A_irq(0);	pin1 = find_isa_irq_pin(0, mp_INT);	pin2 = find_isa_irq_pin(0, mp_ExtINT);	apic_printk(APIC_VERBOSE,KERN_INFO "..TIMER: vector=0x%02X pin1=%d pin2=%d\n", vector, pin1, pin2);	if (pin1 != -1) {		/*		 * Ok, does IRQ0 through the IOAPIC work?		 */		unmask_IO_APIC_irq(0);		if (timer_irq_works()) {			nmi_watchdog_default();			if (nmi_watchdog == NMI_IO_APIC) {				disable_8259A_irq(0);				setup_nmi();				enable_8259A_irq(0);				check_nmi_watchdog();			}			return;		}		clear_IO_APIC_pin(0, pin1);		apic_printk(APIC_QUIET,KERN_ERR "..MP-BIOS bug: 8254 timer not connected to IO-APIC\n");	}	apic_printk(APIC_VERBOSE,KERN_INFO "...trying to set up timer (IRQ0) through the 8259A ... ");	if (pin2 != -1) {		apic_printk(APIC_VERBOSE,"\n..... (found pin %d) ...", pin2);		/*		 * legacy devices should be connected to IO APIC #0		 */		setup_ExtINT_IRQ0_pin(pin2, vector);		if (timer_irq_works()) {			printk("works.\n");			nmi_watchdog_default();			if (nmi_watchdog == NMI_IO_APIC) {				setup_nmi();				check_nmi_watchdog();			}			return;		}		/*		 * Cleanup, just in case ...		 */		clear_IO_APIC_pin(0, pin2);	}	printk(" failed.\n");	if (nmi_watchdog) {		printk(KERN_WARNING "timer doesn't work through the IO-APIC - disabling NMI Watchdog!\n");		nmi_watchdog = 0;	}	apic_printk(APIC_VERBOSE, KERN_INFO "...trying to set up timer as Virtual Wire IRQ...");	disable_8259A_irq(0);	irq_desc[0].handler = &lapic_irq_type;	apic_write_around(APIC_LVT0, APIC_DM_FIXED | vector);	/* Fixed mode */	enable_8259A_irq(0);	if (timer_irq_works()) {		apic_printk(APIC_QUIET, " works.\n");		return;	}	apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_FIXED | vector);	apic_printk(APIC_VERBOSE," failed.\n");	apic_printk(APIC_VERBOSE, KERN_INFO "...trying to set up timer as ExtINT IRQ...");	init_8259A(0);	make_8259A_irq(0);	apic_write_around(APIC_LVT0, APIC_DM_EXTINT);	unlock_ExtINT_logic();	if (timer_irq_works()) {		apic_printk(APIC_VERBOSE," works.\n");		return;	}	apic_printk(APIC_VERBOSE," failed :(.\n");	panic("IO-APIC + timer doesn't work! Try using the 'noapic' kernel parameter\n");}/* * * IRQ's that are handled by the PIC in the MPS IOAPIC case. * - IRQ2 is the cascade IRQ, and cannot be a io-apic IRQ. *   Linux doesn't really care, as it's not actually used *   for any interrupt handling anyway. */#define PIC_IRQS	(1<<2)void __init setup_IO_APIC(void){	enable_IO_APIC();	if (acpi_ioapic)		io_apic_irqs = ~0;	/* all IRQs go through IOAPIC */	else		io_apic_irqs = ~PIC_IRQS;	apic_printk(APIC_VERBOSE, "ENABLING IO-APIC IRQs\n");	/*	 * Set up the IO-APIC IRQ routing table.	 */	if (!acpi_ioapic)		setup_ioapic_ids_from_mpc();	sync_Arb_IDs();	setup_IO_APIC_irqs();	init_IO_APIC_traps();	check_timer();	if (!acpi_ioapic)		print_IO_APIC();}struct sysfs_ioapic_data {	struct sys_device dev;	struct IO_APIC_route_entry entry[0];};static struct sysfs_ioapic_data * mp_ioapic_data[MAX_IO_APICS];static int ioapic_suspend(struct sys_device *dev, u32 state){	struct IO_APIC_route_entry *entry;	struct sysfs_ioapic_data *data;	unsigned long flags;	int i;	data = container_of(dev, struct sysfs_ioapic_data, dev);	entry = data->entry;	spin_lock_irqsave(&ioapic_lock, flags);	for (i = 0; i < nr_ioapic_registers[dev->id]; i ++, entry ++ ) {		*(((int *)entry) + 1) = io_apic_read(dev->id, 0x11 + 2 * i);		*(((int *)entry) + 0) = io_apic_read(dev->id, 0x10 + 2 * i);	}	spin_unlock_irqrestore(&ioapic_lock, flags);	return 0;}static int ioapic_resume(struct sys_device *dev){	struct IO_APIC_route_entry *entry;	struct sysfs_ioapic_data *data;	unsigned long flags;	union IO_APIC_reg_00 reg_00;	int i;	data = container_of(dev, struct sysfs_ioapic_data, dev);	entry = data->entry;	spin_lock_irqsave(&ioapic_lock, flags);	reg_00.raw = io_apic_read(dev->id, 0);	if (reg_00.bits.ID != mp_ioapics[dev->id].mpc_apicid) {		reg_00.bits.ID = mp_ioapics[dev->id].mpc_apicid;		io_apic_write(dev->id, 0, reg_00.raw);	}	for (i = 0; i < nr_ioapic_registers[dev->id]; i ++, entry ++ ) {		io_apic_write(dev->id, 0x11+2*i, *(((int *)entry)+1));		io_apic_write(dev->id, 0x10+2*i, *(((int *)entry)+0));	}	spin_unlock_irqrestore(&ioapic_lock, flags);	return 0;}static struct sysdev_class ioapic_sysdev_class = {	set_kset_name("ioapic"),	.suspend = ioapic_suspend,	.resume = ioapic_resume,};static int __init ioapic_init_sysfs(void){	struct sys_device * dev;	int i, size, error = 0;	error = sysdev_class_register(&ioapic_sysdev_class);	if (error)		return error;	for (i = 0; i < nr_ioapics; i++ ) {		size = sizeof(struct sys_device) + nr_ioapic_registers[i]			* sizeof(struct IO_APIC_route_entry);		mp_ioapic_data[i] = kmalloc(size, GFP_KERNEL);		if (!mp_ioapic_data[i]) {			printk(KERN_ERR "Can't suspend/resume IOAPIC %d\n", i);			continue;		}		memset(mp_ioapic_data[i], 0, size);		dev = &mp_ioapic_data[i]->dev;		dev->id = i;		dev->cls = &ioapic_sysdev_class;		error = sysdev_register(dev);		if (error) {			kfree(mp_ioapic_data[i]);			mp_ioapic_data[i] = NULL;			printk(KERN_ERR "Can't suspend/resume IOAPIC %d\n", i);			continue;		}	}	return 0;}device_initcall(ioapic_init_sysfs);/* --------------------------------------------------------------------------                          ACPI-based IOAPIC Configuration   -------------------------------------------------------------------------- */#ifdef CONFIG_ACPI_BOOT#define IO_APIC_MAX_ID		15int __init io_apic_get_unique_id (int ioapic, int apic_id){	union IO_APIC_reg_00 reg_00;	static physid_mask_t apic_id_map;	unsigned long flags;	int i = 0;	/*	 * The P4 platform supports up to 256 APIC IDs on two separate APIC 	 * buses (one for LAPICs, one for IOAPICs), where predecessors only 	 * supports up to 16 on one shared APIC bus.	 * 	 * TBD: Expand LAPIC/IOAPIC support on P4-class systems to take full	 *      advantage of new APIC bus architecture.	 */	if (physids_empty(apic_id_map))		apic_id_map = phys_cpu_present_map;	spin_lock_irqsave(&ioapic_lock, flags);	reg_00.raw = io_apic_read(ioapic, 0);	spin_unlock_irqrestore(&ioapic_lock, flags);	if (apic_id >= IO_APIC_MAX_ID) {		apic_printk(APIC_QUIET, KERN_WARNING "IOAPIC[%d]: Invalid apic_id %d, trying "			"%d\n", ioapic, apic_id, reg_00.bits.ID);		apic_id = reg_00.bits.ID;	}	/*	 * 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 (physid_isset(apic_id, apic_id_map)) {		for (i = 0; i < IO_APIC_MAX_ID; i++) {			if (!physid_isset(i, apic_id_map))				break;		}		if (i == IO_APIC_MAX_ID)			panic("Max apic_id exceeded!\n");		apic_printk(APIC_VERBOSE, KERN_WARNING "IOAPIC[%d]: apic_id %d already used, "			"trying %d\n", ioapic, apic_id, i);		apic_id = i;	} 	physid_set(apic_id, apic_id_map);	if (reg_00.bits.ID != apic_id) {		reg_00.bits.ID = apic_id;		spin_lock_irqsave(&ioapic_lock, flags);		io_apic_write(ioapic, 0, reg_00.raw);		reg_00.raw = io_apic_read(ioapic, 0);		spin_unlock_irqrestore(&ioapic_lock, flags);		/* Sanity check */		if (reg_00.bits.ID != apic_id)			panic("IOAPIC[%d]: Unable change apic_id!\n", ioapic);	}	apic_printk(APIC_VERBOSE,KERN_INFO "IOAPIC[%d]: Assigned apic_id %d\n", ioapic, apic_id);	return apic_id;}int __init io_apic_get_version (int ioapic){	union IO_APIC_reg_01	reg_01;	unsigned long flags;	spin_lock_irqsave(&ioapic_lock, flags);	reg_01.raw = io_apic_read(ioapic, 1);	spin_unlock_irqrestore(&ioapic_lock, flags);	return reg_01.bits.version;}int __init io_apic_get_redir_entries (int ioapic){	union IO_APIC_reg_01	reg_01;	unsigned long flags;	spin_lock_irqsave(&ioapic_lock, flags);	reg_01.raw = io_apic_read(ioapic, 1);	spin_unlock_irqrestore(&ioapic_lock, flags);	return reg_01.bits.entries;}int io_apic_set_pci_routing (int ioapic, int pin, int irq, int edge_level, int active_high_low){	struct IO_APIC_route_entry entry;	unsigned long flags;	if (!IO_APIC_IRQ(irq)) {		apic_printk(APIC_QUIET,KERN_ERR "IOAPIC[%d]: Invalid reference to IRQ 0\n",			ioapic);		return -EINVAL;	}	/*	 * Generate a PCI IRQ routing entry and program the IOAPIC accordingly.	 * Note that we mask (disable) IRQs now -- these get enabled when the	 * corresponding device driver registers for this IRQ.	 */	memset(&entry,0,sizeof(entry));	entry.delivery_mode = dest_LowestPrio;	entry.dest_mode = INT_DELIVERY_MODE;	entry.dest.logical.logical_dest = cpu_mask_to_apicid(TARGET_CPUS);	entry.trigger = edge_level;	entry.polarity = active_high_low;	entry.mask = 1;					 /* Disabled (masked) */	/*	 * IRQs < 16 are already in the irq_2_pin[] map	 */	if (irq >= 16)		add_pin_to_irq(irq, ioapic, pin);	entry.vector = assign_irq_vector(irq);	apic_printk(APIC_VERBOSE,KERN_DEBUG "IOAPIC[%d]: Set PCI routing entry (%d-%d -> 0x%x -> "		"IRQ %d Mode:%i Active:%i)\n", ioapic, 	       mp_ioapics[ioapic].mpc_apicid, pin, entry.vector, irq,	       edge_level, active_high_low);	ioapic_register_intr(irq, entry.vector, edge_level);	if (!ioapic && (irq < 16))		disable_8259A_irq(irq);	spin_lock_irqsave(&ioapic_lock, flags);	io_apic_write(ioapic, 0x11+2*pin, *(((int *)&entry)+1));	io_apic_write(ioapic, 0x10+2*pin, *(((int *)&entry)+0));	spin_unlock_irqrestore(&ioapic_lock, flags);	return 0;}#endif /*CONFIG_ACPI_BOOT*/#ifndef CONFIG_SMPvoid send_IPI_self(int vector){	unsigned int cfg;       /*        * Wait for idle.        */	apic_wait_icr_idle();	cfg = APIC_DM_FIXED | APIC_DEST_SELF | vector | APIC_DEST_LOGICAL;	/*	 * Send the IPI. The write to APIC_ICR fires this off.	 */	apic_write_around(APIC_ICR, cfg);}#endif/* * This function currently is only a helper for the i386 smp boot process where * we need to reprogram the ioredtbls to cater for the cpus which have come online * so mask in all cases should simply be TARGET_CPUS */void __init setup_ioapic_dest(void){	int pin, ioapic, irq, irq_entry;	if (skip_ioapic_setup == 1)		return;	for (ioapic = 0; ioapic < nr_ioapics; ioapic++) {		for (pin = 0; pin < nr_ioapic_registers[ioapic]; pin++) {			irq_entry = find_irq_entry(ioapic, pin, mp_INT);			if (irq_entry == -1)				continue;			irq = pin_2_irq(irq_entry, ioapic, pin);			set_ioapic_affinity_irq(irq, TARGET_CPUS);		}	}}

⌨️ 快捷键说明

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