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 */ 51 unsigned int microdata = 50; 52 53 void 54 microfind(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