[kernel] Fix clock period vs frequency error
Calling `spinwait()` was hanging due to improper computation of the clock rate because justin did a dumb at math. Also the period can be greater than 1ns, so the clock's units were updated to microseconds.
This commit is contained in:
@@ -74,8 +74,7 @@ lapic::calibrate_timer()
|
|||||||
apic_write(m_base, lapic_timer_init, initial);
|
apic_write(m_base, lapic_timer_init, initial);
|
||||||
|
|
||||||
uint64_t us = 200000;
|
uint64_t us = 200000;
|
||||||
uint64_t ns = us * 1000;
|
clock::get().spinwait(us);
|
||||||
clock::get().spinwait(ns);
|
|
||||||
|
|
||||||
uint32_t remaining = apic_read(m_base, lapic_timer_cur);
|
uint32_t remaining = apic_read(m_base, lapic_timer_cur);
|
||||||
uint32_t ticks_total = initial - remaining;
|
uint32_t ticks_total = initial - remaining;
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ clock::clock(uint64_t rate, clock::source source_func, void *data) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
clock::spinwait(uint64_t ns) const
|
clock::spinwait(uint64_t us) const
|
||||||
{
|
{
|
||||||
uint64_t when = m_source(m_data) + ns;
|
uint64_t when = m_source(m_data) + us;
|
||||||
while (value() < when);
|
while (value() < when);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ public:
|
|||||||
using source = uint64_t (*)(void*);
|
using source = uint64_t (*)(void*);
|
||||||
|
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
/// \arg rate Number of source ticks per ns
|
/// \arg rate Number of source ticks per us
|
||||||
/// \arg source Function for the clock source
|
/// \arg source Function for the clock source
|
||||||
/// \arg data Data to pass to the source function
|
/// \arg data Data to pass to the source function
|
||||||
clock(uint64_t rate, source source_func, void *data);
|
clock(uint64_t rate, source source_func, void *data);
|
||||||
|
|
||||||
/// Get the current value of the clock.
|
/// Get the current value of the clock.
|
||||||
/// \returns Current value of the source, in ns
|
/// \returns Current value of the source, in us
|
||||||
/// TODO: optimize divison by finding a multiply and
|
/// TODO: optimize divison by finding a multiply and
|
||||||
/// shift value instead
|
/// shift value instead
|
||||||
inline uint64_t value() const { return m_source(m_data) / m_rate; }
|
inline uint64_t value() const { return m_source(m_data) / m_rate; }
|
||||||
@@ -28,15 +28,15 @@ public:
|
|||||||
inline void update() { m_current = value(); }
|
inline void update() { m_current = value(); }
|
||||||
|
|
||||||
/// Wait in a tight loop
|
/// Wait in a tight loop
|
||||||
/// \arg interval Time to wait, in ns
|
/// \arg interval Time to wait, in us
|
||||||
void spinwait(uint64_t ns) const;
|
void spinwait(uint64_t us) const;
|
||||||
|
|
||||||
/// Get the master clock
|
/// Get the master clock
|
||||||
static clock & get() { return *s_instance; }
|
static clock & get() { return *s_instance; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint64_t m_current; ///< current ns count
|
uint64_t m_current; ///< current us count
|
||||||
uint64_t m_rate; ///< source ticks per ns
|
uint64_t m_rate; ///< source ticks per us
|
||||||
void *m_data;
|
void *m_data;
|
||||||
source m_source;
|
source m_source;
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ acpi_table_header::validate(uint32_t expected_type) const
|
|||||||
return !expected_type || (expected_type == type);
|
return !expected_type || (expected_type == type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void irq2_callback(void *)
|
void irq2_callback(void *)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -344,6 +343,7 @@ device_manager::init_drivers()
|
|||||||
log::info(logs::clock, "Created master clock using HPET 0: Rate %d", h.rate());
|
log::info(logs::clock, "Created master clock using HPET 0: Rate %d", h.rate());
|
||||||
} else {
|
} else {
|
||||||
//TODO: APIC clock?
|
//TODO: APIC clock?
|
||||||
|
kassert(0, "No HPET master clock");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,6 +360,20 @@ device_manager::install_irq(unsigned irq, const char *name, irq_callback cb, voi
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
device_manager::uninstall_irq(unsigned irq, irq_callback cb, void *data)
|
||||||
|
{
|
||||||
|
if (irq >= m_irqs.count())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const irq_allocation &existing = m_irqs[irq];
|
||||||
|
if (existing.callback != cb || existing.data != data)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_irqs[irq] = {nullptr, nullptr, nullptr};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
device_manager::allocate_msi(const char *name, pci_device &device, irq_callback cb, void *data)
|
device_manager::allocate_msi(const char *name, pci_device &device, irq_callback cb, void *data)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -55,6 +55,16 @@ public:
|
|||||||
irq_callback cb,
|
irq_callback cb,
|
||||||
void *data);
|
void *data);
|
||||||
|
|
||||||
|
/// Uninstall an IRQ callback for a device
|
||||||
|
/// \arg irq IRQ to install the handler for
|
||||||
|
/// \arg cb Callback to call when the interrupt is received
|
||||||
|
/// \arg data Data to pass to the callback
|
||||||
|
/// \returns True if an IRQ was uninstalled successfully
|
||||||
|
bool uninstall_irq(
|
||||||
|
unsigned irq,
|
||||||
|
irq_callback cb,
|
||||||
|
void *data);
|
||||||
|
|
||||||
/// Allocate an MSI IRQ for a device
|
/// Allocate an MSI IRQ for a device
|
||||||
/// \arg name Name of the interrupt, for display to user
|
/// \arg name Name of the interrupt, for display to user
|
||||||
/// \arg device Device this MSI is being allocated for
|
/// \arg device Device this MSI is being allocated for
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ hpet::hpet(uint8_t index, uint64_t *base) :
|
|||||||
m_timers = ((caps >> 8) & 0x1f) + 1;
|
m_timers = ((caps >> 8) & 0x1f) + 1;
|
||||||
m_period = (caps >> 32);
|
m_period = (caps >> 32);
|
||||||
|
|
||||||
// setup_timer_interrupts(0, 2, 1000000, true);
|
setup_timer_interrupts(0, 2, 1000, true);
|
||||||
// bool installed = device_manager::get()
|
// bool installed = device_manager::get()
|
||||||
// .install_irq(2, "HPET Timer", hpet_irq_callback, this);
|
// .install_irq(2, "HPET Timer", hpet_irq_callback, this);
|
||||||
// kassert(installed, "Installing HPET IRQ handler");
|
// kassert(installed, "Installing HPET IRQ handler");
|
||||||
@@ -57,6 +57,7 @@ hpet::hpet(uint8_t index, uint64_t *base) :
|
|||||||
|
|
||||||
log::debug(logs::timer, "HPET %d timer %d:", index, 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, " int type: %d", (config >> 1) & 1);
|
||||||
|
log::debug(logs::timer, " int enable: %d", (config >> 2) & 1);
|
||||||
log::debug(logs::timer, " timer type: %d", (config >> 3) & 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, " periodic cap: %d", (config >> 4) & 1);
|
||||||
log::debug(logs::timer, " bits: %d", ((config >> 5) & 1) ? 64 : 32);
|
log::debug(logs::timer, " bits: %d", ((config >> 5) & 1) ? 64 : 32);
|
||||||
@@ -72,10 +73,10 @@ hpet::hpet(uint8_t index, uint64_t *base) :
|
|||||||
void
|
void
|
||||||
hpet::setup_timer_interrupts(unsigned timer, uint8_t irq, uint64_t interval, bool periodic)
|
hpet::setup_timer_interrupts(unsigned timer, uint8_t irq, uint64_t interval, bool periodic)
|
||||||
{
|
{
|
||||||
constexpr uint64_t femto_per_ns = 1000000ull;
|
constexpr uint64_t femto_per_us = 1000000000ull;
|
||||||
*timer_comparator(m_base, timer) =
|
*timer_comparator(m_base, timer) =
|
||||||
*counter_value(m_base) +
|
*counter_value(m_base) +
|
||||||
(interval * femto_per_ns) / m_period;
|
(interval * femto_per_us) / m_period;
|
||||||
|
|
||||||
*timer_config(m_base, timer) = (irq << 9) | ((periodic ? 1 : 0) << 3);
|
*timer_config(m_base, timer) = (irq << 9) | ((periodic ? 1 : 0) << 3);
|
||||||
}
|
}
|
||||||
@@ -102,9 +103,7 @@ void
|
|||||||
hpet::enable()
|
hpet::enable()
|
||||||
{
|
{
|
||||||
log::debug(logs::timer, "HPET %d enabling", m_index);
|
log::debug(logs::timer, "HPET %d enabling", m_index);
|
||||||
|
*configuration(m_base) = (*configuration(m_base) & 0x3) | 1;
|
||||||
enable_timer(0);
|
|
||||||
*configuration(m_base) = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t
|
uint64_t
|
||||||
|
|||||||
@@ -20,12 +20,8 @@ public:
|
|||||||
/// Configure the timer and start it running.
|
/// Configure the timer and start it running.
|
||||||
void enable();
|
void enable();
|
||||||
|
|
||||||
/// Wait in a tight loop while reading the HPET counter.
|
/// Get the timer rate in ticks per us
|
||||||
/// \arg ns Number of nanoseconds to wait
|
inline uint64_t rate() const { return 1000000000/m_period; }
|
||||||
void spinwait(uint64_t ns) const;
|
|
||||||
|
|
||||||
/// Get the timer rate in ticks per ns
|
|
||||||
inline uint64_t rate() const { return m_period * 1000000ull; }
|
|
||||||
|
|
||||||
/// Get the current timer value
|
/// Get the current timer value
|
||||||
uint64_t value() const;
|
uint64_t value() const;
|
||||||
|
|||||||
Reference in New Issue
Block a user