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