kconfig: use pkg-config for ncurses detection
[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 #include <arch/uaccess.h>
17
18 static uint16_t pit_divisor;
19 static uint8_t pit_mode;
20
21 static uint64_t compute_tsc_freq(void)
22 {
23         uint64_t tscval[2];
24
25         /* some boards have this unmasked early on. */
26         pic_mask_irq(0, 0 + PIC1_OFFSET);
27         pit_set_timer(0xffff, TIMER_RATEGEN);
28         tscval[0] = read_tsc();
29         udelay_pit(1000000);
30         tscval[1] = read_tsc();
31         return tscval[1] - tscval[0];
32 }
33
34 static void set_tsc_freq(void)
35 {
36         uint64_t msr_val, tsc_freq = 0;
37         bool computed = FALSE;
38
39         if (!read_msr_safe(MSR_PLATFORM_INFO, &msr_val))
40                 tsc_freq = __proc_global_info.bus_freq
41                            * ((msr_val >> 8) & 0xff);
42         /* Even if we have the MSR, it might have given us 0. (QEMU). */
43         if (!tsc_freq) {
44                 tsc_freq = compute_tsc_freq();
45                 computed = TRUE;
46         }
47         __proc_global_info.tsc_freq = tsc_freq;
48         printk("TSC Frequency: %llu%s\n", tsc_freq,
49                computed ? " (computed)" : "");
50 }
51
52 static uint64_t compute_bus_freq(void)
53 {
54         uint32_t timercount[2];
55
56         __lapic_set_timer(0xffffffff, IdtLAPIC_TIMER, FALSE,
57                           LAPIC_TIMER_DIVISOR_BITS);
58         // Mask the LAPIC Timer, so we never receive this interrupt (minor race)
59         mask_lapic_lvt(MSR_LAPIC_LVT_TIMER);
60         timercount[0] = apicrget(MSR_LAPIC_CURRENT_COUNT);
61         udelay_pit(1000000);
62         timercount[1] = apicrget(MSR_LAPIC_CURRENT_COUNT);
63         /* The time base for the timer is derived from the processor's bus
64          * clock, divided by the value specified in the divide configuration
65          * register.  Note we mult and div by the divisor, saving the actual
66          * freq (even though we don't use it yet). */
67         return (timercount[0] - timercount[1]) * LAPIC_TIMER_DIVISOR_VAL;
68 }
69
70 static uint64_t lookup_bus_freq(void)
71 {
72         /* Got these from the good book for any model supporting
73          * MSR_PLATFORM_INFO.  If they don't support that MSR, we're going to
74          * compute the TSC anyways.
75          *
76          * A couple models weren't in the book, but were reported at:
77          * http://a4lg.com/tech/x86/database/x86-families-and-models.en.html.
78          * Feel free to add more.  If we fail here, we'll compute it manually
79          * and be off slightly. */
80         switch ((x86_family << 16) | x86_model) {
81         case 0x6001a:
82         case 0x6001e:
83         case 0x6001f:
84         case 0x6002e:
85                 /* Nehalem */
86                 return 133333333;
87         case 0x60025:
88         case 0x6002c:
89         case 0x6002f:   /* from a4lg.com */
90                 /* Westmere */
91                 return 133333333;
92         case 0x6002a:
93         case 0x6002d:
94                 /* Sandy Bridge */
95                 return 100000000;
96         case 0x6003a:   /* from a4lg.com */
97         case 0x6003e:
98                 /* Ivy Bridge */
99                 return 100000000;
100         case 0x6003c:
101         case 0x6003f:
102         case 0x60045:
103         case 0x60046:
104                 /* Haswell */
105                 return 100000000;
106         case 0x6003d:
107         case 0x6004f:
108         case 0x60056:
109                 /* Broadwell */
110                 return 100000000;
111         case 0x6004d:
112                 /* Sky Lake */
113                 return 100000000;
114         case 0x60057:
115                 /* Knights Landing */
116                 return 100000000;
117         }
118         return 0;
119 }
120
121 static void set_bus_freq(void)
122 {
123         uint64_t bus_freq;
124         bool computed = FALSE;
125
126         bus_freq = lookup_bus_freq();
127         if (!bus_freq) {
128                 bus_freq = compute_bus_freq();
129                 computed = TRUE;
130         }
131         __proc_global_info.bus_freq = bus_freq;
132         printk("Bus Frequency: %llu%s\n", bus_freq,
133                computed ? " (computed)" : "");
134 }
135
136 void timer_init(void)
137 {
138         set_bus_freq();
139         assert(__proc_global_info.bus_freq);
140         set_tsc_freq();
141 }
142
143 void pit_set_timer(uint32_t divisor, uint32_t mode)
144 {
145         if (divisor & 0xffff0000)
146                 warn("Divisor too large!");
147         mode = TIMER_SEL0|TIMER_16BIT|mode;
148         outb(TIMER_MODE, mode);
149         outb(TIMER_CNTR0, divisor & 0xff);
150         outb(TIMER_CNTR0, (divisor >> 8) );
151         pit_mode = mode;
152         pit_divisor = divisor;
153         // cprintf("timer mode set to %d, divisor %d\n",mode, divisor);
154 }
155
156 static int getpit(void)
157 {
158         int high, low;
159         // TODO: need a lock to protect access to PIT
160         
161         /* Select counter 0 and latch counter value. */
162         outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
163         
164         low = inb(TIMER_CNTR0);
165         high = inb(TIMER_CNTR0);
166         
167         return ((high << 8) | low);
168 }
169
170 // forces cpu to relax for usec miliseconds.  declared in kern/include/time.h
171 void udelay(uint64_t usec)
172 {
173         #if !defined(__BOCHS__)
174         if (__proc_global_info.tsc_freq != 0)
175         {
176                 uint64_t start, end, now;
177
178                 start = read_tsc();
179         end = start + usec2tsc(usec);
180         //cprintf("start %llu, end %llu\n", start, end);
181                 if (end == 0) cprintf("This is terribly wrong \n");
182                 do {
183             cpu_relax();
184             now = read_tsc();
185                         //cprintf("now %llu\n", now);
186                 } while (now < end || (now > start && end < start));
187         return;
188
189         } else
190         #endif
191         {
192                 udelay_pit(usec);
193         }
194 }
195
196 void udelay_pit(uint64_t usec)
197 {
198         int64_t delta, prev_tick, tick, ticks_left;
199
200         if (usec <= 0)
201                 return;
202
203         prev_tick = getpit();
204         /*
205          * Calculate ticks as (usec * (i8254_freq / 1e6)) rounded up
206          * without using floating point and without any avoidable overflows.
207          */
208         ticks_left = ((usec * PIT_FREQ) + 999999) / 1000000;
209         while (ticks_left > 0) {
210                 tick = getpit();
211                 delta = prev_tick - tick;
212                 prev_tick = tick;
213                 if (delta < 0) {
214                         // counter looped around during the delta time period
215                         delta += pit_divisor; // maximum count
216                         if (delta < 0)
217                                 delta = 0;
218                 }
219                 ticks_left -= delta;
220         }
221 }
222
223 uint64_t gettimer(void)
224 {
225         return read_tsc();
226 }
227
228 uint64_t getfreq(void)
229 {
230         return __proc_global_info.tsc_freq;
231 }
232
233 void set_core_timer(uint32_t usec, bool periodic)
234 {
235         if (usec)
236                 lapic_set_timer(usec, periodic);
237         else
238                 lapic_disable_timer();
239 }