97 lines
2.3 KiB
C
97 lines
2.3 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>
|
|
|
|
/*
|
|
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;
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
/*
|
|
Base frequency = 1.193182 MHz
|
|
1 tick per ms (divide by 1000) = roughly 1193 Hz
|
|
*/
|
|
void pit_init()
|
|
{
|
|
uint32_t frequency = 1000; // 1 kHz
|
|
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);
|
|
}
|
|
|
|
// Wait n ticks
|
|
// Given that there's a tick every 1ms, wait n milliseconds
|
|
void timer_wait(uint64_t wait_ticks)
|
|
{
|
|
uint64_t then = ticks + wait_ticks;
|
|
while (ticks < then)
|
|
{
|
|
asm("hlt");
|
|
};
|
|
}
|
|
|
|
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;
|
|
} |