time.c
1 /* 2 * 3 * Copyright (C) 2008 Advanced Micro Devices, Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 /** 30 * @file libc/time.c 31 * General time functions 32 */ 33 34 #define __STDC_FORMAT_MACROS 35 36 #include <libpayload-config.h> 37 #include <libpayload.h> 38 #if CONFIG(LP_ARCH_X86) && CONFIG(LP_NVRAM) 39 #include <arch/rdtsc.h> 40 #endif 41 #include <commonlib/bsd/gcd.h> 42 #include <inttypes.h> 43 44 extern u32 cpu_khz; 45 46 static struct { 47 u64 ticks; 48 time_t secs; 49 suseconds_t usecs; 50 } clock; 51 52 static void update_clock(void) 53 { 54 u64 delta = timer_raw_value() - clock.ticks; 55 int secs; 56 static uint64_t ticks_per_sec = 0; 57 static uint64_t ticks_per_usec = 0; 58 if (!ticks_per_sec) { 59 ticks_per_sec = timer_hz(); 60 ticks_per_usec = timer_hz() / 1000000; 61 } 62 63 clock.ticks += delta; 64 65 secs = (int) (delta / ticks_per_sec); 66 clock.secs += secs; 67 delta -= (secs * ticks_per_sec); 68 clock.usecs += (int)(delta / ticks_per_usec); 69 70 if (clock.usecs > 1000000) { 71 clock.usecs -= 1000000; 72 clock.secs++; 73 } 74 } 75 76 #if CONFIG(LP_NVRAM) 77 78 static unsigned int day_of_year(int mon, int day, int year) 79 { 80 static u8 mdays[12] = { 31, 28, 31, 30, 31, 30, 81 31, 31, 30, 31, 30, 31 }; 82 83 int i, ret = 0; 84 85 for(i = 0; i < mon; i++) { 86 ret += mdays[i]; 87 88 if (i == 1 && (year % 4)) 89 ret++; 90 } 91 92 return (ret + day); 93 } 94 95 static void gettimeofday_init(void) 96 { 97 int days, delta; 98 struct tm tm; 99 100 rtc_read_clock(&tm); 101 clock.ticks = rdtsc(); 102 103 /* Calculate the number of days in the year so far */ 104 days = day_of_year(tm.tm_mon, tm.tm_mday, tm.tm_year + 1900); 105 106 delta = tm.tm_year - 70; 107 108 days += (delta * 365); 109 110 /* Figure leap years */ 111 112 if (delta > 2) 113 days += (delta - 2) / 4; 114 115 clock.secs = (days * 86400) + (tm.tm_hour * 3600) + 116 (tm.tm_min * 60) + tm.tm_sec; 117 } 118 #else 119 static void gettimeofday_init(void) 120 { 121 /* Record the number of ticks */ 122 clock.ticks = timer_raw_value(); 123 } 124 #endif 125 126 /** 127 * Return the current time expressed as seconds from 00:00:00 UTC, 1 Jan 1970. 128 * 129 * @param tp When not NULL, set this to the current time in seconds. 130 * @return The current time in seconds. 131 */ 132 time_t time(time_t *tp) 133 { 134 /* 135 * Call the gtod init when we need it - this keeps the code from 136 * being included in the binary if we don't need it. 137 */ 138 if (!clock.ticks) 139 gettimeofday_init(); 140 141 update_clock(); 142 143 if (tp) 144 *tp = clock.secs; 145 146 return clock.secs; 147 } 148 149 /** 150 * Return the current time broken into a timeval structure. 151 * 152 * @param tv A pointer to a timeval structure. 153 * @param tz Added for compatibility - not used. 154 * @return 0 for success (this function cannot return non-zero currently). 155 */ 156 int gettimeofday(struct timeval *tv, void *tz) 157 { 158 tv->tv_sec = time(NULL); 159 tv->tv_usec = clock.usecs; 160 161 return 0; 162 } 163 164 __attribute__((weak)) 165 void arch_ndelay(uint64_t ns) 166 { 167 uint64_t delta = ns * timer_hz() / NSECS_PER_SEC; 168 uint64_t start = timer_raw_value(); 169 while (timer_raw_value() - start < delta) ; 170 } 171 172 u64 timer_us(u64 base) 173 { 174 static u64 hz; 175 static u32 mult = USECS_PER_SEC; 176 u32 div; 177 178 // Only check timer_hz once. Assume it doesn't change. 179 if (hz == 0) { 180 hz = timer_hz(); 181 if (hz < mult) { 182 printf("Timer frequency %" PRIu64 " is too low, " 183 "must be at least 1MHz.\n", hz); 184 halt(); 185 } 186 div = gcd(hz, mult); 187 hz /= div; 188 mult /= div; 189 } 190 191 return (mult * timer_raw_value()) / hz - base; 192 }