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];
26 static unsigned long tsc_freq, tsc_overflow;
27 static unsigned long tsc_last[SMP_MAX_CPUS];
28 static unsigned long tsc_overflows[SMP_MAX_CPUS];
30 static u64 rdtsc(void)
35 asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
36 return (u64)lo | (((u64)hi) << 32);
40 asm volatile("rdtsc" : "=A" (v));
45 unsigned long tsc_read(void)
47 unsigned int cpu = cpu_id();
50 tmr = ((rdtsc() & 0xffffffffLL) * NS_PER_SEC) / tsc_freq;
51 if (tmr < tsc_last[cpu])
52 tsc_overflows[cpu] += tsc_overflow;
54 return tmr + tsc_overflows[cpu];
57 unsigned long tsc_init(void)
59 unsigned long start_pm, end_pm;
60 u64 start_tsc, end_tsc;
62 start_pm = pm_timer_read();
64 asm volatile("mfence" : : : "memory");
66 while (pm_timer_read() - start_pm < 100 * NS_PER_MSEC)
69 end_pm = pm_timer_read();
71 asm volatile("mfence" : : : "memory");
73 tsc_freq = (end_tsc - start_tsc) * NS_PER_SEC / (end_pm - start_pm);
74 tsc_overflow = (0x100000000L * NS_PER_SEC) / tsc_freq;
79 unsigned long pm_timer_read(void)
81 unsigned int cpu = cpu_id();
84 tmr = ((inl(comm_region->pm_timer_address) & 0x00ffffff) * NS_PER_SEC)
86 if (tmr < pm_timer_last[cpu])
87 pm_timer_overflows[cpu] += PM_TIMER_OVERFLOW;
88 pm_timer_last[cpu] = tmr;
89 return tmr + pm_timer_overflows[cpu];
92 void delay_us(unsigned long microsecs)
94 unsigned long timeout = pm_timer_read() + microsecs * NS_PER_USEC;
96 while ((long)(timeout - pm_timer_read()) > 0)
100 unsigned long apic_timer_init(unsigned int vector)
102 unsigned long start, end;
105 write_msr(X2APIC_TDCR, 3);
107 start = pm_timer_read();
108 write_msr(X2APIC_TMICT, 0xffffffff);
110 while (pm_timer_read() - start < 100 * NS_PER_MSEC)
113 end = pm_timer_read();
114 tmr = read_msr(X2APIC_TMCCT);
116 divided_apic_freq = (0xffffffffULL - tmr) * NS_PER_SEC / (end - start);
118 write_msr(X2APIC_TMICT, 0);
119 write_msr(X2APIC_LVTT, vector);
121 return (divided_apic_freq * 16 + 500) / 1000;
124 void apic_timer_set(unsigned long timeout_ns)
126 unsigned long long ticks =
127 (unsigned long long)timeout_ns * divided_apic_freq;
128 write_msr(X2APIC_TMICT, ticks / NS_PER_SEC);