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
18 #include <stdbool.h>
19 #include <unistd.h>
20 #include <errno.h>
21 #include <time.h>
22 #include <sys/time.h>
23 #include <stdlib.h>
24 #include <limits.h>
25 #include "flash.h"
26 #include "programmer.h"
27
28 /* loops per microsecond */
29 static unsigned long micro = 1;
30
myusec_delay(unsigned int usecs)31 __attribute__ ((noinline)) static void myusec_delay(unsigned int usecs)
32 {
33 unsigned long i;
34 for (i = 0; i < usecs * micro; i++) {
35 /* Make sure the compiler doesn't optimize the loop away. */
36 __asm__ volatile ("" : : "rm" (i) );
37 }
38 }
39
measure_os_delay_resolution(void)40 static unsigned long measure_os_delay_resolution(void)
41 {
42 unsigned long timeusec;
43 struct timeval start, end;
44 unsigned long counter = 0;
45
46 gettimeofday(&start, NULL);
47 timeusec = 0;
48
49 while (!timeusec && (++counter < 1000000000)) {
50 gettimeofday(&end, NULL);
51 timeusec = 1000000 * (end.tv_sec - start.tv_sec) +
52 (end.tv_usec - start.tv_usec);
53 /* Protect against time going forward too much. */
54 if ((end.tv_sec > start.tv_sec) &&
55 ((end.tv_sec - start.tv_sec) >= LONG_MAX / 1000000 - 1))
56 timeusec = 0;
57 /* Protect against time going backwards during leap seconds. */
58 if ((end.tv_sec < start.tv_sec) || (timeusec > LONG_MAX))
59 timeusec = 0;
60 }
61 return timeusec;
62 }
63
measure_delay(unsigned int usecs)64 static unsigned long measure_delay(unsigned int usecs)
65 {
66 unsigned long timeusec;
67 struct timeval start, end;
68
69 gettimeofday(&start, NULL);
70 myusec_delay(usecs);
71 gettimeofday(&end, NULL);
72 timeusec = 1000000 * (end.tv_sec - start.tv_sec) +
73 (end.tv_usec - start.tv_usec);
74 /* Protect against time going forward too much. */
75 if ((end.tv_sec > start.tv_sec) &&
76 ((end.tv_sec - start.tv_sec) >= LONG_MAX / 1000000 - 1))
77 timeusec = LONG_MAX;
78 /* Protect against time going backwards during leap seconds. */
79 if ((end.tv_sec < start.tv_sec) || (timeusec > LONG_MAX))
80 timeusec = 1;
81
82 return timeusec;
83 }
84
myusec_calibrate_delay(void)85 static void myusec_calibrate_delay(void)
86 {
87 unsigned long count = 1000;
88 unsigned long timeusec, resolution;
89 int i, tries = 0;
90
91 msg_pinfo("Calibrating delay loop... ");
92 /* Timing resolution on DJGPP is about 50ms, but measure it precisely. */
93 resolution = measure_os_delay_resolution();
94 if (resolution) {
95 msg_pdbg("OS timer resolution is %lu usecs, ", resolution);
96 } else {
97 msg_pinfo("OS timer resolution is unusable. ");
98 }
99
100 recalibrate:
101 count = 1000;
102 while (1) {
103 timeusec = measure_delay(count);
104 if (timeusec > 1000000 / 4)
105 break;
106 if (count >= ULONG_MAX / 2) {
107 msg_pinfo("timer loop overflow, reduced precision. ");
108 break;
109 }
110 count *= 2;
111 }
112 tries ++;
113
114 /* Avoid division by zero, but in that case the loop is shot anyway. */
115 if (!timeusec)
116 timeusec = 1;
117
118 /* Compute rounded up number of loops per microsecond. */
119 micro = (count * micro) / timeusec + 1;
120 msg_pdbg("%luM loops per second, ", micro);
121
122 /* Did we try to recalibrate less than 5 times? */
123 if (tries < 5) {
124 /* Recheck our timing to make sure we weren't just hitting
125 * a scheduler delay or something similar.
126 */
127 for (i = 0; i < 4; i++) {
128 timeusec = measure_delay(resolution * 10) *
129 100 / (resolution * 10);
130
131 if (timeusec < 90) {
132 msg_pdbg("delay more than 10%% too short (got "
133 "%lu%% of expected delay), "
134 "recalculating... ", timeusec);
135 goto recalibrate;
136 }
137 }
138 } else {
139 msg_perr("delay loop is unreliable, trying to continue ");
140 }
141
142 /* We're interested in the actual precision. */
143 timeusec = measure_delay(resolution * 4);
144 msg_pdbg("%ld myus = %ld us, ", resolution * 4, timeusec);
145
146 msg_pinfo("OK.\n");
147 }
148
149 /* Not very precise sleep. */
internal_sleep(unsigned int usecs)150 void internal_sleep(unsigned int usecs)
151 {
152 sleep(usecs / 1000000);
153 usleep(usecs % 1000000);
154 }
155
156 static const unsigned min_sleep = CONFIG_DELAY_MINIMUM_SLEEP_US;
157
158 /* Precise delay. */
default_delay(unsigned int usecs)159 void default_delay(unsigned int usecs)
160 {
161 static bool calibrated = false;
162
163 if (usecs < min_sleep) {
164 if (!calibrated) {
165 myusec_calibrate_delay();
166 calibrated = true;
167 }
168 myusec_delay(usecs);
169 } else {
170 internal_sleep(usecs);
171 }
172 }
173