source: trunk/udelay.c @ 1280

Revision 1280, 5.2 KB checked in by oxygene, 15 months ago (diff)

Fix and improve libpayload platform support

  • Fix various minor compile issues (eg. include necessary standard headers)
  • Fix compilation of libpayload code paths
  • Provide libpayload support in Makefile
  • Add make target "libflashrom.a" which links non-CLI code to static library

Signed-off-by: Patrick Georgi <patrick.georgi@…>
Tested-with-DOS-crosscompiler-by: Idwer Vollering <vidwer@…>
Acked-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006@…>

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
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 * 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
22#ifndef __LIBPAYLOAD__
23
24#include <unistd.h>
25#include <sys/time.h>
26#include <stdlib.h>
27#include <limits.h>
28#include "flash.h"
29
30/* loops per microsecond */
31static unsigned long micro = 1;
32
33__attribute__ ((noinline)) void myusec_delay(int usecs)
34{
35        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        }
40}
41
42static unsigned long measure_os_delay_resolution(void)
43{
44        unsigned long timeusec;
45        struct timeval start, end;
46        unsigned long counter = 0;
47       
48        gettimeofday(&start, NULL);
49        timeusec = 0;
50       
51        while (!timeusec && (++counter < 1000000000)) {
52                gettimeofday(&end, NULL);
53                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
66static unsigned long measure_delay(int usecs)
67{
68        unsigned long timeusec;
69        struct timeval start, end;
70       
71        gettimeofday(&start, NULL);
72        myusec_delay(usecs);
73        gettimeofday(&end, NULL);
74        timeusec = 1000000 * (end.tv_sec - start.tv_sec) +
75                   (end.tv_usec - start.tv_usec);
76        /* 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;
83
84        return timeusec;
85}
86
87void myusec_calibrate_delay(void)
88{
89        unsigned long count = 1000;
90        unsigned long timeusec, resolution;
91        int i, tries = 0;
92
93        msg_pinfo("Calibrating delay loop... ");
94        resolution = measure_os_delay_resolution();
95        if (resolution) {
96                msg_pdbg("OS timer resolution is %lu usecs, ", resolution);
97        } else {
98                msg_pinfo("OS timer resolution is unusable. ");
99        }
100
101recalibrate:
102        count = 1000;
103        while (1) {
104                timeusec = measure_delay(count);
105                if (timeusec > 1000000 / 4)
106                        break;
107                if (count >= ULONG_MAX / 2) {
108                        msg_pinfo("timer loop overflow, reduced precision. ");
109                        break;
110                }
111                count *= 2;
112        }
113        tries ++;
114
115        /* 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++) {
129                        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);
150                                goto recalibrate;
151                        }
152                }
153        } else {
154                msg_perr("delay loop is unreliable, trying to continue ");
155        }
156
157        /* 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);
166        timeusec = measure_delay(resolution * 4);
167        msg_pdbg("%ld myus = %ld us, ", resolution * 4, timeusec);
168
169        msg_pinfo("OK.\n");
170}
171
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
184#else
185#include <libpayload.h>
186
187void myusec_calibrate_delay(void)
188{
189        get_cpu_speed();
190}
191
192void internal_delay(int usecs)
193{
194        udelay(usecs);
195}
196#endif
Note: See TracBrowser for help on using the repository browser.