:: limine / common / sys / cpu.s2.c 2.2 KB raw

1
#include <stdint.h>
2
#include <stdbool.h>
3
#include <sys/cpu.h>
4
#if defined(UEFI)
5
#include <efi.h>
6
#include <lib/misc.h>
7
#endif
8
9
uint64_t tsc_freq = 0;
10
11
void calibrate_tsc(void) {
12
    tsc_freq = tsc_freq_arch();
13
    if (tsc_freq != 0) {
14
        return;
15
    }
16
17
#if defined(UEFI)
18
    // Calibrate over 10ms. Run multiple rounds and take the smallest
19
    // (least SMI-disrupted) delta.
20
    #define EFI_CALIBRATION_STALL 10000
21
    #define EFI_CALIBRATION_ROUNDS 3
22
23
    uint64_t best_delta = 0;
24
    for (int round = 0; round < EFI_CALIBRATION_ROUNDS; round++) {
25
        uint64_t tsc_start = rdtsc();
26
        gBS->Stall(EFI_CALIBRATION_STALL);
27
        uint64_t tsc_end = rdtsc();
28
29
        if (tsc_end > tsc_start) {
30
            uint64_t delta = tsc_end - tsc_start;
31
            if (delta < best_delta || best_delta == 0) {
32
                best_delta = delta;
33
            }
34
        }
35
    }
36
37
    if (best_delta != 0) {
38
        tsc_freq = best_delta * (1000000ULL / EFI_CALIBRATION_STALL);
39
    }
40
#elif defined(BIOS)
41
    // Calibrate TSC using PIT channel 2.
42
    // PIT oscillator frequency: 1193182 Hz
43
    // Count of 11932 gives ~10ms calibration interval.
44
    // Run multiple rounds and take the smallest (least SMI-disrupted) result.
45
    #define PIT_CALIBRATION_COUNT 11932
46
    #define PIT_CALIBRATION_ROUNDS 3
47
48
    uint8_t port61 = inb(0x61);
49
50
    uint64_t best_delta = 0;
51
    for (int round = 0; round < PIT_CALIBRATION_ROUNDS; round++) {
52
        outb(0x61, port61 & ~0x03); // disable gate and speaker
53
        outb(0x43, 0xb0); // channel 2, lobyte/hibyte, mode 0, binary
54
        outb(0x42, PIT_CALIBRATION_COUNT & 0xff);
55
        outb(0x42, (PIT_CALIBRATION_COUNT >> 8) & 0xff);
56
57
        outb(0x61, (inb(0x61) | 0x01)); // enable gate to start counting
58
        uint64_t tsc_start = rdtsc();
59
60
        while ((inb(0x61) & 0x20) == 0); // wait for output high
61
        uint64_t tsc_end = rdtsc();
62
63
        if (tsc_end > tsc_start) {
64
            uint64_t delta = tsc_end - tsc_start;
65
            if (delta < best_delta || best_delta == 0) {
66
                best_delta = delta;
67
            }
68
        }
69
    }
70
71
    outb(0x61, port61); // restore
72
73
    if (best_delta != 0) {
74
        tsc_freq = best_delta * 1193182 / PIT_CALIBRATION_COUNT;
75
    }
76
#endif
77
}
tab: 248 wrap: offon