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