blob: bae646744072bb9bcb0d2690c3aab79c14864177 [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
Ollie Lhocbbf1252004-03-17 22:22:08 +000022#include <sys/time.h>
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +000023#include <stdlib.h>
24#include <limits.h>
Uwe Hermann0846f892007-08-23 13:34:59 +000025#include "flash.h"
Ollie Lhocbbf1252004-03-17 22:22:08 +000026
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000027/* loops per microsecond */
Ollie Lhocbbf1252004-03-17 22:22:08 +000028unsigned long micro = 1;
29
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000030__attribute__ ((noinline)) void myusec_delay(int usecs)
Ollie Lhocbbf1252004-03-17 22:22:08 +000031{
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000032 unsigned long i;
33 for (i = 0; i < usecs * micro; i++) {
34 /* Make sure the compiler doesn't optimize the loop away. */
35 asm volatile ("" : : "rm" (i) );
36 }
Ollie Lhocbbf1252004-03-17 22:22:08 +000037}
38
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +000039unsigned long measure_delay(int usecs)
40{
41 unsigned long timeusec;
42 struct timeval start, end;
43
44 gettimeofday(&start, 0);
45 myusec_delay(usecs);
46 gettimeofday(&end, 0);
47 timeusec = 1000000 * (end.tv_sec - start.tv_sec) +
48 (end.tv_usec - start.tv_usec);
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000049 /* Protect against time going forward too much. */
50 if ((end.tv_sec > start.tv_sec) &&
51 ((end.tv_sec - start.tv_sec) >= LONG_MAX / 1000000 - 1))
52 timeusec = LONG_MAX;
53 /* Protect against time going backwards during leap seconds. */
54 if ((end.tv_sec < start.tv_sec) || (timeusec > LONG_MAX))
55 timeusec = 1;
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +000056
57 return timeusec;
58}
59
Uwe Hermann7b2969b2009-04-15 10:52:49 +000060void myusec_calibrate_delay(void)
Ollie Lhocbbf1252004-03-17 22:22:08 +000061{
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000062 unsigned long count = 1000;
Ollie Lhocbbf1252004-03-17 22:22:08 +000063 unsigned long timeusec;
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000064 int i, tries = 0;
Ollie Lhocbbf1252004-03-17 22:22:08 +000065
Stefan Reinauer70385642007-04-06 11:58:03 +000066 printf("Calibrating delay loop... ");
67
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000068recalibrate:
Urja Rannikkof6404012010-04-09 00:02:38 +000069 count = 1000;
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000070 while (1) {
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +000071 timeusec = measure_delay(count);
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000072 if (timeusec > 1000000 / 4)
73 break;
74 if (count >= ULONG_MAX / 2) {
75 msg_pinfo("timer loop overflow, reduced precision. ");
76 break;
77 }
Ollie Lhocbbf1252004-03-17 22:22:08 +000078 count *= 2;
Ollie Lhocbbf1252004-03-17 22:22:08 +000079 }
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000080 tries ++;
Ollie Lhocbbf1252004-03-17 22:22:08 +000081
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000082 /* Avoid division by zero, but in that case the loop is shot anyway. */
83 if (!timeusec)
84 timeusec = 1;
85
86 /* Compute rounded up number of loops per microsecond. */
87 micro = (count * micro) / timeusec + 1;
88 msg_pdbg("%luM loops per second, ", micro);
89
90 /* Did we try to recalibrate less than 5 times? */
91 if (tries < 5) {
92 /* Recheck our timing to make sure we weren't just hitting
93 * a scheduler delay or something similar.
94 */
95 for (i = 0; i < 4; i++) {
96 if (measure_delay(100) < 90) {
Urja Rannikkof6404012010-04-09 00:02:38 +000097 msg_pdbg("delay more than 10%% too short, "
Carl-Daniel Hailfinger253101e2010-03-31 23:55:06 +000098 "recalculating... ");
99 goto recalibrate;
100 }
101 }
102 } else {
103 msg_perr("delay loop is unreliable, trying to continue ");
104 }
Ollie Lhocbbf1252004-03-17 22:22:08 +0000105
Carl-Daniel Hailfingerb8114612010-03-27 16:16:01 +0000106 /* We're interested in the actual precision. */
107 timeusec = measure_delay(10);
108 msg_pdbg("10 myus = %ld us, ", timeusec);
109 timeusec = measure_delay(100);
110 msg_pdbg("100 myus = %ld us, ", timeusec);
111 timeusec = measure_delay(1000);
112 msg_pdbg("1000 myus = %ld us, ", timeusec);
113 timeusec = measure_delay(10000);
114 msg_pdbg("10000 myus = %ld us, ", timeusec);
115
Uwe Hermanna502dce2007-10-17 23:55:15 +0000116 printf("OK.\n");
Ollie Lhocbbf1252004-03-17 22:22:08 +0000117}
Carl-Daniel Hailfinger36cc1c82009-12-24 03:11:55 +0000118
119void internal_delay(int usecs)
120{
121 /* If the delay is >1 s, use usleep because timing does not need to
122 * be so precise.
123 */
124 if (usecs > 1000000) {
125 usleep(usecs);
126 } else {
127 myusec_delay(usecs);
128 }
129}
130