1*843e1988Sjohnlev /*
2*843e1988Sjohnlev * CDDL HEADER START
3*843e1988Sjohnlev *
4*843e1988Sjohnlev * The contents of this file are subject to the terms of the
5*843e1988Sjohnlev * Common Development and Distribution License (the "License").
6*843e1988Sjohnlev * You may not use this file except in compliance with the License.
7*843e1988Sjohnlev *
8*843e1988Sjohnlev * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*843e1988Sjohnlev * or http://www.opensolaris.org/os/licensing.
10*843e1988Sjohnlev * See the License for the specific language governing permissions
11*843e1988Sjohnlev * and limitations under the License.
12*843e1988Sjohnlev *
13*843e1988Sjohnlev * When distributing Covered Code, include this CDDL HEADER in each
14*843e1988Sjohnlev * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*843e1988Sjohnlev * If applicable, add the following below this CDDL HEADER, with the
16*843e1988Sjohnlev * fields enclosed by brackets "[]" replaced with your own identifying
17*843e1988Sjohnlev * information: Portions Copyright [yyyy] [name of copyright owner]
18*843e1988Sjohnlev *
19*843e1988Sjohnlev * CDDL HEADER END
20*843e1988Sjohnlev */
21*843e1988Sjohnlev /*
22*843e1988Sjohnlev * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23*843e1988Sjohnlev * Use is subject to license terms.
24*843e1988Sjohnlev */
25*843e1988Sjohnlev
26*843e1988Sjohnlev /* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
27*843e1988Sjohnlev /* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */
28*843e1988Sjohnlev /* All Rights Reserved */
29*843e1988Sjohnlev
30*843e1988Sjohnlev #pragma ident "%Z%%M% %I% %E% SMI"
31*843e1988Sjohnlev
32*843e1988Sjohnlev #include <sys/types.h>
33*843e1988Sjohnlev #include <sys/dl.h>
34*843e1988Sjohnlev #include <sys/param.h>
35*843e1988Sjohnlev #include <sys/pit.h>
36*843e1988Sjohnlev #include <sys/inline.h>
37*843e1988Sjohnlev #include <sys/machlock.h>
38*843e1988Sjohnlev #include <sys/avintr.h>
39*843e1988Sjohnlev #include <sys/smp_impldefs.h>
40*843e1988Sjohnlev #include <sys/archsystm.h>
41*843e1988Sjohnlev #include <sys/systm.h>
42*843e1988Sjohnlev #include <sys/machsystm.h>
43*843e1988Sjohnlev
44*843e1988Sjohnlev #define PIT_COUNTDOWN (PIT_READMODE | PIT_NDIVMODE)
45*843e1988Sjohnlev #define MICROCOUNT 0x2000
46*843e1988Sjohnlev
47*843e1988Sjohnlev /*
48*843e1988Sjohnlev * Loop count for 10 microsecond wait. MUST be initialized for those who
49*843e1988Sjohnlev * insist on calling "tenmicrosec" before the clock has been initialized.
50*843e1988Sjohnlev */
51*843e1988Sjohnlev unsigned int microdata = 50;
52*843e1988Sjohnlev
53*843e1988Sjohnlev void
microfind(void)54*843e1988Sjohnlev microfind(void)
55*843e1988Sjohnlev {
56*843e1988Sjohnlev uint64_t max, count = MICROCOUNT;
57*843e1988Sjohnlev
58*843e1988Sjohnlev /*
59*843e1988Sjohnlev * The algorithm tries to guess a loop count for tenmicrosec such
60*843e1988Sjohnlev * that found will be 0xf000 PIT counts, but because it is only a
61*843e1988Sjohnlev * rough guess there is no guarantee that tenmicrosec will take
62*843e1988Sjohnlev * exactly 0xf000 PIT counts. min is set initially to 0xe000 and
63*843e1988Sjohnlev * represents the number of PIT counts that must elapse in
64*843e1988Sjohnlev * tenmicrosec for microfind to calculate the correct loop count for
65*843e1988Sjohnlev * tenmicrosec. The algorith will successively set count to better
66*843e1988Sjohnlev * approximations until the number of PIT counts elapsed are greater
67*843e1988Sjohnlev * than min. Ideally the first guess should be correct, but as cpu's
68*843e1988Sjohnlev * become faster MICROCOUNT may have to be increased to ensure
69*843e1988Sjohnlev * that the first guess for count is correct. There is no harm
70*843e1988Sjohnlev * leaving MICRCOUNT at 0x2000, the results will be correct, it just
71*843e1988Sjohnlev * may take longer to calculate the correct value for the loop
72*843e1988Sjohnlev * count used by tenmicrosec. In some cases min may be reset as the
73*843e1988Sjohnlev * algorithm progresses in order to facilitate faster cpu's.
74*843e1988Sjohnlev */
75*843e1988Sjohnlev unsigned long found, min = 0xe000;
76*843e1988Sjohnlev ulong_t s;
77*843e1988Sjohnlev unsigned char status;
78*843e1988Sjohnlev
79*843e1988Sjohnlev s = clear_int_flag(); /* disable interrupts */
80*843e1988Sjohnlev
81*843e1988Sjohnlev /*CONSTCOND*/
82*843e1988Sjohnlev while (1) {
83*843e1988Sjohnlev
84*843e1988Sjohnlev /*
85*843e1988Sjohnlev * microdata is the loop count used in tenmicrosec. The first
86*843e1988Sjohnlev * time around microdata is set to 1 to make tenmicrosec
87*843e1988Sjohnlev * return quickly. The purpose of this while loop is to
88*843e1988Sjohnlev * warm the cache for the next time around when the number
89*843e1988Sjohnlev * of PIT counts are measured.
90*843e1988Sjohnlev */
91*843e1988Sjohnlev microdata = 1;
92*843e1988Sjohnlev
93*843e1988Sjohnlev /*CONSTCOND*/
94*843e1988Sjohnlev while (1) {
95*843e1988Sjohnlev /* Put counter 0 in mode 0 */
96*843e1988Sjohnlev outb(PITCTL_PORT, PIT_LOADMODE);
97*843e1988Sjohnlev /* output a count of -1 to counter 0 */
98*843e1988Sjohnlev outb(PITCTR0_PORT, 0xff);
99*843e1988Sjohnlev outb(PITCTR0_PORT, 0xff);
100*843e1988Sjohnlev tenmicrosec();
101*843e1988Sjohnlev
102*843e1988Sjohnlev /* READ BACK counter 0 to latch status and count */
103*843e1988Sjohnlev outb(PITCTL_PORT, PIT_READBACK|PIT_READBACKC0);
104*843e1988Sjohnlev
105*843e1988Sjohnlev /* Read status of counter 0 */
106*843e1988Sjohnlev status = inb(PITCTR0_PORT);
107*843e1988Sjohnlev
108*843e1988Sjohnlev /* Read the value left in the counter */
109*843e1988Sjohnlev found = inb(PITCTR0_PORT) | (inb(PITCTR0_PORT) << 8);
110*843e1988Sjohnlev
111*843e1988Sjohnlev if (microdata != 1)
112*843e1988Sjohnlev break;
113*843e1988Sjohnlev
114*843e1988Sjohnlev microdata = count;
115*843e1988Sjohnlev }
116*843e1988Sjohnlev
117*843e1988Sjohnlev /* verify that the counter began the count-down */
118*843e1988Sjohnlev if (status & (1 << PITSTAT_NULLCNT)) {
119*843e1988Sjohnlev /* microdata is too small */
120*843e1988Sjohnlev count = count << 1;
121*843e1988Sjohnlev
122*843e1988Sjohnlev /*
123*843e1988Sjohnlev * If the cpu is so fast that it cannot load the
124*843e1988Sjohnlev * counting element of the PIT with a very large
125*843e1988Sjohnlev * value for the loop used in tenmicrosec, then
126*843e1988Sjohnlev * the algorithm will not work for this cpu.
127*843e1988Sjohnlev * It is very unlikely there will ever be such
128*843e1988Sjohnlev * an x86.
129*843e1988Sjohnlev */
130*843e1988Sjohnlev if (count > 0x100000000)
131*843e1988Sjohnlev panic("microfind: cpu is too fast");
132*843e1988Sjohnlev
133*843e1988Sjohnlev continue;
134*843e1988Sjohnlev }
135*843e1988Sjohnlev
136*843e1988Sjohnlev /* verify that the counter did not wrap around */
137*843e1988Sjohnlev if (status & (1 << PITSTAT_OUTPUT)) {
138*843e1988Sjohnlev /*
139*843e1988Sjohnlev * microdata is too large. Since there are counts
140*843e1988Sjohnlev * that would have been appropriate for the PIT
141*843e1988Sjohnlev * not to wrap on even a lowly AT, count will never
142*843e1988Sjohnlev * decrease to 1.
143*843e1988Sjohnlev */
144*843e1988Sjohnlev count = count >> 1;
145*843e1988Sjohnlev continue;
146*843e1988Sjohnlev }
147*843e1988Sjohnlev
148*843e1988Sjohnlev /* mode 0 is an n + 1 counter */
149*843e1988Sjohnlev found = 0x10000 - found;
150*843e1988Sjohnlev if (found > min)
151*843e1988Sjohnlev break;
152*843e1988Sjohnlev
153*843e1988Sjohnlev /* verify that the cpu is slow enough to count to 0xf000 */
154*843e1988Sjohnlev count *= 0xf000;
155*843e1988Sjohnlev max = 0x100000001 * found;
156*843e1988Sjohnlev
157*843e1988Sjohnlev /*
158*843e1988Sjohnlev * It is possible that at some point cpu's will become
159*843e1988Sjohnlev * sufficiently fast such that the PIT will not be able to
160*843e1988Sjohnlev * count to 0xf000 within the maximum loop count used in
161*843e1988Sjohnlev * tenmicrosec. In that case the loop count in tenmicrosec
162*843e1988Sjohnlev * may be set to the maximum value because it is unlikely
163*843e1988Sjohnlev * that the cpu will be so fast that tenmicrosec with the
164*843e1988Sjohnlev * maximum loop count will take more than ten microseconds.
165*843e1988Sjohnlev * If the cpu is indeed too fast for the current
166*843e1988Sjohnlev * implementation of tenmicrosec, then there is code below
167*843e1988Sjohnlev * intended to catch that situation.
168*843e1988Sjohnlev */
169*843e1988Sjohnlev if (count >= max) {
170*843e1988Sjohnlev /* cpu is fast, just make it count as high it can */
171*843e1988Sjohnlev count = 0x100000000;
172*843e1988Sjohnlev min = 0;
173*843e1988Sjohnlev continue;
174*843e1988Sjohnlev }
175*843e1988Sjohnlev
176*843e1988Sjohnlev /*
177*843e1988Sjohnlev * Count in the neighborhood of 0xf000 next time around
178*843e1988Sjohnlev * There is no risk of dividing by zero since found is in the
179*843e1988Sjohnlev * range of 0x1 to 0x1000.
180*843e1988Sjohnlev */
181*843e1988Sjohnlev count = count / found;
182*843e1988Sjohnlev }
183*843e1988Sjohnlev
184*843e1988Sjohnlev /*
185*843e1988Sjohnlev * Formula for delaycount is :
186*843e1988Sjohnlev * (loopcount * timer clock speed) / (counter ticks * 1000)
187*843e1988Sjohnlev * Note also that 1000 is for figuring out milliseconds
188*843e1988Sjohnlev */
189*843e1988Sjohnlev count *= PIT_HZ;
190*843e1988Sjohnlev max = ((uint64_t)found) * 100000;
191*843e1988Sjohnlev count = count / max; /* max is never zero */
192*843e1988Sjohnlev
193*843e1988Sjohnlev if (count >= 0x100000001)
194*843e1988Sjohnlev /*
195*843e1988Sjohnlev * This cpu is too fast for the current implementation of
196*843e1988Sjohnlev * tenmicrosec. It is unlikely such a fast x86 will exist.
197*843e1988Sjohnlev */
198*843e1988Sjohnlev panic("microfind: cpu is too fast");
199*843e1988Sjohnlev
200*843e1988Sjohnlev if (count != 0)
201*843e1988Sjohnlev microdata = count;
202*843e1988Sjohnlev else
203*843e1988Sjohnlev microdata = 1;
204*843e1988Sjohnlev
205*843e1988Sjohnlev /* Restore timer channel 0 for BIOS use */
206*843e1988Sjohnlev
207*843e1988Sjohnlev /* write mode to 3, square-wave */
208*843e1988Sjohnlev outb(PITCTL_PORT, PIT_C0 | PIT_LOADMODE | PIT_SQUAREMODE);
209*843e1988Sjohnlev
210*843e1988Sjohnlev /* write 16 bits of 0 for initial count */
211*843e1988Sjohnlev outb(PITCTR0_PORT, 0);
212*843e1988Sjohnlev outb(PITCTR0_PORT, 0);
213*843e1988Sjohnlev
214*843e1988Sjohnlev restore_int_flag(s); /* restore interrupt state */
215*843e1988Sjohnlev }
216