[kernel] Add HPET support, create clock class

Create a clock class which can be queried for current timestamp in
nanoseconds. Also implements a simple HPET class as one possible clock
source.

Tags: time
This commit is contained in:
Justin C. Miller
2020-06-28 17:47:46 -07:00
parent 9b67f87062
commit 6c468a134b
13 changed files with 347 additions and 36 deletions

120
src/kernel/hpet.cpp Normal file
View File

@@ -0,0 +1,120 @@
#include "kernel_memory.h"
#include "kutil/assert.h"
#include "device_manager.h"
#include "hpet.h"
#include "io.h"
#include "log.h"
namespace {
inline uint64_t volatile *capabilities(uint64_t volatile *base) { return base; }
inline uint64_t volatile *configuration(uint64_t volatile *base) { return base+2; }
inline uint64_t volatile *interrupt_status(uint64_t volatile *base) { return base+4; }
inline uint64_t volatile *counter_value(uint64_t volatile *base) { return base+30; }
inline uint64_t volatile *timer_base(uint64_t volatile *base, unsigned i) { return base + 0x20 + (4*i); }
inline uint64_t volatile *timer_config(uint64_t volatile *base, unsigned i) { return timer_base(base, i); }
inline uint64_t volatile *timer_comparator(uint64_t volatile *base, unsigned i) { return timer_base(base, i) + 1; }
}
void
hpet_irq_callback(void *hpet_ptr)
{
if (hpet_ptr)
reinterpret_cast<hpet*>(hpet_ptr)->callback();
}
hpet::hpet(uint8_t index, uint64_t *base) :
m_index(index),
m_base(base)
{
*configuration(m_base) = 0;
*counter_value(m_base) = 0;
uint64_t caps = *capabilities(base);
uint64_t config = *configuration(base);
m_timers = ((caps >> 8) & 0x1f) + 1;
m_period = (caps >> 32);
// setup_timer_interrupts(0, 2, 1000000, true);
// bool installed = device_manager::get()
// .install_irq(2, "HPET Timer", hpet_irq_callback, this);
// kassert(installed, "Installing HPET IRQ handler");
log::debug(logs::timer, "HPET %d capabilities:", index);
log::debug(logs::timer, " revision: %d", caps & 0xff);
log::debug(logs::timer, " timers: %d", m_timers);
log::debug(logs::timer, " bits: %d", ((caps >> 13) & 1) ? 64 : 32);
log::debug(logs::timer, " LRR capable: %d", ((caps >> 15) & 1));
log::debug(logs::timer, " period: %dns", m_period / 1000000);
log::debug(logs::timer, " global enabled: %d", config & 1);
log::debug(logs::timer, " LRR enable: %d", (config >> 1) & 1);
for (unsigned i = 0; i < m_timers; ++i) {
disable_timer(i);
uint64_t config = *timer_config(m_base, i);
log::debug(logs::timer, "HPET %d timer %d:", index, i);
log::debug(logs::timer, " int type: %d", (config >> 1) & 1);
log::debug(logs::timer, " timer type: %d", (config >> 3) & 1);
log::debug(logs::timer, " periodic cap: %d", (config >> 4) & 1);
log::debug(logs::timer, " bits: %d", ((config >> 5) & 1) ? 64 : 32);
log::debug(logs::timer, " 32 mode: %d", (config >> 8) & 1);
log::debug(logs::timer, " int route: %d", (config >> 9) & 0x1f);
log::debug(logs::timer, " FSB enable: %d", (config >> 14) & 1);
log::debug(logs::timer, " FSB capable: %d", (config >> 15) & 1);
log::debug(logs::timer, " rotung cap: %08x", (config >> 32));
}
}
void
hpet::setup_timer_interrupts(unsigned timer, uint8_t irq, uint64_t interval, bool periodic)
{
constexpr uint64_t femto_per_ns = 1000000ull;
*timer_comparator(m_base, timer) =
*counter_value(m_base) +
(interval * femto_per_ns) / m_period;
*timer_config(m_base, timer) = (irq << 9) | ((periodic ? 1 : 0) << 3);
}
void
hpet::enable_timer(unsigned timer)
{
*timer_config(m_base, timer) = *timer_config(m_base, timer) | (1 << 2);
}
void
hpet::disable_timer(unsigned timer)
{
*timer_config(m_base, timer) = *timer_config(m_base, timer) & ~(1ull << 2);
}
void
hpet::callback()
{
log::debug(logs::timer, "HPET %d got irq", m_index);
}
void
hpet::enable()
{
log::debug(logs::timer, "HPET %d enabling", m_index);
enable_timer(0);
*configuration(m_base) = 1;
}
uint64_t
hpet::value() const
{
return *counter_value(m_base);
}
uint64_t
hpet_clock_source(void *data)
{
return reinterpret_cast<hpet*>(data)->value();
}