xref: /openbmc/u-boot/arch/arm/cpu/armv8/generic_timer.c (revision 4ac5df4b41ba46d7e635bdd8d500721c642b0a0d)
183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
20ae76531SDavid Feng /*
30ae76531SDavid Feng  * (C) Copyright 2013
40ae76531SDavid Feng  * David Feng <fenghua@phytium.com.cn>
50ae76531SDavid Feng  */
60ae76531SDavid Feng 
70ae76531SDavid Feng #include <common.h>
80ae76531SDavid Feng #include <command.h>
90ae76531SDavid Feng #include <asm/system.h>
100ae76531SDavid Feng 
1168fd5c13SAndre Przywara DECLARE_GLOBAL_DATA_PTR;
1268fd5c13SAndre Przywara 
130ae76531SDavid Feng /*
140ae76531SDavid Feng  * Generic timer implementation of get_tbclk()
150ae76531SDavid Feng  */
get_tbclk(void)160ae76531SDavid Feng unsigned long get_tbclk(void)
170ae76531SDavid Feng {
180ae76531SDavid Feng 	unsigned long cntfrq;
190ae76531SDavid Feng 	asm volatile("mrs %0, cntfrq_el0" : "=r" (cntfrq));
200ae76531SDavid Feng 	return cntfrq;
210ae76531SDavid Feng }
220ae76531SDavid Feng 
2338651588SAndre Przywara #ifdef CONFIG_SYS_FSL_ERRATUM_A008585
240ae76531SDavid Feng /*
2538651588SAndre Przywara  * FSL erratum A-008585 says that the ARM generic timer counter "has the
2638651588SAndre Przywara  * potential to contain an erroneous value for a small number of core
2738651588SAndre Przywara  * clock cycles every time the timer value changes".
2838651588SAndre Przywara  * This sometimes leads to a consecutive counter read returning a lower
2938651588SAndre Przywara  * value than the previous one, thus reporting the time to go backwards.
3038651588SAndre Przywara  * The workaround is to read the counter twice and only return when the value
3138651588SAndre Przywara  * was the same in both reads.
3238651588SAndre Przywara  * Assumes that the CPU runs in much higher frequency than the timer.
330ae76531SDavid Feng  */
timer_read_counter(void)340ae76531SDavid Feng unsigned long timer_read_counter(void)
350ae76531SDavid Feng {
360ae76531SDavid Feng 	unsigned long cntpct;
37060ef094SYork Sun 	unsigned long temp;
3838651588SAndre Przywara 
390ae76531SDavid Feng 	isb();
400ae76531SDavid Feng 	asm volatile("mrs %0, cntpct_el0" : "=r" (cntpct));
41060ef094SYork Sun 	asm volatile("mrs %0, cntpct_el0" : "=r" (temp));
42060ef094SYork Sun 	while (temp != cntpct) {
43060ef094SYork Sun 		asm volatile("mrs %0, cntpct_el0" : "=r" (cntpct));
44060ef094SYork Sun 		asm volatile("mrs %0, cntpct_el0" : "=r" (temp));
45060ef094SYork Sun 	}
4638651588SAndre Przywara 
470ae76531SDavid Feng 	return cntpct;
480ae76531SDavid Feng }
49*be0d2179SAndre Przywara #elif CONFIG_SUNXI_A64_TIMER_ERRATUM
50*be0d2179SAndre Przywara /*
51*be0d2179SAndre Przywara  * This erratum sometimes flips the lower 11 bits of the counter value
52*be0d2179SAndre Przywara  * to all 0's or all 1's, leading to jumps forwards or backwards.
53*be0d2179SAndre Przywara  * Backwards jumps might be interpreted all roll-overs and be treated as
54*be0d2179SAndre Przywara  * huge jumps forward.
55*be0d2179SAndre Przywara  * The workaround is to check whether the lower 11 bits of the counter are
56*be0d2179SAndre Przywara  * all 0 or all 1, then discard this value and read again.
57*be0d2179SAndre Przywara  * This occasionally discards valid values, but will catch all erroneous
58*be0d2179SAndre Przywara  * reads and fixes the problem reliably. Also this mostly requires only a
59*be0d2179SAndre Przywara  * single read, so does not have any significant overhead.
60*be0d2179SAndre Przywara  * The algorithm was conceived by Samuel Holland.
61*be0d2179SAndre Przywara  */
timer_read_counter(void)62*be0d2179SAndre Przywara unsigned long timer_read_counter(void)
63*be0d2179SAndre Przywara {
64*be0d2179SAndre Przywara 	unsigned long cntpct;
65*be0d2179SAndre Przywara 
66*be0d2179SAndre Przywara 	isb();
67*be0d2179SAndre Przywara 	do {
68*be0d2179SAndre Przywara 		asm volatile("mrs %0, cntpct_el0" : "=r" (cntpct));
69*be0d2179SAndre Przywara 	} while (((cntpct + 1) & GENMASK(10, 0)) <= 1);
70*be0d2179SAndre Przywara 
71*be0d2179SAndre Przywara 	return cntpct;
72*be0d2179SAndre Przywara }
7338651588SAndre Przywara #else
7438651588SAndre Przywara /*
7538651588SAndre Przywara  * timer_read_counter() using the Arm Generic Timer (aka arch timer).
7638651588SAndre Przywara  */
timer_read_counter(void)7738651588SAndre Przywara unsigned long timer_read_counter(void)
7838651588SAndre Przywara {
7938651588SAndre Przywara 	unsigned long cntpct;
8038651588SAndre Przywara 
8138651588SAndre Przywara 	isb();
8238651588SAndre Przywara 	asm volatile("mrs %0, cntpct_el0" : "=r" (cntpct));
8338651588SAndre Przywara 
8438651588SAndre Przywara 	return cntpct;
8538651588SAndre Przywara }
8638651588SAndre Przywara #endif
87b644d3e9SAneesh Bansal 
get_ticks(void)88f97cae95SSimon Glass uint64_t get_ticks(void)
8968fd5c13SAndre Przywara {
9068fd5c13SAndre Przywara 	unsigned long ticks = timer_read_counter();
9168fd5c13SAndre Przywara 
9268fd5c13SAndre Przywara 	gd->arch.tbl = ticks;
9368fd5c13SAndre Przywara 
9468fd5c13SAndre Przywara 	return ticks;
9568fd5c13SAndre Przywara }
9668fd5c13SAndre Przywara 
usec2ticks(unsigned long usec)97b644d3e9SAneesh Bansal unsigned long usec2ticks(unsigned long usec)
98b644d3e9SAneesh Bansal {
99b644d3e9SAneesh Bansal 	ulong ticks;
100b644d3e9SAneesh Bansal 	if (usec < 1000)
101b644d3e9SAneesh Bansal 		ticks = ((usec * (get_tbclk()/1000)) + 500) / 1000;
102b644d3e9SAneesh Bansal 	else
103b644d3e9SAneesh Bansal 		ticks = ((usec / 10) * (get_tbclk() / 100000));
104b644d3e9SAneesh Bansal 
105b644d3e9SAneesh Bansal 	return ticks;
106b644d3e9SAneesh Bansal }
107d0f855f2SMichal Simek 
timer_get_boot_us(void)108d0f855f2SMichal Simek ulong timer_get_boot_us(void)
109d0f855f2SMichal Simek {
110d0f855f2SMichal Simek 	u64 val = get_ticks() * 1000000;
111d0f855f2SMichal Simek 
112d0f855f2SMichal Simek 	return val / get_tbclk();
113d0f855f2SMichal Simek }
114