c291b7a5e0485df9016766fbd78a046b891c8f25
[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 /*
13  * Remaps the Programmable Interrupt Controller to use IRQs 32-47
14  * http://wiki.osdev.org/PIC
15  * Not 100% on this stuff, after looking over 
16  * http://bochs.sourceforge.net/techspec/PORTS.LST  The cascading and other 
17  * stuff might need to be in one command, and after that all we are doing
18  * is toggling masks.
19  */
20 void pic_remap() 
21 {
22         // start initialization
23         outb(PIC1_CMD, 0x11);
24         outb(PIC2_CMD, 0x11);
25         // set new offsets
26         outb(PIC1_DATA, PIC1_OFFSET);
27         outb(PIC2_DATA, PIC2_OFFSET);
28         // set up cascading
29         outb(PIC1_DATA, 0x04);
30         outb(PIC2_DATA, 0x02);
31         // other stuff (put in 8086/88 mode, or whatever)
32         outb(PIC1_DATA, 0x01);
33         outb(PIC2_DATA, 0x01);
34         // set masks, defaulting to all masked for now
35         outb(PIC1_DATA, 0xff);
36         outb(PIC2_DATA, 0xff);
37 }
38
39 void pic_mask_irq(uint8_t irq)
40 {
41         if (irq > 7)
42                 outb(PIC2_DATA, inb(PIC2_DATA) | (1 << (irq - 8)));
43         else
44                 outb(PIC1_DATA, inb(PIC1_DATA) | (1 << irq));
45 }
46
47 void pic_unmask_irq(uint8_t irq)
48 {
49         if (irq > 7) {
50                 outb(PIC2_DATA, inb(PIC2_DATA) & ~(1 << (irq - 8)));
51                 outb(PIC1_DATA, inb(PIC1_DATA) & 0xfd); // make sure irq2 is unmasked
52         } else
53                 outb(PIC1_DATA, inb(PIC1_DATA) & ~(1 << irq));
54 }
55
56 /*
57  * Sets the LAPIC timer to go off after a certain number of ticks.  The primary
58  * clock freq is actually the bus clock, so we really will need to figure out
59  * the timing of the LAPIC timer via other timing.  For now, set it to a
60  * certain number of ticks, and specify an interrupt vector to send to the CPU.
61  * Unmasking is implied.  Ref SDM, 3A, 9.6.4
62  */
63 void lapic_set_timer(uint32_t ticks, uint8_t vector, bool periodic)
64 {
65         // divide the bus clock.  going with the max (128) for now (which is slow)
66         write_mmreg32(LAPIC_TIMER_DIVIDE, 0xa);
67         // set LVT with interrupt handling information
68         write_mmreg32(LAPIC_LVT_TIMER, vector | (periodic << 17));
69         write_mmreg32(LAPIC_TIMER_INIT, ticks);
70         // For debugging when we expand this
71         //cprintf("LAPIC LVT Timer: 0x%08x\n", read_mmreg32(LAPIC_LVT_TIMER));
72         //cprintf("LAPIC Init Count: 0x%08x\n", read_mmreg32(LAPIC_TIMER_INIT));
73         //cprintf("LAPIC Current Count: 0x%08x\n", read_mmreg32(LAPIC_TIMER_CURRENT));
74 }
75
76 uint32_t lapic_get_default_id(void)
77 {
78         uint32_t ebx;
79         cpuid(1, 0, &ebx, 0, 0);
80         // p6 family only uses 4 bits here, and 0xf is reserved for the IOAPIC
81         return (ebx & 0xFF000000) >> 24;
82 }
83
84 void pit_set_timer(uint32_t freq, bool periodic)
85 {
86         uint32_t divisor = PIT_FREQ / freq;
87         if (divisor & 0xffff0000)
88                 warn("Divisor too large!");
89         outb(0x43, 0x32 | (periodic << 2));
90         outb(0x40, divisor & 0xff);
91         outb(0x40, (divisor >> 8) & 0xff);
92 }