xref: /freebsd/sys/isa/isa_common.c (revision 4cf49a43559ed9fdad601bdcccd2c55963008675)
1 /*-
2  * Copyright (c) 1999 Doug Rabson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 /*
29  * Modifications for Intel architecture by Garrett A. Wollman.
30  * Copyright 1998 Massachusetts Institute of Technology
31  *
32  * Permission to use, copy, modify, and distribute this software and
33  * its documentation for any purpose and without fee is hereby
34  * granted, provided that both the above copyright notice and this
35  * permission notice appear in all copies, that both the above
36  * copyright notice and this permission notice appear in all
37  * supporting documentation, and that the name of M.I.T. not be used
38  * in advertising or publicity pertaining to distribution of the
39  * software without specific, written prior permission.  M.I.T. makes
40  * no representations about the suitability of this software for any
41  * purpose.  It is provided "as is" without express or implied
42  * warranty.
43  *
44  * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
45  * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
46  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
47  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
48  * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
49  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
50  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
51  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
52  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
53  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
54  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55  * SUCH DAMAGE.
56  */
57 
58 /*
59  * Parts of the ISA bus implementation common to all architectures.
60  */
61 
62 #include <sys/param.h>
63 #include <sys/systm.h>
64 #include <sys/kernel.h>
65 #include <sys/bus.h>
66 #include <sys/malloc.h>
67 #include <sys/module.h>
68 #include <machine/bus.h>
69 #include <sys/rman.h>
70 
71 #include <machine/resource.h>
72 
73 #include <isa/isavar.h>
74 #include <isa/isa_common.h>
75 #ifdef __alpha__		/* XXX workaround a stupid warning */
76 #include <alpha/isa/isavar.h>
77 #endif
78 
79 MALLOC_DEFINE(M_ISADEV, "isadev", "ISA device");
80 
81 static devclass_t isa_devclass;
82 
83 /*
84  * At 'probe' time, we add all the devices which we know about to the
85  * bus.  The generic attach routine will probe and attach them if they
86  * are alive.
87  */
88 static int
89 isa_probe(device_t dev)
90 {
91 	device_set_desc(dev, "ISA bus");
92 	isa_init();		/* Allow machdep code to initialise */
93 	return bus_generic_probe(dev);
94 }
95 
96 extern device_t isa_bus_device;
97 
98 static int
99 isa_attach(device_t dev)
100 {
101 	/*
102 	 * Arrange for isa_probe_children(dev) to be called later. XXX
103 	 */
104 	isa_bus_device = dev;
105 	return 0;
106 }
107 
108 /*
109  * Find a working set of memory regions for a child using the ranges
110  * in *config  and return the regions in *result. Returns non-zero if
111  * a set of ranges was found.
112  */
113 static int
114 isa_find_memory(device_t child,
115 		struct isa_config *config,
116 		struct isa_config *result)
117 {
118 	int success, i;
119 	struct resource *res[ISA_NMEM];
120 
121 	/*
122 	 * First clear out any existing resource definitions.
123 	 */
124 	for (i = 0; i < ISA_NMEM; i++) {
125 		bus_delete_resource(child, SYS_RES_MEMORY, i);
126 		res[i] = NULL;
127 	}
128 
129 	success = 1;
130 	result->ic_nmem = config->ic_nmem;
131 	for (i = 0; i < config->ic_nmem; i++) {
132 		u_int32_t start, end, size, align;
133 		for (start = config->ic_mem[i].ir_start,
134 			     end = config->ic_mem[i].ir_end,
135 			     size = config->ic_mem[i].ir_size,
136 			     align = config->ic_mem[i].ir_align;
137 		     start + size - 1 <= end;
138 		     start += align) {
139 			bus_set_resource(child, SYS_RES_MEMORY, i,
140 					 start, size);
141 			res[i] = bus_alloc_resource(child,
142 						    SYS_RES_MEMORY, &i,
143 						    0, ~0, 1, RF_ACTIVE);
144 			if (res[i]) {
145 				result->ic_mem[i].ir_start = start;
146 				result->ic_mem[i].ir_end = start + size - 1;
147 				result->ic_mem[i].ir_size = size;
148 				result->ic_mem[i].ir_align = align;
149 				break;
150 			}
151 		}
152 
153 		/*
154 		 * If we didn't find a place for memory range i, then
155 		 * give up now.
156 		 */
157 		if (!res[i]) {
158 			success = 0;
159 			break;
160 		}
161 	}
162 
163 	for (i = 0; i < ISA_NMEM; i++) {
164 		if (res[i])
165 			bus_release_resource(child, SYS_RES_MEMORY,
166 					     i, res[i]);
167 	}
168 
169 	return success;
170 }
171 
172 /*
173  * Find a working set of port regions for a child using the ranges
174  * in *config  and return the regions in *result. Returns non-zero if
175  * a set of ranges was found.
176  */
177 static int
178 isa_find_port(device_t child,
179 	      struct isa_config *config,
180 	      struct isa_config *result)
181 {
182 	int success, i;
183 	struct resource *res[ISA_NPORT];
184 
185 	/*
186 	 * First clear out any existing resource definitions.
187 	 */
188 	for (i = 0; i < ISA_NPORT; i++) {
189 		bus_delete_resource(child, SYS_RES_IOPORT, i);
190 		res[i] = NULL;
191 	}
192 
193 	success = 1;
194 	result->ic_nport = config->ic_nport;
195 	for (i = 0; i < config->ic_nport; i++) {
196 		u_int32_t start, end, size, align;
197 		for (start = config->ic_port[i].ir_start,
198 			     end = config->ic_port[i].ir_end,
199 			     size = config->ic_port[i].ir_size,
200 			     align = config->ic_port[i].ir_align;
201 		     start + size - 1 <= end;
202 		     start += align) {
203 			bus_set_resource(child, SYS_RES_IOPORT, i,
204 					 start, size);
205 			res[i] = bus_alloc_resource(child,
206 						    SYS_RES_IOPORT, &i,
207 						    0, ~0, 1, RF_ACTIVE);
208 			if (res[i]) {
209 				result->ic_port[i].ir_start = start;
210 				result->ic_port[i].ir_end = start + size - 1;
211 				result->ic_port[i].ir_size = size;
212 				result->ic_port[i].ir_align = align;
213 				break;
214 			}
215 		}
216 
217 		/*
218 		 * If we didn't find a place for port range i, then
219 		 * give up now.
220 		 */
221 		if (!res[i]) {
222 			success = 0;
223 			break;
224 		}
225 	}
226 
227 	for (i = 0; i < ISA_NPORT; i++) {
228 		if (res[i])
229 			bus_release_resource(child, SYS_RES_IOPORT,
230 					     i, res[i]);
231 	}
232 
233 	return success;
234 }
235 
236 /*
237  * Return the index of the first bit in the mask (or -1 if mask is empty.
238  */
239 static int
240 find_first_bit(u_int32_t mask)
241 {
242 	return ffs(mask) - 1;
243 }
244 
245 /*
246  * Return the index of the next bit in the mask, or -1 if there are no more.
247  */
248 static int
249 find_next_bit(u_int32_t mask, int bit)
250 {
251 	bit++;
252 	while (bit < 32 && !(mask & (1 << bit)))
253 		bit++;
254 	if (bit != 32)
255 		return bit;
256 	return -1;
257 }
258 
259 /*
260  * Find a working set of irqs for a child using the masks in *config
261  * and return the regions in *result. Returns non-zero if a set of
262  * irqs was found.
263  */
264 static int
265 isa_find_irq(device_t child,
266 	     struct isa_config *config,
267 	     struct isa_config *result)
268 {
269 	int success, i;
270 	struct resource *res[ISA_NIRQ];
271 
272 	/*
273 	 * First clear out any existing resource definitions.
274 	 */
275 	for (i = 0; i < ISA_NIRQ; i++) {
276 		bus_delete_resource(child, SYS_RES_IRQ, i);
277 		res[i] = NULL;
278 	}
279 
280 	success = 1;
281 	result->ic_nirq = config->ic_nirq;
282 	for (i = 0; i < config->ic_nirq; i++) {
283 		u_int32_t mask = config->ic_irqmask[i];
284 		int irq;
285 		for (irq = find_first_bit(mask);
286 		     irq != -1;
287 		     irq = find_next_bit(mask, irq)) {
288 			bus_set_resource(child, SYS_RES_IRQ, i,
289 					 irq, 1);
290 			res[i] = bus_alloc_resource(child,
291 						    SYS_RES_IRQ, &i,
292 						    0, ~0, 1, RF_ACTIVE);
293 			if (res[i]) {
294 				result->ic_irqmask[i] = (1 << irq);
295 				break;
296 			}
297 		}
298 
299 		/*
300 		 * If we didn't find a place for irq range i, then
301 		 * give up now.
302 		 */
303 		if (!res[i]) {
304 			success = 0;
305 			break;
306 		}
307 	}
308 
309 	for (i = 0; i < ISA_NIRQ; i++) {
310 		if (res[i])
311 			bus_release_resource(child, SYS_RES_IRQ,
312 					     i, res[i]);
313 	}
314 
315 	return success;
316 }
317 
318 /*
319  * Find a working set of drqs for a child using the masks in *config
320  * and return the regions in *result. Returns non-zero if a set of
321  * drqs was found.
322  */
323 static int
324 isa_find_drq(device_t child,
325 	     struct isa_config *config,
326 	     struct isa_config *result)
327 {
328 	int success, i;
329 	struct resource *res[ISA_NDRQ];
330 
331 	/*
332 	 * First clear out any existing resource definitions.
333 	 */
334 	for (i = 0; i < ISA_NDRQ; i++) {
335 		bus_delete_resource(child, SYS_RES_DRQ, i);
336 		res[i] = NULL;
337 	}
338 
339 	success = 1;
340 	result->ic_ndrq = config->ic_ndrq;
341 	for (i = 0; i < config->ic_ndrq; i++) {
342 		u_int32_t mask = config->ic_drqmask[i];
343 		int drq;
344 		for (drq = find_first_bit(mask);
345 		     drq != -1;
346 		     drq = find_next_bit(mask, drq)) {
347 			bus_set_resource(child, SYS_RES_DRQ, i,
348 					 drq, 1);
349 			res[i] = bus_alloc_resource(child,
350 						    SYS_RES_DRQ, &i,
351 						    0, ~0, 1, RF_ACTIVE);
352 			if (res[i]) {
353 				result->ic_drqmask[i] = (1 << drq);
354 				break;
355 			}
356 		}
357 
358 		/*
359 		 * If we didn't find a place for drq range i, then
360 		 * give up now.
361 		 */
362 		if (!res[i]) {
363 			success = 0;
364 			break;
365 		}
366 	}
367 
368 	for (i = 0; i < ISA_NDRQ; i++) {
369 		if (res[i])
370 			bus_release_resource(child, SYS_RES_DRQ,
371 					     i, res[i]);
372 	}
373 
374 	return success;
375 }
376 
377 /*
378  * Attempt to find a working set of resources for a device. Return
379  * non-zero if a working configuration is found.
380  */
381 static int
382 isa_assign_resources(device_t child)
383 {
384 	struct isa_device *idev = DEVTOISA(child);
385 	struct isa_config_entry *ice;
386 	struct isa_config config;
387 
388 	bzero(&config, sizeof config);
389 	TAILQ_FOREACH(ice, &idev->id_configs, ice_link) {
390 		if (!isa_find_memory(child, &ice->ice_config, &config))
391 			continue;
392 		if (!isa_find_port(child, &ice->ice_config, &config))
393 			continue;
394 		if (!isa_find_irq(child, &ice->ice_config, &config))
395 			continue;
396 		if (!isa_find_drq(child, &ice->ice_config, &config))
397 			continue;
398 
399 		/*
400 		 * A working configuration was found enable the device
401 		 * with this configuration.
402 		 */
403 		if (idev->id_config_cb) {
404 			idev->id_config_cb(idev->id_config_arg,
405 					   &config, 1);
406 			return 1;
407 		}
408 	}
409 
410 	/*
411 	 * Disable the device.
412 	 */
413 	if (device_get_desc(child))
414 	    device_printf(child, "<%s> can't assign resources\n",
415 			  device_get_desc(child));
416 	else
417 	    device_printf(child, "can't assign resources\n");
418 	bzero(&config, sizeof config);
419 	if (idev->id_config_cb)
420 		idev->id_config_cb(idev->id_config_arg, &config, 0);
421 	device_disable(child);
422 
423 	return 0;
424 }
425 
426 /*
427  * Called after other devices have initialised to probe for isa devices.
428  */
429 void
430 isa_probe_children(device_t dev)
431 {
432 	device_t *children;
433 	int nchildren, i;
434 
435 	if (device_get_children(dev, &children, &nchildren))
436 		return;
437 
438 	/*
439 	 * First disable all pnp devices so that they don't get
440 	 * matched by legacy probes.
441 	 */
442 	for (i = 0; i < nchildren; i++) {
443 		device_t child = children[i];
444 		struct isa_device *idev = DEVTOISA(child);
445 		struct isa_config config;
446 
447 		bzero(&config, sizeof config);
448 		if (idev->id_config_cb)
449 			idev->id_config_cb(idev->id_config_arg, &config, 0);
450 	}
451 
452 	/*
453 	 * Next probe all non-pnp devices so that they claim their
454 	 * resources first.
455 	 */
456 	for (i = 0; i < nchildren; i++) {
457 		device_t child = children[i];
458 		struct isa_device *idev = DEVTOISA(child);
459 
460 		if (TAILQ_FIRST(&idev->id_configs))
461 			continue;
462 
463 		device_probe_and_attach(child);
464 	}
465 
466 	/*
467 	 * Finally assign resource to pnp devices and probe them.
468 	 */
469 	for (i = 0; i < nchildren; i++) {
470 		device_t child = children[i];
471 		struct isa_device* idev = DEVTOISA(child);
472 
473 		if (!TAILQ_FIRST(&idev->id_configs))
474 			continue;
475 
476 		if (isa_assign_resources(child)) {
477 			struct resource_list *rl = &idev->id_resources;
478 			struct resource_list_entry *rle;
479 
480 			device_probe_and_attach(child);
481 
482 			/*
483 			 * Claim any unallocated resources to keep other
484 			 * devices from using them.
485 			 */
486 			SLIST_FOREACH(rle, rl, link) {
487 				if (!rle->res) {
488 					int rid = rle->rid;
489 					resource_list_alloc(rl, dev, child,
490 							    rle->type,
491 							    &rid,
492 							    0, ~0, 1,
493 							    RF_ACTIVE);
494 				}
495 			}
496 		}
497 	}
498 
499 	free(children, M_TEMP);
500 }
501 
502 /*
503  * Add a new child with default ivars.
504  */
505 static device_t
506 isa_add_child(device_t dev, int order, const char *name, int unit)
507 {
508 	struct	isa_device *idev;
509 
510 	idev = malloc(sizeof(struct isa_device), M_ISADEV, M_NOWAIT);
511 	if (!idev)
512 		return 0;
513 	bzero(idev, sizeof *idev);
514 
515 	resource_list_init(&idev->id_resources);
516 	TAILQ_INIT(&idev->id_configs);
517 
518 	return device_add_child_ordered(dev, order, name, unit, idev);
519 }
520 
521 static void
522 isa_print_resources(struct resource_list *rl, const char *name, int type,
523 		    int count, const char *format)
524 {
525 	struct resource_list_entry *rle;
526 	int printed;
527 	int i;
528 
529 	printed = 0;
530 	for (i = 0; i < count; i++) {
531 		rle = resource_list_find(rl, type, i);
532 		if (rle) {
533 			if (printed == 0)
534 				printf(" %s ", name);
535 			else if (printed > 0)
536 				printf(",");
537 			printed++;
538 			printf(format, rle->start);
539 			if (rle->count > 1) {
540 				printf("-");
541 				printf(format, rle->start + rle->count - 1);
542 			}
543 		} else if (i > 3) {
544 			/* check the first few regardless */
545 			break;
546 		}
547 	}
548 }
549 
550 static int
551 isa_print_child(device_t bus, device_t dev)
552 {
553 	struct	isa_device *idev = DEVTOISA(dev);
554 	struct resource_list *rl = &idev->id_resources;
555 	int retval = 0;
556 
557 	retval += bus_print_child_header(bus, dev);
558 
559 	if (SLIST_FIRST(rl) || device_get_flags(dev))
560 		retval += printf(" at");
561 
562 	isa_print_resources(rl, "port", SYS_RES_IOPORT, ISA_NPORT, "%#lx");
563 	isa_print_resources(rl, "iomem", SYS_RES_MEMORY, ISA_NMEM, "%#lx");
564 	isa_print_resources(rl, "irq", SYS_RES_IRQ, ISA_NIRQ, "%ld");
565 	isa_print_resources(rl, "drq", SYS_RES_DRQ, ISA_NDRQ, "%ld");
566 	if (device_get_flags(dev))
567 		retval += printf(" flags %#x", device_get_flags(dev));
568 
569 	retval += bus_print_child_footer(bus, dev);
570 
571 	return (retval);
572 }
573 
574 static int
575 isa_read_ivar(device_t bus, device_t dev, int index, uintptr_t * result)
576 {
577 	struct isa_device* idev = DEVTOISA(dev);
578 	struct resource_list *rl = &idev->id_resources;
579 	struct resource_list_entry *rle;
580 
581 	switch (index) {
582 	case ISA_IVAR_PORT_0:
583 		rle = resource_list_find(rl, SYS_RES_IOPORT, 0);
584 		if (rle)
585 			*result = rle->start;
586 		else
587 			*result = -1;
588 		break;
589 
590 	case ISA_IVAR_PORT_1:
591 		rle = resource_list_find(rl, SYS_RES_IOPORT, 1);
592 		if (rle)
593 			*result = rle->start;
594 		else
595 			*result = -1;
596 		break;
597 
598 	case ISA_IVAR_PORTSIZE_0:
599 		rle = resource_list_find(rl, SYS_RES_IOPORT, 0);
600 		if (rle)
601 			*result = rle->count;
602 		else
603 			*result = 0;
604 		break;
605 
606 	case ISA_IVAR_PORTSIZE_1:
607 		rle = resource_list_find(rl, SYS_RES_IOPORT, 1);
608 		if (rle)
609 			*result = rle->count;
610 		else
611 			*result = 0;
612 		break;
613 
614 	case ISA_IVAR_MADDR_0:
615 		rle = resource_list_find(rl, SYS_RES_MEMORY, 0);
616 		if (rle)
617 			*result = rle->start;
618 		else
619 			*result = -1;
620 		break;
621 
622 	case ISA_IVAR_MADDR_1:
623 		rle = resource_list_find(rl, SYS_RES_MEMORY, 1);
624 		if (rle)
625 			*result = rle->start;
626 		else
627 			*result = -1;
628 		break;
629 
630 	case ISA_IVAR_MSIZE_0:
631 		rle = resource_list_find(rl, SYS_RES_MEMORY, 0);
632 		if (rle)
633 			*result = rle->count;
634 		else
635 			*result = 0;
636 		break;
637 
638 	case ISA_IVAR_MSIZE_1:
639 		rle = resource_list_find(rl, SYS_RES_MEMORY, 1);
640 		if (rle)
641 			*result = rle->count;
642 		else
643 			*result = 0;
644 		break;
645 
646 	case ISA_IVAR_IRQ_0:
647 		rle = resource_list_find(rl, SYS_RES_IRQ, 0);
648 		if (rle)
649 			*result = rle->start;
650 		else
651 			*result = -1;
652 		break;
653 
654 	case ISA_IVAR_IRQ_1:
655 		rle = resource_list_find(rl, SYS_RES_IRQ, 1);
656 		if (rle)
657 			*result = rle->start;
658 		else
659 			*result = -1;
660 		break;
661 
662 	case ISA_IVAR_DRQ_0:
663 		rle = resource_list_find(rl, SYS_RES_DRQ, 0);
664 		if (rle)
665 			*result = rle->start;
666 		else
667 			*result = -1;
668 		break;
669 
670 	case ISA_IVAR_DRQ_1:
671 		rle = resource_list_find(rl, SYS_RES_DRQ, 1);
672 		if (rle)
673 			*result = rle->start;
674 		else
675 			*result = -1;
676 		break;
677 
678 	case ISA_IVAR_VENDORID:
679 		*result = idev->id_vendorid;
680 		break;
681 
682 	case ISA_IVAR_SERIAL:
683 		*result = idev->id_serial;
684 		break;
685 
686 	case ISA_IVAR_LOGICALID:
687 		*result = idev->id_logicalid;
688 		break;
689 
690 	case ISA_IVAR_COMPATID:
691 		*result = idev->id_compatid;
692 		break;
693 
694 	default:
695 		return ENOENT;
696 	}
697 
698 	return 0;
699 }
700 
701 static int
702 isa_write_ivar(device_t bus, device_t dev,
703 	       int index, uintptr_t value)
704 {
705 	struct isa_device* idev = DEVTOISA(dev);
706 
707 	switch (index) {
708 	case ISA_IVAR_PORT_0:
709 	case ISA_IVAR_PORT_1:
710 	case ISA_IVAR_PORTSIZE_0:
711 	case ISA_IVAR_PORTSIZE_1:
712 	case ISA_IVAR_MADDR_0:
713 	case ISA_IVAR_MADDR_1:
714 	case ISA_IVAR_MSIZE_0:
715 	case ISA_IVAR_MSIZE_1:
716 	case ISA_IVAR_IRQ_0:
717 	case ISA_IVAR_IRQ_1:
718 	case ISA_IVAR_DRQ_0:
719 	case ISA_IVAR_DRQ_1:
720 		return EINVAL;
721 
722 	case ISA_IVAR_VENDORID:
723 		idev->id_vendorid = value;
724 		break;
725 
726 	case ISA_IVAR_SERIAL:
727 		idev->id_serial = value;
728 		break;
729 
730 	case ISA_IVAR_LOGICALID:
731 		idev->id_logicalid = value;
732 		break;
733 
734 	case ISA_IVAR_COMPATID:
735 		idev->id_compatid = value;
736 		break;
737 
738 	default:
739 		return (ENOENT);
740 	}
741 
742 	return (0);
743 }
744 
745 /*
746  * Free any resources which the driver missed or which we were holding for
747  * it (see isa_probe_children).
748  */
749 static void
750 isa_child_detached(device_t dev, device_t child)
751 {
752 	struct isa_device* idev = DEVTOISA(child);
753 	struct resource_list *rl = &idev->id_resources;
754 	struct resource_list_entry *rle;
755 
756 	SLIST_FOREACH(rle, &idev->id_resources, link) {
757 		if (rle->res)
758 			resource_list_release(rl, dev, child,
759 					      rle->type,
760 					      rle->rid,
761 					      rle->res);
762 	}
763 }
764 
765 static int
766 isa_set_resource(device_t dev, device_t child, int type, int rid,
767 		 u_long start, u_long count)
768 {
769 	struct isa_device* idev = DEVTOISA(child);
770 	struct resource_list *rl = &idev->id_resources;
771 
772 	if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY
773 	    && type != SYS_RES_IRQ && type != SYS_RES_DRQ)
774 		return EINVAL;
775 	if (rid < 0)
776 		return EINVAL;
777 	if (type == SYS_RES_IOPORT && rid >= ISA_NPORT)
778 		return EINVAL;
779 	if (type == SYS_RES_MEMORY && rid >= ISA_NMEM)
780 		return EINVAL;
781 	if (type == SYS_RES_IRQ && rid >= ISA_NIRQ)
782 		return EINVAL;
783 	if (type == SYS_RES_DRQ && rid >= ISA_NDRQ)
784 		return EINVAL;
785 
786 	resource_list_add(rl, type, rid, start, start + count - 1, count);
787 
788 	return 0;
789 }
790 
791 static int
792 isa_get_resource(device_t dev, device_t child, int type, int rid,
793 		 u_long *startp, u_long *countp)
794 {
795 	struct isa_device* idev = DEVTOISA(child);
796 	struct resource_list *rl = &idev->id_resources;
797 	struct resource_list_entry *rle;
798 
799 	rle = resource_list_find(rl, type, rid);
800 	if (!rle)
801 		return ENOENT;
802 
803 	*startp = rle->start;
804 	*countp = rle->count;
805 
806 	return 0;
807 }
808 
809 static void
810 isa_delete_resource(device_t dev, device_t child, int type, int rid)
811 {
812 	struct isa_device* idev = DEVTOISA(child);
813 	struct resource_list *rl = &idev->id_resources;
814 	resource_list_delete(rl, type, rid);
815 }
816 
817 static int
818 isa_add_config(device_t dev, device_t child,
819 	       int priority, struct isa_config *config)
820 {
821 	struct isa_device* idev = DEVTOISA(child);
822 	struct isa_config_entry *newice, *ice;
823 
824 	newice = malloc(sizeof *ice, M_DEVBUF, M_NOWAIT);
825 	if (!newice)
826 		return ENOMEM;
827 
828 	newice->ice_priority = priority;
829 	newice->ice_config = *config;
830 
831 	TAILQ_FOREACH(ice, &idev->id_configs, ice_link) {
832 		if (ice->ice_priority > priority)
833 			break;
834 	}
835 	if (ice)
836 		TAILQ_INSERT_BEFORE(ice, newice, ice_link);
837 	else
838 		TAILQ_INSERT_TAIL(&idev->id_configs, newice, ice_link);
839 
840 	return 0;
841 }
842 
843 static void
844 isa_set_config_callback(device_t dev, device_t child,
845 			isa_config_cb *fn, void *arg)
846 {
847 	struct isa_device* idev = DEVTOISA(child);
848 
849 	idev->id_config_cb = fn;
850 	idev->id_config_arg = arg;
851 }
852 
853 static int
854 isa_pnp_probe(device_t dev, device_t child, struct isa_pnp_id *ids)
855 {
856 	struct isa_device* idev = DEVTOISA(child);
857 
858 	if (!idev->id_vendorid)
859 		return ENOENT;
860 
861 	while (ids->ip_id) {
862 		/*
863 		 * Really ought to support >1 compat id per device.
864 		 */
865 		if (idev->id_logicalid == ids->ip_id
866 		    || idev->id_compatid == ids->ip_id) {
867 			if (ids->ip_desc)
868 				device_set_desc(child, ids->ip_desc);
869 			return 0;
870 		}
871 		ids++;
872 	}
873 
874 	return ENXIO;
875 }
876 
877 static device_method_t isa_methods[] = {
878 	/* Device interface */
879 	DEVMETHOD(device_probe,		isa_probe),
880 	DEVMETHOD(device_attach,	isa_attach),
881 	DEVMETHOD(device_detach,	bus_generic_detach),
882 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
883 	DEVMETHOD(device_suspend,	bus_generic_suspend),
884 	DEVMETHOD(device_resume,	bus_generic_resume),
885 
886 	/* Bus interface */
887 	DEVMETHOD(bus_add_child,	isa_add_child),
888 	DEVMETHOD(bus_print_child,	isa_print_child),
889 	DEVMETHOD(bus_read_ivar,	isa_read_ivar),
890 	DEVMETHOD(bus_write_ivar,	isa_write_ivar),
891 	DEVMETHOD(bus_child_detached,	isa_child_detached),
892 	DEVMETHOD(bus_alloc_resource,	isa_alloc_resource),
893 	DEVMETHOD(bus_release_resource,	isa_release_resource),
894 	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
895 	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
896 	DEVMETHOD(bus_setup_intr,	isa_setup_intr),
897 	DEVMETHOD(bus_teardown_intr,	isa_teardown_intr),
898 	DEVMETHOD(bus_set_resource,	isa_set_resource),
899 	DEVMETHOD(bus_get_resource,	isa_get_resource),
900 	DEVMETHOD(bus_delete_resource,	isa_delete_resource),
901 
902 	/* ISA interface */
903 	DEVMETHOD(isa_add_config,	isa_add_config),
904 	DEVMETHOD(isa_set_config_callback, isa_set_config_callback),
905 	DEVMETHOD(isa_pnp_probe,	isa_pnp_probe),
906 
907 	{ 0, 0 }
908 };
909 
910 static driver_t isa_driver = {
911 	"isa",
912 	isa_methods,
913 	1,			/* no softc */
914 };
915 
916 /*
917  * ISA can be attached to a PCI-ISA bridge or directly to the nexus.
918  */
919 DRIVER_MODULE(isa, isab, isa_driver, isa_devclass, 0, 0);
920 #ifdef __i386__
921 DRIVER_MODULE(isa, nexus, isa_driver, isa_devclass, 0, 0);
922 #endif
923 
924 /*
925  * A fallback driver for reporting un-matched pnp devices.
926  */
927 
928 static int
929 unknown_probe(device_t dev)
930 {
931 	/*
932 	 * Only match pnp devices.
933 	 */
934 	if (isa_get_vendorid(dev) != 0)
935 		return -100;
936 	return ENXIO;
937 }
938 
939 static int
940 unknown_attach(device_t dev)
941 {
942 	return 0;
943 }
944 
945 static int
946 unknown_detach(device_t dev)
947 {
948 	return 0;
949 }
950 
951 static device_method_t unknown_methods[] = {
952 	/* Device interface */
953 	DEVMETHOD(device_probe,		unknown_probe),
954 	DEVMETHOD(device_attach,	unknown_attach),
955 	DEVMETHOD(device_detach,	unknown_detach),
956 
957 	{ 0, 0 }
958 };
959 
960 static driver_t unknown_driver = {
961 	"unknown",
962 	unknown_methods,
963 	1,			/* no softc */
964 };
965 
966 static devclass_t unknown_devclass;
967 
968 DRIVER_MODULE(unknown, isa, unknown_driver, unknown_devclass, 0, 0);
969