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 * NS_PER_SEC) / 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) & 0x00ffffff) * NS_PER_SEC)
33 overflows += PM_TIMER_OVERFLOW;
35 return tmr + overflows;
38 void delay_us(unsigned long microsecs)
40 unsigned long timeout = pm_timer_read() + microsecs * NS_PER_USEC;
42 while ((long)(timeout - pm_timer_read()) > 0)
46 unsigned long apic_timer_init(unsigned int vector)
48 unsigned long start, end;
51 write_msr(X2APIC_TDCR, 3);
53 start = pm_timer_read();
54 write_msr(X2APIC_TMICT, 0xffffffff);
56 while (pm_timer_read() - start < 100 * NS_PER_MSEC)
59 end = pm_timer_read();
60 tmr = read_msr(X2APIC_TMCCT);
62 divided_apic_freq = (0xffffffffULL - tmr) * NS_PER_SEC / (end - start);
64 write_msr(X2APIC_TMICT, 0);
65 write_msr(X2APIC_LVTT, vector);
67 return (divided_apic_freq * 16 + 500) / 1000;
70 void apic_timer_set(unsigned long timeout_ns)
72 unsigned long long ticks =
73 (unsigned long long)timeout_ns * divided_apic_freq;
74 write_msr(X2APIC_TMICT, ticks / NS_PER_SEC);