blob: 5faa0045005ee5bbf982cc96c7f550c490749b12 [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>
Ollie Lhocbbf1252004-03-17 22:22:08 +000025#include <sys/time.h>
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +000026#include <stdlib.h>
27#include <limits.h>
Uwe Hermann0846f892007-08-23 13:34:59 +000028#include "flash.h"
Ollie Lhocbbf1252004-03-17 22:22:08 +000029
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000030/* loops per microsecond */
Carl-Daniel Hailfingerad3cc552010-07-03 11:02:10 +000031static unsigned long micro = 1;
Ollie Lhocbbf1252004-03-17 22:22:08 +000032
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000033__attribute__ ((noinline)) void myusec_delay(int usecs)
Ollie Lhocbbf1252004-03-17 22:22:08 +000034{
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000035 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 }
Ollie Lhocbbf1252004-03-17 22:22:08 +000040}
41
Carl-Daniel Hailfingerad3cc552010-07-03 11:02:10 +000042static unsigned long measure_os_delay_resolution(void)
Carl-Daniel Hailfingerb929d112010-06-03 21:48:13 +000043{
44 unsigned long timeusec;
45 struct timeval start, end;
46 unsigned long counter = 0;
47
Peter Huewe73f8ec82011-01-24 19:15:51 +000048 gettimeofday(&start, NULL);
Carl-Daniel Hailfingerb929d112010-06-03 21:48:13 +000049 timeusec = 0;
50
51 while (!timeusec && (++counter < 1000000000)) {
Peter Huewe73f8ec82011-01-24 19:15:51 +000052 gettimeofday(&end, NULL);
Carl-Daniel Hailfingerb929d112010-06-03 21:48:13 +000053 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
Carl-Daniel Hailfingerad3cc552010-07-03 11:02:10 +000066static unsigned long measure_delay(int usecs)
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +000067{
68 unsigned long timeusec;
69 struct timeval start, end;
70
Peter Huewe73f8ec82011-01-24 19:15:51 +000071 gettimeofday(&start, NULL);
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +000072 myusec_delay(usecs);
Peter Huewe73f8ec82011-01-24 19:15:51 +000073 gettimeofday(&end, NULL);
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +000074 timeusec = 1000000 * (end.tv_sec - start.tv_sec) +
75 (end.tv_usec - start.tv_usec);
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000076 /* 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;
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +000083
84 return timeusec;
85}
86
Uwe Hermann7b2969b2009-04-15 10:52:49 +000087void myusec_calibrate_delay(void)
Ollie Lhocbbf1252004-03-17 22:22:08 +000088{
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000089 unsigned long count = 1000;
Carl-Daniel Hailfingerb929d112010-06-03 21:48:13 +000090 unsigned long timeusec, resolution;
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000091 int i, tries = 0;
Ollie Lhocbbf1252004-03-17 22:22:08 +000092
Carl-Daniel Hailfinger831e8f42010-05-30 22:24:40 +000093 msg_pinfo("Calibrating delay loop... ");
Carl-Daniel Hailfingerb929d112010-06-03 21:48:13 +000094 resolution = measure_os_delay_resolution();
95 if (resolution) {
Carl-Daniel Hailfinger9f5f2152010-06-04 23:20:21 +000096 msg_pdbg("OS timer resolution is %lu usecs, ", resolution);
Carl-Daniel Hailfingerb929d112010-06-03 21:48:13 +000097 } else {
98 msg_pinfo("OS timer resolution is unusable. ");
99 }
Stefan Reinauer70385642007-04-06 11:58:03 +0000100
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +0000101recalibrate:
Urja Rannikkof6404012010-04-09 00:02:38 +0000102 count = 1000;
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +0000103 while (1) {
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +0000104 timeusec = measure_delay(count);
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +0000105 if (timeusec > 1000000 / 4)
106 break;
107 if (count >= ULONG_MAX / 2) {
108 msg_pinfo("timer loop overflow, reduced precision. ");
109 break;
110 }
Ollie Lhocbbf1252004-03-17 22:22:08 +0000111 count *= 2;
Ollie Lhocbbf1252004-03-17 22:22:08 +0000112 }
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +0000113 tries ++;
Ollie Lhocbbf1252004-03-17 22:22:08 +0000114
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +0000115 /* 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++) {
Carl-Daniel Hailfingerb929d112010-06-03 21:48:13 +0000129 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);
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +0000150 goto recalibrate;
151 }
152 }
153 } else {
154 msg_perr("delay loop is unreliable, trying to continue ");
155 }
Ollie Lhocbbf1252004-03-17 22:22:08 +0000156
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +0000157 /* 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);
Carl-Daniel Hailfingerb929d112010-06-03 21:48:13 +0000166 timeusec = measure_delay(resolution * 4);
167 msg_pdbg("%ld myus = %ld us, ", resolution * 4, timeusec);
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +0000168
Carl-Daniel Hailfinger831e8f42010-05-30 22:24:40 +0000169 msg_pinfo("OK.\n");
Ollie Lhocbbf1252004-03-17 22:22:08 +0000170}
Carl-Daniel Hailfinger36cc1c82009-12-24 03:11:55 +0000171
172void 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
Patrick Georgia9095a92010-09-30 17:03:32 +0000184#else
Patrick Georgi97bc95c2011-03-08 07:17:44 +0000185#include <libpayload.h>
Patrick Georgia9095a92010-09-30 17:03:32 +0000186
187void myusec_calibrate_delay(void)
188{
189 get_cpu_speed();
190}
191
192void internal_delay(int usecs)
193{
194 udelay(usecs);
195}
196#endif