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;
24 static unsigned long pm_timer_last[SMP_MAX_CPUS];
25 static unsigned long pm_timer_overflows[SMP_MAX_CPUS];
27 unsigned long pm_timer_read(void)
29 unsigned int cpu = cpu_id();
32 tmr = ((inl(comm_region->pm_timer_address) & 0x00ffffff) * NS_PER_SEC)
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];
40 void delay_us(unsigned long microsecs)
42 unsigned long timeout = pm_timer_read() + microsecs * NS_PER_USEC;
44 while ((long)(timeout - pm_timer_read()) > 0)
48 unsigned long apic_timer_init(unsigned int vector)
50 unsigned long start, end;
53 write_msr(X2APIC_TDCR, 3);
55 start = pm_timer_read();
56 write_msr(X2APIC_TMICT, 0xffffffff);
58 while (pm_timer_read() - start < 100 * NS_PER_MSEC)
61 end = pm_timer_read();
62 tmr = read_msr(X2APIC_TMCCT);
64 divided_apic_freq = (0xffffffffULL - tmr) * NS_PER_SEC / (end - start);
66 write_msr(X2APIC_TMICT, 0);
67 write_msr(X2APIC_LVTT, vector);
69 return (divided_apic_freq * 16 + 500) / 1000;
72 void apic_timer_set(unsigned long timeout_ns)
74 unsigned long long ticks =
75 (unsigned long long)timeout_ns * divided_apic_freq;
76 write_msr(X2APIC_TMICT, ticks / NS_PER_SEC);