Fixed corner case with alarm rel_time
[akaros.git] / kern / arch / i686 / apic.c
1 /*
2  * Copyright (c) 2009 The Regents of the University of California
3  * Barret Rhoden <brho@cs.berkeley.edu>
4  * See LICENSE for details.
5  */
6
7 #ifdef __SHARC__
8 #pragma nosharc
9 #define SINIT(x) x
10 #endif
11
12 #include <arch/mmu.h>
13 #include <arch/x86.h>
14 #include <arch/arch.h>
15 #include <arch/apic.h>
16 #include <ros/timer.h>
17 #include <assert.h>
18 #include <stdio.h>
19
20 system_timing_t RO system_timing = {0, 0, 0xffff, 0};
21
22 /*
23  * Remaps the Programmable Interrupt Controller to use IRQs 32-47
24  * http://wiki.osdev.org/PIC
25  * Not 100% on this stuff, after looking over 
26  * http://bochs.sourceforge.net/techspec/PORTS.LST  The cascading and other 
27  * stuff might need to be in one command, and after that all we are doing
28  * is toggling masks.
29  */
30 void pic_remap() 
31 {
32         // start initialization
33         outb(PIC1_CMD, 0x11);
34         outb(PIC2_CMD, 0x11);
35         // set new offsets
36         outb(PIC1_DATA, PIC1_OFFSET);
37         outb(PIC2_DATA, PIC2_OFFSET);
38         // set up cascading
39         outb(PIC1_DATA, 0x04);
40         outb(PIC2_DATA, 0x02);
41         // other stuff (put in 8086/88 mode, or whatever)
42         outb(PIC1_DATA, 0x01);
43         outb(PIC2_DATA, 0x01);
44         // set masks, defaulting to all masked for now
45         outb(PIC1_DATA, 0xff);
46         outb(PIC2_DATA, 0xff);
47 }
48
49 void pic_mask_irq(uint8_t irq)
50 {
51         if (irq > 7)
52                 outb(PIC2_DATA, inb(PIC2_DATA) | (1 << (irq - 8)));
53         else
54                 outb(PIC1_DATA, inb(PIC1_DATA) | (1 << irq));
55 }
56
57 void pic_unmask_irq(uint8_t irq)
58 {
59         if (irq > 7) {
60                 outb(PIC2_DATA, inb(PIC2_DATA) & ~(1 << (irq - 8)));
61                 outb(PIC1_DATA, inb(PIC1_DATA) & 0xfb); // make sure irq2 is unmasked
62         } else
63                 outb(PIC1_DATA, inb(PIC1_DATA) & ~(1 << irq));
64 }
65
66 /*
67  * Sets the LAPIC timer to go off after a certain number of ticks.  The primary
68  * clock freq is actually the bus clock, which we figure out during timer_init
69  * Unmasking is implied.  Ref SDM, 3A, 9.6.4
70  */
71 void __lapic_set_timer(uint32_t ticks, uint8_t vec, bool periodic, uint8_t div)
72 {
73         // clears bottom bit and then set divider
74         write_mmreg32(LAPIC_TIMER_DIVIDE, (read_mmreg32(LAPIC_TIMER_DIVIDE) &~0xf) |
75                       (div & 0xf));
76         // set LVT with interrupt handling information
77         write_mmreg32(LAPIC_LVT_TIMER, vec | (periodic << 17));
78         write_mmreg32(LAPIC_TIMER_INIT, ticks);
79         // For debugging when we expand this
80         //cprintf("LAPIC LVT Timer: 0x%08x\n", read_mmreg32(LAPIC_LVT_TIMER));
81         //cprintf("LAPIC Init Count: 0x%08x\n", read_mmreg32(LAPIC_TIMER_INIT));
82         //cprintf("LAPIC Current Count: 0x%08x\n", read_mmreg32(LAPIC_TIMER_CURRENT));
83 }
84
85 void lapic_set_timer(uint32_t usec, bool periodic)
86 {
87         // divide the bus clock by 128, which is the max.
88         uint32_t ticks = (usec * system_timing.bus_freq / 128) / 1000000;
89         assert(ticks > 0);
90         __lapic_set_timer(ticks, LAPIC_TIMER_DEFAULT_VECTOR, periodic,
91                           LAPIC_TIMER_DEFAULT_DIVISOR);
92 }
93
94 void set_core_timer(uint32_t usec, bool periodic)
95 {
96         if (usec)
97                 lapic_set_timer(usec, periodic);
98         else
99                 lapic_disable_timer();
100 }
101
102 uint32_t lapic_get_default_id(void)
103 {
104         uint32_t ebx;
105         cpuid(1, 0, &ebx, 0, 0);
106         // p6 family only uses 4 bits here, and 0xf is reserved for the IOAPIC
107         return (ebx & 0xFF000000) >> 24;
108 }
109
110 // timer init calibrates both tsc timer and lapic timer using PIT
111 void timer_init(void){
112         uint64_t tscval[2];
113         long timercount[2];
114         pit_set_timer(0xffff, TIMER_RATEGEN);
115         // assume tsc exist
116         tscval[0] = read_tsc();
117         udelay_pit(1000000);
118         tscval[1] = read_tsc();
119         system_timing.tsc_freq = SINIT(tscval[1] - tscval[0]);
120         
121         cprintf("TSC Frequency: %llu\n", system_timing.tsc_freq);
122
123         __lapic_set_timer(0xffffffff, LAPIC_TIMER_DEFAULT_VECTOR, FALSE,
124                           LAPIC_TIMER_DEFAULT_DIVISOR);
125         // Mask the LAPIC Timer, so we never receive this interrupt (minor race)
126         mask_lapic_lvt(LAPIC_LVT_TIMER);
127         timercount[0] = read_mmreg32(LAPIC_TIMER_CURRENT);
128         udelay_pit(1000000);
129         timercount[1] = read_mmreg32(LAPIC_TIMER_CURRENT);
130         system_timing.bus_freq = SINIT((timercount[0] - timercount[1])*128);
131                 
132         cprintf("Bus Frequency: %llu\n", system_timing.bus_freq);
133 }
134
135 void pit_set_timer(uint32_t divisor, uint32_t mode)
136 {
137         if (divisor & 0xffff0000)
138                 warn("Divisor too large!");
139         mode = TIMER_SEL0|TIMER_16BIT|mode;
140         outb(TIMER_MODE, mode); 
141         outb(TIMER_CNTR0, divisor & 0xff);
142         outb(TIMER_CNTR0, (divisor >> 8) );
143         system_timing.pit_mode = SINIT(mode);
144         system_timing.pit_divisor = SINIT(divisor);
145         // cprintf("timer mode set to %d, divisor %d\n",mode, divisor);
146 }
147
148 static int getpit()
149 {
150     int high, low;
151         // TODO: need a lock to protect access to PIT
152
153     /* Select timer0 and latch counter value. */
154     outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
155     
156     low = inb(TIMER_CNTR0);
157     high = inb(TIMER_CNTR0);
158
159     return ((high << 8) | low);
160 }
161
162 // forces cpu to relax for usec miliseconds.  declared in kern/include/timing.h
163 void udelay(uint64_t usec)
164 {
165         #if !defined(__BOCHS__)
166         if (system_timing.tsc_freq != 0)
167         {
168                 uint64_t start, end, now;
169
170                 start = read_tsc();
171         end = start + (system_timing.tsc_freq * usec) / 1000000;
172         //cprintf("start %llu, end %llu\n", start, end);
173                 if (end == 0) cprintf("This is terribly wrong \n");
174                 do {
175             cpu_relax();
176             now = read_tsc();
177                         //cprintf("now %llu\n", now);
178                 } while (now < end || (now > start && end < start));
179         return;
180
181         } else
182         #endif
183         {
184                 udelay_pit(usec);
185         }
186 }
187
188 void udelay_pit(uint64_t usec)
189 {
190         
191         int64_t delta, prev_tick, tick, ticks_left;
192         prev_tick = getpit();
193         /*
194          * Calculate (n * (i8254_freq / 1e6)) without using floating point
195          * and without any avoidable overflows.
196          */
197         if (usec <= 0)
198                 ticks_left = 0;
199         // some optimization from bsd code
200         else if (usec < 256)
201                 /*
202                  * Use fixed point to avoid a slow division by 1000000.
203                  * 39099 = 1193182 * 2^15 / 10^6 rounded to nearest.
204                  * 2^15 is the first power of 2 that gives exact results
205                  * for n between 0 and 256.
206                  */
207                 ticks_left = ((uint64_t)usec * 39099 + (1 << 15) - 1) >> 15;
208         else
209                 // round up the ticks left
210                 ticks_left = ((uint64_t)usec * (long long)PIT_FREQ+ 999999)
211                              / 1000000; 
212         while (ticks_left > 0) {
213                 tick = getpit();
214                 delta = prev_tick - tick;
215                 prev_tick = tick;
216                 if (delta < 0) {
217                         // counter looped around during the delta time period
218                         delta += system_timing.pit_divisor; // maximum count 
219                         if (delta < 0)
220                                 delta = 0;
221                 }
222                 ticks_left -= delta;
223         }
224 }
225
226 uint64_t gettimer(void)
227 {
228         return read_tsc();      
229 }
230
231 uint64_t getfreq(void)
232 {
233         return system_timing.tsc_freq;
234 }
235