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