libpayload: Add gettimeofday() and friends
[coreboot.git] / payloads / libpayload / libc / time.c
1 /*
2  * This file is part of the libpayload project.
3  *
4  * Copyright (C) 2008 Advanced Micro Devices, Inc.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <libpayload.h>
31 #include <arch/rdtsc.h>
32
33 extern u32 cpu_khz;
34
35 static struct {
36         u64 ticks;
37         time_t secs;
38         suseconds_t usecs;
39 } clock;
40
41 #define TICKS_PER_SEC (cpu_khz * 1000)
42 #define TICKS_PER_USEC (cpu_khz / 1000)
43
44 static void update_clock(void)
45 {
46         u64 delta = rdtsc() - clock.ticks;
47         int secs;
48
49         clock.ticks += delta;
50
51         secs = (int) (delta / TICKS_PER_SEC);
52         clock.secs += secs;
53         delta -= (secs * TICKS_PER_SEC);
54         clock.usecs += (int) (delta / TICKS_PER_USEC);
55
56         if (clock.usecs > 1000000) {
57                 clock.usecs -= 1000000;
58                 clock.secs++;
59         }
60 }
61
62 #ifdef CONFIG_NVRAM
63
64 static unsigned int day_of_year(int mon, int day, int year)
65 {
66         static u8 mdays[12] = { 31, 28, 31, 30, 31, 30,
67                                 31, 31, 30, 31, 30, 31 };
68
69         int i, ret = 0;
70
71         for(i = 0; i < mon; i++) {
72                 ret += mdays[i];
73
74                 if (i == 1 && (year % 4))
75                         ret++;
76         }
77
78         return (ret + day);
79 }
80
81 static void gettimeofday_init(void)
82 {
83         int days, delta;
84         struct tm tm;
85
86         rtc_read_clock(&tm);
87         clock.ticks = rdtsc();
88
89         /* Calculate the number of days in the year so far */
90         days = day_of_year(tm.tm_mon, tm.tm_mday, tm.tm_year + 1900);
91
92         delta = tm.tm_year - 70;
93
94         days += (delta * 365);
95
96         /* Figure leap years */
97
98         if (delta > 2)
99           days += (delta - 2) / 4;
100
101         clock.secs = (days * 86400) + (tm.tm_hour * 3600) +
102                 (tm.tm_min * 60) + tm.tm_sec;
103 }
104 #else
105 static void gettimeofday_init(void)
106 {
107         /* Record the number of ticks */
108         clock.ticks = rdtsc();
109 }
110 #endif
111
112 int gettimeofday(struct timeval *tv, void *tz)
113 {
114         /* Call the gtod init when we need it - this keeps
115            the code from being included in the binary if we don't
116            need it
117         */
118
119         if (!clock.ticks)
120                 gettimeofday_init();
121
122         update_clock();
123
124         tv->tv_sec = clock.secs;
125         tv->tv_usec = clock.usecs;
126
127         return 0;
128 }