2 * Jailhouse, a Linux-based partitioning hypervisor
4 * Copyright (c) Siemens AG, 2013-2016
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 tsc_freq = cmdline_parse_int("tsc_freq", 0);
65 start_pm = pm_timer_read();
67 asm volatile("mfence" : : : "memory");
69 while (pm_timer_read() - start_pm < 100 * NS_PER_MSEC)
72 end_pm = pm_timer_read();
74 asm volatile("mfence" : : : "memory");
76 tsc_freq = (end_tsc - start_tsc) * NS_PER_SEC / (end_pm - start_pm);
79 tsc_overflow = (0x100000000L * NS_PER_SEC) / tsc_freq;
84 unsigned long pm_timer_read(void)
86 unsigned int cpu = cpu_id();
89 tmr = ((inl(comm_region->pm_timer_address) & 0x00ffffff) * NS_PER_SEC)
91 if (tmr < pm_timer_last[cpu])
92 pm_timer_overflows[cpu] += PM_TIMER_OVERFLOW;
93 pm_timer_last[cpu] = tmr;
94 return tmr + pm_timer_overflows[cpu];
97 void delay_us(unsigned long microsecs)
99 unsigned long timeout = pm_timer_read() + microsecs * NS_PER_USEC;
101 while ((long)(timeout - pm_timer_read()) > 0)
105 unsigned long apic_timer_init(unsigned int vector)
107 unsigned long long apic_freq;
108 unsigned long start, end;
111 apic_freq = cmdline_parse_int("apic_freq", 0);
113 if (apic_freq == 0) {
114 write_msr(X2APIC_TDCR, 3);
116 start = pm_timer_read();
117 write_msr(X2APIC_TMICT, 0xffffffff);
119 while (pm_timer_read() - start < 100 * NS_PER_MSEC)
122 end = pm_timer_read();
123 tmr = read_msr(X2APIC_TMCCT);
125 write_msr(X2APIC_TMICT, 0);
127 divided_apic_freq = (0xffffffffULL - tmr) * NS_PER_SEC / (end - start);
128 apic_freq = (divided_apic_freq * 16 + 500) / 1000;
130 divided_apic_freq = apic_freq / 16;
134 write_msr(X2APIC_LVTT, vector);
139 void apic_timer_set(unsigned long timeout_ns)
141 unsigned long long ticks =
142 (unsigned long long)timeout_ns * divided_apic_freq;
143 write_msr(X2APIC_TMICT, ticks / NS_PER_SEC);