]> rtime.felk.cvut.cz Git - jailhouse.git/blob - inmates/lib/x86/timing.c
inmates: x86: Make pm_timer_read SMP-safe
[jailhouse.git] / inmates / lib / x86 / timing.c
1 /*
2  * Jailhouse, a Linux-based partitioning hypervisor
3  *
4  * Copyright (c) Siemens AG, 2013, 2014
5  *
6  * Authors:
7  *  Jan Kiszka <jan.kiszka@siemens.com>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2.  See
10  * the COPYING file in the top-level directory.
11  */
12
13 #include <inmate.h>
14
15 #define PM_TIMER_HZ             3579545
16 #define PM_TIMER_OVERFLOW      ((0x1000000 * NS_PER_SEC) / PM_TIMER_HZ)
17
18 #define X2APIC_LVTT             0x832
19 #define X2APIC_TMICT            0x838
20 #define X2APIC_TMCCT            0x839
21 #define X2APIC_TDCR             0x83e
22
23 static unsigned long divided_apic_freq;
24 static unsigned long pm_timer_last[SMP_MAX_CPUS];
25 static unsigned long pm_timer_overflows[SMP_MAX_CPUS];
26
27 unsigned long pm_timer_read(void)
28 {
29         unsigned int cpu = cpu_id();
30         unsigned long tmr;
31
32         tmr = ((inl(comm_region->pm_timer_address) & 0x00ffffff) * NS_PER_SEC)
33                 / PM_TIMER_HZ;
34         if (tmr < pm_timer_last[cpu])
35                 pm_timer_overflows[cpu] += PM_TIMER_OVERFLOW;
36         pm_timer_last[cpu] = tmr;
37         return tmr + pm_timer_overflows[cpu];
38 }
39
40 void delay_us(unsigned long microsecs)
41 {
42         unsigned long timeout = pm_timer_read() + microsecs * NS_PER_USEC;
43
44         while ((long)(timeout - pm_timer_read()) > 0)
45                 cpu_relax();
46 }
47
48 unsigned long apic_timer_init(unsigned int vector)
49 {
50         unsigned long start, end;
51         unsigned long tmr;
52
53         write_msr(X2APIC_TDCR, 3);
54
55         start = pm_timer_read();
56         write_msr(X2APIC_TMICT, 0xffffffff);
57
58         while (pm_timer_read() - start < 100 * NS_PER_MSEC)
59                 cpu_relax();
60
61         end = pm_timer_read();
62         tmr = read_msr(X2APIC_TMCCT);
63
64         divided_apic_freq = (0xffffffffULL - tmr) * NS_PER_SEC / (end - start);
65
66         write_msr(X2APIC_TMICT, 0);
67         write_msr(X2APIC_LVTT, vector);
68
69         return (divided_apic_freq * 16 + 500) / 1000;
70 }
71
72 void apic_timer_set(unsigned long timeout_ns)
73 {
74         unsigned long long ticks =
75                 (unsigned long long)timeout_ns * divided_apic_freq;
76         write_msr(X2APIC_TMICT, ticks / NS_PER_SEC);
77 }