vaseboot/VasEBoot-core/kern/i386/tsc_pmtimer.c

89 lines
2.5 KiB
C

/* kern/i386/tsc.c - x86 TSC time source implementation
* Requires Pentium or better x86 CPU that supports the RDTSC instruction.
* This module uses the PIT to calibrate the TSC to
* real time.
*
* VasEBoot -- GRand Unified Bootloader
* Copyright (C) 2008 Free Software Foundation, Inc.
*
* VasEBoot is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VasEBoot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VasEBoot. If not, see <http://www.gnu.org/licenses/>.
*/
#include <VasEBoot/types.h>
#include <VasEBoot/time.h>
#include <VasEBoot/misc.h>
#include <VasEBoot/i386/tsc.h>
#include <VasEBoot/i386/pmtimer.h>
#include <VasEBoot/acpi.h>
#include <VasEBoot/cpu/io.h>
VasEBoot_uint64_t
VasEBoot_pmtimer_wait_count_tsc (VasEBoot_port_t pmtimer,
VasEBoot_uint16_t num_pm_ticks)
{
VasEBoot_uint32_t start;
VasEBoot_uint32_t last;
VasEBoot_uint32_t cur, end;
VasEBoot_uint64_t start_tsc;
VasEBoot_uint64_t end_tsc;
int num_iter = 0;
start = VasEBoot_inl (pmtimer) & 0xffffff;
last = start;
end = start + num_pm_ticks;
start_tsc = VasEBoot_get_tsc ();
while (1)
{
cur = VasEBoot_inl (pmtimer) & 0xffffff;
if (cur < last)
cur |= 0x1000000;
num_iter++;
if (cur >= end)
{
end_tsc = VasEBoot_get_tsc ();
return end_tsc - start_tsc;
}
/* Check for broken PM timer.
50000000 TSCs is between 5 ms (10GHz) and 200 ms (250 MHz)
if after this time we still don't have 1 ms on pmtimer, then
pmtimer is broken.
*/
if ((num_iter & 0xffffff) == 0 && VasEBoot_get_tsc () - start_tsc > 5000000) {
return 0;
}
}
}
int
VasEBoot_tsc_calibrate_from_pmtimer (void)
{
struct VasEBoot_acpi_fadt *fadt;
VasEBoot_port_t pmtimer;
VasEBoot_uint64_t tsc_diff;
fadt = VasEBoot_acpi_find_fadt ();
if (!fadt)
return 0;
pmtimer = fadt->pmtimer;
if (!pmtimer)
return 0;
/* It's 3.579545 MHz clock. Wait 1 ms. */
tsc_diff = VasEBoot_pmtimer_wait_count_tsc (pmtimer, 3580);
if (tsc_diff == 0)
return 0;
VasEBoot_tsc_rate = VasEBoot_divmod64 ((1ULL << 32), tsc_diff, 0);
return 1;
}