Introduce the new timer subsystem.
[people/mdeck/gpxe.git] / src / arch / i386 / drivers / timer_rtdsc.c
1
2 #include <gpxe/init.h>
3 #include <gpxe/timer.h>
4 #include <stdio.h>
5 #include <bits/cpu.h>
6 #include <bits/timer2.h>
7 #include <io.h>
8
9
10 #define rdtsc(low,high) \
11      __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
12
13 #define rdtscll(val) \
14      __asm__ __volatile__ ("rdtsc" : "=A" (val))
15
16 static unsigned long long calibrate_tsc(void)
17 {
18         uint32_t startlow, starthigh;
19         uint32_t endlow, endhigh;
20
21         rdtsc(startlow,starthigh);
22         i386_timer2_udelay(USECS_IN_MSEC/2);
23         rdtsc(endlow,endhigh);
24
25         /* 64-bit subtract - gcc just messes up with long longs */
26         /* XXX ORLY? Check it. */
27         __asm__("subl %2,%0\n\t"
28                 "sbbl %3,%1"
29                 :"=a" (endlow), "=d" (endhigh)
30                 :"g" (startlow), "g" (starthigh),
31                 "0" (endlow), "1" (endhigh));
32
33         /* Error: ECPUTOOFAST */
34         if (endhigh)
35                 goto bad_ctc;
36
37         endlow *= MSECS_IN_SEC*2;
38         return endlow;
39
40         /*
41          * The CTC wasn't reliable: we got a hit on the very first read,
42          * or the CPU was so fast/slow that the quotient wouldn't fit in
43          * 32 bits..
44          */
45 bad_ctc:
46         return 0;
47 }
48 static uint32_t clocks_per_second = 0;
49
50 static tick_t rtdsc_currticks(void)
51 {
52         uint32_t clocks_high, clocks_low;
53         uint32_t currticks;
54
55         /* Read the Time Stamp Counter */
56         rdtsc(clocks_low, clocks_high);
57
58         /* currticks = clocks / clocks_per_tick; */
59         __asm__("divl %1"
60                 :"=a" (currticks)
61                 :"r" (clocks_per_second/USECS_IN_SEC), "0" (clocks_low), "d" (clocks_high));
62
63         return currticks;
64 }
65
66 static int rtdsc_ts_init(void)
67 {
68
69         struct cpuinfo_x86 cpu_info;
70
71         get_cpuinfo(&cpu_info);
72         if (cpu_info.features & X86_FEATURE_TSC) {
73                 clocks_per_second = calibrate_tsc();
74                 if (clocks_per_second) {
75                         DBG("RTDSC Ticksource installed. CPU running at %ld Mhz\n",
76                                 clocks_per_second/(1000*1000));
77                         return 0;
78                 }
79         }
80
81         printf("RTDSC timer not available on this machine.\n");
82         return 1;
83 }
84
85 struct timer rtdsc_ts __timer (01) = {
86         .init = rtdsc_ts_init,
87         .udelay = generic_currticks_udelay,
88         .currticks = rtdsc_currticks,
89 };
90