/** * ========================================================================= * File : hpet.cpp * Project : 0 A.D. * Description : Timer implementation using timeGetTime * ========================================================================= */ // license: GPL; see lib/license.txt #include "precompiled.h" #include "hpet.h" #include "lib/sysdep/win/win.h" #include "lib/sysdep/win/mahaf.h" #include "lib/sysdep/acpi.h" #include "lib/bits.h" #pragma pack(push, 1) struct HpetDescriptionTable { AcpiTable header; u32 eventTimerBlockId; AcpiGenericAddress baseAddress; u8 sequenceNumber; u16 minimumPeriodicTicks; u8 attributes; }; struct CounterHPET::HpetRegisters { u64 capabilities; u64 reserved1; u64 config; u64 reserved2; u64 interruptStatus; u64 reserved3[25]; u64 counterValue; u64 reserved4; // .. followed by blocks for timers 0..31 }; #pragma pack(pop) static const u64 CAP_SIZE64 = BIT64(13); static const u64 CONFIG_ENABLE = BIT64(0); //----------------------------------------------------------------------------- LibError CounterHPET::Activate() { if(!mahaf_Init()) return ERR::FAIL; // NOWARN (no Administrator privileges) if(!acpi_Init()) WARN_RETURN(ERR::FAIL); const HpetDescriptionTable* hpet = (const HpetDescriptionTable*)acpi_GetTable("HPET"); if(!hpet) return ERR::NO_SYS; // NOWARN (HPET not reported by BIOS) debug_assert(hpet->baseAddress.addressSpaceId == ACPI_AS_MEMORY); m_hpetRegisters = (volatile HpetRegisters*)mahaf_MapPhysicalMemory(hpet->baseAddress.address, sizeof(HpetRegisters)); if(!m_hpetRegisters) WARN_RETURN(ERR::NO_MEM); // start the counter (if not already running) // note: do not reset value to 0 to avoid interfering with any // other users of the timer (e.g. Vista QPC) m_hpetRegisters->config |= CONFIG_ENABLE; return INFO::OK; } void CounterHPET::Shutdown() { if(m_hpetRegisters) mahaf_UnmapPhysicalMemory((void*)m_hpetRegisters); acpi_Shutdown(); mahaf_Shutdown(); } bool CounterHPET::IsSafe() const { // the HPET having been created to address other timers' problems, // it has no issues of its own. return true; } u64 CounterHPET::Counter() const { // note: we assume the data bus can do atomic 64-bit transfers, // which has been the case since the original Pentium. // (note: see implementation of GetTickCount for an algorithm to // cope with non-atomic reads) return m_hpetRegisters->counterValue; } /** * WHRT uses this to ensure the counter (running at nominal frequency) * doesn't overflow more than once during CALIBRATION_INTERVAL_MS. **/ uint CounterHPET::CounterBits() const { const u64 caps = m_hpetRegisters->capabilities; const uint counterBits = (caps & CAP_SIZE64)? 64 : 32; return counterBits; } /** * initial measurement of the tick rate. not necessarily correct * (e.g. when using TSC: wcpu_ClockFrequency isn't exact). **/ double CounterHPET::NominalFrequency() const { const u64 caps = m_hpetRegisters->capabilities; const u32 timerPeriod_fs = bits64(caps, 32, 63); const double frequency = 1e15 / timerPeriod_fs; return frequency; }