isa.c revision 2056
14Srgrimes/*-
24Srgrimes * Copyright (c) 1991 The Regents of the University of California.
34Srgrimes * All rights reserved.
44Srgrimes *
54Srgrimes * This code is derived from software contributed to Berkeley by
64Srgrimes * William Jolitz.
74Srgrimes *
84Srgrimes * Redistribution and use in source and binary forms, with or without
94Srgrimes * modification, are permitted provided that the following conditions
104Srgrimes * are met:
114Srgrimes * 1. Redistributions of source code must retain the above copyright
124Srgrimes *    notice, this list of conditions and the following disclaimer.
134Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
144Srgrimes *    notice, this list of conditions and the following disclaimer in the
154Srgrimes *    documentation and/or other materials provided with the distribution.
164Srgrimes * 3. All advertising materials mentioning features or use of this software
174Srgrimes *    must display the following acknowledgement:
184Srgrimes *	This product includes software developed by the University of
194Srgrimes *	California, Berkeley and its contributors.
204Srgrimes * 4. Neither the name of the University nor the names of its contributors
214Srgrimes *    may be used to endorse or promote products derived from this software
224Srgrimes *    without specific prior written permission.
234Srgrimes *
244Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
254Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
264Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
274Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
284Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
294Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
304Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
314Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
324Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
334Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
344Srgrimes * SUCH DAMAGE.
354Srgrimes *
36593Srgrimes *	from: @(#)isa.c	7.2 (Berkeley) 5/13/91
372056Swollman *	$Id: isa.c,v 1.19 1994/08/10 04:39:52 wollman Exp $
384Srgrimes */
394Srgrimes
404Srgrimes/*
414Srgrimes * code to manage AT bus
424Srgrimes *
434Srgrimes * 92/08/18  Frank P. MacLachlan (fpm@crash.cts.com):
444Srgrimes * Fixed uninitialized variable problem and added code to deal
454Srgrimes * with DMA page boundaries in isa_dmarangecheck().  Fixed word
464Srgrimes * mode DMA count compution and reorganized DMA setup code in
474Srgrimes * isa_dmastart()
484Srgrimes */
494Srgrimes
502056Swollman#include <sys/param.h>
512056Swollman#include <sys/systm.h>		/* isn't it a joy */
522056Swollman#include <sys/kernel.h>		/* to have three of these */
532056Swollman#include <sys/conf.h>
542056Swollman#include <sys/file.h>
552056Swollman#include <sys/buf.h>
562056Swollman#include <sys/uio.h>
572056Swollman#include <sys/syslog.h>
582056Swollman#include <sys/malloc.h>
592056Swollman#include <sys/rlist.h>
602056Swollman#include <machine/segments.h>
612056Swollman#include <vm/vm.h>
621549Srgrimes#include <machine/spl.h>
632056Swollman#include <i386/isa/isa_device.h>
642056Swollman#include <i386/isa/isa.h>
652056Swollman#include <i386/isa/icu.h>
662056Swollman#include <i386/isa/ic/i8237.h>
672056Swollman#include <i386/isa/ic/i8042.h>
684Srgrimes
694Srgrimes/*
704Srgrimes**  Register definitions for DMA controller 1 (channels 0..3):
714Srgrimes*/
724Srgrimes#define	DMA1_CHN(c)	(IO_DMA1 + 1*(2*(c)))	/* addr reg for channel c */
734Srgrimes#define	DMA1_SMSK	(IO_DMA1 + 1*10)	/* single mask register */
744Srgrimes#define	DMA1_MODE	(IO_DMA1 + 1*11)	/* mode register */
754Srgrimes#define	DMA1_FFC	(IO_DMA1 + 1*12)	/* clear first/last FF */
764Srgrimes
774Srgrimes/*
784Srgrimes**  Register definitions for DMA controller 2 (channels 4..7):
794Srgrimes*/
80630Srgrimes#define	DMA2_CHN(c)	(IO_DMA2 + 2*(2*(c)))	/* addr reg for channel c */
814Srgrimes#define	DMA2_SMSK	(IO_DMA2 + 2*10)	/* single mask register */
824Srgrimes#define	DMA2_MODE	(IO_DMA2 + 2*11)	/* mode register */
834Srgrimes#define	DMA2_FFC	(IO_DMA2 + 2*12)	/* clear first/last FF */
844Srgrimes
85798Swollmanvoid config_isadev __P((struct isa_device *, u_int *));
864Srgrimes
874Srgrimes/*
88593Srgrimes * print a conflict message
894Srgrimes */
90593Srgrimesvoid
91593Srgrimesconflict(dvp, tmpdvp, item, reason, format)
92593Srgrimes	struct isa_device	*dvp, *tmpdvp;
93593Srgrimes	int			item;
94593Srgrimes	char			*reason;
95593Srgrimes	char			*format;
96593Srgrimes{
97593Srgrimes	printf("%s%d not probed due to %s conflict with %s%d at ",
98593Srgrimes		dvp->id_driver->name, dvp->id_unit, reason,
99593Srgrimes		tmpdvp->id_driver->name, tmpdvp->id_unit);
100593Srgrimes	printf(format, item);
101593Srgrimes	printf("\n");
1024Srgrimes}
1034Srgrimes
1044Srgrimes/*
105593Srgrimes * Check to see if things are alread in use, like IRQ's, I/O addresses
106593Srgrimes * and Memory addresses.
1074Srgrimes */
108593Srgrimesint
109593Srgrimeshaveseen(dvp, tmpdvp)
110593Srgrimes	struct	isa_device *dvp, *tmpdvp;
1114Srgrimes{
112593Srgrimes	int	status = 0;
1134Srgrimes
114593Srgrimes	/*
115593Srgrimes	 * Only check against devices that have already been found
116593Srgrimes	 */
117593Srgrimes	if (tmpdvp->id_alive) {
118593Srgrimes		/*
119593Srgrimes		 * Check for I/O address conflict.  We can only check the
120593Srgrimes		 * starting address of the device against the range of the
121593Srgrimes		 * device that has already been probed since we do not
122593Srgrimes		 * know how many I/O addresses this device uses.
123593Srgrimes		 */
124593Srgrimes		if (tmpdvp->id_alive != -1) {
125593Srgrimes			if ((dvp->id_iobase >= tmpdvp->id_iobase) &&
126593Srgrimes			    (dvp->id_iobase <=
127593Srgrimes				  (tmpdvp->id_iobase + tmpdvp->id_alive - 1))) {
128593Srgrimes				conflict(dvp, tmpdvp, dvp->id_iobase,
129593Srgrimes					 "I/O address", "0x%x");
130593Srgrimes				status = 1;
131593Srgrimes			}
1324Srgrimes		}
133593Srgrimes		/*
134593Srgrimes		 * Check for Memory address conflict.  We can check for
135593Srgrimes		 * range overlap, but it will not catch all cases since the
136593Srgrimes		 * driver may adjust the msize paramater during probe, for
137593Srgrimes		 * now we just check that the starting address does not
138593Srgrimes		 * fall within any allocated region.
139593Srgrimes		 * XXX could add a second check after the probe for overlap,
140593Srgrimes		 * since at that time we would know the full range.
141593Srgrimes		 * XXX KERNBASE is a hack, we should have vaddr in the table!
142593Srgrimes		 */
143593Srgrimes		if(tmpdvp->id_maddr) {
144593Srgrimes			if((KERNBASE + dvp->id_maddr >= tmpdvp->id_maddr) &&
145593Srgrimes			   (KERNBASE + dvp->id_maddr <=
146593Srgrimes			   (tmpdvp->id_maddr + tmpdvp->id_msize - 1))) {
147593Srgrimes				conflict(dvp, tmpdvp, dvp->id_maddr, "maddr",
148593Srgrimes					"0x%x");
149593Srgrimes				status = 1;
1504Srgrimes			}
151593Srgrimes		}
152736Salm#ifndef COM_MULTIPORT
153593Srgrimes		/*
154593Srgrimes		 * Check for IRQ conflicts.
155593Srgrimes		 */
156593Srgrimes		if(tmpdvp->id_irq) {
157593Srgrimes			if (tmpdvp->id_irq == dvp->id_irq) {
158593Srgrimes				conflict(dvp, tmpdvp, ffs(dvp->id_irq) - 1,
159593Srgrimes					"irq", "%d");
160593Srgrimes				status = 1;
1614Srgrimes			}
162593Srgrimes		}
163736Salm#endif
164593Srgrimes		/*
165593Srgrimes		 * Check for DRQ conflicts.
166593Srgrimes		 */
167593Srgrimes		if(tmpdvp->id_drq != -1) {
168593Srgrimes			if (tmpdvp->id_drq == dvp->id_drq) {
169593Srgrimes				conflict(dvp, tmpdvp, dvp->id_drq,
170593Srgrimes					"drq", "%d");
171593Srgrimes				status = 1;
1724Srgrimes			}
173593Srgrimes		}
174593Srgrimes	}
175593Srgrimes	return (status);
176593Srgrimes}
1774Srgrimes
178593Srgrimes/*
179593Srgrimes * Search through all the isa_devtab_* tables looking for anything that
180593Srgrimes * conflicts with the current device.
181593Srgrimes */
182593Srgrimesint
183593Srgrimeshaveseen_isadev(dvp)
184593Srgrimes	struct isa_device *dvp;
185593Srgrimes{
186593Srgrimes	struct isa_device *tmpdvp;
187593Srgrimes	int	status = 0;
1884Srgrimes
189593Srgrimes	for (tmpdvp = isa_devtab_tty; tmpdvp->id_driver; tmpdvp++) {
190593Srgrimes		status |= haveseen(dvp, tmpdvp);
191593Srgrimes	}
192593Srgrimes	for (tmpdvp = isa_devtab_bio; tmpdvp->id_driver; tmpdvp++) {
193593Srgrimes		status |= haveseen(dvp, tmpdvp);
194593Srgrimes	}
195593Srgrimes	for (tmpdvp = isa_devtab_net; tmpdvp->id_driver; tmpdvp++) {
196593Srgrimes		status |= haveseen(dvp, tmpdvp);
197593Srgrimes	}
198593Srgrimes	for (tmpdvp = isa_devtab_null; tmpdvp->id_driver; tmpdvp++) {
199593Srgrimes		status |= haveseen(dvp, tmpdvp);
200593Srgrimes	}
201593Srgrimes	return(status);
2024Srgrimes}
203593Srgrimes
2044Srgrimes/*
2054Srgrimes * Configure all ISA devices
2064Srgrimes */
207593Srgrimesvoid
2084Srgrimesisa_configure() {
2094Srgrimes	struct isa_device *dvp;
2104Srgrimes
2114Srgrimes	enable_intr();
2124Srgrimes	splhigh();
2134Srgrimes	INTREN(IRQ_SLAVE);
214593Srgrimes	printf("Probing for devices on the ISA bus:\n");
215593Srgrimes	for (dvp = isa_devtab_tty; dvp->id_driver; dvp++) {
216593Srgrimes		if (!haveseen_isadev(dvp))
2171321Sdg			config_isadev(dvp,&tty_imask);
218593Srgrimes	}
219593Srgrimes	for (dvp = isa_devtab_bio; dvp->id_driver; dvp++) {
220593Srgrimes		if (!haveseen_isadev(dvp))
2211321Sdg			config_isadev(dvp,&bio_imask);
222593Srgrimes	}
223593Srgrimes	for (dvp = isa_devtab_net; dvp->id_driver; dvp++) {
224593Srgrimes		if (!haveseen_isadev(dvp))
2251321Sdg			config_isadev(dvp,&net_imask);
226593Srgrimes	}
227593Srgrimes	for (dvp = isa_devtab_null; dvp->id_driver; dvp++) {
228593Srgrimes		if (!haveseen_isadev(dvp))
229593Srgrimes			config_isadev(dvp,(u_int *) NULL);
230593Srgrimes	}
2311321Sdg	bio_imask |= SWI_CLOCK_MASK;
2321321Sdg	net_imask |= SWI_NET_MASK;
2331321Sdg	tty_imask |= SWI_TTY_MASK;
2341321Sdg
235593Srgrimes/*
2361321Sdg * XXX we should really add the tty device to net_imask when the line is
237593Srgrimes * switched to SLIPDISC, and then remove it when it is switched away from
2381321Sdg * SLIPDISC.  No need to block out ALL ttys during a splimp when only one
239593Srgrimes * of them is running slip.
2401321Sdg *
2411321Sdg * XXX actually, blocking all ttys during a splimp doesn't matter so much
2421321Sdg * with sio because the serial interrupt layer doesn't use tty_imask.  Only
2431321Sdg * non-serial ttys suffer.  It's more stupid that ALL 'net's are blocked
2441321Sdg * during spltty.
245593Srgrimes */
2464Srgrimes#include "sl.h"
2474Srgrimes#if NSL > 0
2481321Sdg	net_imask |= tty_imask;
2491321Sdg	tty_imask = net_imask;
2504Srgrimes#endif
2511321Sdg	/* bio_imask |= tty_imask ;  can some tty devices use buffers? */
2521321Sdg#ifdef DIAGNOSTIC
2531321Sdg	printf("bio_imask %x tty_imask %x net_imask %x\n",
2541321Sdg	       bio_imask, tty_imask, net_imask);
2551321Sdg#endif
2564Srgrimes	splnone();
2574Srgrimes}
2584Srgrimes
2594Srgrimes/*
2604Srgrimes * Configure an ISA device.
2614Srgrimes */
262798Swollmanvoid
2634Srgrimesconfig_isadev(isdp, mp)
2644Srgrimes	struct isa_device *isdp;
2654Srgrimes	u_int *mp;
2664Srgrimes{
267593Srgrimes	struct isa_driver *dp = isdp->id_driver;
2684Srgrimes
269593Srgrimes	if (isdp->id_maddr) {
270593Srgrimes		extern u_int atdevbase;
2714Srgrimes
272593Srgrimes		isdp->id_maddr -= 0xa0000; /* XXX should be a define */
273593Srgrimes		isdp->id_maddr += atdevbase;
274593Srgrimes	}
275593Srgrimes	isdp->id_alive = (*dp->probe)(isdp);
276593Srgrimes	if (isdp->id_alive) {
277593Srgrimes		/*
278593Srgrimes		 * Only print the I/O address range if id_alive != -1
279593Srgrimes		 * Right now this is a temporary fix just for the new
280593Srgrimes		 * NPX code so that if it finds a 486 that can use trap
281593Srgrimes		 * 16 it will not report I/O addresses.
282593Srgrimes		 * Rod Grimes 04/26/94
283593Srgrimes		 */
284593Srgrimes		printf("%s%d", dp->name, isdp->id_unit);
285593Srgrimes		if (isdp->id_alive != -1) {
286593Srgrimes 			printf(" at 0x%x", isdp->id_iobase);
287593Srgrimes 			if ((isdp->id_iobase + isdp->id_alive - 1) !=
288593Srgrimes 			     isdp->id_iobase) {
289593Srgrimes 				printf("-0x%x",
290593Srgrimes				       isdp->id_iobase +
291593Srgrimes				       isdp->id_alive - 1);
292593Srgrimes			}
2934Srgrimes		}
294593Srgrimes		if(isdp->id_irq)
295593Srgrimes			printf(" irq %d", ffs(isdp->id_irq) - 1);
296593Srgrimes		if (isdp->id_drq != -1)
297593Srgrimes			printf(" drq %d", isdp->id_drq);
298593Srgrimes		if (isdp->id_maddr)
299593Srgrimes			printf(" maddr 0x%x", kvtop(isdp->id_maddr));
300593Srgrimes		if (isdp->id_msize)
301593Srgrimes			printf(" msize %d", isdp->id_msize);
302593Srgrimes		if (isdp->id_flags)
303593Srgrimes			printf(" flags 0x%x", isdp->id_flags);
3041002Srgrimes		if (isdp->id_iobase) {
3051002Srgrimes			if (isdp->id_iobase < 0x100) {
3061002Srgrimes				printf(" on motherboard\n");
3071002Srgrimes			} else {
3081002Srgrimes				if (isdp->id_iobase >= 0x1000) {
3091002Srgrimes					printf (" on eisa\n");
3101002Srgrimes				} else {
3111002Srgrimes					printf (" on isa\n");
3121002Srgrimes				}
3131002Srgrimes			}
3141002Srgrimes		}
3154Srgrimes
316593Srgrimes		(*dp->attach)(isdp);
31724Srgrimes
318593Srgrimes		if(isdp->id_irq) {
319593Srgrimes			int intrno;
3204Srgrimes
321593Srgrimes			intrno = ffs(isdp->id_irq)-1;
322593Srgrimes			setidt(ICU_OFFSET+intrno, isdp->id_intr,
323593Srgrimes				 SDT_SYS386IGT, SEL_KPL);
324593Srgrimes			if(mp) {
325593Srgrimes				INTRMASK(*mp,isdp->id_irq);
3264Srgrimes			}
327593Srgrimes			INTREN(isdp->id_irq);
3284Srgrimes		}
329593Srgrimes	} else {
330593Srgrimes		printf("%s%d not found", dp->name, isdp->id_unit);
331593Srgrimes		if (isdp->id_iobase) {
332593Srgrimes			printf(" at 0x%x", isdp->id_iobase);
333593Srgrimes		}
334593Srgrimes		printf("\n");
335593Srgrimes	}
3364Srgrimes}
3374Srgrimes
3384Srgrimes#define	IDTVEC(name)	__CONCAT(X,name)
3394Srgrimes/* default interrupt vector table entries */
340879Swollmantypedef void inthand_t();
341879Swollmantypedef void (*inthand_func_t)();
342879Swollmanextern inthand_t
343879Swollman	IDTVEC(intr0), IDTVEC(intr1), IDTVEC(intr2), IDTVEC(intr3),
3444Srgrimes	IDTVEC(intr4), IDTVEC(intr5), IDTVEC(intr6), IDTVEC(intr7),
3454Srgrimes	IDTVEC(intr8), IDTVEC(intr9), IDTVEC(intr10), IDTVEC(intr11),
3464Srgrimes	IDTVEC(intr12), IDTVEC(intr13), IDTVEC(intr14), IDTVEC(intr15);
3474Srgrimes
3481321Sdgstatic inthand_func_t defvec[ICU_LEN] = {
3494Srgrimes	&IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3),
3504Srgrimes	&IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7),
3514Srgrimes	&IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11),
3524Srgrimes	&IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15) };
3534Srgrimes
3544Srgrimes/*
3554Srgrimes * Fill in default interrupt table (in case of spuruious interrupt
3564Srgrimes * during configuration of kernel, setup interrupt control unit
3574Srgrimes */
358798Swollmanvoid
359798Swollmanisa_defaultirq()
360798Swollman{
3614Srgrimes	int i;
3624Srgrimes
3634Srgrimes	/* icu vectors */
3641321Sdg	for (i = 0; i < ICU_LEN; i++)
3651321Sdg		setidt(ICU_OFFSET + i, defvec[i], SDT_SYS386IGT, SEL_KPL);
3664Srgrimes
3674Srgrimes	/* initialize 8259's */
3684Srgrimes	outb(IO_ICU1, 0x11);		/* reset; program device, four bytes */
3694Srgrimes	outb(IO_ICU1+1, NRSVIDT);	/* starting at this vector index */
3704Srgrimes	outb(IO_ICU1+1, 1<<2);		/* slave on line 2 */
3714Srgrimes#ifdef AUTO_EOI_1
3724Srgrimes	outb(IO_ICU1+1, 2 | 1);		/* auto EOI, 8086 mode */
3734Srgrimes#else
3744Srgrimes	outb(IO_ICU1+1, 1);		/* 8086 mode */
3754Srgrimes#endif
3764Srgrimes	outb(IO_ICU1+1, 0xff);		/* leave interrupts masked */
3774Srgrimes	outb(IO_ICU1, 0x0a);		/* default to IRR on read */
3784Srgrimes	outb(IO_ICU1, 0xc0 | (3 - 1));	/* pri order 3-7, 0-2 (com2 first) */
3794Srgrimes
3804Srgrimes	outb(IO_ICU2, 0x11);		/* reset; program device, four bytes */
3814Srgrimes	outb(IO_ICU2+1, NRSVIDT+8);	/* staring at this vector index */
3824Srgrimes	outb(IO_ICU2+1,2);		/* my slave id is 2 */
3834Srgrimes#ifdef AUTO_EOI_2
3844Srgrimes	outb(IO_ICU2+1, 2 | 1);		/* auto EOI, 8086 mode */
3854Srgrimes#else
3864Srgrimes	outb(IO_ICU2+1,1);		/* 8086 mode */
3874Srgrimes#endif
3884Srgrimes	outb(IO_ICU2+1, 0xff);		/* leave interrupts masked */
3894Srgrimes	outb(IO_ICU2, 0x0a);		/* default to IRR on read */
3904Srgrimes}
3914Srgrimes
3924Srgrimes/* region of physical memory known to be contiguous */
3934Srgrimesvm_offset_t isaphysmem;
3944Srgrimesstatic caddr_t dma_bounce[8];		/* XXX */
3954Srgrimesstatic char bounced[8];		/* XXX */
3964Srgrimes#define MAXDMASZ 512		/* XXX */
3974Srgrimes
3984Srgrimes/* high byte of address is stored in this port for i-th dma channel */
3994Srgrimesstatic short dmapageport[8] =
4004Srgrimes	{ 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a };
4014Srgrimes
4024Srgrimes/*
4034Srgrimes * isa_dmacascade(): program 8237 DMA controller channel to accept
4044Srgrimes * external dma control by a board.
4054Srgrimes */
4064Srgrimesvoid isa_dmacascade(unsigned chan)
4074Srgrimes{
4084Srgrimes	if (chan > 7)
4094Srgrimes		panic("isa_dmacascade: impossible request");
4104Srgrimes
4114Srgrimes	/* set dma channel mode, and set dma channel mode */
4124Srgrimes	if ((chan & 4) == 0) {
4134Srgrimes		outb(DMA1_MODE, DMA37MD_CASCADE | chan);
4144Srgrimes		outb(DMA1_SMSK, chan);
4154Srgrimes	} else {
4164Srgrimes		outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3));
4174Srgrimes		outb(DMA2_SMSK, chan & 3);
4184Srgrimes	}
4194Srgrimes}
4204Srgrimes
4214Srgrimes/*
4224Srgrimes * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment
4234Srgrimes * problems by using a bounce buffer.
4244Srgrimes */
4254Srgrimesvoid isa_dmastart(int flags, caddr_t addr, unsigned nbytes, unsigned chan)
4264Srgrimes{	vm_offset_t phys;
4274Srgrimes	int waport;
4284Srgrimes	caddr_t newaddr;
4294Srgrimes
4304Srgrimes	if (    chan > 7
4314Srgrimes	    || (chan < 4 && nbytes > (1<<16))
4324Srgrimes	    || (chan >= 4 && (nbytes > (1<<17) || (u_int)addr & 1)))
4334Srgrimes		panic("isa_dmastart: impossible request");
4344Srgrimes
4354Srgrimes	if (isa_dmarangecheck(addr, nbytes, chan)) {
4364Srgrimes		if (dma_bounce[chan] == 0)
4374Srgrimes			dma_bounce[chan] =
4384Srgrimes				/*(caddr_t)malloc(MAXDMASZ, M_TEMP, M_WAITOK);*/
4394Srgrimes				(caddr_t) isaphysmem + NBPG*chan;
4404Srgrimes		bounced[chan] = 1;
4414Srgrimes		newaddr = dma_bounce[chan];
4424Srgrimes		*(int *) newaddr = 0;	/* XXX */
4434Srgrimes
4444Srgrimes		/* copy bounce buffer on write */
4454Srgrimes		if (!(flags & B_READ))
4464Srgrimes			bcopy(addr, newaddr, nbytes);
4474Srgrimes		addr = newaddr;
4484Srgrimes	}
4494Srgrimes
4504Srgrimes	/* translate to physical */
4514Srgrimes	phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr);
4524Srgrimes
4534Srgrimes	if ((chan & 4) == 0) {
4544Srgrimes		/*
4554Srgrimes		 * Program one of DMA channels 0..3.  These are
4564Srgrimes		 * byte mode channels.
4574Srgrimes		 */
4584Srgrimes		/* set dma channel mode, and reset address ff */
4594Srgrimes		if (flags & B_READ)
4604Srgrimes			outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|chan);
4614Srgrimes		else
4624Srgrimes			outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_READ|chan);
4634Srgrimes		outb(DMA1_FFC, 0);
4644Srgrimes
4654Srgrimes		/* send start address */
4664Srgrimes		waport =  DMA1_CHN(chan);
4674Srgrimes		outb(waport, phys);
4684Srgrimes		outb(waport, phys>>8);
4694Srgrimes		outb(dmapageport[chan], phys>>16);
4704Srgrimes
4714Srgrimes		/* send count */
4724Srgrimes		outb(waport + 1, --nbytes);
4734Srgrimes		outb(waport + 1, nbytes>>8);
4744Srgrimes
4754Srgrimes		/* unmask channel */
4764Srgrimes		outb(DMA1_SMSK, chan);
4774Srgrimes	} else {
4784Srgrimes		/*
4794Srgrimes		 * Program one of DMA channels 4..7.  These are
4804Srgrimes		 * word mode channels.
4814Srgrimes		 */
4824Srgrimes		/* set dma channel mode, and reset address ff */
4834Srgrimes		if (flags & B_READ)
4844Srgrimes			outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3));
4854Srgrimes		else
4864Srgrimes			outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_READ|(chan&3));
4874Srgrimes		outb(DMA2_FFC, 0);
4884Srgrimes
4894Srgrimes		/* send start address */
4904Srgrimes		waport = DMA2_CHN(chan - 4);
4914Srgrimes		outb(waport, phys>>1);
4924Srgrimes		outb(waport, phys>>9);
4934Srgrimes		outb(dmapageport[chan], phys>>16);
4944Srgrimes
4954Srgrimes		/* send count */
4964Srgrimes		nbytes >>= 1;
4974Srgrimes		outb(waport + 2, --nbytes);
4984Srgrimes		outb(waport + 2, nbytes>>8);
4994Srgrimes
5004Srgrimes		/* unmask channel */
5014Srgrimes		outb(DMA2_SMSK, chan & 3);
5024Srgrimes	}
5034Srgrimes}
5044Srgrimes
5054Srgrimesvoid isa_dmadone(int flags, caddr_t addr, int nbytes, int chan)
5064Srgrimes{
5074Srgrimes
5084Srgrimes	/* copy bounce buffer on read */
5094Srgrimes	/*if ((flags & (B_PHYS|B_READ)) == (B_PHYS|B_READ))*/
5104Srgrimes	if (bounced[chan]) {
5114Srgrimes		bcopy(dma_bounce[chan], addr, nbytes);
5124Srgrimes		bounced[chan] = 0;
5134Srgrimes	}
5144Srgrimes}
5154Srgrimes
5164Srgrimes/*
5174Srgrimes * Check for problems with the address range of a DMA transfer
5184Srgrimes * (non-contiguous physical pages, outside of bus address space,
5194Srgrimes * crossing DMA page boundaries).
5204Srgrimes * Return true if special handling needed.
5214Srgrimes */
5224Srgrimes
523798Swollmanint
5244Srgrimesisa_dmarangecheck(caddr_t va, unsigned length, unsigned chan) {
5254Srgrimes	vm_offset_t phys, priorpage = 0, endva;
5264Srgrimes	u_int dma_pgmsk = (chan & 4) ?  ~(128*1024-1) : ~(64*1024-1);
5274Srgrimes
5284Srgrimes	endva = (vm_offset_t)round_page(va + length);
5294Srgrimes	for (; va < (caddr_t) endva ; va += NBPG) {
5304Srgrimes		phys = trunc_page(pmap_extract(pmap_kernel(), (vm_offset_t)va));
5314Srgrimes#define ISARAM_END	RAM_END
5324Srgrimes		if (phys == 0)
5334Srgrimes			panic("isa_dmacheck: no physical page present");
5341323Sache		if (phys >= ISARAM_END)
5354Srgrimes			return (1);
5364Srgrimes		if (priorpage) {
5374Srgrimes			if (priorpage + NBPG != phys)
5384Srgrimes				return (1);
5394Srgrimes			/* check if crossing a DMA page boundary */
5404Srgrimes			if (((u_int)priorpage ^ (u_int)phys) & dma_pgmsk)
5414Srgrimes				return (1);
5424Srgrimes		}
5434Srgrimes		priorpage = phys;
5444Srgrimes	}
5454Srgrimes	return (0);
5464Srgrimes}
5474Srgrimes
5484Srgrimes/* head of queue waiting for physmem to become available */
5494Srgrimesstruct buf isa_physmemq;
5504Srgrimes
5514Srgrimes/* blocked waiting for resource to become free for exclusive use */
5524Srgrimesstatic isaphysmemflag;
5534Srgrimes/* if waited for and call requested when free (B_CALL) */
5544Srgrimesstatic void (*isaphysmemunblock)(); /* needs to be a list */
5554Srgrimes
5564Srgrimes/*
5574Srgrimes * Allocate contiguous physical memory for transfer, returning
5584Srgrimes * a *virtual* address to region. May block waiting for resource.
5594Srgrimes * (assumed to be called at splbio())
5604Srgrimes */
5614Srgrimescaddr_t
5624Srgrimesisa_allocphysmem(caddr_t va, unsigned length, void (*func)()) {
5634Srgrimes
5644Srgrimes	isaphysmemunblock = func;
5654Srgrimes	while (isaphysmemflag & B_BUSY) {
5664Srgrimes		isaphysmemflag |= B_WANTED;
567798Swollman		tsleep((caddr_t)&isaphysmemflag, PRIBIO, "isaphys", 0);
5684Srgrimes	}
5694Srgrimes	isaphysmemflag |= B_BUSY;
5704Srgrimes
5714Srgrimes	return((caddr_t)isaphysmem);
5724Srgrimes}
5734Srgrimes
5744Srgrimes/*
5754Srgrimes * Free contiguous physical memory used for transfer.
5764Srgrimes * (assumed to be called at splbio())
5774Srgrimes */
5784Srgrimesvoid
5794Srgrimesisa_freephysmem(caddr_t va, unsigned length) {
5804Srgrimes
5814Srgrimes	isaphysmemflag &= ~B_BUSY;
5824Srgrimes	if (isaphysmemflag & B_WANTED) {
5834Srgrimes		isaphysmemflag &= B_WANTED;
584798Swollman		wakeup((caddr_t)&isaphysmemflag);
5854Srgrimes		if (isaphysmemunblock)
5864Srgrimes			(*isaphysmemunblock)();
5874Srgrimes	}
5884Srgrimes}
5894Srgrimes
5902001Swollman#define NMI_PARITY (1 << 7)
5912001Swollman#define NMI_IOCHAN (1 << 6)
5922001Swollman#define ENMI_WATCHDOG (1 << 7)
5932001Swollman#define ENMI_BUSTIMER (1 << 6)
5942001Swollman#define ENMI_IOSTATUS (1 << 5)
5952001Swollman
5964Srgrimes/*
5974Srgrimes * Handle a NMI, possibly a machine check.
5984Srgrimes * return true to panic system, false to ignore.
5994Srgrimes */
600798Swollmanint
601798Swollmanisa_nmi(cd)
602798Swollman	int cd;
603798Swollman{
6042001Swollman	int isa_port = inb(0x61);
6052001Swollman	int eisa_port = inb(0x461);
6062001Swollman	if(isa_port & NMI_PARITY) {
6072001Swollman		panic("RAM parity error, likely hardware failure.");
6082001Swollman	} else if(isa_port & NMI_IOCHAN) {
6092001Swollman		panic("I/O channel check, likely hardware failure.");
6102001Swollman	} else if(eisa_port & ENMI_WATCHDOG) {
6112001Swollman		panic("EISA watchdog timer expired, likely hardware failure.");
6122001Swollman	} else if(eisa_port & ENMI_BUSTIMER) {
6132001Swollman		panic("EISA bus timeout, likely hardware failure.");
6142001Swollman	} else if(eisa_port & ENMI_IOSTATUS) {
6152001Swollman		panic("EISA I/O port status error.");
6162001Swollman	} else {
6172001Swollman		printf("\nNMI ISA %x, EISA %x\n", isa_port, eisa_port);
6182001Swollman		return(0);
6192001Swollman	}
6204Srgrimes}
6214Srgrimes
6224Srgrimes/*
6234Srgrimes * Caught a stray interrupt, notify
6244Srgrimes */
625798Swollmanvoid
626798Swollmanisa_strayintr(d)
627798Swollman	int d;
628798Swollman{
6294Srgrimes
6304Srgrimes	/* DON'T BOTHER FOR NOW! */
6314Srgrimes	/* for some reason, we get bursts of intr #7, even if not enabled! */
6324Srgrimes	/*
6334Srgrimes	 * Well the reason you got bursts of intr #7 is because someone
6344Srgrimes	 * raised an interrupt line and dropped it before the 8259 could
6354Srgrimes	 * prioritize it.  This is documented in the intel data book.  This
6364Srgrimes	 * means you have BAD hardware!  I have changed this so that only
6374Srgrimes	 * the first 5 get logged, then it quits logging them, and puts
6384Srgrimes	 * out a special message. rgrimes 3/25/1993
6394Srgrimes	 */
6404Srgrimes	extern u_long intrcnt_stray;
6414Srgrimes
6424Srgrimes	intrcnt_stray++;
6434Srgrimes	if (intrcnt_stray <= 5)
6444Srgrimes		log(LOG_ERR,"ISA strayintr %x\n", d);
6454Srgrimes	if (intrcnt_stray == 5)
6464Srgrimes		log(LOG_CRIT,"Too many ISA strayintr not logging any more\n");
6474Srgrimes}
6484Srgrimes
6494Srgrimes/*
6504Srgrimes * find an ISA device in a given isa_devtab_* table, given
6514Srgrimes * the table to search, the expected id_driver entry, and the unit number.
6524Srgrimes *
6534Srgrimes * this function is defined in isa_device.h, and this location is debatable;
6544Srgrimes * i put it there because it's useless w/o, and directly operates on
6554Srgrimes * the other stuff in that file.
6564Srgrimes *
6574Srgrimes */
6584Srgrimes
6594Srgrimesstruct isa_device *find_isadev(table, driverp, unit)
6604Srgrimes     struct isa_device *table;
6614Srgrimes     struct isa_driver *driverp;
6624Srgrimes     int unit;
6634Srgrimes{
6644Srgrimes  if (driverp == NULL) /* sanity check */
6654Srgrimes    return NULL;
6664Srgrimes
6674Srgrimes  while ((table->id_driver != driverp) || (table->id_unit != unit)) {
6684Srgrimes    if (table->id_driver == 0)
6694Srgrimes      return NULL;
6704Srgrimes
6714Srgrimes    table++;
6724Srgrimes  }
6734Srgrimes
6744Srgrimes  return table;
6754Srgrimes}
6764Srgrimes
6774Srgrimes/*
6784Srgrimes * Return nonzero if a (masked) irq is pending for a given device.
6794Srgrimes */
6804Srgrimesint
6814Srgrimesisa_irq_pending(dvp)
6824Srgrimes	struct isa_device *dvp;
6834Srgrimes{
6844Srgrimes	unsigned id_irq;
6854Srgrimes
6864Srgrimes	id_irq = (unsigned short) dvp->id_irq;	/* XXX silly type in struct */
6874Srgrimes	if (id_irq & 0xff)
6884Srgrimes		return (inb(IO_ICU1) & id_irq);
6894Srgrimes	return (inb(IO_ICU2) & (id_irq >> 8));
6904Srgrimes}
691