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