mirror of
https://github.com/RGBCube/serenity
synced 2025-05-21 22:25:07 +00:00

The RTC and CMOS are currently only supported for x86 platforms and use specific x86 instructions to produce only certain x86 plaform operations and results, therefore, we move them to the Arch/x86 specific directory.
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/common/CMOS.h>
|
|
#include <Kernel/Time/RTC.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;
|
|
}
|
|
|
|
}
|