2 * Jailhouse, a Linux-based partitioning hypervisor
4 * Copyright (c) Siemens AG, 2013, 2014
7 * Jan Kiszka <jan.kiszka@siemens.com>
9 * This work is licensed under the terms of the GNU GPL, version 2. See
10 * the COPYING file in the top-level directory.
15 #define PM_TIMER_HZ 3579545
16 #define PM_TIMER_OVERFLOW ((0x1000000 * 1000000000ULL) / PM_TIMER_HZ)
18 #define X2APIC_LVTT 0x832
19 #define X2APIC_TMICT 0x838
20 #define X2APIC_TMCCT 0x839
21 #define X2APIC_TDCR 0x83e
23 static unsigned long divided_apic_freq;
25 unsigned long pm_timer_read(void)
27 static unsigned long last, overflows;
30 tmr = (inl(comm_region->pm_timer_address) * NS_PER_SEC) / PM_TIMER_HZ;
32 overflows += PM_TIMER_OVERFLOW;
34 return tmr + overflows;
37 unsigned long apic_timer_init(unsigned int vector)
39 unsigned long start, end;
42 write_msr(X2APIC_TDCR, 3);
44 start = pm_timer_read();
45 write_msr(X2APIC_TMICT, 0xffffffff);
47 while (pm_timer_read() - start < 100 * NS_PER_MSEC)
50 end = pm_timer_read();
51 tmr = read_msr(X2APIC_TMCCT);
53 divided_apic_freq = (0xffffffffULL - tmr) * NS_PER_SEC / (end - start);
55 write_msr(X2APIC_TMICT, 0);
56 write_msr(X2APIC_LVTT, vector);
58 return (divided_apic_freq * 16 + 500) / 1000;
61 void apic_timer_set(unsigned long timeout_ns)
63 unsigned long long ticks =
64 (unsigned long long)timeout_ns * divided_apic_freq;
65 write_msr(X2APIC_TMICT, ticks / NS_PER_SEC);