From 83b95c135e158d4a5eb5a6d5033a3c73587505fc Mon Sep 17 00:00:00 2001 From: Timon Kruiper Date: Mon, 17 Oct 2022 14:59:35 +0200 Subject: [PATCH] Kernel/aarch64: Implement HardwareTimer for RPi::Timer This makes the RPi::Timer suitable for use in the TimeManagement code. --- Kernel/Arch/aarch64/RPi/Timer.cpp | 57 +++++++++++++++++++++++-------- Kernel/Arch/aarch64/RPi/Timer.h | 34 +++++++++++++++--- Kernel/Time/HardwareTimer.h | 5 ++- 3 files changed, 77 insertions(+), 19 deletions(-) diff --git a/Kernel/Arch/aarch64/RPi/Timer.cpp b/Kernel/Arch/aarch64/RPi/Timer.cpp index 42ed09bd37..afec4c7654 100644 --- a/Kernel/Arch/aarch64/RPi/Timer.cpp +++ b/Kernel/Arch/aarch64/RPi/Timer.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, Nico Weber + * Copyright (c) 2022, Timon Kruiper * * SPDX-License-Identifier: BSD-2-Clause */ @@ -30,15 +31,21 @@ enum FlagBits { }; Timer::Timer() - : IRQHandler(1) + : HardwareTimer(1) , m_registers(MMIO::the().peripheral(0x3000)) { + // FIXME: Actually query the frequency of the timer. By default it is 100MHz. + m_frequency = 1e6; + + set_interrupt_interval_usec(m_frequency / OPTIMAL_TICKS_PER_SECOND_RATE); + enable_interrupt_mode(); } -Timer& Timer::the() +Timer::~Timer() = default; + +NonnullLockRefPtr Timer::initialize() { - static AK::NeverDestroyed instance; - return *instance; + return adopt_lock_ref(*new Timer); } u64 Timer::microseconds_since_boot() @@ -52,23 +59,45 @@ u64 Timer::microseconds_since_boot() return (static_cast(high) << 32) | low; } -bool Timer::handle_irq(RegisterState const&) +bool Timer::handle_irq(RegisterState const& regs) { - dmesgln("Timer fired: {} us", m_current_timer_value); - - m_current_timer_value += m_interrupt_interval; - set_compare(TimerID::Timer1, m_current_timer_value); + auto result = HardwareTimer::handle_irq(regs); + set_compare(TimerID::Timer1, microseconds_since_boot() + m_interrupt_interval); clear_interrupt(TimerID::Timer1); - return true; + + return result; }; +u64 Timer::update_time(u64& seconds_since_boot, u32& ticks_this_second, bool query_only) +{ + // Should only be called by the time keeper interrupt handler! + u64 current_value = microseconds_since_boot(); + u64 delta_ticks = m_main_counter_drift; + if (current_value >= m_main_counter_last_read) { + delta_ticks += current_value - m_main_counter_last_read; + } else { + // the counter wrapped around + delta_ticks += (NumericLimits::max() - m_main_counter_last_read + 1) + current_value; + } + + u64 ticks_since_last_second = (u64)ticks_this_second + delta_ticks; + auto ticks_per_second = frequency(); + seconds_since_boot += ticks_since_last_second / ticks_per_second; + ticks_this_second = ticks_since_last_second % ticks_per_second; + + if (!query_only) { + m_main_counter_drift = 0; + m_main_counter_last_read = current_value; + } + + // Return the time passed (in ns) since last time update_time was called + return (delta_ticks * 1000000000ull) / ticks_per_second; +} + void Timer::enable_interrupt_mode() { - m_current_timer_value = microseconds_since_boot(); - m_current_timer_value += m_interrupt_interval; - set_compare(TimerID::Timer1, m_current_timer_value); - + set_compare(TimerID::Timer1, microseconds_since_boot() + m_interrupt_interval); enable_irq(); } diff --git a/Kernel/Arch/aarch64/RPi/Timer.h b/Kernel/Arch/aarch64/RPi/Timer.h index f6bd611b9d..bfac7242e2 100644 --- a/Kernel/Arch/aarch64/RPi/Timer.h +++ b/Kernel/Arch/aarch64/RPi/Timer.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, Nico Weber + * Copyright (c) 2022, Timon Kruiper * * SPDX-License-Identifier: BSD-2-Clause */ @@ -8,15 +9,36 @@ #include #include +#include +#include namespace Kernel::RPi { struct TimerRegisters; -class Timer : public IRQHandler { +class Timer final : public HardwareTimer { public: - Timer(); - static Timer& the(); + virtual ~Timer(); + + static NonnullLockRefPtr initialize(); + + virtual HardwareTimerType timer_type() const override { return HardwareTimerType::RPiTimer; } + virtual StringView model() const override { return "RPi Timer"sv; } + virtual size_t ticks_per_second() const override { return m_frequency; } + + virtual bool is_periodic() const override { TODO_AARCH64(); } + virtual bool is_periodic_capable() const override { TODO_AARCH64(); } + virtual void set_periodic() override { TODO_AARCH64(); } + virtual void set_non_periodic() override { TODO_AARCH64(); } + virtual void disable() override { TODO_AARCH64(); } + + virtual void reset_to_default_ticks_per_second() override { TODO_AARCH64(); } + virtual bool try_to_set_frequency(size_t) override { TODO_AARCH64(); } + virtual bool is_capable_of_frequency(size_t) const override { TODO_AARCH64(); } + virtual size_t calculate_nearest_possible_frequency(size_t) const override { TODO_AARCH64(); } + + // FIXME: Share code with HPET::update_time + u64 update_time(u64& seconds_since_boot, u32& ticks_this_second, bool query_only); u64 microseconds_since_boot(); @@ -43,6 +65,8 @@ public: static u32 set_clock_rate(ClockID, u32 rate_hz, bool skip_setting_turbo = true); private: + Timer(); + enum class TimerID : u32 { Timer0 = 0, Timer1 = 1, @@ -57,7 +81,9 @@ private: TimerRegisters volatile* m_registers; u32 m_interrupt_interval { 0 }; - u32 m_current_timer_value { 0 }; + + u64 m_main_counter_last_read { 0 }; + u64 m_main_counter_drift { 0 }; }; } diff --git a/Kernel/Time/HardwareTimer.h b/Kernel/Time/HardwareTimer.h index a3a3d75d60..b46fd39c62 100644 --- a/Kernel/Time/HardwareTimer.h +++ b/Kernel/Time/HardwareTimer.h @@ -17,7 +17,10 @@ enum class HardwareTimerType { i8253 = 0x1, /* PIT */ RTC = 0x2, /* Real Time Clock */ HighPrecisionEventTimer = 0x3, /* also known as IA-PC HPET */ - LocalAPICTimer = 0x4 /* Local APIC */ + LocalAPICTimer = 0x4, /* Local APIC */ +#if ARCH(AARCH64) + RPiTimer = 0x5 +#endif }; template