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