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