122 lines
3.0 KiB
C
122 lines
3.0 KiB
C
/*
|
|
* @author xamidev <xamidev@riseup.net>
|
|
* @brief Programmable Interval Timer init and enabling
|
|
* @license GPL-3.0-only
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <io/serial/serial.h>
|
|
#include <kernel.h>
|
|
#include <config.h>
|
|
|
|
/*
|
|
For now, the timer module will be using the PIC.
|
|
Even though it's quite old, it's still supported by newer CPUs
|
|
and it will be precise enough for what we'll do. Also it's easier
|
|
to implement than ACPI etc. (we may upgrade to ACPI when we're
|
|
interested in multi-core functionnality like SMP)
|
|
*/
|
|
|
|
volatile uint64_t ticks = 0;
|
|
|
|
extern struct init_status init;
|
|
|
|
/*
|
|
* pic_remap - Remap the Programmable Interrupt Controller
|
|
*
|
|
* By default, interrupts are mapped at the wrong place.
|
|
* This function remaps interrupt numbers so interrupts
|
|
* don't conflict with each other.
|
|
*/
|
|
void pic_remap()
|
|
{
|
|
uint8_t master_mask = inb(0x21);
|
|
uint8_t slave_mask = inb(0xA1);
|
|
|
|
// ICW1: start initialization
|
|
outb(0x20, 0x11);
|
|
outb(0xA0, 0x11);
|
|
|
|
// ICW2: vector offsets
|
|
outb(0x21, 0x20); // Master PIC -> 0x20
|
|
outb(0xA1, 0x28); // Slave PIC -> 0x28
|
|
|
|
// ICW3: tell Master about Slave at IRQ2 (0000 0100)
|
|
outb(0x21, 0x04);
|
|
// ICW3: tell Slave its cascade identity (0000 0010)
|
|
outb(0xA1, 0x02);
|
|
|
|
// ICW4: 8086 mode
|
|
outb(0x21, 0x01);
|
|
outb(0xA1, 0x01);
|
|
|
|
// Restore saved masks
|
|
outb(0x21, master_mask);
|
|
outb(0xA1, slave_mask);
|
|
}
|
|
|
|
/*
|
|
* pic_enable - Enable the Programmable Interrupt Controller
|
|
*
|
|
* This function enables IRQ0 and IRQ1, which correspond to
|
|
* the timer and keyboard interrupts, respectively.
|
|
*/
|
|
void pic_enable()
|
|
{
|
|
// Enabling IRQ0 (unmasking it) but not the others
|
|
uint8_t mask = inb(0x21);
|
|
mask &= ~(1 << 0); // Set IRQ0 (timer, clear bit 0)
|
|
//mask &= ~(1 << 1); // Set IRQ1 (PS/2 Keyboard, clear bit 1)
|
|
outb(0x21, mask);
|
|
}
|
|
|
|
/*
|
|
* pit_init - Initialization of the Programmable Interval Timer
|
|
*
|
|
* The PIT is the simplest timer we can get working on x86.
|
|
* It has a base frequency of 1.193182 MHz.
|
|
* A custom frequency can be set using TIMER_FREQUENCY macro.
|
|
*/
|
|
void pit_init()
|
|
{
|
|
uint32_t frequency = TIMER_FREQUENCY;
|
|
uint32_t divisor = 1193182 / frequency;
|
|
|
|
// Set PIT to mode 3, channel 0
|
|
outb(0x43, 0x36); // 0x36
|
|
|
|
// Send divisor (low byte, then high byte)
|
|
outb(0x40, divisor & 0xFF);
|
|
outb(0x40, (divisor >> 8) & 0xFF);
|
|
}
|
|
|
|
/*
|
|
* timer_wait - Wait for X ticks
|
|
*
|
|
* By default, the timer frequency is 1000Hz, meaning
|
|
* ticks are equal to milliseconds.
|
|
*/
|
|
void timer_wait(uint64_t wait_ticks)
|
|
{
|
|
uint64_t then = ticks + wait_ticks;
|
|
while (ticks < then) {
|
|
asm("hlt");
|
|
};
|
|
}
|
|
|
|
/*
|
|
* timer_init - Initialization of the timer
|
|
*
|
|
* This function wakes the PIT.
|
|
*/
|
|
void timer_init()
|
|
{
|
|
// Remapping the PIC, because at startup it conflicts with
|
|
// the reserved IRQs we have for faults/exceptions etc.
|
|
// so we move its IRQ0 to something not reserved (32)
|
|
pic_remap();
|
|
pic_enable();
|
|
pit_init();
|
|
DEBUG("PIT initialized");
|
|
init.timer = true;
|
|
} |