[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:
120
src/kernel/hpet.cpp
Normal file
120
src/kernel/hpet.cpp
Normal 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();
|
||||
}
|
||||
Reference in New Issue
Block a user