eisaconf.c revision 45804
1/*
2 * EISA bus probe and attach routines
3 *
4 * Copyright (c) 1995, 1996 Justin T. Gibbs.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice immediately at the beginning of the file, without modification,
12 *    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. The name of the author may not be used to endorse or promote products
17 *    derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
23 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 *	$Id: eisaconf.c,v 1.38 1999/04/18 15:50:33 peter Exp $
32 */
33
34#include "opt_eisa.h"
35
36#include <sys/param.h>
37#include <sys/systm.h>
38#include <sys/queue.h>
39#include <sys/malloc.h>
40#include <sys/kernel.h>
41#include <sys/module.h>
42#include <sys/bus.h>
43
44#include <machine/limits.h>
45#include <machine/bus.h>
46#include <machine/resource.h>
47#include <sys/rman.h>
48
49#include <i386/eisa/eisaconf.h>
50
51#include <sys/interrupt.h>
52
53typedef struct resvaddr {
54        u_long	addr;				/* start address */
55        u_long	size;				/* size of reserved area */
56	int	flags;
57	struct resource *res;			/* resource manager handle */
58	LIST_ENTRY(resvaddr) links;		/* List links */
59} resvaddr_t;
60
61LIST_HEAD(resvlist, resvaddr);
62
63struct irq_node {
64	int	irq_no;
65	void	*idesc;
66	TAILQ_ENTRY(irq_node) links;
67};
68
69TAILQ_HEAD(irqlist, irq_node);
70
71struct eisa_ioconf {
72	int		slot;
73	struct resvlist	ioaddrs;	/* list of reserved I/O ranges */
74	struct resvlist maddrs;		/* list of reserved memory ranges */
75	struct irqlist	irqs;		/* list of reserved irqs */
76};
77
78/* To be replaced by the "super device" generic device structure... */
79struct eisa_device {
80	eisa_id_t		id;
81	struct eisa_ioconf	ioconf;
82};
83
84
85/*
86 * Local function declarations and static variables
87 */
88#if 0
89static void eisa_reg_print __P((struct eisa_device *e_dev,
90				char *string, char *separator));
91static int eisa_add_resvaddr __P((struct eisa_device *e_dev,
92				  struct resvlist *head, u_long	base,
93				  u_long size, int flags));
94static int eisa_reg_resvaddr __P((struct eisa_device *e_dev,
95				  struct resvlist *head, resvaddr_t *resvaddr,
96				  int *reg_count));
97#endif
98
99#if 0
100/*
101 * Keep some state about what we've printed so far
102 * to make probe output pretty.
103 */
104static struct {
105	int	in_registration;/* reg_start has been called */
106	int	num_interrupts;
107	int	num_ioaddrs;
108	int	num_maddrs;
109	int	column;		/* How much we have output so far. */
110#define	MAX_COL 80
111} reg_state;
112#endif
113
114/* Global variable, so UserConfig can change it. */
115#ifndef EISA_SLOTS
116#define EISA_SLOTS 10   /* PCI clashes with higher ones.. fix later */
117#endif
118int num_eisa_slots = EISA_SLOTS;
119
120static devclass_t eisa_devclass;
121
122static int
123mainboard_probe(device_t dev)
124{
125	char *idstring;
126	eisa_id_t id = eisa_get_id(dev);
127
128	if (eisa_get_slot(dev) != 0)
129		return (ENXIO);
130
131	idstring = (char *)malloc(8 + sizeof(" (System Board)") + 1,
132				  M_DEVBUF, M_NOWAIT);
133	if (idstring == NULL) {
134		panic("Eisa probe unable to malloc");
135	}
136	sprintf(idstring, "%c%c%c%x%x (System Board)",
137		EISA_MFCTR_CHAR0(id),
138		EISA_MFCTR_CHAR1(id),
139		EISA_MFCTR_CHAR2(id),
140		EISA_PRODUCT_ID(id),
141		EISA_REVISION_ID(id));
142	device_set_desc(dev, idstring);
143
144	return (0);
145}
146
147static int
148mainboard_attach(device_t dev)
149{
150    return (0);
151}
152
153static device_method_t mainboard_methods[] = {
154	/* Device interface */
155	DEVMETHOD(device_probe,		mainboard_probe),
156	DEVMETHOD(device_attach,	mainboard_attach),
157
158	{ 0, 0 }
159};
160
161static driver_t mainboard_driver = {
162	"mainboard",
163	mainboard_methods,
164	DRIVER_TYPE_MISC,
165	1,
166};
167
168static devclass_t mainboard_devclass;
169
170DRIVER_MODULE(mainboard, eisa, mainboard_driver, mainboard_devclass, 0, 0);
171
172/*
173** probe for EISA devices
174*/
175static int
176eisa_probe(device_t dev)
177{
178	int i,slot;
179	struct eisa_device *e_dev;
180	int eisaBase = 0xc80;
181	eisa_id_t eisa_id;
182
183	device_set_desc(dev, "EISA bus");
184
185	for (slot = 0; slot < num_eisa_slots; eisaBase+=0x1000, slot++) {
186		int id_size = sizeof(eisa_id);
187		eisa_id = 0;
188    		for( i = 0; i < id_size; i++ ) {
189			outb(eisaBase,0x80 + i); /*Some cards require priming*/
190			eisa_id |= inb(eisaBase+i) << ((id_size-i-1)*CHAR_BIT);
191		}
192		if (eisa_id & 0x80000000)
193			continue;  /* no EISA card in slot */
194
195		/* Prepare an eisa_device_node for this slot */
196		e_dev = (struct eisa_device *)malloc(sizeof(*e_dev),
197						     M_DEVBUF, M_NOWAIT);
198		if (!e_dev) {
199			printf("eisa0: cannot malloc eisa_device");
200			break; /* Try to attach what we have already */
201		}
202		bzero(e_dev, sizeof(*e_dev));
203
204		e_dev->id = eisa_id;
205
206		e_dev->ioconf.slot = slot;
207
208		/* Initialize our lists of reserved addresses */
209		LIST_INIT(&(e_dev->ioconf.ioaddrs));
210		LIST_INIT(&(e_dev->ioconf.maddrs));
211		TAILQ_INIT(&(e_dev->ioconf.irqs));
212
213		device_add_child(dev, NULL, -1, e_dev);
214	}
215
216	return 0;
217}
218
219static void
220eisa_print_child(device_t dev, device_t child)
221{
222	/* XXX print resource descriptions? */
223	printf(" at slot %d", eisa_get_slot(child));
224	printf(" on %s", device_get_nameunit(dev));
225}
226
227static int
228eisa_find_irq(struct eisa_device *e_dev, int rid)
229{
230	int i;
231	struct irq_node *irq;
232
233	for (i = 0, irq = TAILQ_FIRST(&e_dev->ioconf.irqs);
234	     i < rid && irq;
235	     i++, irq = TAILQ_NEXT(irq, links))
236		;
237
238	if (irq)
239		return irq->irq_no;
240	else
241		return -1;
242}
243
244static struct resvaddr *
245eisa_find_maddr(struct eisa_device *e_dev, int rid)
246{
247	int i;
248	struct resvaddr *resv;
249
250	for (i = 0, resv = LIST_FIRST(&e_dev->ioconf.maddrs);
251	     i < rid && resv;
252	     i++, resv = LIST_NEXT(resv, links))
253		;
254
255	return resv;
256}
257
258static struct resvaddr *
259eisa_find_ioaddr(struct eisa_device *e_dev, int rid)
260{
261	int i;
262	struct resvaddr *resv;
263
264	for (i = 0, resv = LIST_FIRST(&e_dev->ioconf.ioaddrs);
265	     i < rid && resv;
266	     i++, resv = LIST_NEXT(resv, links))
267		;
268
269	return resv;
270}
271
272static int
273eisa_read_ivar(device_t dev, device_t child, int which, u_long *result)
274{
275	struct eisa_device *e_dev = device_get_ivars(child);
276
277	switch (which) {
278	case EISA_IVAR_SLOT:
279		*result = e_dev->ioconf.slot;
280		break;
281
282	case EISA_IVAR_ID:
283		*result = e_dev->id;
284		break;
285
286	case EISA_IVAR_IRQ:
287		/* XXX only first irq */
288		*result = eisa_find_irq(e_dev, 0);
289		break;
290
291	default:
292		return (ENOENT);
293	}
294
295	return (0);
296}
297
298static int
299eisa_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
300{
301	return (EINVAL);
302}
303
304static struct resource *
305eisa_alloc_resource(device_t dev, device_t child, int type, int *rid,
306		    u_long start, u_long end, u_long count, u_int flags)
307{
308	int isdefault;
309	struct eisa_device *e_dev = device_get_ivars(child);
310	struct resource *rv, **rvp = 0;
311
312	isdefault = (device_get_parent(child) == dev
313		     && start == 0UL && end == ~0UL && count == 1);
314
315	switch (type) {
316	case SYS_RES_IRQ:
317		if (isdefault) {
318			int irq = eisa_find_irq(e_dev, *rid);
319			if (irq == -1)
320				return 0;
321			start = end = irq;
322			count = 1;
323		}
324		break;
325
326	case SYS_RES_MEMORY:
327		if (isdefault) {
328			struct resvaddr *resv;
329
330			resv = eisa_find_maddr(e_dev, *rid);
331			if (!resv)
332				return 0;
333
334			start = resv->addr;
335			end = resv->size - 1;
336			count = resv->size;
337			rvp = &resv->res;
338		}
339		break;
340
341	case SYS_RES_IOPORT:
342		if (isdefault) {
343			struct resvaddr *resv;
344
345			resv = eisa_find_ioaddr(e_dev, *rid);
346			if (!resv)
347				return 0;
348
349			start = resv->addr;
350			end = resv->size - 1;
351			count = resv->size;
352			rvp = &resv->res;
353		}
354		break;
355
356	default:
357		return 0;
358	}
359
360	rv = BUS_ALLOC_RESOURCE(device_get_parent(dev), child,
361				 type, rid, start, end, count, flags);
362	if (rvp)
363		*rvp = rv;
364
365	return rv;
366}
367
368static int
369eisa_release_resource(device_t dev, device_t child, int type, int rid,
370		      struct resource *r)
371{
372	int rv;
373	struct eisa_device *e_dev = device_get_ivars(child);
374	struct resvaddr *resv = 0;
375
376	switch (type) {
377	case SYS_RES_IRQ:
378		if (eisa_find_irq(e_dev, rid) == -1)
379			return EINVAL;
380		break;
381
382	case SYS_RES_MEMORY:
383		if (device_get_parent(child) == dev)
384			resv = eisa_find_maddr(e_dev, rid);
385		break;
386
387
388	case SYS_RES_IOPORT:
389		if (device_get_parent(child) == dev)
390			resv = eisa_find_ioaddr(e_dev, rid);
391		break;
392
393	default:
394		return (ENOENT);
395	}
396
397	rv = BUS_RELEASE_RESOURCE(device_get_parent(dev), child, type, rid, r);
398
399	if (rv == 0) {
400		if (resv)
401			resv->res = 0;
402	}
403
404	return rv;
405}
406
407#if 0
408
409/* Interrupt and I/O space registration facitlities */
410void
411eisa_reg_start(e_dev)
412	struct eisa_device *e_dev;
413{
414	/*
415	 * Announce the device.
416	 */
417	char *string;
418
419	reg_state.in_registration = 1;
420	reg_state.num_interrupts = 0;
421	reg_state.num_ioaddrs = 0;
422	reg_state.num_maddrs = 0;
423	reg_state.column = 0;
424
425	string = malloc(strlen(e_dev->full_name) + sizeof(" <>") + /*NULL*/1,
426			M_TEMP, M_NOWAIT);
427	if(!string) {
428		printf("eisa0: cannot malloc device description string\n");
429		return;
430	}
431	sprintf(string, " <%s>", e_dev->full_name);
432	eisa_reg_print(e_dev, string, /*separator=*/NULL);
433	free(string, M_TEMP);
434}
435
436/*
437 * Output registration information mindfull of screen wrap.
438 * Output an optional character separator before the string
439 * if the line does not wrap.
440 */
441static void
442eisa_reg_print(e_dev, string, separator)
443	struct eisa_device *e_dev;
444	char *string;
445	char *separator;
446{
447	int len = strlen(string);
448
449	if(separator)
450		len++;
451
452	if(reg_state.column + len > MAX_COL) {
453		printf("\n");
454		reg_state.column = 0;
455	}
456	else if(separator) {
457		printf("%c", *separator);
458		reg_state.column++;
459	}
460
461	if(reg_state.column == 0)
462		reg_state.column += printf("%s%ld:%s",
463					   e_dev->driver->name,
464					   e_dev->unit,
465					   string);
466	else
467		reg_state.column += printf("%s", string);
468}
469
470/* Interrupt and I/O space registration facitlities */
471void
472eisa_reg_end(e_dev)
473	struct eisa_device *e_dev;
474{
475	if( reg_state.in_registration )
476	{
477		char string[25];
478
479		snprintf(string, sizeof(string), " on %s0 slot %d",
480			mainboard_drv.name,
481			e_dev->ioconf.slot);
482		eisa_reg_print(e_dev, string, NULL);
483		printf("\n");
484		reg_state.in_registration = 0;
485	}
486	else
487		printf("eisa_reg_end called outside of a "
488		       "registration session\n");
489}
490
491#endif /* 0 */
492
493int
494eisa_add_intr(dev, irq)
495	device_t dev;
496	int irq;
497{
498	struct eisa_device *e_dev = device_get_ivars(dev);
499	struct	irq_node *irq_info;
500
501	irq_info = (struct irq_node *)malloc(sizeof(*irq_info), M_DEVBUF,
502					     M_NOWAIT);
503	if (irq_info == NULL)
504		return (1);
505
506	irq_info->irq_no = irq;
507	irq_info->idesc = NULL;
508	TAILQ_INSERT_TAIL(&e_dev->ioconf.irqs, irq_info, links);
509	return 0;
510}
511
512#if 0
513
514int
515eisa_reg_intr(e_dev, irq, func, arg, maskptr, shared)
516	struct eisa_device *e_dev;
517	int   irq;
518	void (*func)(void *);
519	void  *arg;
520	u_int *maskptr;
521	int   shared;
522{
523	char string[25];
524	char separator = ',';
525
526#if NOT_YET
527	/*
528	 * Punt on conflict detection for the moment.
529	 * I want to develop a generic routine to do
530	 * this for all device types.
531	 */
532	int checkthese = CC_IRQ;
533	if (haveseen_dev(dev, checkthese))
534        	return 1;
535#endif
536	if (reg_state.in_registration) {
537		/*
538		 * Find the first instance of this irq that has a
539		 * NULL idesc.
540		 */
541		struct irq_node *cur_irq;
542
543		cur_irq = TAILQ_FIRST(&e_dev->ioconf.irqs);
544		while (cur_irq != NULL) {
545			if (cur_irq->irq_no == irq
546			 && cur_irq->idesc == NULL) {
547				/* XXX use cfg->devdata  */
548                		void *dev_instance = (void *)-1;
549
550				cur_irq->idesc = intr_create(dev_instance,
551							     irq,
552							     func,
553							     arg,
554							     maskptr, 0);
555				break;
556			}
557			cur_irq = TAILQ_NEXT(cur_irq, links);
558		}
559
560		if (cur_irq == NULL || cur_irq->idesc == NULL)
561			return (-1);
562	} else {
563		return EPERM;
564	}
565
566	snprintf(string, sizeof(string), " irq %d", irq);
567	eisa_reg_print(e_dev, string, reg_state.num_interrupts ?
568				      &separator : NULL);
569	reg_state.num_interrupts++;
570	return (0);
571}
572
573int
574eisa_release_intr(e_dev, irq, func)
575	struct eisa_device *e_dev;
576	int   irq;
577	void  (*func)(void *);
578{
579	int	result;
580	struct	irq_node *cur_irq;
581
582	result = -1;
583	cur_irq = TAILQ_FIRST(&e_dev->ioconf.irqs);
584       	while (cur_irq != NULL) {
585		if (cur_irq->irq_no == irq) {
586			struct	irq_node *next_irq;
587
588			next_irq = TAILQ_NEXT(cur_irq, links);
589			if (cur_irq->idesc != NULL)
590				intr_destroy(cur_irq->idesc);
591			TAILQ_REMOVE(&e_dev->ioconf.irqs, cur_irq, links);
592			free(cur_irq, M_DEVBUF);
593			cur_irq = next_irq;
594			result = 0;
595		} else {
596			cur_irq = TAILQ_NEXT(cur_irq, links);
597		}
598	}
599	if (result != 0) {
600		printf("%s%ld: Attempted to release an interrupt (%d) "
601		       "it doesn't own\n", e_dev->driver->name,
602			e_dev->unit, irq);
603	}
604
605	return (result);
606}
607
608int
609eisa_enable_intr(e_dev, irq)
610	struct eisa_device *e_dev;
611	int irq;
612{
613	struct	irq_node *cur_irq;
614	int	result;
615
616	result = -1;
617	cur_irq = TAILQ_FIRST(&e_dev->ioconf.irqs);
618       	while (cur_irq != NULL) {
619		if (cur_irq->irq_no == irq
620		 && cur_irq->idesc != NULL) {
621			result = intr_connect(cur_irq->idesc);
622		}
623		cur_irq = TAILQ_NEXT(cur_irq, links);
624	}
625	return (result);
626}
627
628#endif /* 0 */
629
630static int
631eisa_add_resvaddr(e_dev, head, base, size, flags)
632	struct eisa_device *e_dev;
633	struct resvlist *head;
634	u_long	base;
635	u_long	size;
636	int	flags;
637{
638	resvaddr_t *reservation;
639
640	reservation = (resvaddr_t *)malloc(sizeof(resvaddr_t),
641					   M_DEVBUF, M_NOWAIT);
642	if(!reservation)
643		return (ENOMEM);
644
645	reservation->addr = base;
646	reservation->size = size;
647	reservation->flags = flags;
648
649	if (!head->lh_first) {
650		LIST_INSERT_HEAD(head, reservation, links);
651	}
652	else {
653		resvaddr_t *node;
654		for(node = head->lh_first; node; node = node->links.le_next) {
655			if (node->addr > reservation->addr) {
656				/*
657				 * List is sorted in increasing
658				 * address order.
659				 */
660				LIST_INSERT_BEFORE(node, reservation, links);
661				break;
662			}
663
664			if (node->addr == reservation->addr) {
665				/*
666				 * If the entry we want to add
667				 * matches any already in here,
668				 * fail.
669				 */
670				free(reservation, M_DEVBUF);
671				return (EEXIST);
672			}
673
674			if (!node->links.le_next) {
675				LIST_INSERT_AFTER(node, reservation, links);
676				break;
677			}
678		}
679	}
680	return (0);
681}
682
683int
684eisa_add_mspace(dev, mbase, msize, flags)
685	device_t dev;
686	u_long	mbase;
687	u_long	msize;
688	int	flags;
689{
690	struct eisa_device *e_dev = device_get_ivars(dev);
691	return	eisa_add_resvaddr(e_dev, &(e_dev->ioconf.maddrs), mbase, msize,
692				  flags);
693}
694
695int
696eisa_add_iospace(dev, iobase, iosize, flags)
697	device_t dev;
698	u_long	iobase;
699	u_long	iosize;
700	int	flags;
701{
702	struct eisa_device *e_dev = device_get_ivars(dev);
703	return	eisa_add_resvaddr(e_dev, &(e_dev->ioconf.ioaddrs), iobase,
704				  iosize, flags);
705}
706
707#if 0
708
709static int
710eisa_reg_resvaddr(e_dev, head, resvaddr, reg_count)
711	struct eisa_device *e_dev;
712	struct resvlist *head;
713	resvaddr_t *resvaddr;
714	int *reg_count;
715{
716	if (reg_state.in_registration) {
717		resvaddr_t *node;
718		/*
719		 * Ensure that this resvaddr is actually in the devices'
720		 * reservation list.
721		 */
722		for(node = head->lh_first; node;
723		    node = node->links.le_next) {
724			if (node == resvaddr) {
725				char buf[35];
726				char separator = ',';
727				char *string = buf;
728
729				if (*reg_count == 0) {
730					/* First time */
731					string += sprintf(string, " at");
732				}
733
734				if (node->size == 1
735				  || (node->flags & RESVADDR_BITMASK))
736					sprintf(string, " 0x%lx", node->addr);
737				else
738					sprintf(string, " 0x%lx-0x%lx",
739						node->addr,
740						node->addr + node->size - 1);
741				eisa_reg_print(e_dev, buf,
742						*reg_count ? &separator : NULL);
743				(*reg_count)++;
744				return (0);
745			}
746		}
747		return (ENOENT);
748	}
749	return EPERM;
750}
751
752int
753eisa_reg_mspace(e_dev, resvaddr)
754	struct eisa_device *e_dev;
755	resvaddr_t *resvaddr;
756{
757#ifdef NOT_YET
758	/*
759	 * Punt on conflict detection for the moment.
760	 * I want to develop a generic routine to do
761	 * this for all device types.
762	 */
763	int checkthese = CC_MADDR;
764	if (haveseen_dev(dev, checkthese))
765		return -1;
766#endif
767	return (eisa_reg_resvaddr(e_dev, &(e_dev->ioconf.maddrs), resvaddr,
768				  &(reg_state.num_maddrs)));
769}
770
771int
772eisa_reg_iospace(e_dev, resvaddr)
773	struct eisa_device *e_dev;
774	resvaddr_t *resvaddr;
775{
776#ifdef NOT_YET
777	/*
778	 * Punt on conflict detection for the moment.
779	 * I want to develop a generic routine to do
780	 * this for all device types.
781	 */
782	int checkthese = CC_IOADDR;
783	if (haveseen_dev(dev, checkthese))
784		return -1;
785#endif
786	return (eisa_reg_resvaddr(e_dev, &(e_dev->ioconf.ioaddrs), resvaddr,
787				  &(reg_state.num_ioaddrs)));
788}
789
790#endif
791
792static device_method_t eisa_methods[] = {
793	/* Device interface */
794	DEVMETHOD(device_probe,		eisa_probe),
795	DEVMETHOD(device_attach,	bus_generic_attach),
796	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
797
798	/* Bus interface */
799	DEVMETHOD(bus_print_child,	eisa_print_child),
800	DEVMETHOD(bus_read_ivar,	eisa_read_ivar),
801	DEVMETHOD(bus_write_ivar,	eisa_write_ivar),
802	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),
803	DEVMETHOD(bus_alloc_resource,	eisa_alloc_resource),
804	DEVMETHOD(bus_release_resource,	eisa_release_resource),
805	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
806	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
807	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
808	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
809
810	{ 0, 0 }
811};
812
813static driver_t eisa_driver = {
814	"eisa",
815	eisa_methods,
816	DRIVER_TYPE_MISC,
817	1,			/* no softc */
818};
819
820DRIVER_MODULE(eisa, isab, eisa_driver, eisa_devclass, 0, 0);
821