flashrom 

flashrom Svn Source Tree

Root/trunk/udelay.c

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
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 <time.h>
26#include <sys/time.h>
27#include <stdlib.h>
28#include <limits.h>
29#include "flash.h"
30
31/* loops per microsecond */
32static unsigned long micro = 1;
33
34__attribute__ ((noinline)) void myusec_delay(unsigned int usecs)
35{
36unsigned long i;
37for (i = 0; i < usecs * micro; i++) {
38/* Make sure the compiler doesn't optimize the loop away. */
39__asm__ volatile ("" : : "rm" (i) );
40}
41}
42
43static unsigned long measure_os_delay_resolution(void)
44{
45unsigned long timeusec;
46struct timeval start, end;
47unsigned long counter = 0;
48
49gettimeofday(&start, NULL);
50timeusec = 0;
51
52while (!timeusec && (++counter < 1000000000)) {
53gettimeofday(&end, NULL);
54timeusec = 1000000 * (end.tv_sec - start.tv_sec) +
55 (end.tv_usec - start.tv_usec);
56/* Protect against time going forward too much. */
57if ((end.tv_sec > start.tv_sec) &&
58 ((end.tv_sec - start.tv_sec) >= LONG_MAX / 1000000 - 1))
59timeusec = 0;
60/* Protect against time going backwards during leap seconds. */
61if ((end.tv_sec < start.tv_sec) || (timeusec > LONG_MAX))
62timeusec = 0;
63}
64return timeusec;
65}
66
67static unsigned long measure_delay(unsigned int usecs)
68{
69unsigned long timeusec;
70struct timeval start, end;
71
72gettimeofday(&start, NULL);
73myusec_delay(usecs);
74gettimeofday(&end, NULL);
75timeusec = 1000000 * (end.tv_sec - start.tv_sec) +
76 (end.tv_usec - start.tv_usec);
77/* Protect against time going forward too much. */
78if ((end.tv_sec > start.tv_sec) &&
79 ((end.tv_sec - start.tv_sec) >= LONG_MAX / 1000000 - 1))
80timeusec = LONG_MAX;
81/* Protect against time going backwards during leap seconds. */
82if ((end.tv_sec < start.tv_sec) || (timeusec > LONG_MAX))
83timeusec = 1;
84
85return timeusec;
86}
87
88void myusec_calibrate_delay(void)
89{
90unsigned long count = 1000;
91unsigned long timeusec, resolution;
92int i, tries = 0;
93
94msg_pinfo("Calibrating delay loop... ");
95resolution = measure_os_delay_resolution();
96if (resolution) {
97msg_pdbg("OS timer resolution is %lu usecs, ", resolution);
98} else {
99msg_pinfo("OS timer resolution is unusable. ");
100}
101
102recalibrate:
103count = 1000;
104while (1) {
105timeusec = measure_delay(count);
106if (timeusec > 1000000 / 4)
107break;
108if (count >= ULONG_MAX / 2) {
109msg_pinfo("timer loop overflow, reduced precision. ");
110break;
111}
112count *= 2;
113}
114tries ++;
115
116/* Avoid division by zero, but in that case the loop is shot anyway. */
117if (!timeusec)
118timeusec = 1;
119
120/* Compute rounded up number of loops per microsecond. */
121micro = (count * micro) / timeusec + 1;
122msg_pdbg("%luM loops per second, ", micro);
123
124/* Did we try to recalibrate less than 5 times? */
125if (tries < 5) {
126/* Recheck our timing to make sure we weren't just hitting
127 * a scheduler delay or something similar.
128 */
129for (i = 0; i < 4; i++) {
130if (resolution && (resolution < 10)) {
131timeusec = measure_delay(100);
132} else if (resolution &&
133 (resolution < ULONG_MAX / 200)) {
134timeusec = measure_delay(resolution * 10) *
135 100 / (resolution * 10);
136} else {
137/* This workaround should be active for broken
138 * OS and maybe libpayload. The criterion
139 * here is horrible or non-measurable OS timer
140 * resolution which will result in
141 * measure_delay(100)=0 whereas a longer delay
142 * (1000 ms) may be sufficient
143 * to get a nonzero time measurement.
144 */
145timeusec = measure_delay(1000000) / 10000;
146}
147if (timeusec < 90) {
148msg_pdbg("delay more than 10%% too short (got "
149 "%lu%% of expected delay), "
150 "recalculating... ", timeusec);
151goto recalibrate;
152}
153}
154} else {
155msg_perr("delay loop is unreliable, trying to continue ");
156}
157
158/* We're interested in the actual precision. */
159timeusec = measure_delay(10);
160msg_pdbg("10 myus = %ld us, ", timeusec);
161timeusec = measure_delay(100);
162msg_pdbg("100 myus = %ld us, ", timeusec);
163timeusec = measure_delay(1000);
164msg_pdbg("1000 myus = %ld us, ", timeusec);
165timeusec = measure_delay(10000);
166msg_pdbg("10000 myus = %ld us, ", timeusec);
167timeusec = measure_delay(resolution * 4);
168msg_pdbg("%ld myus = %ld us, ", resolution * 4, timeusec);
169
170msg_pinfo("OK.\n");
171}
172
173/* Not very precise sleep. */
174void internal_sleep(unsigned int usecs)
175{
176#if IS_WINDOWS
177Sleep((usecs + 999) / 1000);
178#elif defined(__DJGPP__)
179sleep(usecs / 1000000);
180usleep(usecs % 1000000);
181#else
182nanosleep(&(struct timespec){usecs / 1000000, (usecs * 1000) % 1000000000UL}, NULL);
183#endif
184}
185
186/* Precise delay. */
187void internal_delay(unsigned int usecs)
188{
189/* If the delay is >1 s, use internal_sleep because timing does not need to be so precise. */
190if (usecs > 1000000) {
191internal_sleep(usecs);
192} else {
193myusec_delay(usecs);
194}
195}
196
197#else
198#include <libpayload.h>
199
200void myusec_calibrate_delay(void)
201{
202get_cpu_speed();
203}
204
205void internal_delay(unsigned int usecs)
206{
207udelay(usecs);
208}
209#endif

Archive Download this file

Revision: HEAD