1 | /*␊ |
2 | * Copyright 2008 Islam Ahmed Zaid. All rights reserved. <azismed@gmail.com>␊ |
3 | * AsereBLN: 2009: cleanup and bugfix␊ |
4 | */␊ |
5 | ␊ |
6 | #ifndef __LIBSAIO_CPU_H␊ |
7 | #define __LIBSAIO_CPU_H␊ |
8 | ␊ |
9 | #include "platform.h"␊ |
10 | ␊ |
11 | extern void scan_cpu(PlatformInfo_t *);␊ |
12 | ␊ |
13 | // DFE: These two constants come from Linux except CLOCK_TICK_RATE replaced with CLKNUM␊ |
14 | #define CALIBRATE_TIME_MSEC␉30␉␉/* 30 msecs */␊ |
15 | #define CALIBRATE_LATCH␉␉((CLKNUM * CALIBRATE_TIME_MSEC + 1000/2)/1000)␊ |
16 | ␊ |
17 | static inline uint64_t rdtsc64(void)␊ |
18 | {␊ |
19 | ␉uint64_t ret;␊ |
20 | ␉__asm__ volatile("rdtsc" : "=A" (ret));␊ |
21 | ␉return ret;␊ |
22 | }␊ |
23 | ␊ |
24 | static inline uint64_t rdmsr64(uint32_t msr)␊ |
25 | {␊ |
26 | uint64_t ret;␊ |
27 | __asm__ volatile("rdmsr" : "=A" (ret) : "c" (msr));␊ |
28 | return ret;␊ |
29 | }␊ |
30 | ␊ |
31 | static inline void wrmsr64(uint32_t msr, uint64_t val)␊ |
32 | {␊ |
33 | ␉__asm__ volatile("wrmsr" : : "c" (msr), "A" (val));␊ |
34 | }␊ |
35 | ␊ |
36 | static inline void intel_waitforsts(void) {␊ |
37 | ␉uint32_t inline_timeout = 100000;␊ |
38 | ␉while (rdmsr64(MSR_IA32_PERF_STATUS) & (1 << 21)) { if (!inline_timeout--) break; }␊ |
39 | }␊ |
40 | ␊ |
41 | static inline void do_cpuid(uint32_t selector, uint32_t *data)␊ |
42 | {␊ |
43 | ␉asm volatile ("cpuid"␊ |
44 | ␉␉␉␉ : "=a" (data[0]),␊ |
45 | ␉␉␉␉ "=b" (data[1]),␊ |
46 | ␉␉␉␉ "=c" (data[2]),␊ |
47 | ␉␉␉␉ "=d" (data[3])␊ |
48 | ␉␉␉␉ : "a" (selector));␊ |
49 | }␊ |
50 | ␊ |
51 | static inline void do_cpuid2(uint32_t selector, uint32_t selector2, uint32_t *data)␊ |
52 | {␊ |
53 | ␉asm volatile ("cpuid"␊ |
54 | ␉␉␉␉ : "=a" (data[0]),␊ |
55 | ␉␉␉␉ "=b" (data[1]),␊ |
56 | ␉␉␉␉ "=c" (data[2]),␊ |
57 | ␉␉␉␉ "=d" (data[3])␊ |
58 | ␉␉␉␉ : "a" (selector), "c" (selector2));␊ |
59 | }␊ |
60 | ␊ |
61 | // DFE: enable_PIT2 and disable_PIT2 come from older xnu␊ |
62 | ␊ |
63 | /*␊ |
64 | * Enable or disable timer 2.␊ |
65 | * Port 0x61 controls timer 2:␊ |
66 | * bit 0 gates the clock,␊ |
67 | * bit 1 gates output to speaker.␊ |
68 | */␊ |
69 | static inline void enable_PIT2(void)␊ |
70 | {␊ |
71 | /* Enable gate, disable speaker */␊ |
72 | __asm__ volatile(␊ |
73 | ␉␉␉␉␉ " inb $0x61,%%al \n\t"␊ |
74 | ␉␉␉␉␉ " and $0xFC,%%al \n\t" /* & ~0x03 */␊ |
75 | ␉␉␉␉␉ " or $1,%%al \n\t"␊ |
76 | ␉␉␉␉␉ " outb %%al,$0x61 \n\t"␊ |
77 | ␉␉␉␉␉ : : : "%al" );␊ |
78 | }␊ |
79 | ␊ |
80 | static inline void disable_PIT2(void)␊ |
81 | {␊ |
82 | /* Disable gate and output to speaker */␊ |
83 | __asm__ volatile(␊ |
84 | ␉␉␉␉␉ " inb $0x61,%%al \n\t"␊ |
85 | ␉␉␉␉␉ " and $0xFC,%%al \n\t"␉/* & ~0x03 */␊ |
86 | ␉␉␉␉␉ " outb %%al,$0x61 \n\t"␊ |
87 | ␉␉␉␉␉ : : : "%al" );␊ |
88 | }␊ |
89 | ␊ |
90 | // DFE: set_PIT2_mode0, poll_PIT2_gate, and measure_tsc_frequency are␊ |
91 | // roughly based on Linux code␊ |
92 | ␊ |
93 | /* Set the 8254 channel 2 to mode 0 with the specified value.␊ |
94 | In mode 0, the counter will initially set its gate low when the␊ |
95 | timer expires. For this to be useful, you ought to set it high␊ |
96 | before calling this function. The enable_PIT2 function does this.␊ |
97 | */␊ |
98 | static inline void set_PIT2_mode0(uint16_t value)␊ |
99 | {␊ |
100 | __asm__ volatile(␊ |
101 | ␉␉␉␉␉ " movb $0xB0,%%al \n\t"␊ |
102 | ␉␉␉␉␉ " outb␉%%al,$0x43␉\n\t"␊ |
103 | ␉␉␉␉␉ " movb␉%%dl,%%al␉\n\t"␊ |
104 | ␉␉␉␉␉ " outb␉%%al,$0x42␉\n\t"␊ |
105 | ␉␉␉␉␉ " movb␉%%dh,%%al␉\n\t"␊ |
106 | ␉␉␉␉␉ " outb␉%%al,$0x42"␊ |
107 | ␉␉␉␉␉ : : "d"(value) /*: no clobber */ );␊ |
108 | }␊ |
109 | ␊ |
110 | /* Returns the number of times the loop ran before the PIT2 signaled */␊ |
111 | static inline unsigned long poll_PIT2_gate(void)␊ |
112 | {␊ |
113 | unsigned long count = 0;␊ |
114 | unsigned char nmi_sc_val;␊ |
115 | do {␊ |
116 | ++count;␊ |
117 | __asm__ volatile(␊ |
118 | ␉␉␉␉␉␉ "inb␉$0x61,%0"␊ |
119 | ␉␉␉␉␉␉ : "=a"(nmi_sc_val) /*:*/ /* no input */ /*:*/ /* no clobber */);␊ |
120 | } while( (nmi_sc_val & 0x20) == 0);␊ |
121 | return count;␊ |
122 | }␊ |
123 | ␊ |
124 | inline static void␊ |
125 | set_PIT2(int value)␊ |
126 | {␊ |
127 | /*␊ |
128 | * First, tell the clock we are going to write 16 bits to the counter␊ |
129 | * and enable one-shot mode (command 0xB8 to port 0x43)␊ |
130 | * Then write the two bytes into the PIT2 clock register (port 0x42).␊ |
131 | * Loop until the value is "realized" in the clock,␊ |
132 | * this happens on the next tick.␊ |
133 | */␊ |
134 | asm volatile(␊ |
135 | " movb $0xB8,%%al \n\t"␊ |
136 | " outb %%al,$0x43 \n\t"␊ |
137 | " movb %%dl,%%al \n\t"␊ |
138 | " outb %%al,$0x42 \n\t"␊ |
139 | " movb %%dh,%%al \n\t"␊ |
140 | " outb %%al,$0x42 \n"␊ |
141 | "1: inb $0x42,%%al \n\t" ␊ |
142 | " inb $0x42,%%al \n\t"␊ |
143 | " cmp %%al,%%dh \n\t"␊ |
144 | " jne 1b"␊ |
145 | : : "d"(value) : "%al");␊ |
146 | }␊ |
147 | ␊ |
148 | ␊ |
149 | inline static uint64_t␊ |
150 | get_PIT2(unsigned int *value)␊ |
151 | {␊ |
152 | register uint64_t result;␊ |
153 | /*␊ |
154 | * This routine first latches the time (command 0x80 to port 0x43),␊ |
155 | * then gets the time stamp so we know how long the read will take later.␊ |
156 | * Read (from port 0x42) and return the current value of the timer.␊ |
157 | */␊ |
158 | #ifdef __i386__␊ |
159 | asm volatile(␊ |
160 | " xorl %%ecx,%%ecx \n\t"␊ |
161 | " movb $0x80,%%al \n\t"␊ |
162 | " outb %%al,$0x43 \n\t"␊ |
163 | " rdtsc \n\t"␊ |
164 | " pushl %%eax \n\t"␊ |
165 | " inb $0x42,%%al \n\t"␊ |
166 | " movb %%al,%%cl \n\t"␊ |
167 | " inb $0x42,%%al \n\t"␊ |
168 | " movb %%al,%%ch \n\t"␊ |
169 | " popl %%eax "␊ |
170 | : "=A"(result), "=c"(*value));␊ |
171 | #else /* __x86_64__ */␊ |
172 | asm volatile(␊ |
173 | ␉␉" xorq %%rcx,%%rcx \n\t"␊ |
174 | ␉␉" movb $0x80,%%al \n\t"␊ |
175 | ␉␉" outb %%al,$0x43 \n\t"␊ |
176 | ␉␉" rdtsc \n\t"␊ |
177 | ␉␉" pushq %%rax \n\t"␊ |
178 | ␉␉" inb $0x42,%%al \n\t"␊ |
179 | ␉␉" movb %%al,%%cl \n\t"␊ |
180 | ␉␉" inb $0x42,%%al \n\t"␊ |
181 | ␉␉" movb %%al,%%ch \n\t"␊ |
182 | ␉␉" popq %%rax "␊ |
183 | ␉␉: "=A"(result), "=c"(*value));␊ |
184 | #endif␊ |
185 | ␊ |
186 | return result;␊ |
187 | }␊ |
188 | ␊ |
189 | #endif /* !__LIBSAIO_CPU_H */␊ |
190 | |