|  | /* 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(); | 
|  | } |