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