APIC Timer: Difference between revisions
Jump to navigation
Jump to search
[unchecked revision] | [unchecked revision] |
Content deleted Content added
m Nuke repeated newlines |
New APIC timer setup example code. |
||
Line 90: | Line 90: | ||
* interrupt 39: spurious irq, IRQ7 |
* interrupt 39: spurious irq, IRQ7 |
||
If you've already changed this, modify accordingly. |
If you've already changed this, modify accordingly. |
||
=== Example code in ASM === |
=== Example code in ASM === |
||
Line 198: | Line 199: | ||
mov dword [apic+APIC_TMRDIV], 03h |
mov dword [apic+APIC_TMRDIV], 03h |
||
</source> |
</source> |
||
=== Example code in C === |
=== Example code in C === |
||
This code is an example of how to initialize the APIC timer so that it ticks every 10 milliseconds. This is done by letting the APIC timer run, waiting for 10ms using the PIT and then getting the number of ticks that were done from the APIC timer. It assumes that you have functions to "write"/"read" the APIC's registers and "pit_prepare_sleep"/"pit_perform_sleep" to perform an as accurate as possible measuring of the timers frequency. |
|||
<source lang="c"> |
<source lang="c"> |
||
void apic_start_timer() { |
|||
void apic_timer_init(uint32_t quantum){ |
|||
// Tell APIC timer to use divider 16 |
|||
uint32_t tmp, cpubusfreq; |
|||
write(APIC_REGISTER_TIMER_DIV, 0x3); |
|||
//set up isrs |
|||
// Prepare the PIT to sleep for 10ms (10000µs) |
|||
writegate(32,isr_dummytmr); |
|||
pit_prepare_sleep(10000); |
|||
writegate(39,isr_spurious); |
|||
⚫ | |||
//initialize LAPIC to a well known state |
|||
write(APIC_REGISTER_TIMER_INITCNT, 0xFFFFFFFF); |
|||
(uint32_t*)(apic+APIC_DFR)=0xFFFFFFFF; |
|||
(uint32_t*)(apic+APIC_LDR)=((uint32_t*)(apic+APIC_LDR)&0x00FFFFFF)|1); |
|||
// Perform PIT-supported sleep |
|||
(uint32_t*)(apic+APIC_LVT_TMR)=APIC_DISABLE; |
|||
pit_perform_sleep(); |
|||
(uint32_t*)(apic+APIC_LVT_PERF)=APIC_NMI; |
|||
(uint32_t*)(apic+APIC_LVT_LINT0)=APIC_DISABLE; |
|||
⚫ | |||
(uint32_t*)(apic+APIC_LVT_LINT1)=APIC_DISABLE; |
|||
write(APIC_REGISTER_LVT_TIMER, APIC_LVT_INT_MASKED); |
|||
(uint32_t*)(apic+APIC_TASKPRIOR)=0; |
|||
// Now we know how often the APIC timer has ticked in 10ms |
|||
//okay, now we can enable APIC |
|||
uint32_t ticksIn10ms = 0xFFFFFFFF - read(APIC_REGISTER_TIMER_CURRCNT); |
|||
//global enable |
|||
cpuSetAPICBase(cpuGetAPICBase()); |
|||
// Start timer as periodic on IRQ 0, divider 16, with the number of ticks we counted |
|||
//software enable, map spurious interrupt to dummy isr |
|||
write(APIC_REGISTER_LVT_TIMER, 32 | APIC_LVT_TIMER_MODE_PERIODIC); |
|||
(uint32_t*)(apic+APIC_SPURIOUS)=39|APIC_SW_ENABLE; |
|||
write(APIC_REGISTER_TIMER_DIV, 0x3); |
|||
//map APIC timer to an interrupt, and by that enable it in one-shot mode |
|||
write(APIC_REGISTER_TIMER_INITCNT, ticksIn10ms); |
|||
(uint32_t*)(apic+APIC_LVT_TMR)=32; |
|||
//set up divide value to 16 |
|||
(uint32_t*)(apic+APIC_TMRDIV)=0x03; |
|||
//initialize PIT Ch 2 in one-shot mode |
|||
//waiting 1 sec could slow down boot time considerably, |
|||
//so we'll wait 1/100 sec, and multiply the counted ticks |
|||
outb(0x61,inb(0x61)&0xFD)|1); |
|||
outb(0x43,0xB2); // Switch to Mode 0, if you get problems!!! |
|||
//1193180/100 Hz = 11931 = 2e9bh |
|||
outb(0x42,0x9B); //LSB |
|||
in(0x60); //short delay |
|||
outb(0x42,0x2E); //MSB |
|||
//reset PIT one-shot counter (start counting) |
|||
(uint8_t)tmp=inb(0x61)&0xFE; |
|||
outb(0x61,(uint8_t)tmp); //gate low |
|||
outb(0x61,(uint8_t)tmp|1); //gate high |
|||
⚫ | |||
(uint32_t*)(apic+APIC_TMRINITCNT)=0xFFFFFFFF; |
|||
//now wait until PIT counter reaches zero |
|||
while(!(inb(0x61)&0x20)); |
|||
⚫ | |||
(uint32_t*)(apic+APIC_LVT_TMR)=APIC_DISABLE; |
|||
//now do the math... |
|||
cpubusfreq=((0xFFFFFFFF-(uint32_t*)(apic+APIC_TMRCURRCNT))+1)*16*100; |
|||
tmp=cpubusfreq/quantum/16; |
|||
//sanity check, now tmp holds appropriate number of ticks, use it as APIC timer counter initializer |
|||
(uint32_t*)(apic+APIC_TMRINITCNT)=(tmp<16?16:tmp); |
|||
//finally re-enable timer in periodic mode |
|||
(uint32_t*)(apic+APIC_LVT_TMR)=32|TMR_PERIODIC; |
|||
//setting divide value register again not needed by the manuals |
|||
//although I have found buggy hardware that required it |
|||
(uint32_t*)(apic+APIC_TMRDIV)=0x03; |
|||
} |
} |
||
</source> |
</source> |