uboot/arch/arm/cpu/armv8/generic_timer.c
<<
>>
Prefs
   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 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 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 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