2007-05-26 18:57:39 +02:00
|
|
|
/**
|
|
|
|
* =========================================================================
|
|
|
|
* File : hpet.cpp
|
|
|
|
* Project : 0 A.D.
|
2007-12-20 21:09:19 +01:00
|
|
|
* Description : Timer implementation using High Precision Event Timer
|
2007-05-26 18:57:39 +02:00
|
|
|
* =========================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
};
|
|
|
|
|
2007-05-28 11:25:38 +02:00
|
|
|
struct CounterHPET::HpetRegisters
|
2007-05-26 18:57:39 +02:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
2007-05-28 11:25:38 +02:00
|
|
|
LibError CounterHPET::Activate()
|
2007-05-26 18:57:39 +02:00
|
|
|
{
|
2007-09-07 22:17:57 +02:00
|
|
|
if(mahaf_IsPhysicalMappingDangerous())
|
|
|
|
return ERR::FAIL; // NOWARN (happens on Win2k)
|
2007-05-28 11:25:38 +02:00
|
|
|
if(!mahaf_Init())
|
|
|
|
return ERR::FAIL; // NOWARN (no Administrator privileges)
|
|
|
|
if(!acpi_Init())
|
2007-09-07 22:17:57 +02:00
|
|
|
WARN_RETURN(ERR::FAIL); // shouldn't fail, since we've checked mahaf_IsPhysicalMappingDangerous
|
2007-05-26 18:57:39 +02:00
|
|
|
const HpetDescriptionTable* hpet = (const HpetDescriptionTable*)acpi_GetTable("HPET");
|
|
|
|
if(!hpet)
|
2007-05-28 11:25:38 +02:00
|
|
|
return ERR::NO_SYS; // NOWARN (HPET not reported by BIOS)
|
2007-05-26 18:57:39 +02:00
|
|
|
debug_assert(hpet->baseAddress.addressSpaceId == ACPI_AS_MEMORY);
|
|
|
|
m_hpetRegisters = (volatile HpetRegisters*)mahaf_MapPhysicalMemory(hpet->baseAddress.address, sizeof(HpetRegisters));
|
|
|
|
if(!m_hpetRegisters)
|
2007-05-28 11:25:38 +02:00
|
|
|
WARN_RETURN(ERR::NO_MEM);
|
2007-05-26 18:57:39 +02:00
|
|
|
|
|
|
|
// 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;
|
2007-05-28 11:25:38 +02:00
|
|
|
|
|
|
|
return INFO::OK;
|
2007-05-26 18:57:39 +02:00
|
|
|
}
|
|
|
|
|
2007-05-28 11:25:38 +02:00
|
|
|
void CounterHPET::Shutdown()
|
2007-05-26 18:57:39 +02:00
|
|
|
{
|
2007-05-30 00:39:36 +02:00
|
|
|
if(m_hpetRegisters)
|
|
|
|
mahaf_UnmapPhysicalMemory((void*)m_hpetRegisters);
|
2007-05-26 18:57:39 +02:00
|
|
|
|
|
|
|
acpi_Shutdown();
|
2007-05-28 11:25:38 +02:00
|
|
|
mahaf_Shutdown();
|
2007-05-26 18:57:39 +02:00
|
|
|
}
|
|
|
|
|
2007-05-28 11:25:38 +02:00
|
|
|
bool CounterHPET::IsSafe() const
|
2007-05-26 18:57:39 +02:00
|
|
|
{
|
2007-06-08 19:44:24 +02:00
|
|
|
// the HPET having been created to address other timers' problems,
|
|
|
|
// it has no issues of its own.
|
2007-05-26 18:57:39 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2007-05-28 11:25:38 +02:00
|
|
|
u64 CounterHPET::Counter() const
|
2007-05-26 18:57:39 +02:00
|
|
|
{
|
2007-05-28 11:25:38 +02:00
|
|
|
// 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;
|
2007-05-26 18:57:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* WHRT uses this to ensure the counter (running at nominal frequency)
|
|
|
|
* doesn't overflow more than once during CALIBRATION_INTERVAL_MS.
|
|
|
|
**/
|
2007-05-28 11:25:38 +02:00
|
|
|
uint CounterHPET::CounterBits() const
|
2007-05-26 18:57:39 +02:00
|
|
|
{
|
2007-05-28 11:25:38 +02:00
|
|
|
const u64 caps = m_hpetRegisters->capabilities;
|
|
|
|
const uint counterBits = (caps & CAP_SIZE64)? 64 : 32;
|
|
|
|
return counterBits;
|
2007-05-26 18:57:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* initial measurement of the tick rate. not necessarily correct
|
2007-09-23 17:36:29 +02:00
|
|
|
* (e.g. when using TSC: cpu_ClockFrequency isn't exact).
|
2007-05-26 18:57:39 +02:00
|
|
|
**/
|
2007-05-28 11:25:38 +02:00
|
|
|
double CounterHPET::NominalFrequency() const
|
2007-05-26 18:57:39 +02:00
|
|
|
{
|
2007-05-28 11:25:38 +02:00
|
|
|
const u64 caps = m_hpetRegisters->capabilities;
|
|
|
|
const u32 timerPeriod_fs = bits64(caps, 32, 63);
|
|
|
|
const double frequency = 1e15 / timerPeriod_fs;
|
|
|
|
return frequency;
|
2007-05-26 18:57:39 +02:00
|
|
|
}
|