blob: 24d80ea89ac37ae3f5ceb5226932cedbd9d84807 [file] [log] [blame]
Angel Pons118a9c72020-04-02 23:48:34 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Vadim Bendebury6f72d692011-09-21 16:12:39 -07002
Aaron Durbin1936f6c2015-07-03 17:04:21 -05003#include <assert.h>
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +03004#include <stddef.h>
Vadim Bendebury6f72d692011-09-21 16:12:39 -07005#include <stdint.h>
6#include <console/console.h>
7#include <cbmem.h>
Aaron Durbin1936f6c2015-07-03 17:04:21 -05008#include <symbols.h>
Aaron Durbin2daadf82015-05-01 16:48:54 -05009#include <timer.h>
Vadim Bendebury6f72d692011-09-21 16:12:39 -070010#include <timestamp.h>
Kyösti Mälkki5a5c8862014-01-26 14:41:54 +020011#include <smp/node.h>
Vadim Bendebury6f72d692011-09-21 16:12:39 -070012
Raul E Rangelb3e02202018-05-11 11:08:08 -060013#define MAX_TIMESTAMPS 192
Vadim Bendebury6f72d692011-09-21 16:12:39 -070014
Julius Werner8c093772016-02-09 16:09:15 -080015DECLARE_OPTIONAL_REGION(timestamp);
16
Kyösti Mälkki8b936892019-09-12 13:45:15 +030017/* This points to the active timestamp_table and can change within a stage
Kyösti Mälkki3dd23a52019-08-22 15:06:50 +030018 as CBMEM comes available. */
Arthur Heymans1a711632019-11-20 20:38:29 +010019static struct timestamp_table *glob_ts_table;
Aaron Durbin1936f6c2015-07-03 17:04:21 -050020
Kyösti Mälkki3c559e72019-09-07 13:25:47 +030021static void timestamp_cache_init(struct timestamp_table *ts_cache,
Aaron Durbin1936f6c2015-07-03 17:04:21 -050022 uint64_t base)
23{
Kyösti Mälkki3c559e72019-09-07 13:25:47 +030024 ts_cache->num_entries = 0;
Kyösti Mälkki3c559e72019-09-07 13:25:47 +030025 ts_cache->base_time = base;
Kyösti Mälkki8b936892019-09-12 13:45:15 +030026 ts_cache->max_entries = (REGION_SIZE(timestamp) -
27 offsetof(struct timestamp_table, entries))
28 / sizeof(struct timestamp_entry);
Aaron Durbin1936f6c2015-07-03 17:04:21 -050029}
30
Kyösti Mälkki3c559e72019-09-07 13:25:47 +030031static struct timestamp_table *timestamp_cache_get(void)
Aaron Durbin1936f6c2015-07-03 17:04:21 -050032{
Kyösti Mälkki3c559e72019-09-07 13:25:47 +030033 struct timestamp_table *ts_cache = NULL;
Aaron Durbin1936f6c2015-07-03 17:04:21 -050034
Kyösti Mälkki8b936892019-09-12 13:45:15 +030035 if (!ENV_ROMSTAGE_OR_BEFORE)
36 return NULL;
37
38 if (REGION_SIZE(timestamp) < sizeof(*ts_cache)) {
39 BUG();
40 } else {
Kyösti Mälkki3dd23a52019-08-22 15:06:50 +030041 ts_cache = (void *)_timestamp;
Aaron Durbin1936f6c2015-07-03 17:04:21 -050042 }
43
Aaron Durbin1936f6c2015-07-03 17:04:21 -050044 return ts_cache;
45}
46
47static struct timestamp_table *timestamp_alloc_cbmem_table(void)
Vadim Bendebury6f72d692011-09-21 16:12:39 -070048{
Lee Leahyb2d834a2017-03-08 16:52:22 -080049 struct timestamp_table *tst;
Vadim Bendebury6f72d692011-09-21 16:12:39 -070050
51 tst = cbmem_add(CBMEM_ID_TIMESTAMP,
52 sizeof(struct timestamp_table) +
53 MAX_TIMESTAMPS * sizeof(struct timestamp_entry));
54
Kyösti Mälkkie0344172015-05-26 06:23:02 +030055 if (!tst)
Aaron Durbin1936f6c2015-07-03 17:04:21 -050056 return NULL;
Vadim Bendebury6f72d692011-09-21 16:12:39 -070057
Aaron Durbin1936f6c2015-07-03 17:04:21 -050058 tst->base_time = 0;
Vadim Bendebury6f72d692011-09-21 16:12:39 -070059 tst->max_entries = MAX_TIMESTAMPS;
60 tst->num_entries = 0;
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +030061
Aaron Durbin1936f6c2015-07-03 17:04:21 -050062 return tst;
Vadim Bendebury6f72d692011-09-21 16:12:39 -070063}
64
Aaron Durbin2daadf82015-05-01 16:48:54 -050065/* Determine if one should proceed into timestamp code. This is for protecting
66 * systems that have multiple processors running in romstage -- namely AMD
67 * based x86 platforms. */
68static int timestamp_should_run(void)
69{
Subrata Banik42c44c22019-05-15 20:27:04 +053070 /*
71 * Only check boot_cpu() in other stages than
72 * ENV_PAYLOAD_LOADER on x86.
73 */
Kyösti Mälkki7336f972020-06-08 06:05:03 +030074 if ((!ENV_PAYLOAD_LOADER && ENV_X86) && !boot_cpu())
Aaron Durbin2daadf82015-05-01 16:48:54 -050075 return 0;
76
77 return 1;
78}
79
Aaron Durbin1936f6c2015-07-03 17:04:21 -050080static struct timestamp_table *timestamp_table_get(void)
Vadim Bendebury6f72d692011-09-21 16:12:39 -070081{
Arthur Heymans1a711632019-11-20 20:38:29 +010082 if (glob_ts_table)
83 return glob_ts_table;
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +030084
Arthur Heymans1a711632019-11-20 20:38:29 +010085 glob_ts_table = timestamp_cache_get();
Aaron Durbin1936f6c2015-07-03 17:04:21 -050086
Arthur Heymans1a711632019-11-20 20:38:29 +010087 return glob_ts_table;
Aaron Durbin1936f6c2015-07-03 17:04:21 -050088}
89
Kyösti Mälkki3dd23a52019-08-22 15:06:50 +030090static void timestamp_table_set(struct timestamp_table *ts)
91{
Arthur Heymans1a711632019-11-20 20:38:29 +010092 glob_ts_table = ts;
Kyösti Mälkki3dd23a52019-08-22 15:06:50 +030093}
94
Martin Rothb22bbe22018-03-07 15:32:16 -070095static const char *timestamp_name(enum timestamp_id id)
96{
97 int i;
98
99 for (i = 0; i < ARRAY_SIZE(timestamp_ids); i++) {
100 if (timestamp_ids[i].id == id)
101 return timestamp_ids[i].name;
102 }
103
104 return "Unknown timestamp ID";
105}
106
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500107static void timestamp_add_table_entry(struct timestamp_table *ts_table,
108 enum timestamp_id id, uint64_t ts_time)
109{
110 struct timestamp_entry *tse;
111
Ben Gardnerf5fd4c92015-11-20 13:25:25 -0600112 if (ts_table->num_entries >= ts_table->max_entries)
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300113 return;
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700114
115 tse = &ts_table->entries[ts_table->num_entries++];
116 tse->entry_id = id;
Kyösti Mälkkid548edd2020-01-09 08:41:46 +0200117 tse->entry_stamp = ts_time;
Ben Gardnerf5fd4c92015-11-20 13:25:25 -0600118
119 if (ts_table->num_entries == ts_table->max_entries)
120 printk(BIOS_ERR, "ERROR: Timestamp table full\n");
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700121}
122
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500123void timestamp_add(enum timestamp_id id, uint64_t ts_time)
124{
125 struct timestamp_table *ts_table;
126
Kyösti Mälkki187e4c42019-02-20 17:38:45 +0200127 if (!timestamp_should_run())
128 return;
129
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500130 ts_table = timestamp_table_get();
131
132 if (!ts_table) {
133 printk(BIOS_ERR, "ERROR: No timestamp table found\n");
134 return;
135 }
136
Kyösti Mälkkid548edd2020-01-09 08:41:46 +0200137 ts_time -= ts_table->base_time;
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500138 timestamp_add_table_entry(ts_table, id, ts_time);
Kyösti Mälkki8b93cb72020-01-09 08:41:46 +0200139
140 if (CONFIG(TIMESTAMPS_ON_CONSOLE))
141 printk(BIOS_INFO, "Timestamp - %s: %llu\n", timestamp_name(id), ts_time);
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500142}
143
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700144void timestamp_add_now(enum timestamp_id id)
145{
Stefan Reinauer3a6550d2013-08-01 13:31:44 -0700146 timestamp_add(id, timestamp_get());
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700147}
Stefan Reinauer4221a192012-10-15 15:23:20 -0700148
Stefan Reinauer3a6550d2013-08-01 13:31:44 -0700149void timestamp_init(uint64_t base)
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300150{
Kyösti Mälkki3c559e72019-09-07 13:25:47 +0300151 struct timestamp_table *ts_cache;
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500152
Kyösti Mälkki3dd23a52019-08-22 15:06:50 +0300153 assert(ENV_ROMSTAGE_OR_BEFORE);
154
Aaron Durbin2daadf82015-05-01 16:48:54 -0500155 if (!timestamp_should_run())
Kyösti Mälkkif56ff902013-09-08 13:10:28 +0300156 return;
157
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500158 ts_cache = timestamp_cache_get();
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300159
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500160 if (!ts_cache) {
161 printk(BIOS_ERR, "ERROR: No timestamp cache to init\n");
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300162 return;
163 }
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300164
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500165 timestamp_cache_init(ts_cache, base);
Kyösti Mälkki3c559e72019-09-07 13:25:47 +0300166 timestamp_table_set(ts_cache);
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300167}
168
Kyösti Mälkki72e634f2019-09-05 18:16:25 +0300169static void timestamp_sync_cache_to_cbmem(struct timestamp_table *ts_cbmem_table)
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300170{
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500171 uint32_t i;
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500172 struct timestamp_table *ts_cache_table;
Kyösti Mälkki3dd23a52019-08-22 15:06:50 +0300173
174 ts_cache_table = timestamp_table_get();
175 if (!ts_cache_table) {
Kyösti Mälkki72e634f2019-09-05 18:16:25 +0300176 printk(BIOS_ERR, "ERROR: No timestamp cache found\n");
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500177 return;
178 }
179
180 /*
181 * There's no need to worry about the base_time fields being out of
Kyösti Mälkki513a1a82018-06-03 12:29:50 +0300182 * sync because only the following configuration is used/supported:
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500183 *
Kyösti Mälkki513a1a82018-06-03 12:29:50 +0300184 * Timestamps get initialized before ramstage, which implies
185 * CBMEM initialization in romstage.
Julius Werner8c093772016-02-09 16:09:15 -0800186 * This requires the board to define a TIMESTAMP() region in its
187 * memlayout.ld (default on x86). The base_time from timestamp_init()
188 * (usually called from bootblock.c on most non-x86 boards) persists
189 * in that region until it gets synced to CBMEM in romstage.
190 * In ramstage, the BSS cache's base_time will be 0 until the second
191 * sync, which will adjust the timestamps in there to the correct
192 * base_time (from CBMEM) with the timestamp_add_table_entry() below.
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500193 *
Julius Werner8c093772016-02-09 16:09:15 -0800194 * If you try to initialize timestamps before ramstage but don't define
195 * a TIMESTAMP region, all operations will fail (safely), and coreboot
Kyösti Mälkkid548edd2020-01-09 08:41:46 +0200196 * will behave as if timestamps collection was disabled.
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500197 */
Kyösti Mälkki3dd23a52019-08-22 15:06:50 +0300198
Kyösti Mälkkid548edd2020-01-09 08:41:46 +0200199 /* Inherit cache base_time. */
200 ts_cbmem_table->base_time = ts_cache_table->base_time;
201
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500202 for (i = 0; i < ts_cache_table->num_entries; i++) {
203 struct timestamp_entry *tse = &ts_cache_table->entries[i];
204 timestamp_add_table_entry(ts_cbmem_table, tse->entry_id,
205 tse->entry_stamp);
206 }
207
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500208 /* Cache no longer required. */
209 ts_cache_table->num_entries = 0;
Kyösti Mälkki3d45c402013-09-07 20:26:36 +0300210}
Kyösti Mälkkicbf5bdf2013-09-10 00:07:21 +0300211
Kyösti Mälkki72e634f2019-09-05 18:16:25 +0300212static void timestamp_reinit(int is_recovery)
213{
214 struct timestamp_table *ts_cbmem_table;
215
216 if (!timestamp_should_run())
217 return;
218
Kyösti Mälkkib6b13c92019-09-12 12:58:50 +0300219 /* First time into romstage we make a clean new table. For platforms that travel
220 through this path on resume, ARCH_X86 S3, timestamps are also reset. */
221 if (ENV_ROMSTAGE) {
Kyösti Mälkki72e634f2019-09-05 18:16:25 +0300222 ts_cbmem_table = timestamp_alloc_cbmem_table();
Kyösti Mälkkib6b13c92019-09-12 12:58:50 +0300223 } else {
224 /* Find existing table in cbmem. */
225 ts_cbmem_table = cbmem_find(CBMEM_ID_TIMESTAMP);
226 }
Kyösti Mälkki72e634f2019-09-05 18:16:25 +0300227
228 if (ts_cbmem_table == NULL) {
229 printk(BIOS_ERR, "ERROR: No timestamp table allocated\n");
230 timestamp_table_set(NULL);
231 return;
232 }
233
234 if (ENV_ROMSTAGE)
235 timestamp_sync_cache_to_cbmem(ts_cbmem_table);
236
237 /* Seed the timestamp tick frequency in ENV_PAYLOAD_LOADER. */
238 if (ENV_PAYLOAD_LOADER)
239 ts_cbmem_table->tick_freq_mhz = timestamp_tick_freq_mhz();
240
241 timestamp_table_set(ts_cbmem_table);
242}
243
Kyösti Mälkki806ea08b2017-07-15 20:42:20 +0300244void timestamp_rescale_table(uint16_t N, uint16_t M)
245{
246 uint32_t i;
247 struct timestamp_table *ts_table;
248
249 if (!timestamp_should_run())
250 return;
251
252 if (N == 0 || M == 0)
253 return;
254
255 ts_table = timestamp_table_get();
256
257 /* No timestamp table found */
258 if (ts_table == NULL) {
259 printk(BIOS_ERR, "ERROR: No timestamp table found\n");
260 return;
261 }
262
263 ts_table->base_time /= M;
264 ts_table->base_time *= N;
265 for (i = 0; i < ts_table->num_entries; i++) {
266 struct timestamp_entry *tse = &ts_table->entries[i];
267 tse->entry_stamp /= M;
268 tse->entry_stamp *= N;
269 }
270}
271
Werner Zeh35cceb82017-09-12 07:42:54 +0200272/*
273 * Get the time in microseconds since boot (or more precise: since timestamp
274 * table was initialized).
275 */
276uint32_t get_us_since_boot(void)
277{
278 struct timestamp_table *ts = timestamp_table_get();
279
280 if (ts == NULL || ts->tick_freq_mhz == 0)
281 return 0;
282 return (timestamp_get() - ts->base_time) / ts->tick_freq_mhz;
283}
284
Kyösti Mälkki72e634f2019-09-05 18:16:25 +0300285ROMSTAGE_CBMEM_INIT_HOOK(timestamp_reinit)
286POSTCAR_CBMEM_INIT_HOOK(timestamp_reinit)
287RAMSTAGE_CBMEM_INIT_HOOK(timestamp_reinit)
Aaron Durbin9e80e272015-05-01 16:48:54 -0500288
289/* Provide default timestamp implementation using monotonic timer. */
Aaron Durbin64031672018-04-21 14:45:32 -0600290uint64_t __weak timestamp_get(void)
Aaron Durbin9e80e272015-05-01 16:48:54 -0500291{
292 struct mono_time t1, t2;
293
Julius Wernercd49cce2019-03-05 16:53:33 -0800294 if (!CONFIG(HAVE_MONOTONIC_TIMER))
Aaron Durbin7aafe532015-05-07 11:32:30 -0500295 return 0;
296
Aaron Durbin9e80e272015-05-01 16:48:54 -0500297 mono_time_set_usecs(&t1, 0);
298 timer_monotonic_get(&t2);
299
300 return mono_time_diff_microseconds(&t1, &t2);
301}
Aaron Durbinc49014e2015-08-30 21:19:55 -0500302
303/* Like timestamp_get() above this matches up with microsecond granularity. */
Aaron Durbin64031672018-04-21 14:45:32 -0600304int __weak timestamp_tick_freq_mhz(void)
Aaron Durbinc49014e2015-08-30 21:19:55 -0500305{
306 return 1;
307}