/* * (c) 2008-2009 Adam Lackorzynski , * Alexander Warg , * Frank Mehnert * economic rights: Technische Universität Dresden (Germany) * This file is part of TUD:OS and distributed under the terms of the * GNU Lesser General Public License 2.1. * Please see the COPYING-LGPL-2.1 file for details. */ #include #include #include #include #include #include l4_uint32_t l4_scaler_tsc_to_ns; l4_uint32_t l4_scaler_tsc_to_us; l4_uint32_t l4_scaler_ns_to_tsc; l4_uint32_t l4_scaler_tsc_linux; static inline l4_uint32_t muldiv (l4_uint32_t a, l4_uint32_t mul, l4_uint32_t div) { l4_uint32_t dummy; asm ("mull %4 ; divl %3\n\t" :"=a" (a), "=d" (dummy) :"a" (a), "c" (div), "d" (mul)); return a; } /* * Return 2^32 / (tsc clocks per usec) * * Note, l4_tsc_init(L4_TSC_INIT_CALIBRATE) needs to have * I/O ports 0x20, 0x42, 0x43 and 0x61 */ L4_CV l4_uint32_t l4_tsc_init (int constraint, l4_kernel_info_t *kip) { l4_scaler_tsc_linux = 0; if (constraint != L4_TSC_INIT_CALIBRATE) { /* * First, lets try to get the info out of the kernel info page so that * we don't need to do port i/o. If we're unable to get the information * there, measure it ourselves. */ if (kip) { if (kip->frequency_cpu && kip->frequency_cpu < 50000000 /* sanity check*/) { l4_scaler_tsc_linux = muldiv(1U<<30, 4000, kip->frequency_cpu); l4_scaler_ns_to_tsc = muldiv(1U<<27, kip->frequency_cpu, 1000000); /* l4_scaler_ns_to_tsc = (2^32 * Hz) / (32 * 1.000.000.000) */ } else printf("CPU frequency not set in KIP or invalid.\n"); } else printf("No KIP available!\n"); } if ( (l4_scaler_tsc_linux == 0) && (constraint != L4_TSC_INIT_KERNEL)) { const unsigned clock_tick_rate = 1193180; const unsigned calibrate_time = 50000 /*us*/ + 1; const unsigned calibrate_latch = clock_tick_rate / 20; /* 20Hz = 50ms */ // l4_umword_t flags; l4_uint64_t tsc_start, tsc_end; register l4_uint32_t count; /* disable interrupts */ // l4util_flags_save(&flags); // l4util_cli(); /* Set the Gate high, disable speaker */ l4util_out8 ((l4util_in8 (0x61) & ~0x02) | 0x01, 0x61); l4util_out8 (0xb0, 0x43); /* binary, mode 0, LSB/MSB, Ch 2 */ l4util_out8 (calibrate_latch & 0xff, 0x42); /* LSB of count */ l4util_out8 (calibrate_latch >> 8, 0x42); /* MSB of count */ tsc_start = l4_rdtsc (); count = 0; do { count++; } while ((l4util_in8 (0x61) & 0x20) == 0); tsc_end = l4_rdtsc (); /* restore flags */ // l4util_flags_restore(&flags); /* Error: ECTCNEVERSET */ if (count <= 1) goto bad_ctc; /* 64-bit subtract - gcc just messes up with long longs */ tsc_end -= tsc_start; /* Error: ECPUTOOFAST */ if (tsc_end & 0xffffffff00000000LL) goto bad_ctc; /* Error: ECPUTOOSLOW */ if ((tsc_end & 0xffffffffL) <= calibrate_time) goto bad_ctc; l4_scaler_tsc_linux = muldiv(1U<<30, (1U<<2) * calibrate_time, (l4_uint32_t)tsc_end); l4_scaler_ns_to_tsc = muldiv(((1ULL<<32)/1000ULL), (l4_uint32_t)tsc_end, calibrate_time * (1<<5)); } l4_scaler_tsc_to_ns = muldiv(l4_scaler_tsc_linux, 1000, 1<<5); l4_scaler_tsc_to_us = l4_scaler_tsc_linux; return l4_scaler_tsc_linux; /* * The CTC wasn't reliable: we got a hit on the very first read, * or the CPU was so fast/slow that the quotient wouldn't fit in * 32 bits.. */ bad_ctc: return 0; } L4_CV l4_uint32_t l4_get_hz (void) { if (!l4_scaler_tsc_to_ns) return 0; return (l4_uint32_t)(l4_ns_to_tsc(1000000000UL)); }