Moved timing parameters into proc_global_info (XCC)
[akaros.git] / kern / arch / x86 / time.c
1 /* Copyright (c) 2009 The Regents of the University of California
2  * David (Yu) Zhu <yuzhu@cs.berkeley.edu>
3  * Barret Rhoden <brho@cs.berkeley.edu>
4  *
5  * See LICENSE for details. */
6
7 #include <arch/x86.h>
8 #include <arch/arch.h>
9 #include <arch/pic.h>
10 #include <arch/apic.h>
11 #include <time.h>
12 #include <trap.h>
13 #include <assert.h>
14 #include <stdio.h>
15 #include <ros/procinfo.h>
16
17 static uint16_t pit_divisor;
18 static uint8_t pit_mode;
19
20 // timer init calibrates both tsc timer and lapic timer using PIT
21 void timer_init(void){
22         /* some boards have this unmasked early on. */
23         pic_mask_irq(0, 0 + PIC1_OFFSET);
24         uint64_t tscval[2];
25         long timercount[2];
26         pit_set_timer(0xffff, TIMER_RATEGEN);
27         // assume tsc exist
28         tscval[0] = read_tsc();
29         udelay_pit(1000000);
30         tscval[1] = read_tsc();
31         __proc_global_info.tsc_freq = tscval[1] - tscval[0];
32         cprintf("TSC Frequency: %llu\n", __proc_global_info.tsc_freq);
33         __lapic_set_timer(0xffffffff, IdtLAPIC_TIMER, FALSE,
34                           LAPIC_TIMER_DIVISOR_BITS);
35         // Mask the LAPIC Timer, so we never receive this interrupt (minor race)
36         mask_lapic_lvt(MSR_LAPIC_LVT_TIMER);
37         timercount[0] = apicrget(MSR_LAPIC_CURRENT_COUNT);
38         udelay_pit(1000000);
39         timercount[1] = apicrget(MSR_LAPIC_CURRENT_COUNT);
40         __proc_global_info.bus_freq = (timercount[0] - timercount[1])
41                                  * LAPIC_TIMER_DIVISOR_VAL;
42         /* The time base for the timer is derived from the processor's bus clock,
43          * divided by the value specified in the divide configuration register.
44          * Note we mult and div by the divisor, saving the actual freq (even though
45          * we don't use it yet). */
46         cprintf("Bus Frequency: %llu\n", __proc_global_info.bus_freq);
47 }
48
49 void pit_set_timer(uint32_t divisor, uint32_t mode)
50 {
51         if (divisor & 0xffff0000)
52                 warn("Divisor too large!");
53         mode = TIMER_SEL0|TIMER_16BIT|mode;
54         outb(TIMER_MODE, mode);
55         outb(TIMER_CNTR0, divisor & 0xff);
56         outb(TIMER_CNTR0, (divisor >> 8) );
57         pit_mode = mode;
58         pit_divisor = divisor;
59         // cprintf("timer mode set to %d, divisor %d\n",mode, divisor);
60 }
61
62 static int getpit()
63 {
64     int high, low;
65         // TODO: need a lock to protect access to PIT
66
67     /* Select counter 0 and latch counter value. */
68     outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
69
70     low = inb(TIMER_CNTR0);
71     high = inb(TIMER_CNTR0);
72
73     return ((high << 8) | low);
74 }
75
76 // forces cpu to relax for usec miliseconds.  declared in kern/include/time.h
77 void udelay(uint64_t usec)
78 {
79         #if !defined(__BOCHS__)
80         if (__proc_global_info.tsc_freq != 0)
81         {
82                 uint64_t start, end, now;
83
84                 start = read_tsc();
85         end = start + usec2tsc(usec);
86         //cprintf("start %llu, end %llu\n", start, end);
87                 if (end == 0) cprintf("This is terribly wrong \n");
88                 do {
89             cpu_relax();
90             now = read_tsc();
91                         //cprintf("now %llu\n", now);
92                 } while (now < end || (now > start && end < start));
93         return;
94
95         } else
96         #endif
97         {
98                 udelay_pit(usec);
99         }
100 }
101
102 void udelay_pit(uint64_t usec)
103 {
104         int64_t delta, prev_tick, tick, ticks_left;
105         prev_tick = getpit();
106         /*
107          * Calculate (n * (i8254_freq / 1e6)) without using floating point
108          * and without any avoidable overflows.
109          */
110         if (usec <= 0)
111                 ticks_left = 0;
112         // some optimization from bsd code
113         else if (usec < 256)
114                 /*
115                  * Use fixed point to avoid a slow division by 1000000.
116                  * 39099 = 1193182 * 2^15 / 10^6 rounded to nearest.
117                  * 2^15 is the first power of 2 that gives exact results
118                  * for n between 0 and 256.
119                  */
120                 ticks_left = ((uint64_t)usec * 39099 + (1 << 15) - 1) >> 15;
121         else
122                 // round up the ticks left
123                 ticks_left = ((uint64_t)usec * (long long)PIT_FREQ+ 999999)
124                              / 1000000;
125         while (ticks_left > 0) {
126                 tick = getpit();
127                 delta = prev_tick - tick;
128                 prev_tick = tick;
129                 if (delta < 0) {
130                         // counter looped around during the delta time period
131                         delta += pit_divisor; // maximum count
132                         if (delta < 0)
133                                 delta = 0;
134                 }
135                 ticks_left -= delta;
136         }
137 }
138
139 uint64_t gettimer(void)
140 {
141         return read_tsc();
142 }
143
144 uint64_t getfreq(void)
145 {
146         return __proc_global_info.tsc_freq;
147 }
148
149 void set_core_timer(uint32_t usec, bool periodic)
150 {
151         if (usec)
152                 lapic_set_timer(usec, periodic);
153         else
154                 lapic_disable_timer();
155 }