blob: 03d9b8abdf9ed0ddcd01895fc29f6ab064844d79 [file] [log] [blame]
Vadim Bendebury6f72d692011-09-21 16:12:39 -07001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2011 The ChromiumOS Authors. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
Vadim Bendebury6f72d692011-09-21 16:12:39 -070014 */
15
Aaron Durbin1936f6c2015-07-03 17:04:21 -050016#include <assert.h>
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +030017#include <stddef.h>
Vadim Bendebury6f72d692011-09-21 16:12:39 -070018#include <stdint.h>
19#include <console/console.h>
20#include <cbmem.h>
Aaron Durbin1936f6c2015-07-03 17:04:21 -050021#include <symbols.h>
Aaron Durbin2daadf82015-05-01 16:48:54 -050022#include <timer.h>
Vadim Bendebury6f72d692011-09-21 16:12:39 -070023#include <timestamp.h>
Stefan Reinauerfd4f4132013-06-19 12:25:44 -070024#include <arch/early_variables.h>
Aaron Durbin2daadf82015-05-01 16:48:54 -050025#include <rules.h>
Kyösti Mälkki5a5c8862014-01-26 14:41:54 +020026#include <smp/node.h>
Vadim Bendebury6f72d692011-09-21 16:12:39 -070027
Julius Wernera7d92442014-12-02 20:51:19 -080028#define MAX_TIMESTAMPS 60
Vadim Bendebury6f72d692011-09-21 16:12:39 -070029
Aaron Durbin1936f6c2015-07-03 17:04:21 -050030#define MAX_TIMESTAMP_CACHE 16
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +030031
Aaron Durbin1936f6c2015-07-03 17:04:21 -050032struct __attribute__((__packed__)) timestamp_cache {
Aaron Durbinbd1499d2015-07-10 01:51:14 -050033 uint32_t cache_state;
Aaron Durbin1936f6c2015-07-03 17:04:21 -050034 struct timestamp_table table;
35 /* The struct timestamp_table has a 0 length array as its last field.
36 * The following 'entries' array serves as the storage space for the
37 * cache. */
38 struct timestamp_entry entries[MAX_TIMESTAMP_CACHE];
39};
Vadim Bendebury6f72d692011-09-21 16:12:39 -070040
Aaron Durbin1936f6c2015-07-03 17:04:21 -050041#if (IS_ENABLED(CONFIG_HAS_PRECBMEM_TIMESTAMP_REGION) && defined(__PRE_RAM__))
42#define USE_TIMESTAMP_REGION 1
43#else
44#define USE_TIMESTAMP_REGION 0
45#endif
46
47/* The cache location will sit in BSS when in ramstage. */
48#define TIMESTAMP_CACHE_IN_BSS ENV_RAMSTAGE
49
50#define HAS_CBMEM (ENV_ROMSTAGE || ENV_RAMSTAGE)
51
52/* Storage of cache entries during ramstage prior to cbmem coming online. */
53static struct timestamp_cache timestamp_cache;
54
55enum {
56 TIMESTAMP_CACHE_UNINITIALIZED = 0,
57 TIMESTAMP_CACHE_INITIALIZED,
58 TIMESTAMP_CACHE_NOT_NEEDED,
59};
60
61static void timestamp_cache_init(struct timestamp_cache *ts_cache,
62 uint64_t base)
63{
64 ts_cache->table.num_entries = 0;
65 ts_cache->table.max_entries = MAX_TIMESTAMP_CACHE;
66 ts_cache->table.base_time = base;
67 ts_cache->cache_state = TIMESTAMP_CACHE_INITIALIZED;
68}
69
70static struct timestamp_cache *timestamp_cache_get(void)
71{
72 struct timestamp_cache *ts_cache = NULL;
73
74 if (TIMESTAMP_CACHE_IN_BSS) {
75 ts_cache = &timestamp_cache;
76 } else if (USE_TIMESTAMP_REGION) {
77 if (_timestamp_size < sizeof(*ts_cache))
78 BUG();
79 ts_cache = car_get_var_ptr((void *)_timestamp);
80 }
81
82 if (ts_cache && ts_cache->cache_state == TIMESTAMP_CACHE_UNINITIALIZED)
83 timestamp_cache_init(ts_cache, 0);
84
85 return ts_cache;
86}
87
88static struct timestamp_table *timestamp_alloc_cbmem_table(void)
Vadim Bendebury6f72d692011-09-21 16:12:39 -070089{
90 struct timestamp_table* tst;
91
92 tst = cbmem_add(CBMEM_ID_TIMESTAMP,
93 sizeof(struct timestamp_table) +
94 MAX_TIMESTAMPS * sizeof(struct timestamp_entry));
95
Kyösti Mälkkie0344172015-05-26 06:23:02 +030096 if (!tst)
Aaron Durbin1936f6c2015-07-03 17:04:21 -050097 return NULL;
Vadim Bendebury6f72d692011-09-21 16:12:39 -070098
Aaron Durbin1936f6c2015-07-03 17:04:21 -050099 tst->base_time = 0;
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700100 tst->max_entries = MAX_TIMESTAMPS;
101 tst->num_entries = 0;
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300102
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500103 return tst;
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700104}
105
Aaron Durbin2daadf82015-05-01 16:48:54 -0500106/* Determine if one should proceed into timestamp code. This is for protecting
107 * systems that have multiple processors running in romstage -- namely AMD
108 * based x86 platforms. */
109static int timestamp_should_run(void)
110{
111 /* Only check boot_cpu() in other stages than ramstage on x86. */
112 if ((!ENV_RAMSTAGE && IS_ENABLED(CONFIG_ARCH_X86)) && !boot_cpu())
113 return 0;
114
115 return 1;
116}
117
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500118static struct timestamp_table *timestamp_table_get(void)
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700119{
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500120 MAYBE_STATIC struct timestamp_table *ts_table = NULL;
121 struct timestamp_cache *ts_cache;
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300122
Aaron Durbin2daadf82015-05-01 16:48:54 -0500123 if (!timestamp_should_run())
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500124 return NULL;
Kyösti Mälkkif56ff902013-09-08 13:10:28 +0300125
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500126 if (ts_table != NULL)
127 return ts_table;
128
129 ts_cache = timestamp_cache_get();
130
131 if (ts_cache == NULL) {
132 if (HAS_CBMEM)
133 ts_table = cbmem_find(CBMEM_ID_TIMESTAMP);
134 return ts_table;
135 }
136
137 /* Cache is required. */
138 if (ts_cache->cache_state != TIMESTAMP_CACHE_NOT_NEEDED)
139 return &ts_cache->table;
140
141 /* Cache shouldn't be used but there's no backing store. */
142 if (!HAS_CBMEM)
143 return NULL;
144
145 ts_table = cbmem_find(CBMEM_ID_TIMESTAMP);
146
147 return ts_table;
148}
149
150static void timestamp_add_table_entry(struct timestamp_table *ts_table,
151 enum timestamp_id id, uint64_t ts_time)
152{
153 struct timestamp_entry *tse;
154
Ben Gardnerf5fd4c92015-11-20 13:25:25 -0600155 if (ts_table->num_entries >= ts_table->max_entries)
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300156 return;
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700157
158 tse = &ts_table->entries[ts_table->num_entries++];
159 tse->entry_id = id;
Stefan Reinauer3a6550d2013-08-01 13:31:44 -0700160 tse->entry_stamp = ts_time - ts_table->base_time;
Ben Gardnerf5fd4c92015-11-20 13:25:25 -0600161
162 if (ts_table->num_entries == ts_table->max_entries)
163 printk(BIOS_ERR, "ERROR: Timestamp table full\n");
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700164}
165
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500166void timestamp_add(enum timestamp_id id, uint64_t ts_time)
167{
168 struct timestamp_table *ts_table;
169
170 ts_table = timestamp_table_get();
171
172 if (!ts_table) {
173 printk(BIOS_ERR, "ERROR: No timestamp table found\n");
174 return;
175 }
176
177 timestamp_add_table_entry(ts_table, id, ts_time);
178}
179
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700180void timestamp_add_now(enum timestamp_id id)
181{
Stefan Reinauer3a6550d2013-08-01 13:31:44 -0700182 timestamp_add(id, timestamp_get());
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700183}
Stefan Reinauer4221a192012-10-15 15:23:20 -0700184
Stefan Reinauer3a6550d2013-08-01 13:31:44 -0700185void timestamp_init(uint64_t base)
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300186{
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500187 struct timestamp_cache *ts_cache;
188
Aaron Durbin2daadf82015-05-01 16:48:54 -0500189 if (!timestamp_should_run())
Kyösti Mälkkif56ff902013-09-08 13:10:28 +0300190 return;
191
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500192 ts_cache = timestamp_cache_get();
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300193
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500194 if (!ts_cache) {
195 printk(BIOS_ERR, "ERROR: No timestamp cache to init\n");
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300196 return;
197 }
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300198
Aaron Durbinbd1499d2015-07-10 01:51:14 -0500199 /* In the EARLY_CBMEM_INIT case timestamps could have already been
200 * recovered. In those circumstances honor the cache which sits in BSS
201 * as it has already been initialized. */
Aaron Durbin2a983bd2015-07-11 13:36:01 -0500202 if (ENV_RAMSTAGE && IS_ENABLED(CONFIG_EARLY_CBMEM_INIT) &&
Aaron Durbinbd1499d2015-07-10 01:51:14 -0500203 ts_cache->cache_state != TIMESTAMP_CACHE_UNINITIALIZED)
204 return;
205
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500206 timestamp_cache_init(ts_cache, base);
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300207}
208
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500209static void timestamp_sync_cache_to_cbmem(int is_recovery)
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300210{
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500211 uint32_t i;
212 struct timestamp_cache *ts_cache;
213 struct timestamp_table *ts_cache_table;
214 struct timestamp_table *ts_cbmem_table = NULL;
215
Aaron Durbin2daadf82015-05-01 16:48:54 -0500216 if (!timestamp_should_run())
Kyösti Mälkkif56ff902013-09-08 13:10:28 +0300217 return;
218
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500219 ts_cache = timestamp_cache_get();
220
221 /* No timestamp cache found */
222 if (ts_cache == NULL) {
223 printk(BIOS_ERR, "ERROR: No timestamp cache found\n");
224 return;
225 }
226
227 ts_cache_table = &ts_cache->table;
228
229 /* cbmem is being recovered. */
230 if (is_recovery) {
231 /* x86 resume path expects timestamps to be reset. */
232 if (IS_ENABLED(CONFIG_ARCH_ROMSTAGE_X86_32) && ENV_ROMSTAGE)
233 ts_cbmem_table = timestamp_alloc_cbmem_table();
234 else {
235 /* Find existing table in cbmem. */
236 ts_cbmem_table = cbmem_find(CBMEM_ID_TIMESTAMP);
237 /* No existing timestamp table. */
238 if (ts_cbmem_table == NULL)
239 ts_cbmem_table = timestamp_alloc_cbmem_table();
240 }
241 } else
242 /* First time sync. Add new table. */
243 ts_cbmem_table = timestamp_alloc_cbmem_table();
244
245 if (ts_cbmem_table == NULL) {
246 printk(BIOS_ERR, "ERROR: No timestamp table allocated\n");
247 return;
248 }
249
250 /*
251 * There's no need to worry about the base_time fields being out of
252 * sync because the following configurations are used/supported:
253 *
254 * 1. CONFIG_HAS_PRECBMEM_TIMESTAMP_REGION is enabled. This
255 * implies CONFIG_EARLY_CBMEM_INIT so once cbmem comes
256 * online we sync the timestamps to the cbmem storage while
257 * running in romstage. In ramstage the cbmem area is
258 * recovered and utilized.
259 *
260 * 2. CONFIG_LATE_CBMEM_INIT (!CONFIG_EARLY_CBMEM_INIT) is
261 * being used. That means the only cache that exists is
262 * in ramstage. Once cbmem comes online in ramstage those
263 * values are sync'd over.
264 *
265 * Any other combinations will result in inconsistent base_time
266 * values including bizarre timestamp values.
267 */
268 for (i = 0; i < ts_cache_table->num_entries; i++) {
269 struct timestamp_entry *tse = &ts_cache_table->entries[i];
270 timestamp_add_table_entry(ts_cbmem_table, tse->entry_id,
271 tse->entry_stamp);
272 }
273
274 /* Freshly added cbmem table has base_time 0. Inherit cache base_time */
275 if (ts_cbmem_table->base_time == 0)
276 ts_cbmem_table->base_time = ts_cache_table->base_time;
277
Aaron Durbinc49014e2015-08-30 21:19:55 -0500278 /* Seed the timestamp tick frequency in ramstage. */
279 if (ENV_RAMSTAGE)
280 ts_cbmem_table->tick_freq_mhz = timestamp_tick_freq_mhz();
281
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500282 /* Cache no longer required. */
283 ts_cache_table->num_entries = 0;
284 ts_cache->cache_state = TIMESTAMP_CACHE_NOT_NEEDED;
Kyösti Mälkki3d45c402013-09-07 20:26:36 +0300285}
Kyösti Mälkkicbf5bdf2013-09-10 00:07:21 +0300286
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500287ROMSTAGE_CBMEM_INIT_HOOK(timestamp_sync_cache_to_cbmem)
288RAMSTAGE_CBMEM_INIT_HOOK(timestamp_sync_cache_to_cbmem)
Aaron Durbin9e80e272015-05-01 16:48:54 -0500289
290/* Provide default timestamp implementation using monotonic timer. */
291uint64_t __attribute__((weak)) timestamp_get(void)
292{
293 struct mono_time t1, t2;
294
Aaron Durbin7aafe532015-05-07 11:32:30 -0500295 if (!IS_ENABLED(CONFIG_HAVE_MONOTONIC_TIMER))
296 return 0;
297
Aaron Durbin9e80e272015-05-01 16:48:54 -0500298 mono_time_set_usecs(&t1, 0);
299 timer_monotonic_get(&t2);
300
301 return mono_time_diff_microseconds(&t1, &t2);
302}
Aaron Durbinc49014e2015-08-30 21:19:55 -0500303
304/* Like timestamp_get() above this matches up with microsecond granularity. */
305int __attribute__((weak)) timestamp_tick_freq_mhz(void)
306{
307 return 1;
308}