Initial import in Tizen
[profile/ivi/flashrom.git] / udelay.c
1 /*
2  * This file is part of the flashrom project.
3  *
4  * Copyright (C) 2000 Silicon Integrated System Corporation
5  * Copyright (C) 2009,2010 Carl-Daniel Hailfinger
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
20  */
21
22 #ifndef __LIBPAYLOAD__
23
24 #include <unistd.h>
25 #include <sys/time.h>
26 #include <stdlib.h>
27 #include <limits.h>
28 #include "flash.h"
29
30 /* loops per microsecond */
31 static unsigned long micro = 1;
32
33 __attribute__ ((noinline)) void myusec_delay(int usecs)
34 {
35         unsigned long i;
36         for (i = 0; i < usecs * micro; i++) {
37                 /* Make sure the compiler doesn't optimize the loop away. */
38                 asm volatile ("" : : "rm" (i) );
39         }
40 }
41
42 static unsigned long measure_os_delay_resolution(void)
43 {
44         unsigned long timeusec;
45         struct timeval start, end;
46         unsigned long counter = 0;
47         
48         gettimeofday(&start, NULL);
49         timeusec = 0;
50         
51         while (!timeusec && (++counter < 1000000000)) {
52                 gettimeofday(&end, NULL);
53                 timeusec = 1000000 * (end.tv_sec - start.tv_sec) +
54                            (end.tv_usec - start.tv_usec);
55                 /* Protect against time going forward too much. */
56                 if ((end.tv_sec > start.tv_sec) &&
57                     ((end.tv_sec - start.tv_sec) >= LONG_MAX / 1000000 - 1))
58                         timeusec = 0;
59                 /* Protect against time going backwards during leap seconds. */
60                 if ((end.tv_sec < start.tv_sec) || (timeusec > LONG_MAX))
61                         timeusec = 0;
62         }
63         return timeusec;
64 }
65
66 static unsigned long measure_delay(int usecs)
67 {
68         unsigned long timeusec;
69         struct timeval start, end;
70         
71         gettimeofday(&start, NULL);
72         myusec_delay(usecs);
73         gettimeofday(&end, NULL);
74         timeusec = 1000000 * (end.tv_sec - start.tv_sec) +
75                    (end.tv_usec - start.tv_usec);
76         /* Protect against time going forward too much. */
77         if ((end.tv_sec > start.tv_sec) &&
78             ((end.tv_sec - start.tv_sec) >= LONG_MAX / 1000000 - 1))
79                 timeusec = LONG_MAX;
80         /* Protect against time going backwards during leap seconds. */
81         if ((end.tv_sec < start.tv_sec) || (timeusec > LONG_MAX))
82                 timeusec = 1;
83
84         return timeusec;
85 }
86
87 void myusec_calibrate_delay(void)
88 {
89         unsigned long count = 1000;
90         unsigned long timeusec, resolution;
91         int i, tries = 0;
92
93         msg_pinfo("Calibrating delay loop... ");
94         resolution = measure_os_delay_resolution();
95         if (resolution) {
96                 msg_pdbg("OS timer resolution is %lu usecs, ", resolution);
97         } else {
98                 msg_pinfo("OS timer resolution is unusable. ");
99         }
100
101 recalibrate:
102         count = 1000;
103         while (1) {
104                 timeusec = measure_delay(count);
105                 if (timeusec > 1000000 / 4)
106                         break;
107                 if (count >= ULONG_MAX / 2) {
108                         msg_pinfo("timer loop overflow, reduced precision. ");
109                         break;
110                 }
111                 count *= 2;
112         }
113         tries ++;
114
115         /* Avoid division by zero, but in that case the loop is shot anyway. */
116         if (!timeusec)
117                 timeusec = 1;
118         
119         /* Compute rounded up number of loops per microsecond. */
120         micro = (count * micro) / timeusec + 1;
121         msg_pdbg("%luM loops per second, ", micro);
122
123         /* Did we try to recalibrate less than 5 times? */
124         if (tries < 5) {
125                 /* Recheck our timing to make sure we weren't just hitting
126                  * a scheduler delay or something similar.
127                  */
128                 for (i = 0; i < 4; i++) {
129                         if (resolution && (resolution < 10)) {
130                                 timeusec = measure_delay(100);
131                         } else if (resolution && 
132                                    (resolution < ULONG_MAX / 200)) {
133                                 timeusec = measure_delay(resolution * 10) *
134                                            100 / (resolution * 10);
135                         } else {
136                                 /* This workaround should be active for broken
137                                  * OS and maybe libpayload. The criterion
138                                  * here is horrible or non-measurable OS timer
139                                  * resolution which will result in
140                                  * measure_delay(100)=0 whereas a longer delay
141                                  * (1000 ms) may be sufficient
142                                  * to get a nonzero time measurement.
143                                  */
144                                 timeusec = measure_delay(1000000) / 10000;
145                         }
146                         if (timeusec < 90) {
147                                 msg_pdbg("delay more than 10%% too short (got "
148                                          "%lu%% of expected delay), "
149                                          "recalculating... ", timeusec);
150                                 goto recalibrate;
151                         }
152                 }
153         } else {
154                 msg_perr("delay loop is unreliable, trying to continue ");
155         }
156
157         /* We're interested in the actual precision. */
158         timeusec = measure_delay(10);
159         msg_pdbg("10 myus = %ld us, ", timeusec);
160         timeusec = measure_delay(100);
161         msg_pdbg("100 myus = %ld us, ", timeusec);
162         timeusec = measure_delay(1000);
163         msg_pdbg("1000 myus = %ld us, ", timeusec);
164         timeusec = measure_delay(10000);
165         msg_pdbg("10000 myus = %ld us, ", timeusec);
166         timeusec = measure_delay(resolution * 4);
167         msg_pdbg("%ld myus = %ld us, ", resolution * 4, timeusec);
168
169         msg_pinfo("OK.\n");
170 }
171
172 void internal_delay(int usecs)
173 {
174         /* If the delay is >1 s, use usleep because timing does not need to
175          * be so precise.
176          */
177         if (usecs > 1000000) {
178                 usleep(usecs);
179         } else {
180                 myusec_delay(usecs);
181         }
182 }
183
184 #else 
185 #include <libpayload.h>
186
187 void myusec_calibrate_delay(void)
188 {
189         get_cpu_speed();
190 }
191
192 void internal_delay(int usecs)
193 {
194         udelay(usecs);
195 }
196 #endif