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