|
@@ -48,58 +48,78 @@ enum class Attributes {
|
|
|
};
|
|
|
|
|
|
enum class Configuration {
|
|
|
- Enable = 0x1,
|
|
|
- LegacyReplacementRoute = 0x2
|
|
|
+ Enable = 1 << 0,
|
|
|
+ LegacyReplacementRoute = 1 << 1
|
|
|
};
|
|
|
|
|
|
enum class TimerConfiguration : u32 {
|
|
|
- InterruptType = 1 << 1,
|
|
|
+ LevelTriggered = 1 << 1,
|
|
|
InterruptEnable = 1 << 2,
|
|
|
- TimerType = 1 << 3,
|
|
|
+ GeneratePeriodicInterrupt = 1 << 3,
|
|
|
PeriodicInterruptCapable = 1 << 4,
|
|
|
Timer64BitsCapable = 1 << 5,
|
|
|
ValueSet = 1 << 6,
|
|
|
- Force32BitMode = 1 << 7,
|
|
|
+ Force32BitMode = 1 << 8,
|
|
|
FSBInterruptEnable = 1 << 14,
|
|
|
FSBInterruptDelivery = 1 << 15
|
|
|
};
|
|
|
};
|
|
|
|
|
|
-struct [[gnu::packed]] TimerStructure
|
|
|
+struct [[gnu::packed]] HPETRegister
|
|
|
{
|
|
|
- u64 configuration_and_capability;
|
|
|
- u64 comparator_value;
|
|
|
- u64 fsb_interrupt_route;
|
|
|
- u64 reserved;
|
|
|
+ volatile u32 low;
|
|
|
+ volatile u32 high;
|
|
|
};
|
|
|
|
|
|
-struct [[gnu::packed]] HPETCapabilityRegister
|
|
|
+struct [[gnu::packed]] TimerStructure
|
|
|
{
|
|
|
- u32 attributes; // Note: We must do a 32 bit access to offsets 0x0, or 0x4 only, according to HPET spec.
|
|
|
- u32 main_counter_tick_period;
|
|
|
+ volatile u32 capabilities;
|
|
|
+ volatile u32 interrupt_routing;
|
|
|
+ HPETRegister comparator_value;
|
|
|
+ volatile u64 fsb_interrupt_route;
|
|
|
u64 reserved;
|
|
|
};
|
|
|
|
|
|
-struct [[gnu::packed]] HPETRegister
|
|
|
+struct [[gnu::packed]] HPETCapabilityRegister
|
|
|
{
|
|
|
- u64 reg;
|
|
|
+ // Note: We must do a 32 bit access to offsets 0x0, or 0x4 only, according to HPET spec.
|
|
|
+ volatile u32 attributes;
|
|
|
+ volatile u32 main_counter_tick_period;
|
|
|
u64 reserved;
|
|
|
};
|
|
|
|
|
|
struct [[gnu::packed]] HPETRegistersBlock
|
|
|
{
|
|
|
- union {
|
|
|
- HPETCapabilityRegister capabilities;
|
|
|
- HPETRegister raw_capabilites;
|
|
|
- };
|
|
|
+ HPETCapabilityRegister capabilities;
|
|
|
HPETRegister configuration;
|
|
|
+ u64 reserved1;
|
|
|
HPETRegister interrupt_status;
|
|
|
- u8 reserved[0xF0 - 48];
|
|
|
+ u8 reserved2[0xF0 - 0x28];
|
|
|
HPETRegister main_counter_value;
|
|
|
+ u64 reserved3;
|
|
|
TimerStructure timers[3];
|
|
|
- u8 reserved2[0x400 - 0x160];
|
|
|
+ u8 reserved4[0x400 - 0x160];
|
|
|
};
|
|
|
|
|
|
+static_assert(__builtin_offsetof(HPETRegistersBlock, main_counter_value) == 0xf0);
|
|
|
+static_assert(__builtin_offsetof(HPETRegistersBlock, timers[0]) == 0x100);
|
|
|
+static_assert(__builtin_offsetof(HPETRegistersBlock, timers[1]) == 0x120);
|
|
|
+
|
|
|
+static u64 read_register_safe64(const HPETRegister& reg)
|
|
|
+{
|
|
|
+ // As per 2.4.7 this reads the 64 bit value in a consistent manner
|
|
|
+ // using only 32 bit reads
|
|
|
+ u32 low, high = reg.high;
|
|
|
+ for (;;) {
|
|
|
+ low = reg.low;
|
|
|
+ u32 new_high = reg.high;
|
|
|
+ if (new_high == high)
|
|
|
+ break;
|
|
|
+ high = new_high;
|
|
|
+ }
|
|
|
+ return ((u64)high << 32) | (u64)low;
|
|
|
+}
|
|
|
+
|
|
|
static HPET* s_hpet;
|
|
|
static bool hpet_initialized { false };
|
|
|
|
|
@@ -135,7 +155,7 @@ bool HPET::test_and_initialize()
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
- s_hpet = new HPET(PhysicalAddress(hpet));
|
|
|
+ new HPET(PhysicalAddress(hpet));
|
|
|
return true;
|
|
|
}
|
|
|
|
|
@@ -147,11 +167,11 @@ bool HPET::check_for_exisiting_periodic_timers()
|
|
|
|
|
|
auto sdt = map_typed<ACPI::Structures::HPET>(hpet);
|
|
|
ASSERT(sdt->event_timer_block.address_space == 0);
|
|
|
- auto registers = map_typed<volatile HPETRegistersBlock>(PhysicalAddress(sdt->event_timer_block.address));
|
|
|
+ auto registers = map_typed<HPETRegistersBlock>(PhysicalAddress(sdt->event_timer_block.address));
|
|
|
|
|
|
- size_t timers_count = ((registers->raw_capabilites.reg >> 8) & 0x1f) + 1;
|
|
|
+ size_t timers_count = ((registers->capabilities.attributes >> 8) & 0x1f) + 1;
|
|
|
for (size_t index = 0; index < timers_count; index++) {
|
|
|
- if (registers->timers[index].configuration_and_capability & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable)
|
|
|
+ if (registers->timers[index].capabilities & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable)
|
|
|
return true;
|
|
|
}
|
|
|
return false;
|
|
@@ -159,30 +179,67 @@ bool HPET::check_for_exisiting_periodic_timers()
|
|
|
|
|
|
void HPET::global_disable()
|
|
|
{
|
|
|
- registers().configuration.reg = registers().configuration.reg & ~(u32)HPETFlags::Configuration::Enable;
|
|
|
+ auto& regs = registers();
|
|
|
+ regs.configuration.low = regs.configuration.low & ~(u32)HPETFlags::Configuration::Enable;
|
|
|
}
|
|
|
void HPET::global_enable()
|
|
|
{
|
|
|
- registers().configuration.reg = registers().configuration.reg | (u32)HPETFlags::Configuration::Enable;
|
|
|
+ auto& regs = registers();
|
|
|
+ regs.configuration.low = regs.configuration.low | (u32)HPETFlags::Configuration::Enable;
|
|
|
}
|
|
|
|
|
|
-void HPET::set_periodic_comparator_value(const HPETComparator& comparator, u64 value)
|
|
|
+void HPET::update_periodic_comparator_value()
|
|
|
{
|
|
|
- disable(comparator);
|
|
|
- ASSERT(comparator.is_periodic());
|
|
|
- ASSERT(comparator.comparator_number() <= m_comparators.size());
|
|
|
- volatile auto& timer = registers().timers[comparator.comparator_number()];
|
|
|
- timer.configuration_and_capability = timer.configuration_and_capability | (u32)HPETFlags::TimerConfiguration::ValueSet;
|
|
|
- timer.comparator_value = value;
|
|
|
- enable(comparator);
|
|
|
+ // According to 2.3.9.2.2 the only safe way to change the periodic timer frequency
|
|
|
+ // is to disable all periodic timers, reset the main counter and each timer's comparator value.
|
|
|
+ // This introduces time drift, so it should be avoided unless absolutely necessary.
|
|
|
+ global_disable();
|
|
|
+ auto& regs = registers();
|
|
|
+
|
|
|
+ u64 previous_main_value = (u64)regs.main_counter_value.low | ((u64)regs.main_counter_value.high << 32);
|
|
|
+ regs.main_counter_value.low = 0;
|
|
|
+ regs.main_counter_value.high = 0;
|
|
|
+ for (auto& comparator : m_comparators) {
|
|
|
+ auto& timer = regs.timers[comparator.comparator_number()];
|
|
|
+ if (comparator.is_periodic()) {
|
|
|
+ // Note that this means we're restarting all periodic timers. There is no
|
|
|
+ // way to resume periodic timers properly because we reset the main counter
|
|
|
+ // and we can only write the period into the comparator value...
|
|
|
+ timer.capabilities = timer.capabilities | (u32)HPETFlags::TimerConfiguration::ValueSet;
|
|
|
+ u64 value = frequency() / comparator.ticks_per_second();
|
|
|
+#ifdef HPET_DEBUG
|
|
|
+ dbg() << "HPET: Update periodic comparator " << comparator.comparator_number() << " comparator value to " << value << " main value was: " << previous_main_value;
|
|
|
+#endif
|
|
|
+ timer.comparator_value.low = (u32)value;
|
|
|
+ timer.capabilities = timer.capabilities | (u32)HPETFlags::TimerConfiguration::ValueSet;
|
|
|
+ timer.comparator_value.high = (u32)(value >> 32);
|
|
|
+ } else {
|
|
|
+ // Set the new target comparator value to the delta to the remaining ticks
|
|
|
+ u64 current_value = (u64)timer.comparator_value.low | ((u64)timer.comparator_value.high << 32);
|
|
|
+ u64 value = current_value - previous_main_value;
|
|
|
+#ifdef HPET_DEBUG
|
|
|
+ dbg() << "HPET: Update non-periodic comparator " << comparator.comparator_number() << " comparator value from " << current_value << " to " << value << " main value was: " << previous_main_value;
|
|
|
+#endif
|
|
|
+ timer.comparator_value.low = (u32)value;
|
|
|
+ timer.comparator_value.high = (u32)(value >> 32);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ global_enable();
|
|
|
}
|
|
|
|
|
|
-void HPET::set_non_periodic_comparator_value(const HPETComparator& comparator, u64 value)
|
|
|
+void HPET::update_non_periodic_comparator_value(const HPETComparator& comparator)
|
|
|
{
|
|
|
ASSERT_INTERRUPTS_DISABLED();
|
|
|
ASSERT(!comparator.is_periodic());
|
|
|
ASSERT(comparator.comparator_number() <= m_comparators.size());
|
|
|
- registers().timers[comparator.comparator_number()].comparator_value = main_counter_value() + value;
|
|
|
+ auto& regs = registers();
|
|
|
+ auto& timer = regs.timers[comparator.comparator_number()];
|
|
|
+ u64 value = frequency() / comparator.ticks_per_second();
|
|
|
+ // NOTE: If the main counter passes this new value before we finish writing it, we will never receive an interrupt!
|
|
|
+ u64 new_counter_value = read_register_safe64(regs.main_counter_value) + value;
|
|
|
+ timer.comparator_value.high = (u32)(new_counter_value >> 32);
|
|
|
+ timer.comparator_value.low = (u32)new_counter_value;
|
|
|
}
|
|
|
|
|
|
void HPET::enable_periodic_interrupt(const HPETComparator& comparator)
|
|
@@ -192,10 +249,10 @@ void HPET::enable_periodic_interrupt(const HPETComparator& comparator)
|
|
|
#endif
|
|
|
disable(comparator);
|
|
|
ASSERT(comparator.comparator_number() <= m_comparators.size());
|
|
|
- volatile auto& timer = registers().timers[comparator.comparator_number()];
|
|
|
- auto configuration_and_capability = timer.configuration_and_capability;
|
|
|
- ASSERT(configuration_and_capability & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable);
|
|
|
- timer.configuration_and_capability = configuration_and_capability | (u32)HPETFlags::TimerConfiguration::TimerType;
|
|
|
+ auto& timer = registers().timers[comparator.comparator_number()];
|
|
|
+ auto capabilities = timer.capabilities;
|
|
|
+ ASSERT(capabilities & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable);
|
|
|
+ timer.capabilities = capabilities | (u32)HPETFlags::TimerConfiguration::GeneratePeriodicInterrupt;
|
|
|
enable(comparator);
|
|
|
}
|
|
|
void HPET::disable_periodic_interrupt(const HPETComparator& comparator)
|
|
@@ -205,10 +262,10 @@ void HPET::disable_periodic_interrupt(const HPETComparator& comparator)
|
|
|
#endif
|
|
|
disable(comparator);
|
|
|
ASSERT(comparator.comparator_number() <= m_comparators.size());
|
|
|
- auto volatile& timer = registers().timers[comparator.comparator_number()];
|
|
|
- auto configuration_and_capability = timer.configuration_and_capability;
|
|
|
- ASSERT(configuration_and_capability & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable);
|
|
|
- timer.configuration_and_capability = configuration_and_capability & ~(u32)HPETFlags::TimerConfiguration::TimerType;
|
|
|
+ auto& timer = registers().timers[comparator.comparator_number()];
|
|
|
+ auto capabilities = timer.capabilities;
|
|
|
+ ASSERT(capabilities & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable);
|
|
|
+ timer.capabilities = capabilities & ~(u32)HPETFlags::TimerConfiguration::GeneratePeriodicInterrupt;
|
|
|
enable(comparator);
|
|
|
}
|
|
|
|
|
@@ -218,8 +275,8 @@ void HPET::disable(const HPETComparator& comparator)
|
|
|
klog() << "HPET: Disable comparator " << comparator.comparator_number() << ".";
|
|
|
#endif
|
|
|
ASSERT(comparator.comparator_number() <= m_comparators.size());
|
|
|
- volatile auto& timer = registers().timers[comparator.comparator_number()];
|
|
|
- timer.configuration_and_capability = timer.configuration_and_capability & ~(u32)HPETFlags::TimerConfiguration::InterruptEnable;
|
|
|
+ auto& timer = registers().timers[comparator.comparator_number()];
|
|
|
+ timer.capabilities = timer.capabilities & ~(u32)HPETFlags::TimerConfiguration::InterruptEnable;
|
|
|
}
|
|
|
void HPET::enable(const HPETComparator& comparator)
|
|
|
{
|
|
@@ -227,13 +284,8 @@ void HPET::enable(const HPETComparator& comparator)
|
|
|
klog() << "HPET: Enable comparator " << comparator.comparator_number() << ".";
|
|
|
#endif
|
|
|
ASSERT(comparator.comparator_number() <= m_comparators.size());
|
|
|
- volatile auto& timer = registers().timers[comparator.comparator_number()];
|
|
|
- timer.configuration_and_capability = timer.configuration_and_capability | (u32)HPETFlags::TimerConfiguration::InterruptEnable;
|
|
|
-}
|
|
|
-
|
|
|
-u64 HPET::main_counter_value() const
|
|
|
-{
|
|
|
- return registers().main_counter_value.reg;
|
|
|
+ auto& timer = registers().timers[comparator.comparator_number()];
|
|
|
+ timer.capabilities = timer.capabilities | (u32)HPETFlags::TimerConfiguration::InterruptEnable;
|
|
|
}
|
|
|
|
|
|
u64 HPET::frequency() const
|
|
@@ -245,8 +297,8 @@ Vector<unsigned> HPET::capable_interrupt_numbers(const HPETComparator& comparato
|
|
|
{
|
|
|
ASSERT(comparator.comparator_number() <= m_comparators.size());
|
|
|
Vector<unsigned> capable_interrupts;
|
|
|
- auto& comparator_registers = (const volatile TimerStructure&)registers().timers[comparator.comparator_number()];
|
|
|
- u32 interrupt_bitfield = comparator_registers.configuration_and_capability >> 32;
|
|
|
+ auto& comparator_registers = registers().timers[comparator.comparator_number()];
|
|
|
+ u32 interrupt_bitfield = comparator_registers.interrupt_routing;
|
|
|
for (size_t index = 0; index < 32; index++) {
|
|
|
if (interrupt_bitfield & 1)
|
|
|
capable_interrupts.append(index);
|
|
@@ -259,8 +311,8 @@ Vector<unsigned> HPET::capable_interrupt_numbers(u8 comparator_number)
|
|
|
{
|
|
|
ASSERT(comparator_number <= m_comparators.size());
|
|
|
Vector<unsigned> capable_interrupts;
|
|
|
- auto& comparator_registers = (const volatile TimerStructure&)registers().timers[comparator_number];
|
|
|
- u32 interrupt_bitfield = comparator_registers.configuration_and_capability >> 32;
|
|
|
+ auto& comparator_registers = registers().timers[comparator_number];
|
|
|
+ u32 interrupt_bitfield = comparator_registers.interrupt_routing;
|
|
|
for (size_t index = 0; index < 32; index++) {
|
|
|
if (interrupt_bitfield & 1)
|
|
|
capable_interrupts.append(index);
|
|
@@ -272,15 +324,15 @@ Vector<unsigned> HPET::capable_interrupt_numbers(u8 comparator_number)
|
|
|
void HPET::set_comparator_irq_vector(u8 comparator_number, u8 irq_vector)
|
|
|
{
|
|
|
ASSERT(comparator_number <= m_comparators.size());
|
|
|
- auto& comparator_registers = (volatile TimerStructure&)registers().timers[comparator_number];
|
|
|
- comparator_registers.configuration_and_capability = comparator_registers.configuration_and_capability | (irq_vector << 9);
|
|
|
+ auto& comparator_registers = registers().timers[comparator_number];
|
|
|
+ comparator_registers.capabilities = comparator_registers.capabilities | (irq_vector << 9);
|
|
|
}
|
|
|
|
|
|
bool HPET::is_periodic_capable(u8 comparator_number) const
|
|
|
{
|
|
|
ASSERT(comparator_number <= m_comparators.size());
|
|
|
- auto& comparator_registers = (const volatile TimerStructure&)registers().timers[comparator_number];
|
|
|
- return comparator_registers.configuration_and_capability & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable;
|
|
|
+ auto& comparator_registers = registers().timers[comparator_number];
|
|
|
+ return comparator_registers.capabilities & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable;
|
|
|
}
|
|
|
|
|
|
void HPET::set_comparators_to_optimal_interrupt_state(size_t)
|
|
@@ -296,19 +348,20 @@ PhysicalAddress HPET::find_acpi_hpet_registers_block()
|
|
|
return PhysicalAddress(sdt->event_timer_block.address);
|
|
|
}
|
|
|
|
|
|
-const volatile HPETRegistersBlock& HPET::registers() const
|
|
|
+const HPETRegistersBlock& HPET::registers() const
|
|
|
{
|
|
|
- return *(const volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr();
|
|
|
+ return *(const HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr();
|
|
|
}
|
|
|
|
|
|
-volatile HPETRegistersBlock& HPET::registers()
|
|
|
+HPETRegistersBlock& HPET::registers()
|
|
|
{
|
|
|
- return *(volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr();
|
|
|
+ return *(HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr();
|
|
|
}
|
|
|
|
|
|
u64 HPET::calculate_ticks_in_nanoseconds() const
|
|
|
{
|
|
|
- return ABSOLUTE_MAXIMUM_COUNTER_TICK_PERIOD / registers().capabilities.main_counter_tick_period;
|
|
|
+ // ABSOLUTE_MAXIMUM_COUNTER_TICK_PERIOD == 100 nanoseconds
|
|
|
+ return ((u64)registers().capabilities.main_counter_tick_period * 100ull) / ABSOLUTE_MAXIMUM_COUNTER_TICK_PERIOD;
|
|
|
}
|
|
|
|
|
|
HPET::HPET(PhysicalAddress acpi_hpet)
|
|
@@ -316,27 +369,36 @@ HPET::HPET(PhysicalAddress acpi_hpet)
|
|
|
, m_physical_acpi_hpet_registers(find_acpi_hpet_registers_block())
|
|
|
, m_hpet_mmio_region(MM.allocate_kernel_region(m_physical_acpi_hpet_registers.page_base(), PAGE_SIZE, "HPET MMIO", Region::Access::Read | Region::Access::Write))
|
|
|
{
|
|
|
+ s_hpet = this; // Make available as soon as possible so that IRQs can use it
|
|
|
+
|
|
|
auto sdt = map_typed<const volatile ACPI::Structures::HPET>(m_physical_acpi_hpet_table);
|
|
|
m_vendor_id = sdt->pci_vendor_id;
|
|
|
m_minimum_tick = sdt->mininum_clock_tick;
|
|
|
klog() << "HPET: Minimum clock tick - " << m_minimum_tick;
|
|
|
|
|
|
+ auto& regs = registers();
|
|
|
+
|
|
|
// Note: We must do a 32 bit access to offsets 0x0, or 0x4 only.
|
|
|
- size_t timers_count = ((registers().raw_capabilites.reg >> 8) & 0x1f) + 1;
|
|
|
+ size_t timers_count = ((regs.capabilities.attributes >> 8) & 0x1f) + 1;
|
|
|
klog() << "HPET: Timers count - " << timers_count;
|
|
|
+ klog() << "HPET: Main counter size: " << ((regs.capabilities.attributes & (u32)HPETFlags::Attributes::Counter64BitCapable) ? "64 bit" : "32 bit");
|
|
|
+ for (size_t i = 0; i < timers_count; i++) {
|
|
|
+ bool capable_64_bit = regs.timers[i].capabilities & (u32)HPETFlags::TimerConfiguration::Timer64BitsCapable;
|
|
|
+ klog() << "HPET: Timer[" << i << "] comparator size: " << (capable_64_bit ? "64 bit" : "32 bit") << " mode: " << ((!capable_64_bit || (regs.timers[i].capabilities & (u32)HPETFlags::TimerConfiguration::Force32BitMode)) ? "32 bit" : "64 bit");
|
|
|
+ }
|
|
|
ASSERT(timers_count >= 2);
|
|
|
- auto* capabilities_register = (const volatile HPETCapabilityRegister*)®isters().raw_capabilites.reg;
|
|
|
|
|
|
global_disable();
|
|
|
|
|
|
m_frequency = NANOSECOND_PERIOD_TO_HERTZ(calculate_ticks_in_nanoseconds());
|
|
|
- klog() << "HPET: frequency " << m_frequency << " Hz (" << MEGAHERTZ_TO_HERTZ(m_frequency) << " MHz)";
|
|
|
- ASSERT(capabilities_register->main_counter_tick_period <= ABSOLUTE_MAXIMUM_COUNTER_TICK_PERIOD);
|
|
|
+ klog() << "HPET: frequency " << m_frequency << " Hz (" << MEGAHERTZ_TO_HERTZ(m_frequency) << " MHz) resolution: " << calculate_ticks_in_nanoseconds() << "ns";
|
|
|
+ ASSERT(regs.capabilities.main_counter_tick_period <= ABSOLUTE_MAXIMUM_COUNTER_TICK_PERIOD);
|
|
|
|
|
|
// Reset the counter, just in case...
|
|
|
- registers().main_counter_value.reg = 0;
|
|
|
- if (registers().raw_capabilites.reg & (u32)HPETFlags::Attributes::LegacyReplacementRouteCapable)
|
|
|
- registers().configuration.reg = registers().configuration.reg | (u32)HPETFlags::Configuration::LegacyReplacementRoute;
|
|
|
+ regs.main_counter_value.high = 0;
|
|
|
+ regs.main_counter_value.low = 0;
|
|
|
+ if (regs.capabilities.attributes & (u32)HPETFlags::Attributes::LegacyReplacementRouteCapable)
|
|
|
+ regs.configuration.low = regs.configuration.low | (u32)HPETFlags::Configuration::LegacyReplacementRoute;
|
|
|
|
|
|
m_comparators.append(HPETComparator::create(0, 0, is_periodic_capable(0)));
|
|
|
m_comparators.append(HPETComparator::create(1, 8, is_periodic_capable(1)));
|