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