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