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