1 /*
2 * (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
3 * Alexander Warg <warg@os.inf.tu-dresden.de>,
4 * Frank Mehnert <fm3@os.inf.tu-dresden.de>
5 * economic rights: Technische Universität Dresden (Germany)
6 * This file is part of TUD:OS and distributed under the terms of the
7 * GNU Lesser General Public License 2.1.
8 * Please see the COPYING-LGPL-2.1 file for details.
9 */
10 #include <l4/sys/types.h>
11 #include <l4/sys/ipc.h>
12 #include <l4/sys/kip.h>
13 #include <l4/util/port_io.h>
14 #include <l4/util/rdtsc.h>
15 #include <stdio.h>
16
17 l4_uint32_t l4_scaler_tsc_to_ns;
18 l4_uint32_t l4_scaler_tsc_to_us;
19 l4_uint32_t l4_scaler_ns_to_tsc;
20 l4_uint32_t l4_scaler_tsc_linux;
21
22
23 static inline l4_uint32_t
muldiv(l4_uint32_t a,l4_uint32_t mul,l4_uint32_t div)24 muldiv (l4_uint32_t a, l4_uint32_t mul, l4_uint32_t div)
25 {
26 l4_uint32_t dummy;
27 asm ("mull %4 ; divl %3\n\t"
28 :"=a" (a), "=d" (dummy)
29 :"a" (a), "c" (div), "d" (mul));
30 return a;
31 }
32
33
34 /*
35 * Return 2^32 / (tsc clocks per usec)
36 *
37 * Note, l4_tsc_init(L4_TSC_INIT_CALIBRATE) needs to have
38 * I/O ports 0x20, 0x42, 0x43 and 0x61
39 */
40 L4_CV l4_uint32_t
l4_tsc_init(int constraint,l4_kernel_info_t * kip)41 l4_tsc_init (int constraint, l4_kernel_info_t *kip)
42 {
43 l4_scaler_tsc_linux = 0;
44
45 if (constraint != L4_TSC_INIT_CALIBRATE)
46 {
47 /*
48 * First, lets try to get the info out of the kernel info page so that
49 * we don't need to do port i/o. If we're unable to get the information
50 * there, measure it ourselves.
51 */
52 if (kip)
53 {
54 if (kip->frequency_cpu
55 && kip->frequency_cpu < 50000000 /* sanity check*/)
56 {
57 l4_scaler_tsc_linux = muldiv(1U<<30, 4000, kip->frequency_cpu);
58 l4_scaler_ns_to_tsc = muldiv(1U<<27, kip->frequency_cpu, 1000000);
59
60 /* l4_scaler_ns_to_tsc = (2^32 * Hz) / (32 * 1.000.000.000) */
61 }
62 else
63 printf("CPU frequency not set in KIP or invalid.\n");
64 }
65 else
66 printf("No KIP available!\n");
67 }
68 if ( (l4_scaler_tsc_linux == 0)
69 && (constraint != L4_TSC_INIT_KERNEL))
70 {
71 const unsigned clock_tick_rate = 1193180;
72 const unsigned calibrate_time = 50000 /*us*/ + 1;
73 const unsigned calibrate_latch = clock_tick_rate / 20; /* 20Hz = 50ms */
74
75 // l4_umword_t flags;
76 l4_uint64_t tsc_start, tsc_end;
77 register l4_uint32_t count;
78
79 /* disable interrupts */
80 // l4util_flags_save(&flags);
81 // l4util_cli();
82
83 /* Set the Gate high, disable speaker */
84 l4util_out8 ((l4util_in8 (0x61) & ~0x02) | 0x01, 0x61);
85
86 l4util_out8 (0xb0, 0x43); /* binary, mode 0, LSB/MSB, Ch 2 */
87 l4util_out8 (calibrate_latch & 0xff, 0x42); /* LSB of count */
88 l4util_out8 (calibrate_latch >> 8, 0x42); /* MSB of count */
89
90 tsc_start = l4_rdtsc ();
91 count = 0;
92 do
93 {
94 count++;
95 }
96 while ((l4util_in8 (0x61) & 0x20) == 0);
97 tsc_end = l4_rdtsc ();
98
99 /* restore flags */
100 // l4util_flags_restore(&flags);
101
102 /* Error: ECTCNEVERSET */
103 if (count <= 1)
104 goto bad_ctc;
105
106 /* 64-bit subtract - gcc just messes up with long longs */
107 tsc_end -= tsc_start;
108
109 /* Error: ECPUTOOFAST */
110 if (tsc_end & 0xffffffff00000000LL)
111 goto bad_ctc;
112
113 /* Error: ECPUTOOSLOW */
114 if ((tsc_end & 0xffffffffL) <= calibrate_time)
115 goto bad_ctc;
116
117 l4_scaler_tsc_linux = muldiv(1U<<30, (1U<<2) * calibrate_time, (l4_uint32_t)tsc_end);
118 l4_scaler_ns_to_tsc = muldiv(((1ULL<<32)/1000ULL), (l4_uint32_t)tsc_end,
119 calibrate_time * (1<<5));
120 }
121
122 l4_scaler_tsc_to_ns = muldiv(l4_scaler_tsc_linux, 1000, 1<<5);
123 l4_scaler_tsc_to_us = l4_scaler_tsc_linux;
124
125 return l4_scaler_tsc_linux;
126
127 /*
128 * The CTC wasn't reliable: we got a hit on the very first read,
129 * or the CPU was so fast/slow that the quotient wouldn't fit in
130 * 32 bits..
131 */
132 bad_ctc:
133 return 0;
134 }
135
136 L4_CV l4_uint32_t
l4_get_hz(void)137 l4_get_hz (void)
138 {
139 if (!l4_scaler_tsc_to_ns)
140 return 0;
141
142 return (l4_uint32_t)(l4_ns_to_tsc(1000000000UL));
143 }
144
145