Files
jsix/src/kernel/hpet.cpp
Justin C. Miller 11b61ab345 [kernel] Change kernel log levels
The kernel log levels are now numerically reversed so that more-verbose
levels can be added to the end. Replaced 'debug' with 'verbose', and
added new 'spam' level.
2022-09-25 17:25:43 -07:00

119 lines
3.8 KiB
C++

#include "assert.h"
#include "device_manager.h"
#include "hpet.h"
#include "io.h"
#include "logger.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, 1000, true);
// bool installed = device_manager::get()
// .install_irq(2, "HPET Timer", hpet_irq_callback, this);
// kassert(installed, "Installing HPET IRQ handler");
log::spam(logs::timer, "HPET %d capabilities:", index);
log::spam(logs::timer, " revision: %d", caps & 0xff);
log::spam(logs::timer, " timers: %d", m_timers);
log::spam(logs::timer, " bits: %d", ((caps >> 13) & 1) ? 64 : 32);
log::spam(logs::timer, " LRR capable: %d", ((caps >> 15) & 1));
log::spam(logs::timer, " period: %dns", m_period / 1000000);
log::spam(logs::timer, " global enabled: %d", config & 1);
log::spam(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::spam(logs::timer, "HPET %d timer %d:", index, i);
log::spam(logs::timer, " int type: %d", (config >> 1) & 1);
log::spam(logs::timer, " int enable: %d", (config >> 2) & 1);
log::spam(logs::timer, " timer type: %d", (config >> 3) & 1);
log::spam(logs::timer, " periodic cap: %d", (config >> 4) & 1);
log::spam(logs::timer, " bits: %d", ((config >> 5) & 1) ? 64 : 32);
log::spam(logs::timer, " 32 mode: %d", (config >> 8) & 1);
log::spam(logs::timer, " int route: %d", (config >> 9) & 0x1f);
log::spam(logs::timer, " FSB enable: %d", (config >> 14) & 1);
log::spam(logs::timer, " FSB capable: %d", (config >> 15) & 1);
log::spam(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_us = 1000000000ull;
*timer_comparator(m_base, timer) =
*counter_value(m_base) +
(interval * femto_per_us) / 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::spam(logs::timer, "HPET %d got irq", m_index);
}
void
hpet::enable()
{
log::verbose(logs::timer, "HPET %d enabling", m_index);
*configuration(m_base) = (*configuration(m_base) & 0x3) | 1;
}
uint64_t
hpet::value() const
{
return *counter_value(m_base);
}
uint64_t
hpet_clock_source(void *data)
{
return reinterpret_cast<hpet*>(data)->value();
}