1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * (C) Copyright 2000-2009 4 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 5 */ 6 7#include <common.h> 8#include <bootstage.h> 9#include <dm.h> 10#include <errno.h> 11#include <init.h> 12#include <spl.h> 13#include <time.h> 14#include <timer.h> 15#include <watchdog.h> 16#include <div64.h> 17#include <asm/global_data.h> 18#include <asm/io.h> 19#include <linux/delay.h> 20 21#ifndef CONFIG_WD_PERIOD 22# define CONFIG_WD_PERIOD (10 * 1000 * 1000) /* 10 seconds default */ 23#endif 24 25DECLARE_GLOBAL_DATA_PTR; 26 27#ifdef CONFIG_SYS_TIMER_RATE 28/* Returns tick rate in ticks per second */ 29ulong notrace get_tbclk(void) 30{ 31 return CONFIG_SYS_TIMER_RATE; 32} 33#endif 34 35#ifdef CONFIG_SYS_TIMER_COUNTER 36unsigned long notrace timer_read_counter(void) 37{ 38#ifdef CONFIG_SYS_TIMER_COUNTS_DOWN 39 return ~readl(CONFIG_SYS_TIMER_COUNTER); 40#else 41 return readl(CONFIG_SYS_TIMER_COUNTER); 42#endif 43} 44 45ulong timer_get_boot_us(void) 46{ 47 ulong count = timer_read_counter(); 48 49#if CONFIG_SYS_TIMER_RATE == 1000000 50 return count; 51#elif CONFIG_SYS_TIMER_RATE > 1000000 52 return lldiv(count, CONFIG_SYS_TIMER_RATE / 1000000); 53#elif defined(CONFIG_SYS_TIMER_RATE) 54 return (unsigned long long)count * 1000000 / CONFIG_SYS_TIMER_RATE; 55#else 56 /* Assume the counter is in microseconds */ 57 return count; 58#endif 59} 60 61#else 62extern unsigned long __weak timer_read_counter(void); 63#endif 64 65#if CONFIG_IS_ENABLED(TIMER) 66ulong notrace get_tbclk(void) 67{ 68 if (!gd->timer) { 69#ifdef CONFIG_TIMER_EARLY 70 return timer_early_get_rate(); 71#else 72 int ret; 73 74 ret = dm_timer_init(); 75 if (ret) 76 return ret; 77#endif 78 } 79 80 return timer_get_rate(gd->timer); 81} 82 83uint64_t notrace get_ticks(void) 84{ 85 u64 count; 86 int ret; 87 88 if (!gd->timer) { 89#ifdef CONFIG_TIMER_EARLY 90 return timer_early_get_count(); 91#else 92 int ret; 93 94 ret = dm_timer_init(); 95 if (ret) 96 panic("Could not initialize timer (err %d)\n", ret); 97#endif 98 } 99 100 ret = timer_get_count(gd->timer, &count); 101 if (ret) { 102 if (spl_phase() > PHASE_TPL) 103 panic("Could not read count from timer (err %d)\n", 104 ret); 105 else 106 panic("no timer (err %d)\n", ret); 107 } 108 109 return count; 110} 111 112#else /* !CONFIG_TIMER */ 113 114uint64_t __weak notrace get_ticks(void) 115{ 116 unsigned long now = timer_read_counter(); 117 118 /* increment tbu if tbl has rolled over */ 119 if (now < gd->timebase_l) 120 gd->timebase_h++; 121 gd->timebase_l = now; 122 return ((uint64_t)gd->timebase_h << 32) | gd->timebase_l; 123} 124 125#endif /* CONFIG_TIMER */ 126 127/* Returns time in milliseconds */ 128static uint64_t notrace tick_to_time(uint64_t tick) 129{ 130 ulong div = get_tbclk(); 131 132 tick *= CONFIG_SYS_HZ; 133 do_div(tick, div); 134 return tick; 135} 136 137int __weak timer_init(void) 138{ 139 return 0; 140} 141 142/* Returns time in milliseconds */ 143ulong __weak get_timer(ulong base) 144{ 145 return tick_to_time(get_ticks()) - base; 146} 147 148static uint64_t notrace tick_to_time_us(uint64_t tick) 149{ 150 ulong div = get_tbclk() / 1000; 151 152 tick *= CONFIG_SYS_HZ; 153 do_div(tick, div); 154 return tick; 155} 156 157uint64_t __weak get_timer_us(uint64_t base) 158{ 159 return tick_to_time_us(get_ticks()) - base; 160} 161 162unsigned long __weak get_timer_us_long(unsigned long base) 163{ 164 return timer_get_us() - base; 165} 166 167unsigned long __weak notrace timer_get_us(void) 168{ 169 return tick_to_time(get_ticks() * 1000); 170} 171 172uint64_t usec_to_tick(unsigned long usec) 173{ 174 uint64_t tick = usec; 175 tick *= get_tbclk(); 176 do_div(tick, 1000000); 177 return tick; 178} 179 180void __weak __udelay(unsigned long usec) 181{ 182 uint64_t tmp; 183 184 tmp = get_ticks() + usec_to_tick(usec); /* get current timestamp */ 185 186 while (get_ticks() < tmp+1) /* loop till event */ 187 /*NOP*/; 188} 189 190/* ------------------------------------------------------------------------- */ 191 192void udelay(unsigned long usec) 193{ 194 ulong kv; 195 196 do { 197 WATCHDOG_RESET(); 198 kv = usec > CONFIG_WD_PERIOD ? CONFIG_WD_PERIOD : usec; 199 __udelay(kv); 200 usec -= kv; 201 } while(usec); 202} 203