diff --git a/src/arch/x86/timestamp.c b/src/arch/x86/timestamp.c index 9df505a570..d308146070 100644 --- a/src/arch/x86/timestamp.c +++ b/src/arch/x86/timestamp.c @@ -24,3 +24,16 @@ uint64_t timestamp_get(void) { return rdtscll(); } + +unsigned long __attribute__((weak)) tsc_freq_mhz(void) +{ + /* Default to not knowing TSC frequency. cbmem will have to fallback + * on trying to determine it in userspace. */ + return 0; +} + +int timestamp_tick_freq_mhz(void) +{ + /* Chipsets that have a constant TSC provide this value correctly. */ + return tsc_freq_mhz(); +} diff --git a/src/include/cpu/x86/tsc.h b/src/include/cpu/x86/tsc.h index 71d253ba7f..5cf4644328 100644 --- a/src/include/cpu/x86/tsc.h +++ b/src/include/cpu/x86/tsc.h @@ -60,8 +60,7 @@ static inline uint64_t tsc_to_uint64(tsc_t tstamp) } #endif -#if CONFIG_TSC_CONSTANT_RATE +/* Provided by CPU/chipset code for the TSC rate in MHz. */ unsigned long tsc_freq_mhz(void); -#endif #endif /* CPU_X86_TSC_H */ diff --git a/src/include/timestamp.h b/src/include/timestamp.h index 54d69ce132..be33b0ad42 100644 --- a/src/include/timestamp.h +++ b/src/include/timestamp.h @@ -29,7 +29,8 @@ struct timestamp_entry { struct timestamp_table { uint64_t base_time; - uint32_t max_entries; + uint16_t max_entries; + uint16_t tick_freq_mhz; uint32_t num_entries; struct timestamp_entry entries[0]; /* Variable number of entries */ } __attribute__((packed)); @@ -114,5 +115,7 @@ void timestamp_add_now(enum timestamp_id id); /* Implemented by the architecture code */ uint64_t timestamp_get(void); uint64_t get_initial_timestamp(void); +/* Returns timestamp tick frequency in MHz. */ +int timestamp_tick_freq_mhz(void); #endif diff --git a/src/lib/timestamp.c b/src/lib/timestamp.c index ca25093423..21b3d29a53 100644 --- a/src/lib/timestamp.c +++ b/src/lib/timestamp.c @@ -278,6 +278,10 @@ static void timestamp_sync_cache_to_cbmem(int is_recovery) if (ts_cbmem_table->base_time == 0) ts_cbmem_table->base_time = ts_cache_table->base_time; + /* Seed the timestamp tick frequency in ramstage. */ + if (ENV_RAMSTAGE) + ts_cbmem_table->tick_freq_mhz = timestamp_tick_freq_mhz(); + /* Cache no longer required. */ ts_cache_table->num_entries = 0; ts_cache->cache_state = TIMESTAMP_CACHE_NOT_NEEDED; @@ -299,3 +303,9 @@ uint64_t __attribute__((weak)) timestamp_get(void) return mono_time_diff_microseconds(&t1, &t2); } + +/* Like timestamp_get() above this matches up with microsecond granularity. */ +int __attribute__((weak)) timestamp_tick_freq_mhz(void) +{ + return 1; +} diff --git a/util/cbmem/cbmem.c b/util/cbmem/cbmem.c index 6526384eed..0e2f125080 100644 --- a/util/cbmem/cbmem.c +++ b/util/cbmem/cbmem.c @@ -304,7 +304,7 @@ static int parse_cbtable(u64 address, size_t table_size) * read CPU frequency from a sysfs file, return an frequency in Kilohertz as * an int or exit on any error. */ -static u64 get_cpu_freq_KHz(void) +static unsigned long arch_tick_frequency(void) { FILE *cpuf; char freqs[100]; @@ -338,45 +338,50 @@ static u64 get_cpu_freq_KHz(void) freqs, freq_file); exit(1); } - -/* On x86 platforms timestamps are stored - * in CPU cycles (from rdtsc). Hence the - * timestamp divider is the CPU frequency - * in MHz. - */ -u64 arch_convert_raw_ts_entry(u64 ts) -{ - static u64 cpu_freq_mhz = 0; - - if (!cpu_freq_mhz) - cpu_freq_mhz = get_cpu_freq_KHz() / 1000; - - return ts / cpu_freq_mhz; -} - #elif defined(__OpenBSD__) && (defined(__i386__) || defined(__x86_64__)) -u64 arch_convert_raw_ts_entry(u64 ts) +static unsigned long arch_tick_frequency(void) { int mib[2] = { CTL_HW, HW_CPUSPEED }; static int value = 0; size_t value_len = sizeof(value); + /* Return 1 MHz when sysctl fails. */ if ((value == 0) && (sysctl(mib, 2, &value, &value_len, NULL, 0) == -1)) - return ts; + return 1; - return ts / value; + return value; } #else - -/* On non-x86 platforms the timestamp entries - * are not in clock cycles but in usecs - */ -u64 arch_convert_raw_ts_entry(u64 ts) +static unsigned long arch_tick_frequency(void) { - return ts; + /* 1 MHz = 1us. */ + return 1; } #endif +static unsigned long tick_freq_mhz; + +static void timestamp_set_tick_freq(unsigned long table_tick_freq_mhz) +{ + tick_freq_mhz = table_tick_freq_mhz; + + /* Honor table frequency. */ + if (tick_freq_mhz) + return; + + tick_freq_mhz = arch_tick_frequency(); + + if (!tick_freq_mhz) { + fprintf(stderr, "Cannot determine timestamp tick frequency.\n"); + exit(1); + } +} + +u64 arch_convert_raw_ts_entry(u64 ts) +{ + return ts / tick_freq_mhz; +} + /* * Print an integer in 'normalized' form - with commas separating every three * decimal orders. @@ -521,6 +526,8 @@ static void dump_timestamps(void) size = sizeof(*tst_p); tst_p = map_memory_size((unsigned long)timestamps.cbmem_addr, size); + timestamp_set_tick_freq(tst_p->tick_freq_mhz); + printf("%d entries total:\n\n", tst_p->num_entries); size += tst_p->num_entries * sizeof(tst_p->entries[0]);