1 /* Yo, Emacs! we're -*- Linux-C -*- 2 * 3 * Copyright (C) 1993-1995 Bas Laarhoven. 4
5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 2, or (at your option) 8 any later version. 9
10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14
15 You should have received a copy of the GNU General Public License 16 along with this program; see the file COPYING. If not, write to 17 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 18
19 * GP calibration routine for processor speed dependent 20 * functions. 21 */ 22
23 #include <linux/module.h>
24 #include <linux/errno.h>
25 #include <linux/sched.h>
26 #include <linux/ftape.h>
27 #include <asm/system.h>
28 #include <asm/io.h>
29
30 #include "tracing.h"
31 #include "calibr.h"
32 #include "fdc-io.h"
33
34 #undefDEBUG 35
36 unsignedtimestamp(void)
/* */ 37 { 38 unsignedcount;
39 unsignedlongflags;
40
41 save_flags(flags);
42 cli();
43 outb_p(0x00, 0x43); /* latch the count ASAP */ 44 count = inb_p(0x40); /* read the latched count */ 45 count |= inb(0x40) << 8;
46 restore_flags(flags);
47 return (LATCH - count); /* normal: downcounter */ 48 } 49
50 inttimediff(intt0, intt1)
/* */ 51 { 52 /* Calculate difference in usec for timestamp results t0 & t1. 53 * Note that the maximum timespan allowed is 1/HZ or we'll lose ticks! 54 */ 55 if (t1 < t0) { 56 t1 += LATCH;
57 } 58 return (1000 * (t1 - t0)) / ((CLOCK_TICK_RATE + 500) / 1000);
59 } 60
61 /* To get an indication of the I/O performance, 62 * measure the duration of the inb() function. 63 */ 64 voidtime_inb(void)
/* */ 65 { 66 TRACE_FUN(8, "time_inb");
67 inti;
68 intt0, t1;
69 unsignedlongflags;
70 intstatus;
71
72 save_flags(flags);
73 cli();
74 t0 = timestamp();
75 for (i = 0; i < 1000; ++i) { 76 status = inb(fdc.msr);
77 } 78 t1 = timestamp();
79 restore_flags(flags);
80 if (t1 - t0 <= 0) { 81 t1 += LATCH;
82 } 83 TRACEx1(4, "inb() duration: %d nsec", timediff(t0, t1));
84 TRACE_EXIT;
85 } 86
87 /* Haven't studied on why, but there sometimes is a problem 88 * with the tick timer readout. The two bytes get swapped. 89 * This hack solves that problem by doing one extra input. 90 */ 91 voidfix_clock(void)
/* */ 92 { 93 TRACE_FUN(8, "fix_clock");
94 intt;
95 inti;
96
97 for (i = 0; i < 1000; ++i) { 98 t = timestamp();
99 if (t < 0) { 100 inb_p(0x40); /* get in sync again */ 101 TRACE(2, "clock counter fixed");
102 break;
103 } 104 } 105 TRACE_EXIT;
106 } 107
108 /* 109 * Input: function taking int count as parameter. 110 * pointers to calculated calibration variables. 111 */ 112 intcalibrate(char *name, void (*fun) (int), int *calibr_count, int *calibr_time)
/* */ 113 { 114 TRACE_FUN(5, "calibrate");
115 staticintfirst_time = 1;
116 inti;
117 intold_tc = 0;
118 intold_count = 1;
119 intold_time = 1;
120
121 if (first_time) {/* get idea of I/O performance */ 122 fix_clock();
123 time_inb();
124 first_time = 0;
125 } 126 /* value of timeout must be set so that on very slow systems 127 * it will give a time less than one jiffy, and on 128 * very fast systems it'll give reasonable precision. 129 */ 130
131 *calibr_count = 10;
132 for (i = 0; i < 15; ++i) { 133 intt0, t1;
134 unsignedlongflags;
135 intonce;
136 intmultiple;
137 inttc;
138
139 *calibr_time = *calibr_count; /* set TC to 1 */ 140 fun(0); /* dummy, get code into cache */ 141 save_flags(flags);
142 cli();
143 t0 = timestamp();
144 fun(0); /* overhead + one test */ 145 t1 = timestamp();
146 if (t1 < t0) { 147 t1 += LATCH;
148 } 149 once = t1 - t0;
150 t0 = timestamp();
151 fun(*calibr_count); /* overhead + multiple tests */ 152 t1 = timestamp();
153 if (t1 < t0) { 154 t1 += LATCH;
155 } 156 multiple = t1 - t0;
157 restore_flags(flags);
158 *calibr_time = (10000 * (multiple - once)) / (CLOCK_TICK_RATE / 100);
159 --*calibr_count; /* because delta corresponds to this count */ 160 tc = (1000 * *calibr_time) / *calibr_count;
161 TRACEx4(8, "once:%4d us,%5d times:%6d us, TC:%5d ns",
162 (10000 * once) / (CLOCK_TICK_RATE / 100),
163 *calibr_count,
164 (10000 * multiple) / (CLOCK_TICK_RATE / 100),
165 tc);
166 /* 167 * increase the count until the resulting time nears 2/HZ, 168 * then the tc will drop sharply because we lose LATCH counts. 169 */ 170 if (tc <= old_tc / 2) { 171 *calibr_time = old_time;
172 *calibr_count = old_count;
173 break;
174 } 175 old_tc = tc;
176 old_count = *calibr_count;
177 old_time = *calibr_time;
178 *calibr_count *= 2;
179 } 180 TRACEx3(4, "TC for `%s()' = %d nsec (at %d counts)",
181 name, (1000 * *calibr_time) / *calibr_count, *calibr_count);
182 TRACE_EXIT;
183 return 0;
184 }