1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2013
4  * David Feng <fenghua@phytium.com.cn>
5  */
6 
7 #include <bootstage.h>
8 #include <command.h>
9 #include <time.h>
10 #include <asm/global_data.h>
11 #include <asm/system.h>
12 #include <linux/bitops.h>
13 
14 DECLARE_GLOBAL_DATA_PTR;
15 
16 /*
17  * Generic timer implementation of get_tbclk()
18  */
get_tbclk(void)19 unsigned long notrace get_tbclk(void)
20 {
21 	unsigned long cntfrq;
22 	asm volatile("mrs %0, cntfrq_el0" : "=r" (cntfrq));
23 	return cntfrq;
24 }
25 
26 #ifdef CONFIG_SYS_FSL_ERRATUM_A008585
27 /*
28  * FSL erratum A-008585 says that the ARM generic timer counter "has the
29  * potential to contain an erroneous value for a small number of core
30  * clock cycles every time the timer value changes".
31  * This sometimes leads to a consecutive counter read returning a lower
32  * value than the previous one, thus reporting the time to go backwards.
33  * The workaround is to read the counter twice and only return when the value
34  * was the same in both reads.
35  * Assumes that the CPU runs in much higher frequency than the timer.
36  */
timer_read_counter(void)37 unsigned long timer_read_counter(void)
38 {
39 	unsigned long cntpct;
40 	unsigned long temp;
41 
42 	isb();
43 	asm volatile("mrs %0, cntpct_el0" : "=r" (cntpct));
44 	asm volatile("mrs %0, cntpct_el0" : "=r" (temp));
45 	while (temp != cntpct) {
46 		asm volatile("mrs %0, cntpct_el0" : "=r" (cntpct));
47 		asm volatile("mrs %0, cntpct_el0" : "=r" (temp));
48 	}
49 
50 	return cntpct;
51 }
52 #elif CONFIG_SUNXI_A64_TIMER_ERRATUM
53 /*
54  * This erratum sometimes flips the lower 11 bits of the counter value
55  * to all 0's or all 1's, leading to jumps forwards or backwards.
56  * Backwards jumps might be interpreted all roll-overs and be treated as
57  * huge jumps forward.
58  * The workaround is to check whether the lower 11 bits of the counter are
59  * all 0 or all 1, then discard this value and read again.
60  * This occasionally discards valid values, but will catch all erroneous
61  * reads and fixes the problem reliably. Also this mostly requires only a
62  * single read, so does not have any significant overhead.
63  * The algorithm was conceived by Samuel Holland.
64  */
timer_read_counter(void)65 unsigned long timer_read_counter(void)
66 {
67 	unsigned long cntpct;
68 
69 	isb();
70 	do {
71 		asm volatile("mrs %0, cntpct_el0" : "=r" (cntpct));
72 	} while (((cntpct + 1) & GENMASK(10, 0)) <= 1);
73 
74 	return cntpct;
75 }
76 #else
77 /*
78  * timer_read_counter() using the Arm Generic Timer (aka arch timer).
79  */
timer_read_counter(void)80 unsigned long notrace timer_read_counter(void)
81 {
82 	unsigned long cntpct;
83 
84 	isb();
85 	asm volatile("mrs %0, cntpct_el0" : "=r" (cntpct));
86 
87 	return cntpct;
88 }
89 #endif
90 
get_ticks(void)91 uint64_t notrace get_ticks(void)
92 {
93 	unsigned long ticks = timer_read_counter();
94 
95 	gd->arch.tbl = ticks;
96 
97 	return ticks;
98 }
99 
usec2ticks(unsigned long usec)100 unsigned long usec2ticks(unsigned long usec)
101 {
102 	ulong ticks;
103 	if (usec < 1000)
104 		ticks = ((usec * (get_tbclk()/1000)) + 500) / 1000;
105 	else
106 		ticks = ((usec / 10) * (get_tbclk() / 100000));
107 
108 	return ticks;
109 }
110 
timer_get_boot_us(void)111 ulong timer_get_boot_us(void)
112 {
113 	u64 val = get_ticks() * 1000000;
114 
115 	return val / get_tbclk();
116 }
117 
118 #if CONFIG_IS_ENABLED(ARMV8_UDELAY_EVENT_STREAM)
__udelay(unsigned long usec)119 void __udelay(unsigned long usec)
120 {
121 	u64 target = get_ticks() + usec_to_tick(usec);
122 
123 	/* At EL2 or above, use the event stream to avoid polling CNTPCT_EL0 so often */
124 	if (current_el() >= 2) {
125 		u32 cnthctl_val;
126 		const u8 event_period = 0x7;
127 
128 		asm volatile("mrs %0, cnthctl_el2" : "=r" (cnthctl_val));
129 		asm volatile("msr cnthctl_el2, %0" : : "r"
130 			(cnthctl_val | CNTHCTL_EL2_EVNT_EN | CNTHCTL_EL2_EVNT_I(event_period)));
131 
132 		while (get_ticks() + (1ULL << event_period) <= target)
133 			wfe();
134 
135 		/* Reset the event stream */
136 		asm volatile("msr cnthctl_el2, %0" : : "r" (cnthctl_val));
137 	}
138 
139 	/* Fall back to polling CNTPCT_EL0 */
140 	while (get_ticks() <= target)
141 		;
142 }
143 #endif
144