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

📄 apic.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 3 页
字号:
 * This part sets up the APIC 32 bit clock in LVTT1, with HZ interrupts * per second. We assume that the caller has already set up the local * APIC. * * The APIC timer is not exactly sync with the external timer chip, it * closely follows bus clocks. *//* * The timer chip is already set up at HZ interrupts per second here, * but we do not accept timer interrupts yet. We only allow the BP * to calibrate. */static unsigned int __devinit get_8254_timer_count(void){	unsigned long flags;	unsigned int count;	spin_lock_irqsave(&i8253_lock, flags);	outb_p(0x00, PIT_MODE);	count = inb_p(PIT_CH0);	count |= inb_p(PIT_CH0) << 8;	spin_unlock_irqrestore(&i8253_lock, flags);	return count;}/* next tick in 8254 can be caught by catching timer wraparound */static void __devinit wait_8254_wraparound(void){	unsigned int curr_count, prev_count;	curr_count = get_8254_timer_count();	do {		prev_count = curr_count;		curr_count = get_8254_timer_count();		/* workaround for broken Mercury/Neptune */		if (prev_count >= curr_count + 0x100)			curr_count = get_8254_timer_count();	} while (prev_count >= curr_count);}/* * Default initialization for 8254 timers. If we use other timers like HPET, * we override this later */void (*wait_timer_tick)(void) __devinitdata = wait_8254_wraparound;/* * This function sets up the local APIC timer, with a timeout of * 'clocks' APIC bus clock. During calibration we actually call * this function twice on the boot CPU, once with a bogus timeout * value, second time for real. The other (noncalibrating) CPUs * call this function only once, with the real, calibrated value. * * We do reads before writes even if unnecessary, to get around the * P5 APIC double write bug. */#define APIC_DIVISOR 16static void __setup_APIC_LVTT(unsigned int clocks){	unsigned int lvtt_value, tmp_value, ver;	ver = GET_APIC_VERSION(apic_read(APIC_LVR));	lvtt_value = APIC_LVT_TIMER_PERIODIC | LOCAL_TIMER_VECTOR;	if (!APIC_INTEGRATED(ver))		lvtt_value |= SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV);	apic_write_around(APIC_LVTT, lvtt_value);	/*	 * Divide PICLK by 16	 */	tmp_value = apic_read(APIC_TDCR);	apic_write_around(APIC_TDCR, (tmp_value				& ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE))				| APIC_TDR_DIV_16);	apic_write_around(APIC_TMICT, clocks/APIC_DIVISOR);}static void __devinit setup_APIC_timer(unsigned int clocks){	unsigned long flags;	local_irq_save(flags);	/*	 * Wait for IRQ0's slice:	 */	wait_timer_tick();	__setup_APIC_LVTT(clocks);	local_irq_restore(flags);}/* * In this function we calibrate APIC bus clocks to the external * timer. Unfortunately we cannot use jiffies and the timer irq * to calibrate, since some later bootup code depends on getting * the first irq? Ugh. * * We want to do the calibration only once since we * want to have local timer irqs syncron. CPUs connected * by the same APIC bus have the very same bus frequency. * And we want to have irqs off anyways, no accidental * APIC irq that way. */static int __init calibrate_APIC_clock(void){	unsigned long long t1 = 0, t2 = 0;	long tt1, tt2;	long result;	int i;	const int LOOPS = HZ/10;	apic_printk(APIC_VERBOSE, "calibrating APIC timer ...\n");	/*	 * Put whatever arbitrary (but long enough) timeout	 * value into the APIC clock, we just want to get the	 * counter running for calibration.	 */	__setup_APIC_LVTT(1000000000);	/*	 * The timer chip counts down to zero. Let's wait	 * for a wraparound to start exact measurement:	 * (the current tick might have been already half done)	 */	wait_timer_tick();	/*	 * We wrapped around just now. Let's start:	 */	if (cpu_has_tsc)		rdtscll(t1);	tt1 = apic_read(APIC_TMCCT);	/*	 * Let's wait LOOPS wraprounds:	 */	for (i = 0; i < LOOPS; i++)		wait_timer_tick();	tt2 = apic_read(APIC_TMCCT);	if (cpu_has_tsc)		rdtscll(t2);	/*	 * The APIC bus clock counter is 32 bits only, it	 * might have overflown, but note that we use signed	 * longs, thus no extra care needed.	 *	 * underflown to be exact, as the timer counts down ;)	 */	result = (tt1-tt2)*APIC_DIVISOR/LOOPS;	if (cpu_has_tsc)		apic_printk(APIC_VERBOSE, "..... CPU clock speed is "			"%ld.%04ld MHz.\n",			((long)(t2-t1)/LOOPS)/(1000000/HZ),			((long)(t2-t1)/LOOPS)%(1000000/HZ));	apic_printk(APIC_VERBOSE, "..... host bus clock speed is "		"%ld.%04ld MHz.\n",		result/(1000000/HZ),		result%(1000000/HZ));	return result;}static unsigned int calibration_result;void __init setup_boot_APIC_clock(void){	unsigned long flags;	apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n");	using_apic_timer = 1;	local_irq_save(flags);	calibration_result = calibrate_APIC_clock();	/*	 * Now set up the timer for real.	 */	setup_APIC_timer(calibration_result);	local_irq_restore(flags);}void __devinit setup_secondary_APIC_clock(void){	setup_APIC_timer(calibration_result);}void __devinit disable_APIC_timer(void){	if (using_apic_timer) {		unsigned long v;		v = apic_read(APIC_LVTT);		apic_write_around(APIC_LVTT, v | APIC_LVT_MASKED);	}}void enable_APIC_timer(void){	if (using_apic_timer) {		unsigned long v;		v = apic_read(APIC_LVTT);		apic_write_around(APIC_LVTT, v & ~APIC_LVT_MASKED);	}}/* * the frequency of the profiling timer can be changed * by writing a multiplier value into /proc/profile. */int setup_profiling_timer(unsigned int multiplier){	int i;	/*	 * Sanity check. [at least 500 APIC cycles should be	 * between APIC interrupts as a rule of thumb, to avoid	 * irqs flooding us]	 */	if ( (!multiplier) || (calibration_result/multiplier < 500))		return -EINVAL;	/* 	 * Set the new multiplier for each CPU. CPUs don't start using the	 * new values until the next timer interrupt in which they do process	 * accounting. At that time they also adjust their APIC timers	 * accordingly.	 */	for (i = 0; i < NR_CPUS; ++i)		per_cpu(prof_multiplier, i) = multiplier;	return 0;}#undef APIC_DIVISOR/* * Local timer interrupt handler. It does both profiling and * process statistics/rescheduling. * * We do profiling in every local tick, statistics/rescheduling * happen only every 'profiling multiplier' ticks. The default * multiplier is 1 and it can be changed by writing the new multiplier * value into /proc/profile. */inline void smp_local_timer_interrupt(struct pt_regs * regs){	int cpu = smp_processor_id();	profile_tick(CPU_PROFILING, regs);	if (--per_cpu(prof_counter, cpu) <= 0) {		/*		 * The multiplier may have changed since the last time we got		 * to this point as a result of the user writing to		 * /proc/profile. In this case we need to adjust the APIC		 * timer accordingly.		 *		 * Interrupts are already masked off at this point.		 */		per_cpu(prof_counter, cpu) = per_cpu(prof_multiplier, cpu);		if (per_cpu(prof_counter, cpu) !=					per_cpu(prof_old_multiplier, cpu)) {			__setup_APIC_LVTT(					calibration_result/					per_cpu(prof_counter, cpu));			per_cpu(prof_old_multiplier, cpu) =						per_cpu(prof_counter, cpu);		}#ifdef CONFIG_SMP		update_process_times(user_mode_vm(regs));#endif	}	/*	 * We take the 'long' return path, and there every subsystem	 * grabs the apropriate locks (kernel lock/ irq lock).	 *	 * we might want to decouple profiling from the 'long path',	 * and do the profiling totally in assembly.	 *	 * Currently this isn't too much of an issue (performance wise),	 * we can take more than 100K local irqs per second on a 100 MHz P5.	 */}/* * Local APIC timer interrupt. This is the most natural way for doing * local interrupts, but local timer interrupts can be emulated by * broadcast interrupts too. [in case the hw doesn't support APIC timers] * * [ if a single-CPU system runs an SMP kernel then we call the local *   interrupt as well. Thus we cannot inline the local irq ... ] */fastcall void smp_apic_timer_interrupt(struct pt_regs *regs){	int cpu = smp_processor_id();	/*	 * the NMI deadlock-detector uses this.	 */	per_cpu(irq_stat, cpu).apic_timer_irqs++;	/*	 * NOTE! We'd better ACK the irq immediately,	 * because timer handling can be slow.	 */	ack_APIC_irq();	/*	 * update_process_times() expects us to have done irq_enter().	 * Besides, if we don't timer interrupts ignore the global	 * interrupt lock, which is the WrongThing (tm) to do.	 */	irq_enter();	smp_local_timer_interrupt(regs);	irq_exit();}/* * This interrupt should _never_ happen with our APIC/SMP architecture */fastcall void smp_spurious_interrupt(struct pt_regs *regs){	unsigned long v;	irq_enter();	/*	 * Check if this really is a spurious interrupt and ACK it	 * if it is a vectored one.  Just in case...	 * Spurious interrupts should not be ACKed.	 */	v = apic_read(APIC_ISR + ((SPURIOUS_APIC_VECTOR & ~0x1f) >> 1));	if (v & (1 << (SPURIOUS_APIC_VECTOR & 0x1f)))		ack_APIC_irq();	/* see sw-dev-man vol 3, chapter 7.4.13.5 */	printk(KERN_INFO "spurious APIC interrupt on CPU#%d, should never happen.\n",			smp_processor_id());	irq_exit();}/* * This interrupt should never happen with our APIC/SMP architecture */fastcall void smp_error_interrupt(struct pt_regs *regs){	unsigned long v, v1;	irq_enter();	/* First tickle the hardware, only then report what went on. -- REW */	v = apic_read(APIC_ESR);	apic_write(APIC_ESR, 0);	v1 = apic_read(APIC_ESR);	ack_APIC_irq();	atomic_inc(&irq_err_count);	/* Here is what the APIC error bits mean:	   0: Send CS error	   1: Receive CS error	   2: Send accept error	   3: Receive accept error	   4: Reserved	   5: Send illegal vector	   6: Received illegal vector	   7: Illegal register address	*/	printk (KERN_DEBUG "APIC error on CPU%d: %02lx(%02lx)\n",	        smp_processor_id(), v , v1);	irq_exit();}/* * This initializes the IO-APIC and APIC hardware if this is * a UP kernel. */int __init APIC_init_uniprocessor (void){	if (enable_local_apic < 0)		clear_bit(X86_FEATURE_APIC, boot_cpu_data.x86_capability);	if (!smp_found_config && !cpu_has_apic)		return -1;	/*	 * Complain if the BIOS pretends there is one.	 */	if (!cpu_has_apic && APIC_INTEGRATED(apic_version[boot_cpu_physical_apicid])) {		printk(KERN_ERR "BIOS bug, local APIC #%d not detected!...\n",			boot_cpu_physical_apicid);		return -1;	}	verify_local_APIC();	connect_bsp_APIC();	phys_cpu_present_map = physid_mask_of_physid(boot_cpu_physical_apicid);	setup_local_APIC();#ifdef CONFIG_X86_IO_APIC	if (smp_found_config)		if (!skip_ioapic_setup && nr_ioapics)			setup_IO_APIC();#endif	setup_boot_APIC_clock();	return 0;}

⌨️ 快捷键说明

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