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 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