blob: c25d243831d4be0ff119efeef81650b703d948e0 [file] [log] [blame]
Uwe Hermannca7f0e42007-09-09 20:02:45 +00001/*
2 * This file is part of the flashrom project.
3 *
Uwe Hermannd22a1d42007-09-09 20:21:05 +00004 * Copyright (C) 2000 Silicon Integrated System Corporation
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +00005 * Copyright (C) 2009,2010 Carl-Daniel Hailfinger
Uwe Hermannca7f0e42007-09-09 20:02:45 +00006 *
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.
Uwe Hermannca7f0e42007-09-09 20:02:45 +000016 */
17
Patrick Georgia9095a92010-09-30 17:03:32 +000018#ifndef __LIBPAYLOAD__
19
Nico Huber8624e8c2012-11-05 21:46:33 +010020#include <stdbool.h>
Carl-Daniel Hailfinger831e8f42010-05-30 22:24:40 +000021#include <unistd.h>
Nico Huber8624e8c2012-11-05 21:46:33 +010022#include <errno.h>
Stefan Tauner839db6d2015-11-14 02:55:22 +000023#include <time.h>
Ollie Lhocbbf1252004-03-17 22:22:08 +000024#include <sys/time.h>
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +000025#include <stdlib.h>
26#include <limits.h>
Uwe Hermann0846f892007-08-23 13:34:59 +000027#include "flash.h"
Jacob Garber6c683632019-06-21 15:33:09 -060028#include "programmer.h"
Ollie Lhocbbf1252004-03-17 22:22:08 +000029
Nico Huber8624e8c2012-11-05 21:46:33 +010030static bool use_clock_gettime = false;
31
32#if HAVE_CLOCK_GETTIME == 1
33
34#ifdef _POSIX_MONOTONIC_CLOCK
35static clockid_t clock_id = CLOCK_MONOTONIC;
36#else
37static clockid_t clock_id = CLOCK_REALTIME;
38#endif
39
40static void clock_usec_delay(int usecs)
41{
42 struct timespec now;
43 clock_gettime(clock_id, &now);
44
45 const long end_nsec = now.tv_nsec + usecs * 1000L;
46 const struct timespec end = {
47 end_nsec / (1000 * 1000 * 1000) + now.tv_sec,
48 end_nsec % (1000 * 1000 * 1000)
49 };
50 do {
51 clock_gettime(clock_id, &now);
52 } while (now.tv_sec < end.tv_sec || (now.tv_sec == end.tv_sec && now.tv_nsec < end.tv_nsec));
53}
54
55static int clock_check_res(void)
56{
57 struct timespec res;
58 if (!clock_getres(clock_id, &res)) {
59 if (res.tv_sec == 0 && res.tv_nsec <= 100) {
60 msg_pinfo("Using clock_gettime for delay loops (clk_id: %d, resolution: %ldns).\n",
61 (int)clock_id, res.tv_nsec);
62 use_clock_gettime = true;
63 return 1;
64 }
65 } else if (clock_id != CLOCK_REALTIME && errno == EINVAL) {
66 /* Try again with CLOCK_REALTIME. */
67 clock_id = CLOCK_REALTIME;
68 return clock_check_res();
69 }
70 return 0;
71}
72#else
73
74static inline void clock_usec_delay(int usecs) {}
75static inline int clock_check_res(void) { return 0; }
76
77#endif /* HAVE_CLOCK_GETTIME == 1 */
78
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000079/* loops per microsecond */
Carl-Daniel Hailfingerad3cc552010-07-03 11:02:10 +000080static unsigned long micro = 1;
Ollie Lhocbbf1252004-03-17 22:22:08 +000081
Stefan Taunerf80419c2014-05-02 15:41:42 +000082__attribute__ ((noinline)) void myusec_delay(unsigned int usecs)
Ollie Lhocbbf1252004-03-17 22:22:08 +000083{
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000084 unsigned long i;
85 for (i = 0; i < usecs * micro; i++) {
86 /* Make sure the compiler doesn't optimize the loop away. */
Carl-Daniel Hailfinger1c6d2ff2012-08-27 00:44:42 +000087 __asm__ volatile ("" : : "rm" (i) );
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000088 }
Ollie Lhocbbf1252004-03-17 22:22:08 +000089}
90
Carl-Daniel Hailfingerad3cc552010-07-03 11:02:10 +000091static unsigned long measure_os_delay_resolution(void)
Carl-Daniel Hailfingerb929d112010-06-03 21:48:13 +000092{
93 unsigned long timeusec;
94 struct timeval start, end;
95 unsigned long counter = 0;
Elyes HAOUAS0cacb112019-02-04 12:16:38 +010096
Peter Huewe73f8ec82011-01-24 19:15:51 +000097 gettimeofday(&start, NULL);
Carl-Daniel Hailfingerb929d112010-06-03 21:48:13 +000098 timeusec = 0;
Elyes HAOUAS0cacb112019-02-04 12:16:38 +010099
Carl-Daniel Hailfingerb929d112010-06-03 21:48:13 +0000100 while (!timeusec && (++counter < 1000000000)) {
Peter Huewe73f8ec82011-01-24 19:15:51 +0000101 gettimeofday(&end, NULL);
Carl-Daniel Hailfingerb929d112010-06-03 21:48:13 +0000102 timeusec = 1000000 * (end.tv_sec - start.tv_sec) +
103 (end.tv_usec - start.tv_usec);
104 /* Protect against time going forward too much. */
105 if ((end.tv_sec > start.tv_sec) &&
106 ((end.tv_sec - start.tv_sec) >= LONG_MAX / 1000000 - 1))
107 timeusec = 0;
108 /* Protect against time going backwards during leap seconds. */
109 if ((end.tv_sec < start.tv_sec) || (timeusec > LONG_MAX))
110 timeusec = 0;
111 }
112 return timeusec;
113}
114
Stefan Taunerf80419c2014-05-02 15:41:42 +0000115static unsigned long measure_delay(unsigned int usecs)
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +0000116{
117 unsigned long timeusec;
118 struct timeval start, end;
Elyes HAOUAS0cacb112019-02-04 12:16:38 +0100119
Peter Huewe73f8ec82011-01-24 19:15:51 +0000120 gettimeofday(&start, NULL);
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +0000121 myusec_delay(usecs);
Peter Huewe73f8ec82011-01-24 19:15:51 +0000122 gettimeofday(&end, NULL);
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +0000123 timeusec = 1000000 * (end.tv_sec - start.tv_sec) +
124 (end.tv_usec - start.tv_usec);
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +0000125 /* Protect against time going forward too much. */
126 if ((end.tv_sec > start.tv_sec) &&
127 ((end.tv_sec - start.tv_sec) >= LONG_MAX / 1000000 - 1))
128 timeusec = LONG_MAX;
129 /* Protect against time going backwards during leap seconds. */
130 if ((end.tv_sec < start.tv_sec) || (timeusec > LONG_MAX))
131 timeusec = 1;
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +0000132
133 return timeusec;
134}
135
Uwe Hermann7b2969b2009-04-15 10:52:49 +0000136void myusec_calibrate_delay(void)
Ollie Lhocbbf1252004-03-17 22:22:08 +0000137{
Nico Huber8624e8c2012-11-05 21:46:33 +0100138 if (clock_check_res())
139 return;
140
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +0000141 unsigned long count = 1000;
Carl-Daniel Hailfingerb929d112010-06-03 21:48:13 +0000142 unsigned long timeusec, resolution;
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +0000143 int i, tries = 0;
Ollie Lhocbbf1252004-03-17 22:22:08 +0000144
Carl-Daniel Hailfinger831e8f42010-05-30 22:24:40 +0000145 msg_pinfo("Calibrating delay loop... ");
Carl-Daniel Hailfingerb929d112010-06-03 21:48:13 +0000146 resolution = measure_os_delay_resolution();
147 if (resolution) {
Carl-Daniel Hailfinger9f5f2152010-06-04 23:20:21 +0000148 msg_pdbg("OS timer resolution is %lu usecs, ", resolution);
Carl-Daniel Hailfingerb929d112010-06-03 21:48:13 +0000149 } else {
150 msg_pinfo("OS timer resolution is unusable. ");
151 }
Stefan Reinauer70385642007-04-06 11:58:03 +0000152
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +0000153recalibrate:
Urja Rannikkof6404012010-04-09 00:02:38 +0000154 count = 1000;
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +0000155 while (1) {
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +0000156 timeusec = measure_delay(count);
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +0000157 if (timeusec > 1000000 / 4)
158 break;
159 if (count >= ULONG_MAX / 2) {
160 msg_pinfo("timer loop overflow, reduced precision. ");
161 break;
162 }
Ollie Lhocbbf1252004-03-17 22:22:08 +0000163 count *= 2;
Ollie Lhocbbf1252004-03-17 22:22:08 +0000164 }
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +0000165 tries ++;
Ollie Lhocbbf1252004-03-17 22:22:08 +0000166
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +0000167 /* Avoid division by zero, but in that case the loop is shot anyway. */
168 if (!timeusec)
169 timeusec = 1;
Elyes HAOUAS124ef382018-03-27 12:15:09 +0200170
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +0000171 /* Compute rounded up number of loops per microsecond. */
172 micro = (count * micro) / timeusec + 1;
173 msg_pdbg("%luM loops per second, ", micro);
174
175 /* Did we try to recalibrate less than 5 times? */
176 if (tries < 5) {
177 /* Recheck our timing to make sure we weren't just hitting
178 * a scheduler delay or something similar.
179 */
180 for (i = 0; i < 4; i++) {
Carl-Daniel Hailfingerb929d112010-06-03 21:48:13 +0000181 if (resolution && (resolution < 10)) {
182 timeusec = measure_delay(100);
Elyes HAOUAS124ef382018-03-27 12:15:09 +0200183 } else if (resolution &&
Carl-Daniel Hailfingerb929d112010-06-03 21:48:13 +0000184 (resolution < ULONG_MAX / 200)) {
185 timeusec = measure_delay(resolution * 10) *
186 100 / (resolution * 10);
187 } else {
188 /* This workaround should be active for broken
189 * OS and maybe libpayload. The criterion
190 * here is horrible or non-measurable OS timer
191 * resolution which will result in
192 * measure_delay(100)=0 whereas a longer delay
193 * (1000 ms) may be sufficient
194 * to get a nonzero time measurement.
195 */
196 timeusec = measure_delay(1000000) / 10000;
197 }
198 if (timeusec < 90) {
199 msg_pdbg("delay more than 10%% too short (got "
200 "%lu%% of expected delay), "
201 "recalculating... ", timeusec);
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +0000202 goto recalibrate;
203 }
204 }
205 } else {
206 msg_perr("delay loop is unreliable, trying to continue ");
207 }
Ollie Lhocbbf1252004-03-17 22:22:08 +0000208
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +0000209 /* We're interested in the actual precision. */
210 timeusec = measure_delay(10);
211 msg_pdbg("10 myus = %ld us, ", timeusec);
212 timeusec = measure_delay(100);
213 msg_pdbg("100 myus = %ld us, ", timeusec);
214 timeusec = measure_delay(1000);
215 msg_pdbg("1000 myus = %ld us, ", timeusec);
216 timeusec = measure_delay(10000);
217 msg_pdbg("10000 myus = %ld us, ", timeusec);
Carl-Daniel Hailfingerb929d112010-06-03 21:48:13 +0000218 timeusec = measure_delay(resolution * 4);
219 msg_pdbg("%ld myus = %ld us, ", resolution * 4, timeusec);
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +0000220
Carl-Daniel Hailfinger831e8f42010-05-30 22:24:40 +0000221 msg_pinfo("OK.\n");
Ollie Lhocbbf1252004-03-17 22:22:08 +0000222}
Carl-Daniel Hailfinger36cc1c82009-12-24 03:11:55 +0000223
Maksim Kuleshov73dc0db2013-04-05 08:06:10 +0000224/* Not very precise sleep. */
Stefan Taunerf80419c2014-05-02 15:41:42 +0000225void internal_sleep(unsigned int usecs)
Maksim Kuleshov73dc0db2013-04-05 08:06:10 +0000226{
Stefan Taunerb0eee9b2015-01-10 09:32:50 +0000227#if IS_WINDOWS
Maksim Kuleshov73dc0db2013-04-05 08:06:10 +0000228 Sleep((usecs + 999) / 1000);
Stefan Tauner839db6d2015-11-14 02:55:22 +0000229#elif defined(__DJGPP__)
Maksim Kuleshov73dc0db2013-04-05 08:06:10 +0000230 sleep(usecs / 1000000);
231 usleep(usecs % 1000000);
Stefan Tauner839db6d2015-11-14 02:55:22 +0000232#else
233 nanosleep(&(struct timespec){usecs / 1000000, (usecs * 1000) % 1000000000UL}, NULL);
Maksim Kuleshov73dc0db2013-04-05 08:06:10 +0000234#endif
235}
236
237/* Precise delay. */
Stefan Taunerf80419c2014-05-02 15:41:42 +0000238void internal_delay(unsigned int usecs)
Carl-Daniel Hailfinger36cc1c82009-12-24 03:11:55 +0000239{
Brian Norris5a09d1e2024-02-29 16:29:51 -0800240 /* If the delay is >0.1 s, use internal_sleep because timing does not need to be so precise. */
241 if (usecs > 100000) {
Maksim Kuleshov73dc0db2013-04-05 08:06:10 +0000242 internal_sleep(usecs);
Nico Huber8624e8c2012-11-05 21:46:33 +0100243 } else if (use_clock_gettime) {
244 clock_usec_delay(usecs);
Carl-Daniel Hailfinger36cc1c82009-12-24 03:11:55 +0000245 } else {
246 myusec_delay(usecs);
247 }
248}
249
Elyes HAOUAS1b365932018-04-25 16:03:51 +0200250#else
Patrick Georgi97bc95c2011-03-08 07:17:44 +0000251#include <libpayload.h>
Patrick Georgia9095a92010-09-30 17:03:32 +0000252
253void myusec_calibrate_delay(void)
254{
255 get_cpu_speed();
256}
257
Stefan Taunerf80419c2014-05-02 15:41:42 +0000258void internal_delay(unsigned int usecs)
Patrick Georgia9095a92010-09-30 17:03:32 +0000259{
260 udelay(usecs);
261}
262#endif