/* * Copyright (c) 2018-2020 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::arm64 { namespace impl { class KHardwareTimerInterruptTask; } class KHardwareTimer : public KHardwareTimerBase { public: constexpr KHardwareTimer() : KHardwareTimerBase() { /* ... */ } public: /* Public API. */ NOINLINE void Initialize(s32 core_id); 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)) { SetCompareValue(task_time); EnableInterrupt(); } } private: friend class impl::KHardwareTimerInterruptTask; NOINLINE void DoInterruptTask(); 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(); } }; }