mirror of
https://github.com/RGBCube/serenity
synced 2025-05-18 22:15: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.
91 lines
2.4 KiB
C++
91 lines
2.4 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/IO.h>
|
|
#include <Kernel/Arch/x86/NonMaskableInterruptDisabler.h>
|
|
#include <Kernel/Arch/x86/Time/RTC.h>
|
|
#include <Kernel/Arch/x86/common/CMOS.h>
|
|
#include <Kernel/Time/TimeManagement.h>
|
|
|
|
namespace Kernel {
|
|
#define IRQ_TIMER 8
|
|
#define MAX_FREQUENCY 8000
|
|
|
|
NonnullLockRefPtr<RealTimeClock> RealTimeClock::create(Function<void(RegisterState const&)> callback)
|
|
{
|
|
return adopt_lock_ref(*new RealTimeClock(move(callback)));
|
|
}
|
|
RealTimeClock::RealTimeClock(Function<void(RegisterState const&)> callback)
|
|
: HardwareTimer(IRQ_TIMER, move(callback))
|
|
{
|
|
InterruptDisabler disabler;
|
|
NonMaskableInterruptDisabler nmi_disabler;
|
|
enable_irq();
|
|
CMOS::write(0x8B, CMOS::read(0xB) | 0x40);
|
|
reset_to_default_ticks_per_second();
|
|
}
|
|
bool RealTimeClock::handle_irq(RegisterState const& regs)
|
|
{
|
|
auto result = HardwareTimer::handle_irq(regs);
|
|
CMOS::read(0x8C);
|
|
return result;
|
|
}
|
|
|
|
size_t RealTimeClock::ticks_per_second() const
|
|
{
|
|
return m_frequency;
|
|
}
|
|
|
|
void RealTimeClock::reset_to_default_ticks_per_second()
|
|
{
|
|
InterruptDisabler disabler;
|
|
bool success = try_to_set_frequency(1024);
|
|
VERIFY(success);
|
|
}
|
|
|
|
// FIXME: This is a quick & dirty log base 2 with a parameter. Please provide something better in the future.
|
|
static int quick_log2(size_t number)
|
|
{
|
|
int count = 0;
|
|
while (number >>= 1)
|
|
count++;
|
|
return count;
|
|
}
|
|
|
|
bool RealTimeClock::try_to_set_frequency(size_t frequency)
|
|
{
|
|
InterruptDisabler disabler;
|
|
if (!is_capable_of_frequency(frequency))
|
|
return false;
|
|
disable_irq();
|
|
u8 previous_rate = CMOS::read(0x8A);
|
|
u8 rate = quick_log2(32768 / frequency) + 1;
|
|
dbgln("RTC: Set rate to {}", rate);
|
|
CMOS::write(0x8A, (previous_rate & 0xF0) | rate);
|
|
m_frequency = frequency;
|
|
dbgln("RTC: Set frequency to {} Hz", frequency);
|
|
enable_irq();
|
|
return true;
|
|
}
|
|
bool RealTimeClock::is_capable_of_frequency(size_t frequency) const
|
|
{
|
|
VERIFY(frequency != 0);
|
|
if (frequency > MAX_FREQUENCY)
|
|
return false;
|
|
if (32768 % frequency)
|
|
return false;
|
|
|
|
u16 divider = 32768 / frequency;
|
|
return (divider <= 16384 && divider >= 4); // Frequency can be in range of 2 Hz to 8 KHz
|
|
}
|
|
size_t RealTimeClock::calculate_nearest_possible_frequency(size_t frequency) const
|
|
{
|
|
VERIFY(frequency != 0);
|
|
return frequency;
|
|
}
|
|
|
|
}
|