mirror of
https://github.com/RGBCube/serenity
synced 2025-05-18 22:45:08 +00:00

The APICTimer, HPET and RTC (the RTC timer is in the context of the PC RTC here) are timers that exist only in x86 platforms, therefore, we move the handling code and the initialization code to the Arch/x86/Time directory. Other related code patterns in the TimeManagement singleton and in the Random.cpp file are guarded with #ifdef to ensure they are only compiled for x86 builds.
136 lines
3.7 KiB
C++
136 lines
3.7 KiB
C++
/*
|
|
* Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <Kernel/Arch/InterruptDisabler.h>
|
|
#include <Kernel/Arch/x86/Time/HPETComparator.h>
|
|
#include <Kernel/Assertions.h>
|
|
#include <Kernel/Debug.h>
|
|
#include <Kernel/Sections.h>
|
|
#include <Kernel/Time/TimeManagement.h>
|
|
|
|
namespace Kernel {
|
|
|
|
UNMAP_AFTER_INIT NonnullLockRefPtr<HPETComparator> HPETComparator::create(u8 number, u8 irq, bool periodic_capable, bool is_64bit_capable)
|
|
{
|
|
auto timer = adopt_lock_ref(*new HPETComparator(number, irq, periodic_capable, is_64bit_capable));
|
|
timer->register_interrupt_handler();
|
|
return timer;
|
|
}
|
|
|
|
UNMAP_AFTER_INIT HPETComparator::HPETComparator(u8 number, u8 irq, bool periodic_capable, bool is_64bit_capable)
|
|
: HardwareTimer(irq)
|
|
, m_periodic(false)
|
|
, m_periodic_capable(periodic_capable)
|
|
, m_enabled(false)
|
|
, m_is_64bit_capable(is_64bit_capable)
|
|
, m_comparator_number(number)
|
|
{
|
|
}
|
|
|
|
void HPETComparator::disable()
|
|
{
|
|
if (!m_enabled)
|
|
return;
|
|
m_enabled = false;
|
|
HPET::the().disable(*this);
|
|
}
|
|
|
|
void HPETComparator::set_periodic()
|
|
{
|
|
VERIFY(m_periodic_capable);
|
|
m_periodic = true;
|
|
m_enabled = true;
|
|
HPET::the().enable_periodic_interrupt(*this);
|
|
}
|
|
void HPETComparator::set_non_periodic()
|
|
{
|
|
VERIFY(m_periodic_capable);
|
|
m_periodic = false;
|
|
m_enabled = true;
|
|
HPET::the().disable_periodic_interrupt(*this);
|
|
}
|
|
|
|
bool HPETComparator::handle_irq(RegisterState const& regs)
|
|
{
|
|
auto result = HardwareTimer::handle_irq(regs);
|
|
if (!is_periodic())
|
|
set_new_countdown();
|
|
return result;
|
|
}
|
|
|
|
void HPETComparator::set_new_countdown()
|
|
{
|
|
VERIFY_INTERRUPTS_DISABLED();
|
|
VERIFY(m_frequency <= HPET::the().frequency());
|
|
HPET::the().update_non_periodic_comparator_value(*this);
|
|
}
|
|
|
|
size_t HPETComparator::ticks_per_second() const
|
|
{
|
|
return m_frequency;
|
|
}
|
|
|
|
void HPETComparator::reset_to_default_ticks_per_second()
|
|
{
|
|
dbgln("reset_to_default_ticks_per_second");
|
|
m_frequency = OPTIMAL_TICKS_PER_SECOND_RATE;
|
|
if (!is_periodic())
|
|
set_new_countdown();
|
|
else
|
|
try_to_set_frequency(m_frequency);
|
|
}
|
|
bool HPETComparator::try_to_set_frequency(size_t frequency)
|
|
{
|
|
InterruptDisabler disabler;
|
|
if (!is_capable_of_frequency(frequency)) {
|
|
dbgln("HPETComparator: not capable of frequency: {}", frequency);
|
|
return false;
|
|
}
|
|
|
|
auto hpet_frequency = HPET::the().frequency();
|
|
VERIFY(frequency <= hpet_frequency);
|
|
m_frequency = frequency;
|
|
m_enabled = true;
|
|
|
|
dbgln_if(HPET_COMPARATOR_DEBUG, "HPET Comparator: Max frequency {} Hz, want to set {} Hz, periodic: {}", hpet_frequency, frequency, is_periodic());
|
|
|
|
if (is_periodic()) {
|
|
HPET::the().update_periodic_comparator_value();
|
|
} else {
|
|
HPET::the().update_non_periodic_comparator_value(*this);
|
|
}
|
|
HPET::the().enable(*this);
|
|
enable_irq(); // Enable if we haven't already
|
|
return true;
|
|
}
|
|
bool HPETComparator::is_capable_of_frequency(size_t frequency) const
|
|
{
|
|
if (frequency > HPET::the().frequency())
|
|
return false;
|
|
// HPET::update_periodic_comparator_value and HPET::update_non_periodic_comparator_value
|
|
// calculate the best counter based on the desired frequency.
|
|
return true;
|
|
}
|
|
size_t HPETComparator::calculate_nearest_possible_frequency(size_t frequency) const
|
|
{
|
|
if (frequency > HPET::the().frequency())
|
|
return HPET::the().frequency();
|
|
// HPET::update_periodic_comparator_value and HPET::update_non_periodic_comparator_value
|
|
// calculate the best counter based on the desired frequency.
|
|
return frequency;
|
|
}
|
|
|
|
u64 HPETComparator::current_raw() const
|
|
{
|
|
return HPET::the().read_main_counter();
|
|
}
|
|
|
|
u64 HPETComparator::raw_to_ns(u64 raw_delta) const
|
|
{
|
|
return HPET::the().raw_counter_ticks_to_ns(raw_delta);
|
|
}
|
|
|
|
}
|