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