]> rtime.felk.cvut.cz Git - jailhouse.git/blob - inmates/lib/x86/timing.c
x86/tools/inmates: Account for 32-bit PM timers
[jailhouse.git] / inmates / lib / x86 / timing.c
1 /*
2  * Jailhouse, a Linux-based partitioning hypervisor
3  *
4  * Copyright (c) Siemens AG, 2013, 2014
5  * Copyright (c) Valentine Sinitsyn, 2014
6  *
7  * Authors:
8  *  Jan Kiszka <jan.kiszka@siemens.com>
9  *  Valentine Sinitsyn <valentine.sinitsyn@gmail.com>
10  *
11  * This work is licensed under the terms of the GNU GPL, version 2.  See
12  * the COPYING file in the top-level directory.
13  */
14
15 #include <inmate.h>
16
17 #define PM_TIMER_HZ             3579545
18
19 #define X2APIC_LVTT             0x832
20 #define X2APIC_TMICT            0x838
21 #define X2APIC_TMCCT            0x839
22 #define X2APIC_TDCR             0x83e
23
24 static unsigned long divided_apic_freq;
25 static unsigned long pm_timer_overflow;
26
27 void pm_timer_init(void)
28 {
29         /* Initialize pm_timer overflow value, as per ACPI 5.0, Sect. 5.2.9. */
30         if (comm_region->pm_timer_val_ext)
31                 pm_timer_overflow =
32                         ((0x100000000 * 1000000000ULL) / PM_TIMER_HZ);
33         else
34                 pm_timer_overflow =
35                         ((0x1000000 * 1000000000ULL) / PM_TIMER_HZ);
36 }
37
38 unsigned long pm_timer_read(void)
39 {
40         static unsigned long last, overflows;
41         unsigned long tmr;
42
43         tmr = (inl(comm_region->pm_timer_address) * NS_PER_SEC) / PM_TIMER_HZ;
44         if (tmr < last)
45                 overflows += pm_timer_overflow;
46         last = tmr;
47         return tmr + overflows;
48 }
49
50 void delay_us(unsigned long microsecs)
51 {
52         unsigned long timeout = pm_timer_read() + microsecs * NS_PER_USEC;
53
54         while ((long)(timeout - pm_timer_read()) > 0)
55                 cpu_relax();
56 }
57
58 unsigned long apic_timer_init(unsigned int vector)
59 {
60         unsigned long start, end;
61         unsigned long tmr;
62
63         write_msr(X2APIC_TDCR, 3);
64
65         start = pm_timer_read();
66         write_msr(X2APIC_TMICT, 0xffffffff);
67
68         while (pm_timer_read() - start < 100 * NS_PER_MSEC)
69                 cpu_relax();
70
71         end = pm_timer_read();
72         tmr = read_msr(X2APIC_TMCCT);
73
74         divided_apic_freq = (0xffffffffULL - tmr) * NS_PER_SEC / (end - start);
75
76         write_msr(X2APIC_TMICT, 0);
77         write_msr(X2APIC_LVTT, vector);
78
79         return (divided_apic_freq * 16 + 500) / 1000;
80 }
81
82 void apic_timer_set(unsigned long timeout_ns)
83 {
84         unsigned long long ticks =
85                 (unsigned long long)timeout_ns * divided_apic_freq;
86         write_msr(X2APIC_TMICT, ticks / NS_PER_SEC);
87 }