banan-os/kernel/kernel/Timer/PIT.cpp

132 lines
3.1 KiB
C++

#include <kernel/IDT.h>
#include <kernel/InterruptController.h>
#include <kernel/IO.h>
#include <kernel/Processor.h>
#include <kernel/Timer/PIT.h>
#define PIT_IRQ 0
#define TIMER0_CTL 0x40
#define TIMER1_CTL 0x41
#define TIMER2_CTL 0x42
#define PIT_CTL 0x43
#define SELECT_CHANNEL0 0x00
#define SELECT_CHANNEL1 0x40
#define SELECT_CHANNEL2 0x80
#define ACCESS_HI 0x10
#define ACCESS_LO 0x20
#define MODE_RATE_GENERATOR 0x05
#define BASE_FREQUENCY 1193182
#define MS_PER_S 1'000
#define NS_PER_MS 1'000'000
#define NS_PER_S 1'000'000'000
namespace Kernel
{
constexpr uint16_t s_ticks_per_ms = BASE_FREQUENCY / 1000;
BAN::ErrorOr<BAN::UniqPtr<PIT>> PIT::create()
{
PIT* pit = new PIT();
if (pit == nullptr)
return BAN::Error::from_errno(ENOMEM);
pit->initialize();
return BAN::UniqPtr<PIT>::adopt(pit);
}
void PIT::initialize()
{
IO::outb(PIT_CTL, SELECT_CHANNEL0 | ACCESS_LO | ACCESS_HI | MODE_RATE_GENERATOR);
IO::outb(TIMER0_CTL, (s_ticks_per_ms >> 0) & 0xff);
IO::outb(TIMER0_CTL, (s_ticks_per_ms >> 8) & 0xff);
MUST(InterruptController::get().reserve_irq(PIT_IRQ));
set_irq(PIT_IRQ);
enable_interrupt();
}
void PIT::handle_irq()
{
{
SpinLockGuard _(m_lock);
m_system_time_ms++;
}
if (should_invoke_scheduler())
Processor::scheduler().timer_interrupt();
}
uint64_t PIT::read_counter_ns() const
{
SpinLockGuard _(m_lock);
// send latch command
IO::outb(PIT_CTL, SELECT_CHANNEL0);
// read ticks
uint64_t ticks_this_ms { 0 };
ticks_this_ms |= IO::inb(TIMER0_CTL);
ticks_this_ms |= IO::inb(TIMER0_CTL) << 8;
ticks_this_ms = s_ticks_per_ms - ticks_this_ms;
const uint64_t ns_this_ms = ticks_this_ms * NS_PER_S / BASE_FREQUENCY;
return (m_system_time_ms * NS_PER_MS) + ns_this_ms;
}
uint64_t PIT::ms_since_boot() const
{
return read_counter_ns() / NS_PER_MS;
}
uint64_t PIT::ns_since_boot() const
{
return read_counter_ns();
}
timespec PIT::time_since_boot() const
{
uint64_t ns = read_counter_ns();
return timespec {
.tv_sec = static_cast<time_t>(ns / NS_PER_S),
.tv_nsec = static_cast<long>(ns % NS_PER_S)
};
}
void PIT::pre_scheduler_sleep_ns(uint64_t ns)
{
const uint64_t target_ticks = BAN::Math::div_round_up<uint64_t>(ns * BASE_FREQUENCY, NS_PER_S);
SpinLockGuard _(m_lock);
IO::outb(PIT_CTL, SELECT_CHANNEL0 | ACCESS_LO | ACCESS_HI | MODE_RATE_GENERATOR);
IO::outb(TIMER0_CTL, 0);
IO::outb(TIMER0_CTL, 0);
IO::outb(PIT_CTL, SELECT_CHANNEL0 | ACCESS_LO | MODE_RATE_GENERATOR);
IO::outb(TIMER0_CTL, 0xFF);
uint64_t elapsed_ticks = 0;
uint8_t last_ticks = IO::inb(TIMER0_CTL);
while (elapsed_ticks < target_ticks)
{
const uint8_t current_ticks = IO::inb(TIMER0_CTL);
if (last_ticks <= current_ticks)
elapsed_ticks += current_ticks - last_ticks;
else
elapsed_ticks += 0xFF + current_ticks - last_ticks;
last_ticks = current_ticks;
}
IO::outb(PIT_CTL, SELECT_CHANNEL0 | ACCESS_LO | ACCESS_HI | MODE_RATE_GENERATOR);
IO::outb(TIMER0_CTL, (s_ticks_per_ms >> 0) & 0xFF);
IO::outb(TIMER0_CTL, (s_ticks_per_ms >> 8) & 0xFF);
}
}