2 * Jailhouse, a Linux-based partitioning hypervisor
4 * Copyright (c) Siemens AG, 2013, 2014
5 * Copyright (c) Valentine Sinitsyn, 2014
8 * Jan Kiszka <jan.kiszka@siemens.com>
9 * Valentine Sinitsyn <valentine.sinitsyn@gmail.com>
11 * This work is licensed under the terms of the GNU GPL, version 2. See
12 * the COPYING file in the top-level directory.
17 #define PM_TIMER_HZ 3579545
19 #define X2APIC_LVTT 0x832
20 #define X2APIC_TMICT 0x838
21 #define X2APIC_TMCCT 0x839
22 #define X2APIC_TDCR 0x83e
24 static unsigned long divided_apic_freq;
25 static unsigned long pm_timer_overflow;
27 void pm_timer_init(void)
29 /* Initialize pm_timer overflow value, as per ACPI 5.0, Sect. 5.2.9. */
30 if (comm_region->pm_timer_val_ext)
32 ((0x100000000 * 1000000000ULL) / PM_TIMER_HZ);
35 ((0x1000000 * 1000000000ULL) / PM_TIMER_HZ);
38 unsigned long pm_timer_read(void)
40 static unsigned long last, overflows;
43 tmr = (inl(comm_region->pm_timer_address) * NS_PER_SEC) / PM_TIMER_HZ;
45 overflows += pm_timer_overflow;
47 return tmr + overflows;
50 void delay_us(unsigned long microsecs)
52 unsigned long timeout = pm_timer_read() + microsecs * NS_PER_USEC;
54 while ((long)(timeout - pm_timer_read()) > 0)
58 unsigned long apic_timer_init(unsigned int vector)
60 unsigned long start, end;
63 write_msr(X2APIC_TDCR, 3);
65 start = pm_timer_read();
66 write_msr(X2APIC_TMICT, 0xffffffff);
68 while (pm_timer_read() - start < 100 * NS_PER_MSEC)
71 end = pm_timer_read();
72 tmr = read_msr(X2APIC_TMCCT);
74 divided_apic_freq = (0xffffffffULL - tmr) * NS_PER_SEC / (end - start);
76 write_msr(X2APIC_TMICT, 0);
77 write_msr(X2APIC_LVTT, vector);
79 return (divided_apic_freq * 16 + 500) / 1000;
82 void apic_timer_set(unsigned long timeout_ns)
84 unsigned long long ticks =
85 (unsigned long long)timeout_ns * divided_apic_freq;
86 write_msr(X2APIC_TMICT, ticks / NS_PER_SEC);