/* * Copyright (c) Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include namespace ams::kern::arch::arm64 { class KHardwareTimer : public KInterruptTask, public KHardwareTimerBase { private: s64 m_maximum_time; public: constexpr KHardwareTimer() : KInterruptTask(), KHardwareTimerBase(), m_maximum_time(std::numeric_limits::max()) { /* ... */ } public: /* Public API. */ NOINLINE void Initialize(); NOINLINE void Finalize(); static s64 GetTick() { return GetCount(); } void RegisterAbsoluteTask(KTimerTask *task, s64 task_time) { KScopedDisableDispatch dd; KScopedSpinLock lk(this->GetLock()); if (this->RegisterAbsoluteTaskImpl(task, task_time)) { if (task_time <= m_maximum_time) { SetCompareValue(task_time); EnableInterrupt(); } } } private: /* Hardware register accessors. */ static ALWAYS_INLINE void InitializeGlobalTimer() { /* Set kernel control. */ cpu::CounterTimerKernelControlRegisterAccessor(0).SetEl0PctEn(true).Store(); /* Disable the physical timer. */ cpu::CounterTimerPhysicalTimerControlRegisterAccessor(0).SetEnable(false).SetIMask(false).Store(); /* Set the compare value to the maximum. */ cpu::CounterTimerPhysicalTimerCompareValueRegisterAccessor(0).SetCompareValue(std::numeric_limits::max()).Store(); /* Enable the physical timer, with interrupt masked. */ cpu::CounterTimerPhysicalTimerControlRegisterAccessor(0).SetEnable(true).SetIMask(true).Store(); } static ALWAYS_INLINE void EnableInterrupt() { cpu::CounterTimerPhysicalTimerControlRegisterAccessor(0).SetEnable(true).SetIMask(false).Store(); } static ALWAYS_INLINE void DisableInterrupt() { cpu::CounterTimerPhysicalTimerControlRegisterAccessor(0).SetEnable(true).SetIMask(true).Store(); } static ALWAYS_INLINE void StopTimer() { /* Set the compare value to the maximum. */ cpu::CounterTimerPhysicalTimerCompareValueRegisterAccessor(0).SetCompareValue(std::numeric_limits::max()).Store(); /* Disable the physical timer. */ cpu::CounterTimerPhysicalTimerControlRegisterAccessor(0).SetEnable(false).SetIMask(false).Store(); } static ALWAYS_INLINE s64 GetCount() { return cpu::CounterTimerPhysicalCountValueRegisterAccessor().GetCount(); } static ALWAYS_INLINE void SetCompareValue(s64 value) { cpu::CounterTimerPhysicalTimerCompareValueRegisterAccessor(0).SetCompareValue(static_cast(value)).Store(); } public: virtual KInterruptTask *OnInterrupt(s32 interrupt_id) override { MESOSPHERE_UNUSED(interrupt_id); return this; } virtual void DoTask() override; }; }