blob: 0b87c07f70b546624b5378ec11bbae4aed518179 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
|
// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org)
// Licensed under the Apache License, Version 2.0 (see LICENSE file)
// Official repository: https://github.com/ne-foss-org/nekernel
#include <ArchKit/ArchKit.h>
#include <KernelKit/Timer.h>
#include <modules/ACPI/ACPIFactoryInterface.h>
/// ================================================================================
/// @note timer slot 0
/// ================================================================================
#define kHPETSignature ("HPET")
#define kHPETCounterRegValue (0x00)
#define kHPETConfigRegValue (0x20)
#define kHPETCompRegValue (0x24)
#define kHPETInterruptRegValue (0x2C)
/// ================================================================================
///! BUGS: 0
///! @file HalTimer.cc
///! @brief Hardware Timer (HPET)
/// ================================================================================
namespace Kernel::Detail {
struct HPET_BLOCK : public Kernel::SDT {
Kernel::UInt8 hardware_rev_id;
Kernel::UInt8 comparator_count : 5;
Kernel::UInt8 counter_size : 1;
Kernel::UInt8 reserved : 1;
Kernel::UInt8 legacy_replacement : 1;
Kernel::UInt16 pci_vendor_id;
ACPI_ADDRESS address;
Kernel::UInt8 hpet_number;
Kernel::UInt16 minimum_tick;
Kernel::UInt8 page_protection;
} PACKED;
} // namespace Kernel::Detail
using namespace Kernel;
HardwareTimer::HardwareTimer(UInt64 ms) : fWaitFor(ms) {
auto power = PowerFactoryInterface(kHandoverHeader->f_HardwareTables.f_VendorPtr);
auto hpet = (Detail::HPET_BLOCK*) power.Find(kHPETSignature).Leak().Leak();
MUST_PASS(hpet);
fDigitalTimer = (UInt8*) hpet->address.Address;
if (hpet->page_protection) {
HAL::mm_map_page((VoidPtr) fDigitalTimer, (VoidPtr) fDigitalTimer,
HAL::kMMFlagsWr | HAL::kMMFlagsPCD | HAL::kMMFlagsPwt);
}
// if not enabled yet.
if (!(*((volatile UInt64*) ((UInt8*) fDigitalTimer + kHPETConfigRegValue)) & (1 << 0))) {
*((volatile UInt64*) ((UInt8*) fDigitalTimer + kHPETConfigRegValue)) =
*((volatile UInt64*) ((UInt8*) fDigitalTimer + kHPETConfigRegValue)) | (1 << 0) |
(1 << 3); // enable timer & one shot conf
}
}
HardwareTimer::~HardwareTimer() {
fDigitalTimer = nullptr;
fWaitFor = 0;
}
/***********************************************************************************/
/// @brief Wait for the timer to stop spinning.
/***********************************************************************************/
BOOL HardwareTimer::Wait() {
if (fWaitFor < 1) return NO;
if (fWaitFor > 1'000'000) return NO; // max 1000s = 16 minutes
UInt64 hpet_cap = *((volatile UInt64*) (fDigitalTimer));
UInt64 femtoseconds_per_tick = (hpet_cap >> 32);
if (femtoseconds_per_tick == 0) return NO;
volatile UInt64* timer = (volatile UInt64*) (fDigitalTimer + kHPETCounterRegValue);
UInt64 now = *timer;
UInt64 fs_wait = fWaitFor * 1'000'000'000'000ULL;
UInt64 stop_at = now + (fs_wait / femtoseconds_per_tick);
while (*timer < (stop_at)) asm volatile("pause");
return YES;
}
|