summaryrefslogtreecommitdiff
path: root/arch/arm64/lib/timer.c
blob: 605cd18b19a917fc3a0248f6fd9e5f1590396718 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/* SPDX-License-Identifier: GPL-2.0+ */
/*
 * timer.c - generic timer delays, the system counter from D10. the
 * counter is a fixed frequency free running counter, CNTFRQ_EL0
 * carries the frequency, CNTVCT_EL0 the 64 bit count. delays are a
 * busy wait on the counter, no interrupts needed, microsecond and
 * millisecond granularity.
 *
 * CNTVCT_EL0 is the virtual counter view; at EL2 with no offset
 * configured it is the physical count. the read is not speculative
 * and needs an isb to serialize against subsequent counter reads
 * per the counter access rules.
 *
 * Copyright (C) 2026 Bradley Morgan <brads@mainlining.org>
 */

#include <stdint.h>

static uint64_t read_cntfrq(void)
{
	uint64_t v;

	asm volatile("mrs %0, cntfrq_el0" : "=r" (v));
	return v;
}

static uint64_t read_counter(void)
{
	uint64_t v;

	asm volatile("isb\nmrs %0, cntvct_el0" : "=r" (v));
	return v;
}

void tb_udelay(uint32_t us)
{
	uint64_t freq = read_cntfrq();
	uint64_t start = read_counter();
	uint64_t ticks = (uint64_t)us * freq / 1000000ULL;

	while (read_counter() - start < ticks)
		;
}

void tb_mdelay(uint32_t ms)
{
	tb_udelay(ms * 1000);
}