eisaconf.c revision 45791
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.37 1999/01/14 06:22:03 jdp 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	for (slot = 0; slot < num_eisa_slots; eisaBase+=0x1000, slot++) {
184		int id_size = sizeof(eisa_id);
185		eisa_id = 0;
186    		for( i = 0; i < id_size; i++ ) {
187			outb(eisaBase,0x80 + i); /*Some cards require priming*/
188			eisa_id |= inb(eisaBase+i) << ((id_size-i-1)*CHAR_BIT);
189		}
190		if (eisa_id & 0x80000000)
191			continue;  /* no EISA card in slot */
192
193		/* Prepare an eisa_device_node for this slot */
194		e_dev = (struct eisa_device *)malloc(sizeof(*e_dev),
195						     M_DEVBUF, M_NOWAIT);
196		if (!e_dev) {
197			printf("eisa0: cannot malloc eisa_device");
198			break; /* Try to attach what we have already */
199		}
200		bzero(e_dev, sizeof(*e_dev));
201
202		e_dev->id = eisa_id;
203
204		e_dev->ioconf.slot = slot;
205
206		/* Initialize our lists of reserved addresses */
207		LIST_INIT(&(e_dev->ioconf.ioaddrs));
208		LIST_INIT(&(e_dev->ioconf.maddrs));
209		TAILQ_INIT(&(e_dev->ioconf.irqs));
210
211		device_add_child(dev, NULL, -1, e_dev);
212	}
213
214	return 0;
215}
216
217static void
218eisa_print_child(device_t dev, device_t child)
219{
220	/* XXX print resource descriptions? */
221	printf(" at slot %d", eisa_get_slot(child));
222	printf(" on %s", device_get_nameunit(dev));
223}
224
225static int
226eisa_find_irq(struct eisa_device *e_dev, int rid)
227{
228	int i;
229	struct irq_node *irq;
230
231	for (i = 0, irq = TAILQ_FIRST(&e_dev->ioconf.irqs);
232	     i < rid && irq;
233	     i++, irq = TAILQ_NEXT(irq, links))
234		;
235
236	if (irq)
237		return irq->irq_no;
238	else
239		return -1;
240}
241
242static struct resvaddr *
243eisa_find_maddr(struct eisa_device *e_dev, int rid)
244{
245	int i;
246	struct resvaddr *resv;
247
248	for (i = 0, resv = LIST_FIRST(&e_dev->ioconf.maddrs);
249	     i < rid && resv;
250	     i++, resv = LIST_NEXT(resv, links))
251		;
252
253	return resv;
254}
255
256static struct resvaddr *
257eisa_find_ioaddr(struct eisa_device *e_dev, int rid)
258{
259	int i;
260	struct resvaddr *resv;
261
262	for (i = 0, resv = LIST_FIRST(&e_dev->ioconf.ioaddrs);
263	     i < rid && resv;
264	     i++, resv = LIST_NEXT(resv, links))
265		;
266
267	return resv;
268}
269
270static int
271eisa_read_ivar(device_t dev, device_t child, int which, u_long *result)
272{
273	struct eisa_device *e_dev = device_get_ivars(child);
274
275	switch (which) {
276	case EISA_IVAR_SLOT:
277		*result = e_dev->ioconf.slot;
278		break;
279
280	case EISA_IVAR_ID:
281		*result = e_dev->id;
282		break;
283
284	case EISA_IVAR_IRQ:
285		/* XXX only first irq */
286		*result = eisa_find_irq(e_dev, 0);
287		break;
288
289	default:
290		return (ENOENT);
291	}
292
293	return (0);
294}
295
296static int
297eisa_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
298{
299	return (EINVAL);
300}
301
302static struct resource *
303eisa_alloc_resource(device_t dev, device_t child, int type, int *rid,
304		    u_long start, u_long end, u_long count, u_int flags)
305{
306	int isdefault;
307	struct eisa_device *e_dev = device_get_ivars(child);
308	struct resource *rv, **rvp = 0;
309
310	isdefault = (device_get_parent(child) == dev
311		     && start == 0UL && end == ~0UL && count == 1);
312
313	switch (type) {
314	case SYS_RES_IRQ:
315		if (isdefault) {
316			int irq = eisa_find_irq(e_dev, *rid);
317			if (irq == -1)
318				return 0;
319			start = end = irq;
320			count = 1;
321		}
322		break;
323
324	case SYS_RES_MEMORY:
325		if (isdefault) {
326			struct resvaddr *resv;
327
328			resv = eisa_find_maddr(e_dev, *rid);
329			if (!resv)
330				return 0;
331
332			start = resv->addr;
333			end = resv->size - 1;
334			count = resv->size;
335			rvp = &resv->res;
336		}
337		break;
338
339	case SYS_RES_IOPORT:
340		if (isdefault) {
341			struct resvaddr *resv;
342
343			resv = eisa_find_ioaddr(e_dev, *rid);
344			if (!resv)
345				return 0;
346
347			start = resv->addr;
348			end = resv->size - 1;
349			count = resv->size;
350			rvp = &resv->res;
351		}
352		break;
353
354	default:
355		return 0;
356	}
357
358	rv = BUS_ALLOC_RESOURCE(device_get_parent(dev), child,
359				 type, rid, start, end, count, flags);
360	if (rvp)
361		*rvp = rv;
362
363	return rv;
364}
365
366static int
367eisa_release_resource(device_t dev, device_t child, int type, int rid,
368		      struct resource *r)
369{
370	int rv;
371	struct eisa_device *e_dev = device_get_ivars(child);
372	struct resvaddr *resv = 0;
373
374	switch (type) {
375	case SYS_RES_IRQ:
376		if (eisa_find_irq(e_dev, rid) == -1)
377			return EINVAL;
378		break;
379
380	case SYS_RES_MEMORY:
381		if (device_get_parent(child) == dev)
382			resv = eisa_find_maddr(e_dev, rid);
383		break;
384
385
386	case SYS_RES_IOPORT:
387		if (device_get_parent(child) == dev)
388			resv = eisa_find_ioaddr(e_dev, rid);
389		break;
390
391	default:
392		return (ENOENT);
393	}
394
395	rv = BUS_RELEASE_RESOURCE(device_get_parent(dev), child, type, rid, r);
396
397	if (rv == 0) {
398		if (resv)
399			resv->res = 0;
400	}
401
402	return rv;
403}
404
405#if 0
406
407/* Interrupt and I/O space registration facitlities */
408void
409eisa_reg_start(e_dev)
410	struct eisa_device *e_dev;
411{
412	/*
413	 * Announce the device.
414	 */
415	char *string;
416
417	reg_state.in_registration = 1;
418	reg_state.num_interrupts = 0;
419	reg_state.num_ioaddrs = 0;
420	reg_state.num_maddrs = 0;
421	reg_state.column = 0;
422
423	string = malloc(strlen(e_dev->full_name) + sizeof(" <>") + /*NULL*/1,
424			M_TEMP, M_NOWAIT);
425	if(!string) {
426		printf("eisa0: cannot malloc device description string\n");
427		return;
428	}
429	sprintf(string, " <%s>", e_dev->full_name);
430	eisa_reg_print(e_dev, string, /*separator=*/NULL);
431	free(string, M_TEMP);
432}
433
434/*
435 * Output registration information mindfull of screen wrap.
436 * Output an optional character separator before the string
437 * if the line does not wrap.
438 */
439static void
440eisa_reg_print(e_dev, string, separator)
441	struct eisa_device *e_dev;
442	char *string;
443	char *separator;
444{
445	int len = strlen(string);
446
447	if(separator)
448		len++;
449
450	if(reg_state.column + len > MAX_COL) {
451		printf("\n");
452		reg_state.column = 0;
453	}
454	else if(separator) {
455		printf("%c", *separator);
456		reg_state.column++;
457	}
458
459	if(reg_state.column == 0)
460		reg_state.column += printf("%s%ld:%s",
461					   e_dev->driver->name,
462					   e_dev->unit,
463					   string);
464	else
465		reg_state.column += printf("%s", string);
466}
467
468/* Interrupt and I/O space registration facitlities */
469void
470eisa_reg_end(e_dev)
471	struct eisa_device *e_dev;
472{
473	if( reg_state.in_registration )
474	{
475		char string[25];
476
477		snprintf(string, sizeof(string), " on %s0 slot %d",
478			mainboard_drv.name,
479			e_dev->ioconf.slot);
480		eisa_reg_print(e_dev, string, NULL);
481		printf("\n");
482		reg_state.in_registration = 0;
483	}
484	else
485		printf("eisa_reg_end called outside of a "
486		       "registration session\n");
487}
488
489#endif /* 0 */
490
491int
492eisa_add_intr(dev, irq)
493	device_t dev;
494	int irq;
495{
496	struct eisa_device *e_dev = device_get_ivars(dev);
497	struct	irq_node *irq_info;
498
499	irq_info = (struct irq_node *)malloc(sizeof(*irq_info), M_DEVBUF,
500					     M_NOWAIT);
501	if (irq_info == NULL)
502		return (1);
503
504	irq_info->irq_no = irq;
505	irq_info->idesc = NULL;
506	TAILQ_INSERT_TAIL(&e_dev->ioconf.irqs, irq_info, links);
507	return 0;
508}
509
510#if 0
511
512int
513eisa_reg_intr(e_dev, irq, func, arg, maskptr, shared)
514	struct eisa_device *e_dev;
515	int   irq;
516	void (*func)(void *);
517	void  *arg;
518	u_int *maskptr;
519	int   shared;
520{
521	char string[25];
522	char separator = ',';
523
524#if NOT_YET
525	/*
526	 * Punt on conflict detection for the moment.
527	 * I want to develop a generic routine to do
528	 * this for all device types.
529	 */
530	int checkthese = CC_IRQ;
531	if (haveseen_dev(dev, checkthese))
532        	return 1;
533#endif
534	if (reg_state.in_registration) {
535		/*
536		 * Find the first instance of this irq that has a
537		 * NULL idesc.
538		 */
539		struct irq_node *cur_irq;
540
541		cur_irq = TAILQ_FIRST(&e_dev->ioconf.irqs);
542		while (cur_irq != NULL) {
543			if (cur_irq->irq_no == irq
544			 && cur_irq->idesc == NULL) {
545				/* XXX use cfg->devdata  */
546                		void *dev_instance = (void *)-1;
547
548				cur_irq->idesc = intr_create(dev_instance,
549							     irq,
550							     func,
551							     arg,
552							     maskptr, 0);
553				break;
554			}
555			cur_irq = TAILQ_NEXT(cur_irq, links);
556		}
557
558		if (cur_irq == NULL || cur_irq->idesc == NULL)
559			return (-1);
560	} else {
561		return EPERM;
562	}
563
564	snprintf(string, sizeof(string), " irq %d", irq);
565	eisa_reg_print(e_dev, string, reg_state.num_interrupts ?
566				      &separator : NULL);
567	reg_state.num_interrupts++;
568	return (0);
569}
570
571int
572eisa_release_intr(e_dev, irq, func)
573	struct eisa_device *e_dev;
574	int   irq;
575	void  (*func)(void *);
576{
577	int	result;
578	struct	irq_node *cur_irq;
579
580	result = -1;
581	cur_irq = TAILQ_FIRST(&e_dev->ioconf.irqs);
582       	while (cur_irq != NULL) {
583		if (cur_irq->irq_no == irq) {
584			struct	irq_node *next_irq;
585
586			next_irq = TAILQ_NEXT(cur_irq, links);
587			if (cur_irq->idesc != NULL)
588				intr_destroy(cur_irq->idesc);
589			TAILQ_REMOVE(&e_dev->ioconf.irqs, cur_irq, links);
590			free(cur_irq, M_DEVBUF);
591			cur_irq = next_irq;
592			result = 0;
593		} else {
594			cur_irq = TAILQ_NEXT(cur_irq, links);
595		}
596	}
597	if (result != 0) {
598		printf("%s%ld: Attempted to release an interrupt (%d) "
599		       "it doesn't own\n", e_dev->driver->name,
600			e_dev->unit, irq);
601	}
602
603	return (result);
604}
605
606int
607eisa_enable_intr(e_dev, irq)
608	struct eisa_device *e_dev;
609	int irq;
610{
611	struct	irq_node *cur_irq;
612	int	result;
613
614	result = -1;
615	cur_irq = TAILQ_FIRST(&e_dev->ioconf.irqs);
616       	while (cur_irq != NULL) {
617		if (cur_irq->irq_no == irq
618		 && cur_irq->idesc != NULL) {
619			result = intr_connect(cur_irq->idesc);
620		}
621		cur_irq = TAILQ_NEXT(cur_irq, links);
622	}
623	return (result);
624}
625
626#endif /* 0 */
627
628static int
629eisa_add_resvaddr(e_dev, head, base, size, flags)
630	struct eisa_device *e_dev;
631	struct resvlist *head;
632	u_long	base;
633	u_long	size;
634	int	flags;
635{
636	resvaddr_t *reservation;
637
638	reservation = (resvaddr_t *)malloc(sizeof(resvaddr_t),
639					   M_DEVBUF, M_NOWAIT);
640	if(!reservation)
641		return (ENOMEM);
642
643	reservation->addr = base;
644	reservation->size = size;
645	reservation->flags = flags;
646
647	if (!head->lh_first) {
648		LIST_INSERT_HEAD(head, reservation, links);
649	}
650	else {
651		resvaddr_t *node;
652		for(node = head->lh_first; node; node = node->links.le_next) {
653			if (node->addr > reservation->addr) {
654				/*
655				 * List is sorted in increasing
656				 * address order.
657				 */
658				LIST_INSERT_BEFORE(node, reservation, links);
659				break;
660			}
661
662			if (node->addr == reservation->addr) {
663				/*
664				 * If the entry we want to add
665				 * matches any already in here,
666				 * fail.
667				 */
668				free(reservation, M_DEVBUF);
669				return (EEXIST);
670			}
671
672			if (!node->links.le_next) {
673				LIST_INSERT_AFTER(node, reservation, links);
674				break;
675			}
676		}
677	}
678	return (0);
679}
680
681int
682eisa_add_mspace(dev, mbase, msize, flags)
683	device_t dev;
684	u_long	mbase;
685	u_long	msize;
686	int	flags;
687{
688	struct eisa_device *e_dev = device_get_ivars(dev);
689	return	eisa_add_resvaddr(e_dev, &(e_dev->ioconf.maddrs), mbase, msize,
690				  flags);
691}
692
693int
694eisa_add_iospace(dev, iobase, iosize, flags)
695	device_t dev;
696	u_long	iobase;
697	u_long	iosize;
698	int	flags;
699{
700	struct eisa_device *e_dev = device_get_ivars(dev);
701	return	eisa_add_resvaddr(e_dev, &(e_dev->ioconf.ioaddrs), iobase,
702				  iosize, flags);
703}
704
705#if 0
706
707static int
708eisa_reg_resvaddr(e_dev, head, resvaddr, reg_count)
709	struct eisa_device *e_dev;
710	struct resvlist *head;
711	resvaddr_t *resvaddr;
712	int *reg_count;
713{
714	if (reg_state.in_registration) {
715		resvaddr_t *node;
716		/*
717		 * Ensure that this resvaddr is actually in the devices'
718		 * reservation list.
719		 */
720		for(node = head->lh_first; node;
721		    node = node->links.le_next) {
722			if (node == resvaddr) {
723				char buf[35];
724				char separator = ',';
725				char *string = buf;
726
727				if (*reg_count == 0) {
728					/* First time */
729					string += sprintf(string, " at");
730				}
731
732				if (node->size == 1
733				  || (node->flags & RESVADDR_BITMASK))
734					sprintf(string, " 0x%lx", node->addr);
735				else
736					sprintf(string, " 0x%lx-0x%lx",
737						node->addr,
738						node->addr + node->size - 1);
739				eisa_reg_print(e_dev, buf,
740						*reg_count ? &separator : NULL);
741				(*reg_count)++;
742				return (0);
743			}
744		}
745		return (ENOENT);
746	}
747	return EPERM;
748}
749
750int
751eisa_reg_mspace(e_dev, resvaddr)
752	struct eisa_device *e_dev;
753	resvaddr_t *resvaddr;
754{
755#ifdef NOT_YET
756	/*
757	 * Punt on conflict detection for the moment.
758	 * I want to develop a generic routine to do
759	 * this for all device types.
760	 */
761	int checkthese = CC_MADDR;
762	if (haveseen_dev(dev, checkthese))
763		return -1;
764#endif
765	return (eisa_reg_resvaddr(e_dev, &(e_dev->ioconf.maddrs), resvaddr,
766				  &(reg_state.num_maddrs)));
767}
768
769int
770eisa_reg_iospace(e_dev, resvaddr)
771	struct eisa_device *e_dev;
772	resvaddr_t *resvaddr;
773{
774#ifdef NOT_YET
775	/*
776	 * Punt on conflict detection for the moment.
777	 * I want to develop a generic routine to do
778	 * this for all device types.
779	 */
780	int checkthese = CC_IOADDR;
781	if (haveseen_dev(dev, checkthese))
782		return -1;
783#endif
784	return (eisa_reg_resvaddr(e_dev, &(e_dev->ioconf.ioaddrs), resvaddr,
785				  &(reg_state.num_ioaddrs)));
786}
787
788#endif
789
790static device_method_t eisa_methods[] = {
791	/* Device interface */
792	DEVMETHOD(device_probe,		eisa_probe),
793	DEVMETHOD(device_attach,	bus_generic_attach),
794	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
795
796	/* Bus interface */
797	DEVMETHOD(bus_print_child,	eisa_print_child),
798	DEVMETHOD(bus_read_ivar,	eisa_read_ivar),
799	DEVMETHOD(bus_write_ivar,	eisa_write_ivar),
800	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),
801	DEVMETHOD(bus_alloc_resource,	eisa_alloc_resource),
802	DEVMETHOD(bus_release_resource,	eisa_release_resource),
803	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
804	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
805	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
806	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
807
808	{ 0, 0 }
809};
810
811static driver_t eisa_driver = {
812	"eisa",
813	eisa_methods,
814	DRIVER_TYPE_MISC,
815	1,			/* no softc */
816};
817
818DRIVER_MODULE(eisa, isab, eisa_driver, eisa_devclass, 0, 0);
819
820