c49014e750
Add the timestamp tick frequency within the timestamp table so the cbmem utility doesn't try to figure it out on its own. Those paths still exist for x86 systems which don't provide tsc_freq_mhz(). All other non-x86 systems use the monotonic timer which has a 1us granularity or 1MHz. One of the main reasons is that Linux is reporting /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq as the true turbo frequency on turbo enables machines. This change also fixes the p-state values honored in cpufreq for turbo machines in that turbo p-pstates were reported as 100MHz greater than nominal. BUG=chrome-os-partner:44669 BRANCH=firmware-strago-7287.B TEST=Built and booted on glados. Confirmed table frequency honored. Change-Id: I763fe2d9a7b01d0ef5556e5abff36032062f5801 Signed-off-by: Aaron Durbin <adurbin@chromium.org> Reviewed-on: http://review.coreboot.org/11470 Tested-by: build bot (Jenkins) Reviewed-by: Patrick Georgi <pgeorgi@google.com>
311 lines
8.2 KiB
C
311 lines
8.2 KiB
C
/*
|
|
* This file is part of the coreboot project.
|
|
*
|
|
* Copyright (C) 2011 The ChromiumOS Authors. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; version 2 of the License.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <console/console.h>
|
|
#include <cbmem.h>
|
|
#include <symbols.h>
|
|
#include <timer.h>
|
|
#include <timestamp.h>
|
|
#include <arch/early_variables.h>
|
|
#include <rules.h>
|
|
#include <smp/node.h>
|
|
|
|
#define MAX_TIMESTAMPS 60
|
|
|
|
#define MAX_TIMESTAMP_CACHE 16
|
|
|
|
struct __attribute__((__packed__)) timestamp_cache {
|
|
uint32_t cache_state;
|
|
struct timestamp_table table;
|
|
/* The struct timestamp_table has a 0 length array as its last field.
|
|
* The following 'entries' array serves as the storage space for the
|
|
* cache. */
|
|
struct timestamp_entry entries[MAX_TIMESTAMP_CACHE];
|
|
};
|
|
|
|
#if (IS_ENABLED(CONFIG_HAS_PRECBMEM_TIMESTAMP_REGION) && defined(__PRE_RAM__))
|
|
#define USE_TIMESTAMP_REGION 1
|
|
#else
|
|
#define USE_TIMESTAMP_REGION 0
|
|
#endif
|
|
|
|
/* The cache location will sit in BSS when in ramstage. */
|
|
#define TIMESTAMP_CACHE_IN_BSS ENV_RAMSTAGE
|
|
|
|
#define HAS_CBMEM (ENV_ROMSTAGE || ENV_RAMSTAGE)
|
|
|
|
/* Storage of cache entries during ramstage prior to cbmem coming online. */
|
|
static struct timestamp_cache timestamp_cache;
|
|
|
|
enum {
|
|
TIMESTAMP_CACHE_UNINITIALIZED = 0,
|
|
TIMESTAMP_CACHE_INITIALIZED,
|
|
TIMESTAMP_CACHE_NOT_NEEDED,
|
|
};
|
|
|
|
static void timestamp_cache_init(struct timestamp_cache *ts_cache,
|
|
uint64_t base)
|
|
{
|
|
ts_cache->table.num_entries = 0;
|
|
ts_cache->table.max_entries = MAX_TIMESTAMP_CACHE;
|
|
ts_cache->table.base_time = base;
|
|
ts_cache->cache_state = TIMESTAMP_CACHE_INITIALIZED;
|
|
}
|
|
|
|
static struct timestamp_cache *timestamp_cache_get(void)
|
|
{
|
|
struct timestamp_cache *ts_cache = NULL;
|
|
|
|
if (TIMESTAMP_CACHE_IN_BSS) {
|
|
ts_cache = ×tamp_cache;
|
|
} else if (USE_TIMESTAMP_REGION) {
|
|
if (_timestamp_size < sizeof(*ts_cache))
|
|
BUG();
|
|
ts_cache = car_get_var_ptr((void *)_timestamp);
|
|
}
|
|
|
|
if (ts_cache && ts_cache->cache_state == TIMESTAMP_CACHE_UNINITIALIZED)
|
|
timestamp_cache_init(ts_cache, 0);
|
|
|
|
return ts_cache;
|
|
}
|
|
|
|
static struct timestamp_table *timestamp_alloc_cbmem_table(void)
|
|
{
|
|
struct timestamp_table* tst;
|
|
|
|
tst = cbmem_add(CBMEM_ID_TIMESTAMP,
|
|
sizeof(struct timestamp_table) +
|
|
MAX_TIMESTAMPS * sizeof(struct timestamp_entry));
|
|
|
|
if (!tst)
|
|
return NULL;
|
|
|
|
tst->base_time = 0;
|
|
tst->max_entries = MAX_TIMESTAMPS;
|
|
tst->num_entries = 0;
|
|
|
|
return tst;
|
|
}
|
|
|
|
/* Determine if one should proceed into timestamp code. This is for protecting
|
|
* systems that have multiple processors running in romstage -- namely AMD
|
|
* based x86 platforms. */
|
|
static int timestamp_should_run(void)
|
|
{
|
|
/* Only check boot_cpu() in other stages than ramstage on x86. */
|
|
if ((!ENV_RAMSTAGE && IS_ENABLED(CONFIG_ARCH_X86)) && !boot_cpu())
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static struct timestamp_table *timestamp_table_get(void)
|
|
{
|
|
MAYBE_STATIC struct timestamp_table *ts_table = NULL;
|
|
struct timestamp_cache *ts_cache;
|
|
|
|
if (!timestamp_should_run())
|
|
return NULL;
|
|
|
|
if (ts_table != NULL)
|
|
return ts_table;
|
|
|
|
ts_cache = timestamp_cache_get();
|
|
|
|
if (ts_cache == NULL) {
|
|
if (HAS_CBMEM)
|
|
ts_table = cbmem_find(CBMEM_ID_TIMESTAMP);
|
|
return ts_table;
|
|
}
|
|
|
|
/* Cache is required. */
|
|
if (ts_cache->cache_state != TIMESTAMP_CACHE_NOT_NEEDED)
|
|
return &ts_cache->table;
|
|
|
|
/* Cache shouldn't be used but there's no backing store. */
|
|
if (!HAS_CBMEM)
|
|
return NULL;
|
|
|
|
ts_table = cbmem_find(CBMEM_ID_TIMESTAMP);
|
|
|
|
return ts_table;
|
|
}
|
|
|
|
static void timestamp_add_table_entry(struct timestamp_table *ts_table,
|
|
enum timestamp_id id, uint64_t ts_time)
|
|
{
|
|
struct timestamp_entry *tse;
|
|
|
|
if (ts_table->num_entries == ts_table->max_entries) {
|
|
printk(BIOS_ERR, "ERROR: Timestamp table full\n");
|
|
return;
|
|
}
|
|
|
|
tse = &ts_table->entries[ts_table->num_entries++];
|
|
tse->entry_id = id;
|
|
tse->entry_stamp = ts_time - ts_table->base_time;
|
|
}
|
|
|
|
void timestamp_add(enum timestamp_id id, uint64_t ts_time)
|
|
{
|
|
struct timestamp_table *ts_table;
|
|
|
|
ts_table = timestamp_table_get();
|
|
|
|
if (!ts_table) {
|
|
printk(BIOS_ERR, "ERROR: No timestamp table found\n");
|
|
return;
|
|
}
|
|
|
|
timestamp_add_table_entry(ts_table, id, ts_time);
|
|
}
|
|
|
|
void timestamp_add_now(enum timestamp_id id)
|
|
{
|
|
timestamp_add(id, timestamp_get());
|
|
}
|
|
|
|
void timestamp_init(uint64_t base)
|
|
{
|
|
struct timestamp_cache *ts_cache;
|
|
|
|
if (!timestamp_should_run())
|
|
return;
|
|
|
|
ts_cache = timestamp_cache_get();
|
|
|
|
if (!ts_cache) {
|
|
printk(BIOS_ERR, "ERROR: No timestamp cache to init\n");
|
|
return;
|
|
}
|
|
|
|
/* In the EARLY_CBMEM_INIT case timestamps could have already been
|
|
* recovered. In those circumstances honor the cache which sits in BSS
|
|
* as it has already been initialized. */
|
|
if (ENV_RAMSTAGE && IS_ENABLED(CONFIG_EARLY_CBMEM_INIT) &&
|
|
ts_cache->cache_state != TIMESTAMP_CACHE_UNINITIALIZED)
|
|
return;
|
|
|
|
timestamp_cache_init(ts_cache, base);
|
|
}
|
|
|
|
static void timestamp_sync_cache_to_cbmem(int is_recovery)
|
|
{
|
|
uint32_t i;
|
|
struct timestamp_cache *ts_cache;
|
|
struct timestamp_table *ts_cache_table;
|
|
struct timestamp_table *ts_cbmem_table = NULL;
|
|
|
|
if (!timestamp_should_run())
|
|
return;
|
|
|
|
ts_cache = timestamp_cache_get();
|
|
|
|
/* No timestamp cache found */
|
|
if (ts_cache == NULL) {
|
|
printk(BIOS_ERR, "ERROR: No timestamp cache found\n");
|
|
return;
|
|
}
|
|
|
|
ts_cache_table = &ts_cache->table;
|
|
|
|
/* cbmem is being recovered. */
|
|
if (is_recovery) {
|
|
/* x86 resume path expects timestamps to be reset. */
|
|
if (IS_ENABLED(CONFIG_ARCH_ROMSTAGE_X86_32) && ENV_ROMSTAGE)
|
|
ts_cbmem_table = timestamp_alloc_cbmem_table();
|
|
else {
|
|
/* Find existing table in cbmem. */
|
|
ts_cbmem_table = cbmem_find(CBMEM_ID_TIMESTAMP);
|
|
/* No existing timestamp table. */
|
|
if (ts_cbmem_table == NULL)
|
|
ts_cbmem_table = timestamp_alloc_cbmem_table();
|
|
}
|
|
} else
|
|
/* First time sync. Add new table. */
|
|
ts_cbmem_table = timestamp_alloc_cbmem_table();
|
|
|
|
if (ts_cbmem_table == NULL) {
|
|
printk(BIOS_ERR, "ERROR: No timestamp table allocated\n");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* There's no need to worry about the base_time fields being out of
|
|
* sync because the following configurations are used/supported:
|
|
*
|
|
* 1. CONFIG_HAS_PRECBMEM_TIMESTAMP_REGION is enabled. This
|
|
* implies CONFIG_EARLY_CBMEM_INIT so once cbmem comes
|
|
* online we sync the timestamps to the cbmem storage while
|
|
* running in romstage. In ramstage the cbmem area is
|
|
* recovered and utilized.
|
|
*
|
|
* 2. CONFIG_LATE_CBMEM_INIT (!CONFIG_EARLY_CBMEM_INIT) is
|
|
* being used. That means the only cache that exists is
|
|
* in ramstage. Once cbmem comes online in ramstage those
|
|
* values are sync'd over.
|
|
*
|
|
* Any other combinations will result in inconsistent base_time
|
|
* values including bizarre timestamp values.
|
|
*/
|
|
for (i = 0; i < ts_cache_table->num_entries; i++) {
|
|
struct timestamp_entry *tse = &ts_cache_table->entries[i];
|
|
timestamp_add_table_entry(ts_cbmem_table, tse->entry_id,
|
|
tse->entry_stamp);
|
|
}
|
|
|
|
/* Freshly added cbmem table has base_time 0. Inherit cache base_time */
|
|
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;
|
|
}
|
|
|
|
ROMSTAGE_CBMEM_INIT_HOOK(timestamp_sync_cache_to_cbmem)
|
|
RAMSTAGE_CBMEM_INIT_HOOK(timestamp_sync_cache_to_cbmem)
|
|
|
|
/* Provide default timestamp implementation using monotonic timer. */
|
|
uint64_t __attribute__((weak)) timestamp_get(void)
|
|
{
|
|
struct mono_time t1, t2;
|
|
|
|
if (!IS_ENABLED(CONFIG_HAVE_MONOTONIC_TIMER))
|
|
return 0;
|
|
|
|
mono_time_set_usecs(&t1, 0);
|
|
timer_monotonic_get(&t2);
|
|
|
|
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;
|
|
}
|