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

📄 apic.c

📁 该文件是rt_linux
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *	Local APIC handling, local APIC timers * *	(c) 1999, 2000 Ingo Molnar <mingo@redhat.com> * *	Fixes *	Maciej W. Rozycki	:	Bits for genuine 82489DX APICs; *					thanks to Eric Gilmore *					and Rolf G. Tews *					for testing these extensively. *	Maciej W. Rozycki	:	Various updates and fixes. *	Mikael Pettersson	:	Power Management for UP-APIC. */#include <linux/config.h>#include <linux/init.h>#include <linux/mm.h>#include <linux/irq.h>#include <linux/delay.h>#include <linux/bootmem.h>#include <linux/smp_lock.h>#include <linux/interrupt.h>#include <linux/mc146818rtc.h>#include <linux/kernel_stat.h>#include <asm/atomic.h>#include <asm/smp.h>#include <asm/mtrr.h>#include <asm/mpspec.h>#include <asm/pgalloc.h>#include <asm/timex.h>/* Using APIC to generate smp_local_timer_interrupt? */int using_apic_timer = 0;int prof_multiplier[NR_CPUS] = { 1, };int prof_old_multiplier[NR_CPUS] = { 1, };int prof_counter[NR_CPUS] = { 1, };int get_maxlvt(void){	unsigned int v, ver, maxlvt;	v = apic_read(APIC_LVR);	ver = GET_APIC_VERSION(v);	/* 82489DXs do not report # of LVT entries. */	maxlvt = APIC_INTEGRATED(ver) ? GET_APIC_MAXLVT(v) : 2;	return maxlvt;}void clear_local_APIC(void){	int maxlvt;	unsigned int v;	maxlvt = get_maxlvt();	/*	 * Masking an LVT entry on a P6 can trigger a local APIC error	 * if the vector is zero. Mask LVTERR first to prevent this.	 */	if (maxlvt >= 3) {		v = ERROR_APIC_VECTOR; /* any non-zero vector will do */		apic_write_around(APIC_LVTERR, v | APIC_LVT_MASKED);	}	/*	 * Careful: we have to set masks only first to deassert	 * any level-triggered sources.	 */	v = apic_read(APIC_LVTT);	apic_write_around(APIC_LVTT, v | APIC_LVT_MASKED);	v = apic_read(APIC_LVT0);	apic_write_around(APIC_LVT0, v | APIC_LVT_MASKED);	v = apic_read(APIC_LVT1);	apic_write_around(APIC_LVT1, v | APIC_LVT_MASKED);	if (maxlvt >= 4) {		v = apic_read(APIC_LVTPC);		apic_write_around(APIC_LVTPC, v | APIC_LVT_MASKED);	}	/*	 * Clean APIC state for other OSs:	 */	apic_write_around(APIC_LVTT, APIC_LVT_MASKED);	apic_write_around(APIC_LVT0, APIC_LVT_MASKED);	apic_write_around(APIC_LVT1, APIC_LVT_MASKED);	if (maxlvt >= 3)		apic_write_around(APIC_LVTERR, APIC_LVT_MASKED);	if (maxlvt >= 4)		apic_write_around(APIC_LVTPC, APIC_LVT_MASKED);	v = GET_APIC_VERSION(apic_read(APIC_LVR));	if (APIC_INTEGRATED(v)) {	/* !82489DX */		if (maxlvt > 3)			apic_write(APIC_ESR, 0);		apic_read(APIC_ESR);	}}void __init connect_bsp_APIC(void){	if (pic_mode) {		/*		 * Do not trust the local APIC being empty at bootup.		 */		clear_local_APIC();		/*		 * PIC mode, enable APIC mode in the IMCR, i.e.		 * connect BSP's local APIC to INT and NMI lines.		 */		printk("leaving PIC mode, enabling APIC mode.\n");		outb(0x70, 0x22);		outb(0x01, 0x23);	}}void disconnect_bsp_APIC(void){	if (pic_mode) {		/*		 * Put the board back into PIC mode (has an effect		 * only on certain older boards).  Note that APIC		 * interrupts, including IPIs, won't work beyond		 * this point!  The only exception are INIT IPIs.		 */		printk("disabling APIC mode, entering PIC mode.\n");		outb(0x70, 0x22);		outb(0x00, 0x23);	}}void disable_local_APIC(void){	unsigned int value;	clear_local_APIC();	/*	 * Disable APIC (implies clearing of registers	 * for 82489DX!).	 */	value = apic_read(APIC_SPIV);	value &= ~APIC_SPIV_APIC_ENABLED;	apic_write_around(APIC_SPIV, value);}/* * This is to verify that we're looking at a real local APIC. * Check these against your board if the CPUs aren't getting * started for no apparent reason. */int __init verify_local_APIC(void){	unsigned int reg0, reg1;	/*	 * The version register is read-only in a real APIC.	 */	reg0 = apic_read(APIC_LVR);	Dprintk("Getting VERSION: %x\n", reg0);	apic_write(APIC_LVR, reg0 ^ APIC_LVR_MASK);	reg1 = apic_read(APIC_LVR);	Dprintk("Getting VERSION: %x\n", reg1);	/*	 * The two version reads above should print the same	 * numbers.  If the second one is different, then we	 * poke at a non-APIC.	 */	if (reg1 != reg0)		return 0;	/*	 * Check if the version looks reasonably.	 */	reg1 = GET_APIC_VERSION(reg0);	if (reg1 == 0x00 || reg1 == 0xff)		return 0;	reg1 = get_maxlvt();	if (reg1 < 0x02 || reg1 == 0xff)		return 0;	/*	 * The ID register is read/write in a real APIC.	 */	reg0 = apic_read(APIC_ID);	Dprintk("Getting ID: %x\n", reg0);	apic_write(APIC_ID, reg0 ^ APIC_ID_MASK);	reg1 = apic_read(APIC_ID);	Dprintk("Getting ID: %x\n", reg1);	apic_write(APIC_ID, reg0);	if (reg1 != (reg0 ^ APIC_ID_MASK))		return 0;	/*	 * The next two are just to see if we have sane values.	 * They're only really relevant if we're in Virtual Wire	 * compatibility mode, but most boxes are anymore.	 */	reg0 = apic_read(APIC_LVT0);	Dprintk("Getting LVT0: %x\n", reg0);	reg1 = apic_read(APIC_LVT1);	Dprintk("Getting LVT1: %x\n", reg1);	return 1;}void __init sync_Arb_IDs(void){	/*	 * Wait for idle.	 */	apic_wait_icr_idle();	Dprintk("Synchronizing Arb IDs.\n");	apic_write_around(APIC_ICR, APIC_DEST_ALLINC | APIC_INT_LEVELTRIG				| APIC_DM_INIT);}extern void __error_in_apic_c (void);/* * An initial setup of the virtual wire mode. */void __init init_bsp_APIC(void){	unsigned int value, ver;	/*	 * Don't do the setup now if we have a SMP BIOS as the	 * through-I/O-APIC virtual wire mode might be active.	 */	if (smp_found_config || !cpu_has_apic)		return;	value = apic_read(APIC_LVR);	ver = GET_APIC_VERSION(value);	/*	 * Do not trust the local APIC being empty at bootup.	 */	clear_local_APIC();	/*	 * Enable APIC.	 */	value = apic_read(APIC_SPIV);	value &= ~APIC_VECTOR_MASK;	value |= APIC_SPIV_APIC_ENABLED;	value |= APIC_SPIV_FOCUS_DISABLED;	value |= SPURIOUS_APIC_VECTOR;	apic_write_around(APIC_SPIV, value);	/*	 * Set up the virtual wire mode.	 */	apic_write_around(APIC_LVT0, APIC_DM_EXTINT);	value = APIC_DM_NMI;	if (!APIC_INTEGRATED(ver))		/* 82489DX */		value |= APIC_LVT_LEVEL_TRIGGER;	apic_write_around(APIC_LVT1, value);}void __init setup_local_APIC (void){	unsigned int value, ver, maxlvt;	/* Pound the ESR really hard over the head with a big hammer - mbligh */	if (esr_disable) {		apic_write(APIC_ESR, 0);		apic_write(APIC_ESR, 0);		apic_write(APIC_ESR, 0);		apic_write(APIC_ESR, 0);	}	value = apic_read(APIC_LVR);	ver = GET_APIC_VERSION(value);	if ((SPURIOUS_APIC_VECTOR & 0x0f) != 0x0f)		__error_in_apic_c();	/*	 * Double-check wether this APIC is really registered.	 * This is meaningless in clustered apic mode, so we skip it.	 */	if (!clustered_apic_mode &&	    !test_bit(GET_APIC_ID(apic_read(APIC_ID)), &phys_cpu_present_map))		BUG();	/*	 * Intel recommends to set DFR, LDR and TPR before enabling	 * an APIC.  See e.g. "AP-388 82489DX User's Manual" (Intel	 * document number 292116).  So here it goes...	 */	if (!clustered_apic_mode) {		/*		 * In clustered apic mode, the firmware does this for us		 * Put the APIC into flat delivery mode.		 * Must be "all ones" explicitly for 82489DX.		 */		apic_write_around(APIC_DFR, 0xffffffff);		/*		 * Set up the logical destination ID.		 */		value = apic_read(APIC_LDR);		value &= ~APIC_LDR_MASK;		value |= (1<<(smp_processor_id()+24));		apic_write_around(APIC_LDR, value);	}	/*	 * Set Task Priority to 'accept all'. We never change this	 * later on.	 */	value = apic_read(APIC_TASKPRI);	value &= ~APIC_TPRI_MASK;	apic_write_around(APIC_TASKPRI, value);	/*	 * Now that we are all set up, enable the APIC	 */	value = apic_read(APIC_SPIV);	value &= ~APIC_VECTOR_MASK;	/*	 * Enable APIC	 */	value |= APIC_SPIV_APIC_ENABLED;	/*	 * Some unknown Intel IO/APIC (or APIC) errata is biting us with	 * certain networking cards. If high frequency interrupts are	 * happening on a particular IOAPIC pin, plus the IOAPIC routing	 * entry is masked/unmasked at a high rate as well then sooner or	 * later IOAPIC line gets 'stuck', no more interrupts are received	 * from the device. If focus CPU is disabled then the hang goes	 * away, oh well :-(	 *	 * [ This bug can be reproduced easily with a level-triggered	 *   PCI Ne2000 networking cards and PII/PIII processors, dual	 *   BX chipset. ]	 */	/*	 * Actually disabling the focus CPU check just makes the hang less	 * frequent as it makes the interrupt distributon model be more	 * like LRU than MRU (the short-term load is more even across CPUs).	 * See also the comment in end_level_ioapic_irq().  --macro	 */#if 1	/* Enable focus processor (bit==0) */	value &= ~APIC_SPIV_FOCUS_DISABLED;#else	/* Disable focus processor (bit==1) */	value |= APIC_SPIV_FOCUS_DISABLED;#endif	/*	 * Set spurious IRQ vector	 */	value |= SPURIOUS_APIC_VECTOR;	apic_write_around(APIC_SPIV, value);	/*	 * Set up LVT0, LVT1:	 *	 * set up through-local-APIC on the BP's LINT0. This is not	 * strictly necessery in pure symmetric-IO mode, but sometimes	 * we delegate interrupts to the 8259A.	 */	/*	 * TODO: set up through-local-APIC from through-I/O-APIC? --macro	 */	value = apic_read(APIC_LVT0) & APIC_LVT_MASKED;	if (!smp_processor_id() && (pic_mode || !value)) {		value = APIC_DM_EXTINT;		printk("enabled ExtINT on CPU#%d\n", smp_processor_id());	} else {		value = APIC_DM_EXTINT | APIC_LVT_MASKED;		printk("masked ExtINT on CPU#%d\n", smp_processor_id());	}	apic_write_around(APIC_LVT0, value);	/*	 * only the BP should see the LINT1 NMI signal, obviously.	 */	if (!smp_processor_id())		value = APIC_DM_NMI;	else		value = APIC_DM_NMI | APIC_LVT_MASKED;	if (!APIC_INTEGRATED(ver))		/* 82489DX */		value |= APIC_LVT_LEVEL_TRIGGER;	apic_write_around(APIC_LVT1, value);	if (APIC_INTEGRATED(ver) && !esr_disable) {		/* !82489DX */		maxlvt = get_maxlvt();		if (maxlvt > 3)			/* Due to the Pentium erratum 3AP. */			apic_write(APIC_ESR, 0);		value = apic_read(APIC_ESR);		printk("ESR value before enabling vector: %08x\n", value);		value = ERROR_APIC_VECTOR;	/* enables sending errors */		apic_write_around(APIC_LVTERR, value);		/*		 * spec says clear errors after enabling vector.		 */		if (maxlvt > 3)			apic_write(APIC_ESR, 0);		value = apic_read(APIC_ESR);		printk("ESR value after enabling vector: %08x\n", value);	} else {		if (esr_disable)			/*			 * Something untraceble is creating bad interrupts on			 * secondary quads ... for the moment, just leave the			 * ESR disabled - we can't do anything useful with the			 * errors anyway - mbligh			 */			printk("Leaving ESR disabled.\n");		else			printk("No ESR for 82489DX.\n");	}	if (nmi_watchdog == NMI_LOCAL_APIC)		setup_apic_nmi_watchdog();}#ifdef CONFIG_PM#include <linux/slab.h>#include <linux/pm.h>static struct {	/* 'active' is true if the local APIC was enabled by us and	   not the BIOS; this signifies that we are also responsible	   for disabling it before entering apm/acpi suspend */	int active;	/* 'perfctr_pmdev' is here because the current (2.4.1) PM	   callback system doesn't handle hierarchical dependencies */	struct pm_dev *perfctr_pmdev;	/* r/w apic fields */	unsigned int apic_id;	unsigned int apic_taskpri;	unsigned int apic_ldr;	unsigned int apic_dfr;	unsigned int apic_spiv;	unsigned int apic_lvtt;	unsigned int apic_lvtpc;	unsigned int apic_lvt0;	unsigned int apic_lvt1;	unsigned int apic_lvterr;	unsigned int apic_tmict;	unsigned int apic_tdcr;} apic_pm_state;static void apic_pm_suspend(void *data){	unsigned int l, h;	unsigned long flags;	if (apic_pm_state.perfctr_pmdev)		pm_send(apic_pm_state.perfctr_pmdev, PM_SUSPEND, data);	apic_pm_state.apic_id = apic_read(APIC_ID);	apic_pm_state.apic_taskpri = apic_read(APIC_TASKPRI);	apic_pm_state.apic_ldr = apic_read(APIC_LDR);	apic_pm_state.apic_dfr = apic_read(APIC_DFR);	apic_pm_state.apic_spiv = apic_read(APIC_SPIV);	apic_pm_state.apic_lvtt = apic_read(APIC_LVTT);	apic_pm_state.apic_lvtpc = apic_read(APIC_LVTPC);	apic_pm_state.apic_lvt0 = apic_read(APIC_LVT0);	apic_pm_state.apic_lvt1 = apic_read(APIC_LVT1);	apic_pm_state.apic_lvterr = apic_read(APIC_LVTERR);	apic_pm_state.apic_tmict = apic_read(APIC_TMICT);	apic_pm_state.apic_tdcr = apic_read(APIC_TDCR);	__save_flags(flags);	__cli();	disable_local_APIC();	rdmsr(MSR_IA32_APICBASE, l, h);	l &= ~MSR_IA32_APICBASE_ENABLE;	wrmsr(MSR_IA32_APICBASE, l, h);	__restore_flags(flags);}static void apic_pm_resume(void *data){	unsigned int l, h;	unsigned long flags;	__save_flags(flags);	__cli();	rdmsr(MSR_IA32_APICBASE, l, h);	l &= ~MSR_IA32_APICBASE_BASE;	l |= MSR_IA32_APICBASE_ENABLE | APIC_DEFAULT_PHYS_BASE;	wrmsr(MSR_IA32_APICBASE, l, h);	apic_write(APIC_LVTERR, ERROR_APIC_VECTOR | APIC_LVT_MASKED);	apic_write(APIC_ID, apic_pm_state.apic_id);	apic_write(APIC_DFR, apic_pm_state.apic_dfr);	apic_write(APIC_LDR, apic_pm_state.apic_ldr);	apic_write(APIC_TASKPRI, apic_pm_state.apic_taskpri);	apic_write(APIC_SPIV, apic_pm_state.apic_spiv);	apic_write(APIC_LVT0, apic_pm_state.apic_lvt0);	apic_write(APIC_LVT1, apic_pm_state.apic_lvt1);	apic_write(APIC_LVTPC, apic_pm_state.apic_lvtpc);	apic_write(APIC_LVTT, apic_pm_state.apic_lvtt);	apic_write(APIC_TDCR, apic_pm_state.apic_tdcr);	apic_write(APIC_TMICT, apic_pm_state.apic_tmict);	apic_write(APIC_ESR, 0);	apic_read(APIC_ESR);	apic_write(APIC_LVTERR, apic_pm_state.apic_lvterr);	apic_write(APIC_ESR, 0);	apic_read(APIC_ESR);	__restore_flags(flags);	if (apic_pm_state.perfctr_pmdev)		pm_send(apic_pm_state.perfctr_pmdev, PM_RESUME, data);}static int apic_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data){	switch (rqst) {	case PM_SUSPEND:		apic_pm_suspend(data);		break;	case PM_RESUME:		apic_pm_resume(data);		break;	}	return 0;}/* perfctr driver should call this instead of pm_register() */struct pm_dev *apic_pm_register(pm_dev_t type,				unsigned long id,				pm_callback callback){	struct pm_dev *dev;	if (!apic_pm_state.active)		return pm_register(type, id, callback);	if (apic_pm_state.perfctr_pmdev)		return NULL;	/* we're busy */	dev = kmalloc(sizeof(struct pm_dev), GFP_KERNEL);	if (dev) {		memset(dev, 0, sizeof(*dev));		dev->type = type;		dev->id = id;		dev->callback = callback;		apic_pm_state.perfctr_pmdev = dev;	}	return dev;

⌨️ 快捷键说明

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