1 /* SPDX-License-Identifier: GPL-2.0-only */
5 #include <console/console.h>
12 #define MAX_TIMESTAMPS 192
14 /* This points to the active timestamp_table and can change within a stage
15 as CBMEM comes available. */
16 static struct timestamp_table
*glob_ts_table
;
18 static void timestamp_cache_init(struct timestamp_table
*ts_cache
,
21 ts_cache
->num_entries
= 0;
22 ts_cache
->base_time
= base
;
23 ts_cache
->max_entries
= (REGION_SIZE(timestamp
) -
24 offsetof(struct timestamp_table
, entries
))
25 / sizeof(struct timestamp_entry
);
28 static struct timestamp_table
*timestamp_cache_get(void)
30 struct timestamp_table
*ts_cache
= NULL
;
32 if (!ENV_ROMSTAGE_OR_BEFORE
)
35 if (REGION_SIZE(timestamp
) < sizeof(*ts_cache
)) {
38 ts_cache
= (void *)_timestamp
;
44 static struct timestamp_table
*timestamp_alloc_cbmem_table(void)
46 struct timestamp_table
*tst
;
48 tst
= cbmem_add(CBMEM_ID_TIMESTAMP
,
49 sizeof(struct timestamp_table
) +
50 MAX_TIMESTAMPS
* sizeof(struct timestamp_entry
));
56 tst
->max_entries
= MAX_TIMESTAMPS
;
62 /* Determine if one should proceed into timestamp code. This is for protecting
63 * systems that have multiple processors running in romstage -- namely AMD
64 * based x86 platforms. */
65 static int timestamp_should_run(void)
68 * Only check boot_cpu() in other stages than
69 * ENV_PAYLOAD_LOADER on x86.
71 if ((!ENV_PAYLOAD_LOADER
&& ENV_X86
) && !boot_cpu())
77 static struct timestamp_table
*timestamp_table_get(void)
82 glob_ts_table
= timestamp_cache_get();
87 static void timestamp_table_set(struct timestamp_table
*ts
)
92 static const char *timestamp_name(enum timestamp_id id
)
96 for (i
= 0; i
< ARRAY_SIZE(timestamp_ids
); i
++) {
97 if (timestamp_ids
[i
].id
== id
)
98 return timestamp_ids
[i
].name
;
101 return "Unknown timestamp ID";
104 static void timestamp_add_table_entry(struct timestamp_table
*ts_table
,
105 enum timestamp_id id
, int64_t ts_time
)
107 struct timestamp_entry
*tse
;
109 if (ts_table
->num_entries
>= ts_table
->max_entries
)
112 tse
= &ts_table
->entries
[ts_table
->num_entries
++];
114 tse
->entry_stamp
= ts_time
;
116 if (ts_table
->num_entries
== ts_table
->max_entries
)
117 printk(BIOS_ERR
, "Timestamp table full\n");
120 void timestamp_add(enum timestamp_id id
, int64_t ts_time
)
122 struct timestamp_table
*ts_table
;
124 if (!timestamp_should_run())
127 ts_table
= timestamp_table_get();
130 printk(BIOS_ERR
, "No timestamp table found\n");
134 ts_time
-= ts_table
->base_time
;
135 timestamp_add_table_entry(ts_table
, id
, ts_time
);
137 if (CONFIG(TIMESTAMPS_ON_CONSOLE
))
138 printk(BIOS_INFO
, "Timestamp - %s: %lld\n", timestamp_name(id
), ts_time
);
141 void timestamp_add_now(enum timestamp_id id
)
143 timestamp_add(id
, timestamp_get());
146 void timestamp_init(uint64_t base
)
148 struct timestamp_table
*ts_cache
;
150 assert(ENV_ROMSTAGE_OR_BEFORE
);
152 if (!timestamp_should_run())
155 ts_cache
= timestamp_cache_get();
158 printk(BIOS_ERR
, "No timestamp cache to init\n");
162 timestamp_cache_init(ts_cache
, base
);
163 timestamp_table_set(ts_cache
);
166 static void timestamp_sync_cache_to_cbmem(struct timestamp_table
*ts_cbmem_table
)
169 struct timestamp_table
*ts_cache_table
;
171 ts_cache_table
= timestamp_table_get();
172 if (!ts_cache_table
) {
173 printk(BIOS_ERR
, "No timestamp cache found\n");
178 * There's no need to worry about the base_time fields being out of
179 * sync because only the following configuration is used/supported:
181 * Timestamps get initialized before ramstage, which implies
182 * CBMEM initialization in romstage.
183 * This requires the board to define a TIMESTAMP() region in its
184 * memlayout.ld (default on x86). The base_time from timestamp_init()
185 * (usually called from bootblock.c on most non-x86 boards) persists
186 * in that region until it gets synced to CBMEM in romstage.
187 * In ramstage, the BSS cache's base_time will be 0 until the second
188 * sync, which will adjust the timestamps in there to the correct
189 * base_time (from CBMEM) with the timestamp_add_table_entry() below.
191 * If you try to initialize timestamps before ramstage but don't define
192 * a TIMESTAMP region, all operations will fail (safely), and coreboot
193 * will behave as if timestamps collection was disabled.
196 /* Inherit cache base_time. */
197 ts_cbmem_table
->base_time
= ts_cache_table
->base_time
;
199 for (i
= 0; i
< ts_cache_table
->num_entries
; i
++) {
200 struct timestamp_entry
*tse
= &ts_cache_table
->entries
[i
];
201 timestamp_add_table_entry(ts_cbmem_table
, tse
->entry_id
,
205 /* Cache no longer required. */
206 ts_cache_table
->num_entries
= 0;
209 static void timestamp_reinit(int is_recovery
)
211 struct timestamp_table
*ts_cbmem_table
;
213 if (!timestamp_should_run())
216 /* First time into romstage we make a clean new table. For platforms that travel
217 through this path on resume, ARCH_X86 S3, timestamps are also reset. */
218 if (ENV_CREATES_CBMEM
) {
219 ts_cbmem_table
= timestamp_alloc_cbmem_table();
221 /* Find existing table in cbmem. */
222 ts_cbmem_table
= cbmem_find(CBMEM_ID_TIMESTAMP
);
225 if (ts_cbmem_table
== NULL
) {
226 printk(BIOS_ERR
, "No timestamp table allocated\n");
227 timestamp_table_set(NULL
);
231 if (ENV_CREATES_CBMEM
)
232 timestamp_sync_cache_to_cbmem(ts_cbmem_table
);
234 /* Seed the timestamp tick frequency in ENV_PAYLOAD_LOADER. */
235 if (ENV_PAYLOAD_LOADER
)
236 ts_cbmem_table
->tick_freq_mhz
= timestamp_tick_freq_mhz();
238 timestamp_table_set(ts_cbmem_table
);
241 void timestamp_rescale_table(uint16_t N
, uint16_t M
)
244 struct timestamp_table
*ts_table
;
246 if (!timestamp_should_run())
249 if (N
== 0 || M
== 0)
252 ts_table
= timestamp_table_get();
254 /* No timestamp table found */
255 if (ts_table
== NULL
) {
256 printk(BIOS_ERR
, "No timestamp table found\n");
260 ts_table
->base_time
/= M
;
261 ts_table
->base_time
*= N
;
262 for (i
= 0; i
< ts_table
->num_entries
; i
++) {
263 struct timestamp_entry
*tse
= &ts_table
->entries
[i
];
264 tse
->entry_stamp
/= M
;
265 tse
->entry_stamp
*= N
;
270 * Get the time in microseconds since boot (or more precise: since timestamp
271 * table was initialized).
273 uint32_t get_us_since_boot(void)
275 struct timestamp_table
*ts
= timestamp_table_get();
277 if (ts
== NULL
|| ts
->tick_freq_mhz
== 0)
279 return (timestamp_get() - ts
->base_time
) / ts
->tick_freq_mhz
;
282 CBMEM_READY_HOOK(timestamp_reinit
);
284 /* Provide default timestamp implementation using monotonic timer. */
285 uint64_t __weak
timestamp_get(void)
287 struct mono_time t1
, t2
;
289 if (!CONFIG(HAVE_MONOTONIC_TIMER
))
292 mono_time_set_usecs(&t1
, 0);
293 timer_monotonic_get(&t2
);
295 return mono_time_diff_microseconds(&t1
, &t2
);
298 /* Like timestamp_get() above this matches up with microsecond granularity. */
299 int __weak
timestamp_tick_freq_mhz(void)