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