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