isa.c revision 4
1/*-
2 * Copyright (c) 1991 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * William Jolitz.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 *	@(#)isa.c	7.2 (Berkeley) 5/13/91
37 *
38 * PATCHES MAGIC                LEVEL   PATCH THAT GOT US HERE
39 * --------------------         -----   ----------------------
40 * CURRENT PATCH LEVEL:         4       00163
41 * --------------------         -----   ----------------------
42 *
43 * 18 Aug 92	Frank Maclachlan	*See comments below
44 * 25 Mar 93	Rodney W. Grimes	Added counter for stray interrupt,
45 *					turned on logging of stray interrupts,
46 *					Now prints maddr, msize, and flags
47 *					after finding a device.
48 * 26 Apr 93	Bruce Evans		New intr-0.1 code
49 *		Rodney W. Grimes	Only print io address if id_alive != -1
50 * 17 May 93	Rodney W. Grimes	renamed stray interrupt counters to
51 *					work with new intr-0.1 code.
52 *					Enabled printf for interrupt masks to
53 *					aid in bug reports.
54 * 27 May 93	Guido van Rooij		New routine add find_isa_dev
55 */
56static char rcsid[] = "$Header: /usr/src/sys.386bsd/i386/isa/RCS/isa.c,v 1.2 92/01/21 14:34:23 william Exp Locker: root $";
57
58/*
59 * code to manage AT bus
60 *
61 * 92/08/18  Frank P. MacLachlan (fpm@crash.cts.com):
62 * Fixed uninitialized variable problem and added code to deal
63 * with DMA page boundaries in isa_dmarangecheck().  Fixed word
64 * mode DMA count compution and reorganized DMA setup code in
65 * isa_dmastart()
66 */
67
68#include "param.h"
69#include "systm.h"
70#include "conf.h"
71#include "file.h"
72#include "buf.h"
73#include "uio.h"
74#include "syslog.h"
75#include "malloc.h"
76#include "rlist.h"
77#include "machine/segments.h"
78#include "vm/vm.h"
79#include "i386/isa/isa_device.h"
80#include "i386/isa/isa.h"
81#include "i386/isa/icu.h"
82#include "i386/isa/ic/i8237.h"
83#include "i386/isa/ic/i8042.h"
84
85/*
86**  Register definitions for DMA controller 1 (channels 0..3):
87*/
88#define	DMA1_CHN(c)	(IO_DMA1 + 1*(2*(c)))	/* addr reg for channel c */
89#define	DMA1_SMSK	(IO_DMA1 + 1*10)	/* single mask register */
90#define	DMA1_MODE	(IO_DMA1 + 1*11)	/* mode register */
91#define	DMA1_FFC	(IO_DMA1 + 1*12)	/* clear first/last FF */
92
93/*
94**  Register definitions for DMA controller 2 (channels 4..7):
95*/
96#define	DMA2_CHN(c)	(IO_DMA1 + 2*(2*(c)))	/* addr reg for channel c */
97#define	DMA2_SMSK	(IO_DMA2 + 2*10)	/* single mask register */
98#define	DMA2_MODE	(IO_DMA2 + 2*11)	/* mode register */
99#define	DMA2_FFC	(IO_DMA2 + 2*12)	/* clear first/last FF */
100
101int config_isadev __P((struct isa_device *, u_int *));
102
103#ifdef notyet
104struct rlist *isa_iomem;
105
106/*
107 * Configure all ISA devices
108 */
109isa_configure() {
110	struct isa_device *dvp;
111	struct isa_driver *dp;
112
113	splhigh();
114	INTREN(IRQ_SLAVE);
115	/*rlist_free(&isa_iomem, 0xa0000, 0xfffff);*/
116	for (dvp = isa_devtab_tty; dvp; dvp++)
117			(void) config_isadev(dvp, &ttymask);
118	for (dvp = isa_devtab_bio; dvp; dvp++)
119			(void) config_isadev(dvp, &biomask);
120	for (dvp = isa_devtab_net; dvp; dvp++)
121			(void) config_isadev(dvp, &netmask);
122	for (dvp = isa_devtab_null; dvp; dvp++)
123			(void) config_isadev(dvp, (u_int *) NULL);
124#include "sl.h"
125#if NSL > 0
126	netmask |= ttymask;
127	ttymask |= netmask;
128#endif
129/* printf("biomask %x ttymask %x netmask %x\n", biomask, ttymask, netmask); */
130	splnone();
131}
132
133/*
134 * Configure an ISA device.
135 */
136config_isadev(isdp, mp)
137	struct isa_device *isdp;
138	u_int *mp;
139{
140	struct isa_driver *dp;
141	static short drqseen, irqseen;
142
143	if (dp = isdp->id_driver) {
144		/* if a device with i/o memory, convert to virtual address */
145		if (isdp->id_maddr) {
146			extern unsigned int atdevbase;
147
148			isdp->id_maddr -= IOM_BEGIN;
149			isdp->id_maddr += atdevbase;
150		}
151		isdp->id_alive = (*dp->probe)(isdp);
152		if (isdp->id_alive) {
153
154			printf("%s%d at port 0x%x ", dp->name,
155				isdp->id_unit, isdp->id_iobase);
156
157			/* check for conflicts */
158			if (irqseen & isdp->id_irq) {
159				printf("INTERRUPT CONFLICT - irq%d\n",
160					ffs(isdp->id_irq) - 1);
161				return (0);
162			}
163			if (isdp->id_drq != -1
164				&& (drqseen & (1<<isdp->id_drq))) {
165				printf("DMA CONFLICT - drq%d\n", isdp->id_drq);
166				return (0);
167			}
168			/* NEED TO CHECK IOMEM CONFLICT HERE */
169
170			/* allocate and wire in device */
171			if(isdp->id_irq) {
172				int intrno;
173
174				intrno = ffs(isdp->id_irq)-1;
175				printf("irq %d ", intrno);
176				INTREN(isdp->id_irq);
177				if(mp)INTRMASK(*mp,isdp->id_irq);
178				setidt(NRSVIDT + intrno, isdp->id_intr,
179					 SDT_SYS386IGT, SEL_KPL);
180				irqseen |= isdp->id_irq;
181			}
182			if (isdp->id_drq != -1) {
183				printf("drq %d ", isdp->id_drq);
184				drqseen |=  1 << isdp->id_drq;
185			}
186
187			(*dp->attach)(isdp);
188
189			printf("on isa\n");
190		}
191		return (1);
192	} else	return(0);
193}
194#else /* notyet */
195/*
196 * Configure all ISA devices
197 */
198isa_configure() {
199	struct isa_device *dvp;
200	struct isa_driver *dp;
201
202	enable_intr();
203	splhigh();
204	INTREN(IRQ_SLAVE);
205	for (dvp = isa_devtab_tty; config_isadev(dvp,&ttymask); dvp++);
206	for (dvp = isa_devtab_bio; config_isadev(dvp,&biomask); dvp++);
207	for (dvp = isa_devtab_net; config_isadev(dvp,&netmask); dvp++);
208	for (dvp = isa_devtab_null; config_isadev(dvp,(u_int *) NULL); dvp++);
209#include "sl.h"
210#if NSL > 0
211	netmask |= ttymask;
212	ttymask |= netmask;
213#endif
214	/* biomask |= ttymask ;  can some tty devices use buffers? */
215	printf("biomask %x ttymask %x netmask %x\n", biomask, ttymask, netmask);
216	splnone();
217}
218
219/*
220 * Configure an ISA device.
221 */
222config_isadev(isdp, mp)
223	struct isa_device *isdp;
224	u_int *mp;
225{
226	struct isa_driver *dp;
227
228	if (dp = isdp->id_driver) {
229		if (isdp->id_maddr) {
230			extern u_int atdevbase;
231
232			isdp->id_maddr -= 0xa0000; /* XXX should be a define */
233			isdp->id_maddr += atdevbase;
234		}
235		isdp->id_alive = (*dp->probe)(isdp);
236		if (isdp->id_alive) {
237			printf("%s%d", dp->name, isdp->id_unit);
238			/*
239			 * The attach should really be after all the printf's
240			 * but until all the drivers are fixed do it here.
241			 * There is a comment below that shows where this
242			 * really belongs.  Rod Grimes 04/10/93
243			 */
244			(*dp->attach)(isdp);
245			/*
246			 * Only print the I/O address range if id_alive != -1
247			 * Right now this is a temporary fix just for the new
248			 * NPX code so that if it finds a 486 that can use trap
249			 * 16 it will not report I/O addresses.
250			 * Rod Grimes 04/26/94
251			 */
252			if (isdp->id_alive != -1) {
253	 			printf(" at 0x%x", isdp->id_iobase);
254 				if ((isdp->id_iobase + isdp->id_alive - 1) !=
255 				     isdp->id_iobase)
256 					printf("-0x%x",
257 					       isdp->id_iobase +
258					       isdp->id_alive - 1);
259			}
260			if(isdp->id_irq)
261				printf(" irq %d", ffs(isdp->id_irq)-1);
262			if (isdp->id_drq != -1)
263				printf(" drq %d", isdp->id_drq);
264			if (isdp->id_maddr != 0)
265				printf(" maddr 0x%x", kvtop(isdp->id_maddr));
266			if (isdp->id_msize != 0)
267				printf(" msize %d", isdp->id_msize);
268			if (isdp->id_flags != 0)
269				printf(" flags 0x%x", isdp->id_flags);
270			printf(" on isa\n");
271
272			/* This is the place the attach should be done! */
273			if(isdp->id_irq) {
274				int intrno;
275
276				intrno = ffs(isdp->id_irq)-1;
277				setidt(ICU_OFFSET+intrno, isdp->id_intr,
278					 SDT_SYS386IGT, SEL_KPL);
279				if(mp)
280					INTRMASK(*mp,isdp->id_irq);
281				INTREN(isdp->id_irq);
282			}
283		}
284		return (1);
285	} else	return(0);
286}
287#endif /* (!) notyet */
288
289#define	IDTVEC(name)	__CONCAT(X,name)
290/* default interrupt vector table entries */
291extern	IDTVEC(intr0), IDTVEC(intr1), IDTVEC(intr2), IDTVEC(intr3),
292	IDTVEC(intr4), IDTVEC(intr5), IDTVEC(intr6), IDTVEC(intr7),
293	IDTVEC(intr8), IDTVEC(intr9), IDTVEC(intr10), IDTVEC(intr11),
294	IDTVEC(intr12), IDTVEC(intr13), IDTVEC(intr14), IDTVEC(intr15);
295
296static *defvec[16] = {
297	&IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3),
298	&IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7),
299	&IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11),
300	&IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15) };
301
302/* out of range default interrupt vector gate entry */
303extern	IDTVEC(intrdefault);
304
305/*
306 * Fill in default interrupt table (in case of spuruious interrupt
307 * during configuration of kernel, setup interrupt control unit
308 */
309isa_defaultirq() {
310	int i;
311
312	/* icu vectors */
313	for (i = NRSVIDT ; i < NRSVIDT+ICU_LEN ; i++)
314		setidt(i, defvec[i],  SDT_SYS386IGT, SEL_KPL);
315
316	/* out of range vectors */
317	for (i = NRSVIDT; i < NIDT; i++)
318		setidt(i, &IDTVEC(intrdefault), SDT_SYS386IGT, SEL_KPL);
319
320	/* initialize 8259's */
321	outb(IO_ICU1, 0x11);		/* reset; program device, four bytes */
322	outb(IO_ICU1+1, NRSVIDT);	/* starting at this vector index */
323	outb(IO_ICU1+1, 1<<2);		/* slave on line 2 */
324#ifdef AUTO_EOI_1
325	outb(IO_ICU1+1, 2 | 1);		/* auto EOI, 8086 mode */
326#else
327	outb(IO_ICU1+1, 1);		/* 8086 mode */
328#endif
329	outb(IO_ICU1+1, 0xff);		/* leave interrupts masked */
330	outb(IO_ICU1, 0x0a);		/* default to IRR on read */
331	outb(IO_ICU1, 0xc0 | (3 - 1));	/* pri order 3-7, 0-2 (com2 first) */
332
333	outb(IO_ICU2, 0x11);		/* reset; program device, four bytes */
334	outb(IO_ICU2+1, NRSVIDT+8);	/* staring at this vector index */
335	outb(IO_ICU2+1,2);		/* my slave id is 2 */
336#ifdef AUTO_EOI_2
337	outb(IO_ICU2+1, 2 | 1);		/* auto EOI, 8086 mode */
338#else
339	outb(IO_ICU2+1,1);		/* 8086 mode */
340#endif
341	outb(IO_ICU2+1, 0xff);		/* leave interrupts masked */
342	outb(IO_ICU2, 0x0a);		/* default to IRR on read */
343}
344
345/* region of physical memory known to be contiguous */
346vm_offset_t isaphysmem;
347static caddr_t dma_bounce[8];		/* XXX */
348static char bounced[8];		/* XXX */
349#define MAXDMASZ 512		/* XXX */
350
351/* high byte of address is stored in this port for i-th dma channel */
352static short dmapageport[8] =
353	{ 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a };
354
355/*
356 * isa_dmacascade(): program 8237 DMA controller channel to accept
357 * external dma control by a board.
358 */
359void isa_dmacascade(unsigned chan)
360{
361	if (chan > 7)
362		panic("isa_dmacascade: impossible request");
363
364	/* set dma channel mode, and set dma channel mode */
365	if ((chan & 4) == 0) {
366		outb(DMA1_MODE, DMA37MD_CASCADE | chan);
367		outb(DMA1_SMSK, chan);
368	} else {
369		outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3));
370		outb(DMA2_SMSK, chan & 3);
371	}
372}
373
374/*
375 * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment
376 * problems by using a bounce buffer.
377 */
378void isa_dmastart(int flags, caddr_t addr, unsigned nbytes, unsigned chan)
379{	vm_offset_t phys;
380	int waport;
381	caddr_t newaddr;
382
383	if (    chan > 7
384	    || (chan < 4 && nbytes > (1<<16))
385	    || (chan >= 4 && (nbytes > (1<<17) || (u_int)addr & 1)))
386		panic("isa_dmastart: impossible request");
387
388	if (isa_dmarangecheck(addr, nbytes, chan)) {
389		if (dma_bounce[chan] == 0)
390			dma_bounce[chan] =
391				/*(caddr_t)malloc(MAXDMASZ, M_TEMP, M_WAITOK);*/
392				(caddr_t) isaphysmem + NBPG*chan;
393		bounced[chan] = 1;
394		newaddr = dma_bounce[chan];
395		*(int *) newaddr = 0;	/* XXX */
396
397		/* copy bounce buffer on write */
398		if (!(flags & B_READ))
399			bcopy(addr, newaddr, nbytes);
400		addr = newaddr;
401	}
402
403	/* translate to physical */
404	phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr);
405
406	if ((chan & 4) == 0) {
407		/*
408		 * Program one of DMA channels 0..3.  These are
409		 * byte mode channels.
410		 */
411		/* set dma channel mode, and reset address ff */
412		if (flags & B_READ)
413			outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|chan);
414		else
415			outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_READ|chan);
416		outb(DMA1_FFC, 0);
417
418		/* send start address */
419		waport =  DMA1_CHN(chan);
420		outb(waport, phys);
421		outb(waport, phys>>8);
422		outb(dmapageport[chan], phys>>16);
423
424		/* send count */
425		outb(waport + 1, --nbytes);
426		outb(waport + 1, nbytes>>8);
427
428		/* unmask channel */
429		outb(DMA1_SMSK, chan);
430	} else {
431		/*
432		 * Program one of DMA channels 4..7.  These are
433		 * word mode channels.
434		 */
435		/* set dma channel mode, and reset address ff */
436		if (flags & B_READ)
437			outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3));
438		else
439			outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_READ|(chan&3));
440		outb(DMA2_FFC, 0);
441
442		/* send start address */
443		waport = DMA2_CHN(chan - 4);
444		outb(waport, phys>>1);
445		outb(waport, phys>>9);
446		outb(dmapageport[chan], phys>>16);
447
448		/* send count */
449		nbytes >>= 1;
450		outb(waport + 2, --nbytes);
451		outb(waport + 2, nbytes>>8);
452
453		/* unmask channel */
454		outb(DMA2_SMSK, chan & 3);
455	}
456}
457
458void isa_dmadone(int flags, caddr_t addr, int nbytes, int chan)
459{
460
461	/* copy bounce buffer on read */
462	/*if ((flags & (B_PHYS|B_READ)) == (B_PHYS|B_READ))*/
463	if (bounced[chan]) {
464		bcopy(dma_bounce[chan], addr, nbytes);
465		bounced[chan] = 0;
466	}
467}
468
469/*
470 * Check for problems with the address range of a DMA transfer
471 * (non-contiguous physical pages, outside of bus address space,
472 * crossing DMA page boundaries).
473 * Return true if special handling needed.
474 */
475
476isa_dmarangecheck(caddr_t va, unsigned length, unsigned chan) {
477	vm_offset_t phys, priorpage = 0, endva;
478	u_int dma_pgmsk = (chan & 4) ?  ~(128*1024-1) : ~(64*1024-1);
479
480	endva = (vm_offset_t)round_page(va + length);
481	for (; va < (caddr_t) endva ; va += NBPG) {
482		phys = trunc_page(pmap_extract(pmap_kernel(), (vm_offset_t)va));
483#define ISARAM_END	RAM_END
484		if (phys == 0)
485			panic("isa_dmacheck: no physical page present");
486		if (phys > ISARAM_END)
487			return (1);
488		if (priorpage) {
489			if (priorpage + NBPG != phys)
490				return (1);
491			/* check if crossing a DMA page boundary */
492			if (((u_int)priorpage ^ (u_int)phys) & dma_pgmsk)
493				return (1);
494		}
495		priorpage = phys;
496	}
497	return (0);
498}
499
500/* head of queue waiting for physmem to become available */
501struct buf isa_physmemq;
502
503/* blocked waiting for resource to become free for exclusive use */
504static isaphysmemflag;
505/* if waited for and call requested when free (B_CALL) */
506static void (*isaphysmemunblock)(); /* needs to be a list */
507
508/*
509 * Allocate contiguous physical memory for transfer, returning
510 * a *virtual* address to region. May block waiting for resource.
511 * (assumed to be called at splbio())
512 */
513caddr_t
514isa_allocphysmem(caddr_t va, unsigned length, void (*func)()) {
515
516	isaphysmemunblock = func;
517	while (isaphysmemflag & B_BUSY) {
518		isaphysmemflag |= B_WANTED;
519		sleep(&isaphysmemflag, PRIBIO);
520	}
521	isaphysmemflag |= B_BUSY;
522
523	return((caddr_t)isaphysmem);
524}
525
526/*
527 * Free contiguous physical memory used for transfer.
528 * (assumed to be called at splbio())
529 */
530void
531isa_freephysmem(caddr_t va, unsigned length) {
532
533	isaphysmemflag &= ~B_BUSY;
534	if (isaphysmemflag & B_WANTED) {
535		isaphysmemflag &= B_WANTED;
536		wakeup(&isaphysmemflag);
537		if (isaphysmemunblock)
538			(*isaphysmemunblock)();
539	}
540}
541
542/*
543 * Handle a NMI, possibly a machine check.
544 * return true to panic system, false to ignore.
545 */
546isa_nmi(cd) {
547
548	log(LOG_CRIT, "\nNMI port 61 %x, port 70 %x\n", inb(0x61), inb(0x70));
549	return(0);
550}
551
552/*
553 * Caught a stray interrupt, notify
554 */
555isa_strayintr(d) {
556
557	/* DON'T BOTHER FOR NOW! */
558	/* for some reason, we get bursts of intr #7, even if not enabled! */
559	/*
560	 * Well the reason you got bursts of intr #7 is because someone
561	 * raised an interrupt line and dropped it before the 8259 could
562	 * prioritize it.  This is documented in the intel data book.  This
563	 * means you have BAD hardware!  I have changed this so that only
564	 * the first 5 get logged, then it quits logging them, and puts
565	 * out a special message. rgrimes 3/25/1993
566	 */
567	extern u_long intrcnt_stray;
568
569	intrcnt_stray++;
570	if (intrcnt_stray <= 5)
571		log(LOG_ERR,"ISA strayintr %x\n", d);
572	if (intrcnt_stray == 5)
573		log(LOG_CRIT,"Too many ISA strayintr not logging any more\n");
574}
575
576/*
577 * Wait "n" microseconds.
578 * Relies on timer 1 counting down from (TIMER_FREQ / hz) at
579 * (2 * TIMER_FREQ) Hz.
580 * Note: timer had better have been programmed before this is first used!
581 * (The standard programming causes the timer to generate a square wave and
582 * the counter is decremented twice every cycle.)
583 */
584#define	CF		(2 * TIMER_FREQ)
585#define	TIMER_FREQ	1193182	/* XXX - should be elsewhere */
586
587extern int hz;			/* XXX - should be elsewhere */
588
589int DELAY(n)
590	int n;
591{
592	int counter_limit;
593	int prev_tick;
594	int tick;
595	int ticks_left;
596	int sec;
597	int usec;
598
599#ifdef DELAYDEBUG
600	int getit_calls = 1;
601	int n1;
602	static int state = 0;
603
604	if (state == 0) {
605		state = 1;
606		for (n1 = 1; n1 <= 10000000; n1 *= 10)
607			DELAY(n1);
608		state = 2;
609	}
610	if (state == 1)
611		printf("DELAY(%d)...", n);
612#endif
613
614	/*
615	 * Read the counter first, so that the rest of the setup overhead is
616	 * counted.  Guess the initial overhead is 20 usec (on most systems it
617	 * takes about 1.5 usec for each of the i/o's in getit().  The loop
618	 * takes about 6 usec on a 486/33 and 13 usec on a 386/20.  The
619	 * multiplications and divisions to scale the count take a while).
620	 */
621	prev_tick = getit(0, 0);
622	n -= 20;
623
624	/*
625	 * Calculate (n * (CF / 1e6)) without using floating point and without
626	 * any avoidable overflows.
627	 */
628	sec = n / 1000000;
629	usec = n - sec * 1000000;
630	ticks_left = sec * CF
631		     + usec * (CF / 1000000)
632		     + usec * ((CF % 1000000) / 1000) / 1000
633		     + usec * (CF % 1000) / 1000000;
634
635	counter_limit = TIMER_FREQ / hz;
636	while (ticks_left > 0) {
637		tick = getit(0, 0);
638#ifdef DELAYDEBUG
639		++getit_calls;
640#endif
641		if (tick > prev_tick)
642			ticks_left -= prev_tick - (tick - counter_limit);
643		else
644			ticks_left -= prev_tick - tick;
645		prev_tick = tick;
646	}
647#ifdef DELAYDEBUG
648	if (state == 1)
649		printf(" %d calls to getit() at %d usec each\n",
650		       getit_calls, (n + 5) / getit_calls);
651#endif
652}
653
654getit(unit, timer) {
655	int high;
656	int low;
657
658	/*
659	 * XXX - isa.h defines bogus timers.  There's no such timer as
660	 * IO_TIMER_2 = 0x48.  There's a timer in the CMOS RAM chip but
661	 * its interface is quite different.  Neither timer is an 8252.
662	 * We actually only call this with unit = 0 and timer = 0.  It
663	 * could be static...
664	 */
665	/*
666	 * Protect ourself against interrupts.
667	 * XXX - sysbeep() and sysbeepstop() need protection.
668	 */
669	disable_intr();
670	/*
671	 * Latch the count for 'timer' (cc00xxxx, c = counter, x = any).
672	 */
673	outb(IO_TIMER1 + 3, timer << 6);
674
675	low = inb(IO_TIMER1 + timer);
676	high = inb(IO_TIMER1 + timer);
677	enable_intr();
678	return ((high << 8) | low);
679}
680
681static beeping;
682static
683sysbeepstop(f)
684{
685	/* disable counter 2 */
686	outb(0x61, inb(0x61) & 0xFC);
687	if (f)
688		timeout(sysbeepstop, 0, f);
689	else
690		beeping = 0;
691}
692
693void sysbeep(int pitch, int period)
694{
695
696	outb(0x61, inb(0x61) | 3);	/* enable counter 2 */
697	/*
698	 * XXX - move timer stuff to clock.c.
699	 * Program counter 2:
700	 * ccaammmb, c counter, a = access, m = mode, b = BCD
701	 * 1011x110, 11 for aa = LSB then MSB, x11 for mmm = square wave.
702	 */
703	outb(0x43, 0xb6);	/* set command for counter 2, 2 byte write */
704
705	outb(0x42, pitch);
706	outb(0x42, (pitch>>8));
707
708	if (!beeping) {
709		beeping = period;
710		timeout(sysbeepstop, period/2, period);
711	}
712}
713
714/*
715 * Pass command to keyboard controller (8042)
716 */
717unsigned kbc_8042cmd(val) {
718
719	while (inb(KBSTATP)&KBS_IBF);
720	if (val) outb(KBCMDP, val);
721	while (inb(KBSTATP)&KBS_IBF);
722	return (inb(KBDATAP));
723}
724
725/*
726 * find an ISA device in a given isa_devtab_* table, given
727 * the table to search, the expected id_driver entry, and the unit number.
728 *
729 * this function is defined in isa_device.h, and this location is debatable;
730 * i put it there because it's useless w/o, and directly operates on
731 * the other stuff in that file.
732 *
733 */
734
735struct isa_device *find_isadev(table, driverp, unit)
736     struct isa_device *table;
737     struct isa_driver *driverp;
738     int unit;
739{
740  if (driverp == NULL) /* sanity check */
741    return NULL;
742
743  while ((table->id_driver != driverp) || (table->id_unit != unit)) {
744    if (table->id_driver == 0)
745      return NULL;
746
747    table++;
748  }
749
750  return table;
751}
752
753/*
754 * Return nonzero if a (masked) irq is pending for a given device.
755 */
756int
757isa_irq_pending(dvp)
758	struct isa_device *dvp;
759{
760	unsigned id_irq;
761
762	id_irq = (unsigned short) dvp->id_irq;	/* XXX silly type in struct */
763	if (id_irq & 0xff)
764		return (inb(IO_ICU1) & id_irq);
765	return (inb(IO_ICU2) & (id_irq >> 8));
766}
767