Skip to content

Instantly share code, notes, and snippets.

@Tix3Dev
Last active July 28, 2022 10:28
Show Gist options
  • Save Tix3Dev/13310f05f4306d8d93cc1913f1ded1ef to your computer and use it in GitHub Desktop.
Save Tix3Dev/13310f05f4306d8d93cc1913f1ded1ef to your computer and use it in GitHub Desktop.
LAPIC Timer - 2 Ways: Periodic and Oneshot mode
/*
This file is extracted from a modern x86_64 UNIX-like microkernel-based
operating system which is called apoptOS
Everything is openly developed on GitHub: https://github.com/Tix3Dev/apoptOS
Copyright (C) 2022 Yves Vollmeier <https://github.com/Tix3Dev>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#define LAPIC_ID_REG 0x020
#define LAPIC_EOI_REG 0x0B0
#define LAPIC_SPURIOUS_REG 0x0F0
#define LAPIC_ICR0_REG 0x300
#define LAPIC_ICR1_REG 0x310
#define LAPIC_TIMER_REG 0x320
#define LAPIC_TIMER_INITCNT_REG 0x380
#define LAPIC_TIMER_CURCNT_REG 0x390
#define LAPIC_TIMER_DIV_REG 0x3E0
#define LAPIC_ENABLE_BIT (1 << 8)
#define LAPIC_TIMER_DISABLE_BIT (1 << 16)
#define LAPIC_TIMER_PERIODIC_MODE 0x20000
#define US_TIMESLICE_PERIOD 5000
#define LAPIC_TIMER_INT 32
/* 1 Way: Periodic mode */
// unmask timer pin and start periodic LAPIC timer (with specified period)
void lapic_timer_init(void)
{
uint32_t period = lapic_timer_calibrate(US_TIMESLICE_PERIOD);
lapic_write_reg(LAPIC_TIMER_REG, LAPIC_TIMER_INT | LAPIC_TIMER_PERIODIC_MODE);
lapic_write_reg(LAPIC_TIMER_DIV_REG, 0x3);
lapic_write_reg(LAPIC_TIMER_INITCNT_REG, period);
log(INFO, "LAPIC timer initialized - Firing IRQ's from now on\n");
}
// return how many ticks it took the LAPIC timer for the specified amount of microseconds
uint32_t lapic_timer_calibrate(uint32_t us)
{
lapic_write_reg(LAPIC_TIMER_DIV_REG, 0x3);
uint32_t initial_count = 0xFFFFFFFF;
lapic_write_reg(LAPIC_TIMER_INITCNT_REG, initial_count);
hpet_usleep(us);
lapic_write_reg(LAPIC_TIMER_REG, LAPIC_TIMER_DISABLE_BIT);
uint32_t final_count = lapic_read_reg(LAPIC_TIMER_CURCNT_REG);
uint32_t total_ticks = initial_count - final_count;
return total_ticks;
}
lapic_timer_init();
/* 2 Way: Oneshot mode */
static uint32_t lapic_timer_freq = 0;
// globally set the lapic timer frequency through calibration
void lapic_timer_init(void)
{
// frequency = ticks per second -> calibrate 1ms -> multiply by 1000 to get one second
lapic_timer_freq = lapic_timer_calibrate(1000) * 1000;
log(INFO, "LAPIC timer initialized - Oneshot's possible from now on\n");
}
// return how many ticks it took the LAPIC timer for the specified amount of microseconds
uint32_t lapic_timer_calibrate(uint32_t us)
{
lapic_write_reg(LAPIC_TIMER_DIV_REG, 3);
uint32_t initial_count = 0xFFFFFFFF;
lapic_write_reg(LAPIC_TIMER_INITCNT_REG, initial_count);
hpet_usleep(us);
lapic_write_reg(LAPIC_TIMER_REG, LAPIC_TIMER_DISABLE_BIT);
uint32_t final_count = lapic_read_reg(LAPIC_TIMER_CURCNT_REG);
uint32_t total_ticks = initial_count - final_count;
return total_ticks;
}
// send an interrupt vector (through the LVT and not IOAPIC redirection table) after
// the specified amount of microseconds, where interrupt vector = LAPIC_TIMER_INT
void lapic_timer_oneshot(uint32_t us)
{
uint32_t ticks = us * (lapic_timer_freq / 1000000);
lapic_write_reg(LAPIC_TIMER_REG, LAPIC_TIMER_INT);
lapic_write_reg(LAPIC_TIMER_DIV_REG, 3);
lapic_write_reg(LAPIC_TIMER_INITCNT_REG, ticks);
}
lapic_timer_init();
// if desired, e.g. to wait 5 seconds:
// lapic_timer_oneshot(5 * 1000 * 1000);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment