| /* Copyright (c) 2009 The Regents of the University of California | 
 |  * David (Yu) Zhu <yuzhu@cs.berkeley.edu> | 
 |  * Barret Rhoden <brho@cs.berkeley.edu> | 
 |  * | 
 |  * See LICENSE for details. */ | 
 |  | 
 | #include <arch/x86.h> | 
 | #include <arch/arch.h> | 
 | #include <arch/pic.h> | 
 | #include <arch/apic.h> | 
 | #include <time.h> | 
 | #include <trap.h> | 
 | #include <assert.h> | 
 | #include <stdio.h> | 
 | #include <ros/procinfo.h> | 
 | #include <arch/uaccess.h> | 
 |  | 
 | static uint16_t pit_divisor; | 
 | static uint8_t pit_mode; | 
 |  | 
 | static uint64_t compute_tsc_freq(void) | 
 | { | 
 | 	uint64_t tscval[2]; | 
 |  | 
 | 	/* some boards have this unmasked early on. */ | 
 | 	pic_mask_irq(0, 0 + PIC1_OFFSET); | 
 | 	pit_set_timer(0xffff, TIMER_RATEGEN); | 
 | 	tscval[0] = read_tsc(); | 
 | 	udelay_pit(1000000); | 
 | 	tscval[1] = read_tsc(); | 
 | 	return tscval[1] - tscval[0]; | 
 | } | 
 |  | 
 | static void set_tsc_freq(void) | 
 | { | 
 | 	uint64_t msr_val, tsc_freq = 0; | 
 | 	bool computed = FALSE; | 
 |  | 
 | 	if (!read_msr_safe(MSR_PLATFORM_INFO, &msr_val)) | 
 | 		tsc_freq = __proc_global_info.bus_freq | 
 | 			   * ((msr_val >> 8) & 0xff); | 
 | 	/* Even if we have the MSR, it might have given us 0. (QEMU). */ | 
 | 	if (!tsc_freq) { | 
 | 		tsc_freq = compute_tsc_freq(); | 
 | 		computed = TRUE; | 
 | 	} | 
 | 	__proc_global_info.tsc_freq = tsc_freq; | 
 | 	printk("TSC Frequency: %llu%s\n", tsc_freq, | 
 | 	       computed ? " (computed)" : ""); | 
 | } | 
 |  | 
 | static uint64_t compute_bus_freq(void) | 
 | { | 
 | 	uint32_t timercount[2]; | 
 |  | 
 | 	__lapic_set_timer(0xffffffff, IdtLAPIC_TIMER, FALSE, | 
 | 	                  LAPIC_TIMER_DIVISOR_BITS); | 
 | 	// Mask the LAPIC Timer, so we never receive this interrupt (minor race) | 
 | 	mask_lapic_lvt(MSR_LAPIC_LVT_TIMER); | 
 | 	timercount[0] = apicrget(MSR_LAPIC_CURRENT_COUNT); | 
 | 	udelay_pit(1000000); | 
 | 	timercount[1] = apicrget(MSR_LAPIC_CURRENT_COUNT); | 
 | 	/* The time base for the timer is derived from the processor's bus | 
 | 	 * clock, divided by the value specified in the divide configuration | 
 | 	 * register.  Note we mult and div by the divisor, saving the actual | 
 | 	 * freq (even though we don't use it yet). */ | 
 | 	return (timercount[0] - timercount[1]) * LAPIC_TIMER_DIVISOR_VAL; | 
 | } | 
 |  | 
 | static uint64_t lookup_bus_freq(void) | 
 | { | 
 | 	/* Got these from the good book for any model supporting | 
 | 	 * MSR_PLATFORM_INFO.  If they don't support that MSR, we're going to | 
 | 	 * compute the TSC anyways. | 
 | 	 * | 
 | 	 * A couple models weren't in the book, but were reported at: | 
 | 	 * http://a4lg.com/tech/x86/database/x86-families-and-models.en.html. | 
 | 	 * Feel free to add more.  If we fail here, we'll compute it manually | 
 | 	 * and be off slightly. */ | 
 | 	switch ((x86_family << 16) | x86_model) { | 
 | 	case 0x6001a: | 
 | 	case 0x6001e: | 
 | 	case 0x6001f: | 
 | 	case 0x6002e: | 
 | 		/* Nehalem */ | 
 | 		return 133333333; | 
 | 	case 0x60025: | 
 | 	case 0x6002c: | 
 | 	case 0x6002f:	/* from a4lg.com */ | 
 | 		/* Westmere */ | 
 | 		return 133333333; | 
 | 	case 0x6002a: | 
 | 	case 0x6002d: | 
 | 		/* Sandy Bridge */ | 
 | 		return 100000000; | 
 | 	case 0x6003a:	/* from a4lg.com */ | 
 | 	case 0x6003e: | 
 | 		/* Ivy Bridge */ | 
 | 		return 100000000; | 
 | 	case 0x6003c: | 
 | 	case 0x6003f: | 
 | 	case 0x60045: | 
 | 	case 0x60046: | 
 | 		/* Haswell */ | 
 | 		return 100000000; | 
 | 	case 0x6003d: | 
 | 	case 0x6004f: | 
 | 	case 0x60056: | 
 | 		/* Broadwell */ | 
 | 		return 100000000; | 
 | 	case 0x6004d: | 
 | 		/* Sky Lake */ | 
 | 		return 100000000; | 
 | 	case 0x60057: | 
 | 		/* Knights Landing */ | 
 | 		return 100000000; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void set_bus_freq(void) | 
 | { | 
 | 	uint64_t bus_freq; | 
 | 	bool computed = FALSE; | 
 |  | 
 | 	bus_freq = lookup_bus_freq(); | 
 | 	if (!bus_freq) { | 
 | 		bus_freq = compute_bus_freq(); | 
 | 		computed = TRUE; | 
 | 	} | 
 | 	__proc_global_info.bus_freq = bus_freq; | 
 | 	printk("Bus Frequency: %llu%s\n", bus_freq, | 
 | 	       computed ? " (computed)" : ""); | 
 | } | 
 |  | 
 | void timer_init(void) | 
 | { | 
 | 	set_bus_freq(); | 
 | 	assert(__proc_global_info.bus_freq); | 
 | 	set_tsc_freq(); | 
 | } | 
 |  | 
 | void pit_set_timer(uint32_t divisor, uint32_t mode) | 
 | { | 
 | 	if (divisor & 0xffff0000) | 
 | 		warn("Divisor too large!"); | 
 | 	mode = TIMER_SEL0|TIMER_16BIT|mode; | 
 | 	outb(TIMER_MODE, mode); | 
 | 	outb(TIMER_CNTR0, divisor & 0xff); | 
 | 	outb(TIMER_CNTR0, (divisor >> 8) ); | 
 | 	pit_mode = mode; | 
 | 	pit_divisor = divisor; | 
 | 	// cprintf("timer mode set to %d, divisor %d\n",mode, divisor); | 
 | } | 
 |  | 
 | static int getpit(void) | 
 | { | 
 | 	int high, low; | 
 | 	// TODO: need a lock to protect access to PIT | 
 | 	 | 
 | 	/* Select counter 0 and latch counter value. */ | 
 | 	outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); | 
 | 	 | 
 | 	low = inb(TIMER_CNTR0); | 
 | 	high = inb(TIMER_CNTR0); | 
 | 	 | 
 | 	return ((high << 8) | low); | 
 | } | 
 |  | 
 | // forces cpu to relax for usec miliseconds.  declared in kern/include/time.h | 
 | void udelay(uint64_t usec) | 
 | { | 
 | 	#if !defined(__BOCHS__) | 
 | 	if (__proc_global_info.tsc_freq != 0) | 
 | 	{ | 
 | 		uint64_t start, end, now; | 
 |  | 
 | 		start = read_tsc(); | 
 |         end = start + usec2tsc(usec); | 
 |         //cprintf("start %llu, end %llu\n", start, end); | 
 | 		if (end == 0) cprintf("This is terribly wrong \n"); | 
 | 		do { | 
 |             cpu_relax(); | 
 |             now = read_tsc(); | 
 | 			//cprintf("now %llu\n", now); | 
 | 		} while (now < end || (now > start && end < start)); | 
 |         return; | 
 |  | 
 | 	} else | 
 | 	#endif | 
 | 	{ | 
 | 		udelay_pit(usec); | 
 | 	} | 
 | } | 
 |  | 
 | void udelay_pit(uint64_t usec) | 
 | { | 
 | 	int64_t delta, prev_tick, tick, ticks_left; | 
 |  | 
 | 	if (usec <= 0) | 
 | 		return; | 
 |  | 
 | 	prev_tick = getpit(); | 
 | 	/* | 
 | 	 * Calculate ticks as (usec * (i8254_freq / 1e6)) rounded up | 
 | 	 * without using floating point and without any avoidable overflows. | 
 | 	 */ | 
 | 	ticks_left = ((usec * PIT_FREQ) + 999999) / 1000000; | 
 | 	while (ticks_left > 0) { | 
 | 		tick = getpit(); | 
 | 		delta = prev_tick - tick; | 
 | 		prev_tick = tick; | 
 | 		if (delta < 0) { | 
 | 			// counter looped around during the delta time period | 
 | 			delta += pit_divisor; // maximum count | 
 | 			if (delta < 0) | 
 | 				delta = 0; | 
 | 		} | 
 | 		ticks_left -= delta; | 
 | 	} | 
 | } | 
 |  | 
 | uint64_t gettimer(void) | 
 | { | 
 | 	return read_tsc(); | 
 | } | 
 |  | 
 | uint64_t getfreq(void) | 
 | { | 
 | 	return __proc_global_info.tsc_freq; | 
 | } | 
 |  | 
 | void set_core_timer(uint32_t usec, bool periodic) | 
 | { | 
 | 	if (usec) | 
 | 		lapic_set_timer(usec, periodic); | 
 | 	else | 
 | 		lapic_disable_timer(); | 
 | } |