blob: 7c6961d13991f7d66fcd19d59fb5db2a82565fd3 [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.
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
Patrick Georgia9095a92010-09-30 17:03:32 +000022#ifndef __LIBPAYLOAD__
23
Carl-Daniel Hailfinger831e8f42010-05-30 22:24:40 +000024#include <unistd.h>
Stefan Tauner839db6d2015-11-14 02:55:22 +000025#include <time.h>
Ollie Lhocbbf1252004-03-17 22:22:08 +000026#include <sys/time.h>
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +000027#include <stdlib.h>
28#include <limits.h>
Uwe Hermann0846f892007-08-23 13:34:59 +000029#include "flash.h"
Ollie Lhocbbf1252004-03-17 22:22:08 +000030
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000031/* loops per microsecond */
Carl-Daniel Hailfingerad3cc552010-07-03 11:02:10 +000032static unsigned long micro = 1;
Ollie Lhocbbf1252004-03-17 22:22:08 +000033
Stefan Taunerf80419c2014-05-02 15:41:42 +000034__attribute__ ((noinline)) void myusec_delay(unsigned int usecs)
Ollie Lhocbbf1252004-03-17 22:22:08 +000035{
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000036 unsigned long i;
37 for (i = 0; i < usecs * micro; i++) {
38 /* Make sure the compiler doesn't optimize the loop away. */
Carl-Daniel Hailfinger1c6d2ff2012-08-27 00:44:42 +000039 __asm__ volatile ("" : : "rm" (i) );
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000040 }
Ollie Lhocbbf1252004-03-17 22:22:08 +000041}
42
Carl-Daniel Hailfingerad3cc552010-07-03 11:02:10 +000043static unsigned long measure_os_delay_resolution(void)
Carl-Daniel Hailfingerb929d112010-06-03 21:48:13 +000044{
45 unsigned long timeusec;
46 struct timeval start, end;
47 unsigned long counter = 0;
48
Peter Huewe73f8ec82011-01-24 19:15:51 +000049 gettimeofday(&start, NULL);
Carl-Daniel Hailfingerb929d112010-06-03 21:48:13 +000050 timeusec = 0;
51
52 while (!timeusec && (++counter < 1000000000)) {
Peter Huewe73f8ec82011-01-24 19:15:51 +000053 gettimeofday(&end, NULL);
Carl-Daniel Hailfingerb929d112010-06-03 21:48:13 +000054 timeusec = 1000000 * (end.tv_sec - start.tv_sec) +
55 (end.tv_usec - start.tv_usec);
56 /* Protect against time going forward too much. */
57 if ((end.tv_sec > start.tv_sec) &&
58 ((end.tv_sec - start.tv_sec) >= LONG_MAX / 1000000 - 1))
59 timeusec = 0;
60 /* Protect against time going backwards during leap seconds. */
61 if ((end.tv_sec < start.tv_sec) || (timeusec > LONG_MAX))
62 timeusec = 0;
63 }
64 return timeusec;
65}
66
Stefan Taunerf80419c2014-05-02 15:41:42 +000067static unsigned long measure_delay(unsigned int usecs)
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +000068{
69 unsigned long timeusec;
70 struct timeval start, end;
71
Peter Huewe73f8ec82011-01-24 19:15:51 +000072 gettimeofday(&start, NULL);
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +000073 myusec_delay(usecs);
Peter Huewe73f8ec82011-01-24 19:15:51 +000074 gettimeofday(&end, NULL);
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +000075 timeusec = 1000000 * (end.tv_sec - start.tv_sec) +
76 (end.tv_usec - start.tv_usec);
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000077 /* Protect against time going forward too much. */
78 if ((end.tv_sec > start.tv_sec) &&
79 ((end.tv_sec - start.tv_sec) >= LONG_MAX / 1000000 - 1))
80 timeusec = LONG_MAX;
81 /* Protect against time going backwards during leap seconds. */
82 if ((end.tv_sec < start.tv_sec) || (timeusec > LONG_MAX))
83 timeusec = 1;
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +000084
85 return timeusec;
86}
87
Uwe Hermann7b2969b2009-04-15 10:52:49 +000088void myusec_calibrate_delay(void)
Ollie Lhocbbf1252004-03-17 22:22:08 +000089{
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000090 unsigned long count = 1000;
Carl-Daniel Hailfingerb929d112010-06-03 21:48:13 +000091 unsigned long timeusec, resolution;
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000092 int i, tries = 0;
Ollie Lhocbbf1252004-03-17 22:22:08 +000093
Carl-Daniel Hailfinger831e8f42010-05-30 22:24:40 +000094 msg_pinfo("Calibrating delay loop... ");
Carl-Daniel Hailfingerb929d112010-06-03 21:48:13 +000095 resolution = measure_os_delay_resolution();
96 if (resolution) {
Carl-Daniel Hailfinger9f5f2152010-06-04 23:20:21 +000097 msg_pdbg("OS timer resolution is %lu usecs, ", resolution);
Carl-Daniel Hailfingerb929d112010-06-03 21:48:13 +000098 } else {
99 msg_pinfo("OS timer resolution is unusable. ");
100 }
Stefan Reinauer70385642007-04-06 11:58:03 +0000101
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +0000102recalibrate:
Urja Rannikkof6404012010-04-09 00:02:38 +0000103 count = 1000;
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +0000104 while (1) {
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +0000105 timeusec = measure_delay(count);
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +0000106 if (timeusec > 1000000 / 4)
107 break;
108 if (count >= ULONG_MAX / 2) {
109 msg_pinfo("timer loop overflow, reduced precision. ");
110 break;
111 }
Ollie Lhocbbf1252004-03-17 22:22:08 +0000112 count *= 2;
Ollie Lhocbbf1252004-03-17 22:22:08 +0000113 }
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +0000114 tries ++;
Ollie Lhocbbf1252004-03-17 22:22:08 +0000115
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +0000116 /* Avoid division by zero, but in that case the loop is shot anyway. */
117 if (!timeusec)
118 timeusec = 1;
119
120 /* Compute rounded up number of loops per microsecond. */
121 micro = (count * micro) / timeusec + 1;
122 msg_pdbg("%luM loops per second, ", micro);
123
124 /* Did we try to recalibrate less than 5 times? */
125 if (tries < 5) {
126 /* Recheck our timing to make sure we weren't just hitting
127 * a scheduler delay or something similar.
128 */
129 for (i = 0; i < 4; i++) {
Carl-Daniel Hailfingerb929d112010-06-03 21:48:13 +0000130 if (resolution && (resolution < 10)) {
131 timeusec = measure_delay(100);
132 } else if (resolution &&
133 (resolution < ULONG_MAX / 200)) {
134 timeusec = 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 */
145 timeusec = measure_delay(1000000) / 10000;
146 }
147 if (timeusec < 90) {
148 msg_pdbg("delay more than 10%% too short (got "
149 "%lu%% of expected delay), "
150 "recalculating... ", timeusec);
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +0000151 goto recalibrate;
152 }
153 }
154 } else {
155 msg_perr("delay loop is unreliable, trying to continue ");
156 }
Ollie Lhocbbf1252004-03-17 22:22:08 +0000157
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +0000158 /* We're interested in the actual precision. */
159 timeusec = measure_delay(10);
160 msg_pdbg("10 myus = %ld us, ", timeusec);
161 timeusec = measure_delay(100);
162 msg_pdbg("100 myus = %ld us, ", timeusec);
163 timeusec = measure_delay(1000);
164 msg_pdbg("1000 myus = %ld us, ", timeusec);
165 timeusec = measure_delay(10000);
166 msg_pdbg("10000 myus = %ld us, ", timeusec);
Carl-Daniel Hailfingerb929d112010-06-03 21:48:13 +0000167 timeusec = measure_delay(resolution * 4);
168 msg_pdbg("%ld myus = %ld us, ", resolution * 4, timeusec);
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +0000169
Carl-Daniel Hailfinger831e8f42010-05-30 22:24:40 +0000170 msg_pinfo("OK.\n");
Ollie Lhocbbf1252004-03-17 22:22:08 +0000171}
Carl-Daniel Hailfinger36cc1c82009-12-24 03:11:55 +0000172
Maksim Kuleshov73dc0db2013-04-05 08:06:10 +0000173/* Not very precise sleep. */
Stefan Taunerf80419c2014-05-02 15:41:42 +0000174void internal_sleep(unsigned int usecs)
Maksim Kuleshov73dc0db2013-04-05 08:06:10 +0000175{
Stefan Taunerb0eee9b2015-01-10 09:32:50 +0000176#if IS_WINDOWS
Maksim Kuleshov73dc0db2013-04-05 08:06:10 +0000177 Sleep((usecs + 999) / 1000);
Stefan Tauner839db6d2015-11-14 02:55:22 +0000178#elif defined(__DJGPP__)
Maksim Kuleshov73dc0db2013-04-05 08:06:10 +0000179 sleep(usecs / 1000000);
180 usleep(usecs % 1000000);
Stefan Tauner839db6d2015-11-14 02:55:22 +0000181#else
182 nanosleep(&(struct timespec){usecs / 1000000, (usecs * 1000) % 1000000000UL}, NULL);
Maksim Kuleshov73dc0db2013-04-05 08:06:10 +0000183#endif
184}
185
186/* Precise delay. */
Stefan Taunerf80419c2014-05-02 15:41:42 +0000187void internal_delay(unsigned int usecs)
Carl-Daniel Hailfinger36cc1c82009-12-24 03:11:55 +0000188{
Maksim Kuleshov73dc0db2013-04-05 08:06:10 +0000189 /* If the delay is >1 s, use internal_sleep because timing does not need to be so precise. */
Carl-Daniel Hailfinger36cc1c82009-12-24 03:11:55 +0000190 if (usecs > 1000000) {
Maksim Kuleshov73dc0db2013-04-05 08:06:10 +0000191 internal_sleep(usecs);
Carl-Daniel Hailfinger36cc1c82009-12-24 03:11:55 +0000192 } else {
193 myusec_delay(usecs);
194 }
195}
196
Patrick Georgia9095a92010-09-30 17:03:32 +0000197#else
Patrick Georgi97bc95c2011-03-08 07:17:44 +0000198#include <libpayload.h>
Patrick Georgia9095a92010-09-30 17:03:32 +0000199
200void myusec_calibrate_delay(void)
201{
202 get_cpu_speed();
203}
204
Stefan Taunerf80419c2014-05-02 15:41:42 +0000205void internal_delay(unsigned int usecs)
Patrick Georgia9095a92010-09-30 17:03:32 +0000206{
207 udelay(usecs);
208}
209#endif