Files
SystemPaw3/paw/stos/arch/amd64/platform/hpet.c
T
2026-06-27 06:47:29 +00:00

122 lines
2.7 KiB
C

/*
* Copyright (c) 2026, Chloe M.
* Provided under the BSD-3 clause.
*
* Description: HPET driver
* Author: Chloe M.
*/
#include <machine/hpet.h>
#include <machine/hpetreg.h>
#include <drivers/acpi/tables.h>
#include <drivers/acpi/acpi.h>
#include <hal/mmio.h>
#include <mm/vmm.h>
#include <ke/knot.h>
#include <ex/trace.h>
#include <units.h>
#define DTRACE(Fmt, ...) \
TRACE("[ HPET ]: " Fmt, ##__VA_ARGS__)
static VOID *MMIOBase = NULL;
/*
* Write a 64-bit value to an HPET register
*
* @Register: Register to write to
* @Value: Value to write
*/
static VOID
HpetWriteq(UCHAR Register, UQUAD Value)
{
VOID *RegBase;
if (MMIOBase == NULL) {
return;
}
RegBase = PTR_OFFSET(MMIOBase, Register);
MMIOWrite64(RegBase, Value);
}
/*
* Read a 64-bit value from an HPET register
*
* @Register: Register to read from
*/
static UQUAD
HpetReadq(UCHAR Register)
{
VOID *RegBase;
if (MMIOBase == NULL) {
return 0;
}
RegBase = PTR_OFFSET(MMIOBase, Register);
return MMIORead64(RegBase);
}
USIZE
MdHpetTimeUsec(VOID)
{
UQUAD Period, Freq, Caps;
UQUAD Counter;
Caps = HpetReadq(HPET_GENERAL_CAP);
Period = (Caps >> HPET_PERIOD_SHIFT) & HPET_PERIOD_MASK;
Freq = FSEC_PER_SECOND / Period;
Counter = HpetReadq(HPET_MAIN_COUNTER);
return (Counter * USEC_PER_SECOND) / Freq;
}
VOID
MdHpetInit(VOID)
{
ACPI_HPET *Hpet;
UQUAD GeneralCap, GeneralConf;
ULONG ClkPeriod;
UCHAR RevId;
const ACPI_GAS *Gas;
Hpet = AcpiQuery("HPET");
if (Hpet == NULL) {
KeKnot(
KNOT_UNBOUND_RSRC,
"could not detect HPET on this platform\n"
);
}
/* Some informational logging */
DTRACE("detected hpet with pci vendor id : %x\n", Hpet->PciVendorId);
DTRACE("counter size : %d\n", Hpet->CounterSize);
DTRACE("minimum tick : %d\n", Hpet->MinimumTick);
/* Obtain the MMIO base address */
Gas = &Hpet->Gas;
MMIOBase = PMA_TO_VMA(Gas->Address);
GeneralCap = HpetReadq(HPET_GENERAL_CAP);
ClkPeriod = (GeneralCap >> HPET_PERIOD_SHIFT) & HPET_PERIOD_MASK;
RevId = GeneralCap & 0xFF;
DTRACE("counter clk period : %d\n", ClkPeriod);
/*
* Verify that the clock period and the revision ID are not
* garbage values.
*/
if (ClkPeriod > HPET_MAX_CLK_PERIOD)
KeKnot(KNOT_MISC, "hpet: bad counter clk period\n");
if (RevId == 0)
KeKnot(KNOT_MISC, "hpet: bad revision id, must not be zero\n");
HpetWriteq(HPET_MAIN_COUNTER, 0);
/* Enable the counter */
GeneralConf = HpetReadq(HPET_GENERAL_CONF);
GeneralConf |= HPET_GCONF_EN;
HpetWriteq(HPET_GENERAL_CONF, GeneralConf);
}