xref: /openbmc/u-boot/arch/xtensa/lib/time.c (revision c978b524)
1*c978b524SChris Zankel /*
2*c978b524SChris Zankel  * (C) Copyright 2008 - 2013 Tensilica Inc.
3*c978b524SChris Zankel  *
4*c978b524SChris Zankel  * SPDX-License-Identifier:	GPL-2.0+
5*c978b524SChris Zankel  */
6*c978b524SChris Zankel 
7*c978b524SChris Zankel #include <common.h>
8*c978b524SChris Zankel #include <asm/global_data.h>
9*c978b524SChris Zankel #include <linux/stringify.h>
10*c978b524SChris Zankel 
11*c978b524SChris Zankel DECLARE_GLOBAL_DATA_PTR;
12*c978b524SChris Zankel 
13*c978b524SChris Zankel #if XCHAL_HAVE_CCOUNT
14*c978b524SChris Zankel static ulong get_ccount(void)
15*c978b524SChris Zankel {
16*c978b524SChris Zankel 	ulong ccount;
17*c978b524SChris Zankel 	asm volatile ("rsr %0,"__stringify(CCOUNT) : "=a" (ccount));
18*c978b524SChris Zankel 	return ccount;
19*c978b524SChris Zankel }
20*c978b524SChris Zankel #else
21*c978b524SChris Zankel static ulong fake_ccount;
22*c978b524SChris Zankel #define get_ccount() fake_ccount
23*c978b524SChris Zankel #endif
24*c978b524SChris Zankel 
25*c978b524SChris Zankel static void delay_cycles(unsigned cycles)
26*c978b524SChris Zankel {
27*c978b524SChris Zankel #if XCHAL_HAVE_CCOUNT
28*c978b524SChris Zankel 	unsigned expiry = get_ccount() + cycles;
29*c978b524SChris Zankel 	while ((signed)(expiry - get_ccount()) > 0)
30*c978b524SChris Zankel 		;
31*c978b524SChris Zankel #else
32*c978b524SChris Zankel #warning "Without Xtensa timer option, timing will not be accurate."
33*c978b524SChris Zankel 
34*c978b524SChris Zankel 	/*
35*c978b524SChris Zankel 	 * Approximate the cycle count by a loop iteration count.
36*c978b524SChris Zankel 	 * This is highly dependent on config and optimization.
37*c978b524SChris Zankel 	 */
38*c978b524SChris Zankel 
39*c978b524SChris Zankel 	volatile unsigned i;
40*c978b524SChris Zankel 	for (i = cycles >> 4U; i > 0; --i)
41*c978b524SChris Zankel 		;
42*c978b524SChris Zankel 	fake_ccount += cycles;
43*c978b524SChris Zankel #endif
44*c978b524SChris Zankel }
45*c978b524SChris Zankel 
46*c978b524SChris Zankel /*
47*c978b524SChris Zankel  * Delay (busy-wait) for a number of microseconds.
48*c978b524SChris Zankel  */
49*c978b524SChris Zankel 
50*c978b524SChris Zankel void __udelay(unsigned long usec)
51*c978b524SChris Zankel {
52*c978b524SChris Zankel 	ulong lo, hi, i;
53*c978b524SChris Zankel 	ulong mhz = CONFIG_SYS_CLK_FREQ / 1000000;
54*c978b524SChris Zankel 
55*c978b524SChris Zankel 	/* Scale to support full 32-bit usec range */
56*c978b524SChris Zankel 
57*c978b524SChris Zankel 	lo = usec & ((1<<22)-1);
58*c978b524SChris Zankel 	hi = usec >> 22UL;
59*c978b524SChris Zankel 	for (i = 0; i < hi; ++i)
60*c978b524SChris Zankel 		delay_cycles(mhz << 22);
61*c978b524SChris Zankel 	delay_cycles(mhz * lo);
62*c978b524SChris Zankel }
63*c978b524SChris Zankel 
64*c978b524SChris Zankel 
65*c978b524SChris Zankel /*
66*c978b524SChris Zankel  * Return the elapsed time (ticks) since 'base'.
67*c978b524SChris Zankel  */
68*c978b524SChris Zankel 
69*c978b524SChris Zankel ulong get_timer(ulong base)
70*c978b524SChris Zankel {
71*c978b524SChris Zankel 	/* Don't tie up a timer; use cycle counter if available (or fake it) */
72*c978b524SChris Zankel 
73*c978b524SChris Zankel #if XCHAL_HAVE_CCOUNT
74*c978b524SChris Zankel 	register ulong ccount;
75*c978b524SChris Zankel 	__asm__ volatile ("rsr %0, CCOUNT" : "=a"(ccount));
76*c978b524SChris Zankel 	return ccount / (CONFIG_SYS_CLK_FREQ / CONFIG_SYS_HZ) - base;
77*c978b524SChris Zankel #else
78*c978b524SChris Zankel 	/*
79*c978b524SChris Zankel 	 * Add at least the overhead of this call (in cycles).
80*c978b524SChris Zankel 	 * Avoids hanging in case caller doesn't use udelay().
81*c978b524SChris Zankel 	 * Note that functions that don't call udelay() (such as
82*c978b524SChris Zankel 	 * the "sleep" command) will not get a significant delay
83*c978b524SChris Zankel 	 * because there is no time reference.
84*c978b524SChris Zankel 	 */
85*c978b524SChris Zankel 
86*c978b524SChris Zankel 	fake_ccount += 20;
87*c978b524SChris Zankel 	return fake_ccount / (CONFIG_SYS_CLK_FREQ / CONFIG_SYS_HZ) - base;
88*c978b524SChris Zankel #endif
89*c978b524SChris Zankel }
90*c978b524SChris Zankel 
91*c978b524SChris Zankel 
92*c978b524SChris Zankel /*
93*c978b524SChris Zankel  * This function is derived from ARM/PowerPC code (read timebase as long long).
94*c978b524SChris Zankel  * On Xtensa it just returns the timer value.
95*c978b524SChris Zankel  */
96*c978b524SChris Zankel unsigned long long get_ticks(void)
97*c978b524SChris Zankel {
98*c978b524SChris Zankel 	return get_timer(0);
99*c978b524SChris Zankel }
100*c978b524SChris Zankel 
101*c978b524SChris Zankel /*
102*c978b524SChris Zankel  * This function is derived from ARM/PowerPC code (timebase clock frequency).
103*c978b524SChris Zankel  * On Xtensa it returns the number of timer ticks per second.
104*c978b524SChris Zankel  */
105*c978b524SChris Zankel ulong get_tbclk(void)
106*c978b524SChris Zankel {
107*c978b524SChris Zankel 	ulong tbclk;
108*c978b524SChris Zankel 
109*c978b524SChris Zankel 	tbclk = CONFIG_SYS_HZ;
110*c978b524SChris Zankel 	return tbclk;
111*c978b524SChris Zankel }
112*c978b524SChris Zankel 
113*c978b524SChris Zankel #if XCHAL_HAVE_CCOUNT
114*c978b524SChris Zankel unsigned long timer_get_us(void)
115*c978b524SChris Zankel {
116*c978b524SChris Zankel 	unsigned long ccount;
117*c978b524SChris Zankel 
118*c978b524SChris Zankel 	__asm__ volatile ("rsr %0, CCOUNT" : "=a"(ccount));
119*c978b524SChris Zankel 	return ccount / (CONFIG_SYS_CLK_FREQ / 1000000);
120*c978b524SChris Zankel }
121*c978b524SChris Zankel #endif
122