isa_common.c revision 69295
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 69295 2000-11-28 07:12:12Z mdodd $
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
81MALLOC_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 config;
390
391	bzero(&config, sizeof config);
392	TAILQ_FOREACH(ice, &idev->id_configs, ice_link) {
393		if (!isa_find_memory(child, &ice->ice_config, &config))
394			continue;
395		if (!isa_find_port(child, &ice->ice_config, &config))
396			continue;
397		if (!isa_find_irq(child, &ice->ice_config, &config))
398			continue;
399		if (!isa_find_drq(child, &ice->ice_config, &config))
400			continue;
401
402		/*
403		 * A working configuration was found enable the device
404		 * with this configuration.
405		 */
406		if (idev->id_config_cb) {
407			idev->id_config_cb(idev->id_config_arg,
408					   &config, 1);
409			return 1;
410		}
411	}
412
413	/*
414	 * Disable the device.
415	 */
416	bus_print_child_header(device_get_parent(child), child);
417	printf(" can't assign resources\n");
418	if (bootverbose)
419	    isa_print_child(device_get_parent(child), child);
420	bzero(&config, sizeof config);
421	if (idev->id_config_cb)
422		idev->id_config_cb(idev->id_config_arg, &config, 0);
423	device_disable(child);
424
425	return 0;
426}
427
428/*
429 * Called after other devices have initialised to probe for isa devices.
430 */
431void
432isa_probe_children(device_t dev)
433{
434	device_t *children;
435	int nchildren, i;
436
437	/*
438	 * Create all the children by calling driver's identify methods.
439	 */
440	bus_generic_probe(dev);
441
442	if (device_get_children(dev, &children, &nchildren))
443		return;
444
445	/*
446	 * First disable all pnp devices so that they don't get
447	 * matched by legacy probes.
448	 */
449	if (bootverbose)
450		printf("isa_probe_children: disabling PnP devices\n");
451	for (i = 0; i < nchildren; i++) {
452		device_t child = children[i];
453		struct isa_device *idev = DEVTOISA(child);
454		struct isa_config config;
455
456		bzero(&config, sizeof config);
457		if (idev->id_config_cb)
458			idev->id_config_cb(idev->id_config_arg, &config, 0);
459	}
460
461	/*
462	 * Next probe all non-pnp devices so that they claim their
463	 * resources first.
464	 */
465	if (bootverbose)
466		printf("isa_probe_children: probing non-PnP devices\n");
467	for (i = 0; i < nchildren; i++) {
468		device_t child = children[i];
469		struct isa_device *idev = DEVTOISA(child);
470
471		if (TAILQ_FIRST(&idev->id_configs))
472			continue;
473
474		device_probe_and_attach(child);
475	}
476
477	/*
478	 * Finally assign resource to pnp devices and probe them.
479	 */
480	if (bootverbose)
481		printf("isa_probe_children: probing PnP devices\n");
482	for (i = 0; i < nchildren; i++) {
483		device_t child = children[i];
484		struct isa_device* idev = DEVTOISA(child);
485
486		if (!TAILQ_FIRST(&idev->id_configs))
487			continue;
488
489		if (isa_assign_resources(child)) {
490			struct resource_list *rl = &idev->id_resources;
491			struct resource_list_entry *rle;
492
493			device_probe_and_attach(child);
494
495			/*
496			 * Claim any unallocated resources to keep other
497			 * devices from using them.
498			 */
499			SLIST_FOREACH(rle, rl, link) {
500				if (!rle->res) {
501					int rid = rle->rid;
502					resource_list_alloc(rl, dev, child,
503							    rle->type,
504							    &rid,
505							    0, ~0, 1, 0);
506				}
507			}
508		}
509	}
510
511	free(children, M_TEMP);
512
513	isa_running = 1;
514}
515
516/*
517 * Add a new child with default ivars.
518 */
519static device_t
520isa_add_child(device_t dev, int order, const char *name, int unit)
521{
522	device_t child;
523	struct	isa_device *idev;
524
525	idev = malloc(sizeof(struct isa_device), M_ISADEV, M_NOWAIT);
526	if (!idev)
527		return 0;
528	bzero(idev, sizeof *idev);
529
530	resource_list_init(&idev->id_resources);
531	TAILQ_INIT(&idev->id_configs);
532
533	child = device_add_child_ordered(dev, order, name, unit);
534 	device_set_ivars(child, idev);
535
536	return child;
537}
538
539static int
540isa_print_resources(struct resource_list *rl, const char *name, int type,
541		    int count, const char *format)
542{
543	struct resource_list_entry *rle;
544	int printed;
545	int i, retval = 0;;
546
547	printed = 0;
548	for (i = 0; i < count; i++) {
549		rle = resource_list_find(rl, type, i);
550		if (rle) {
551			if (printed == 0)
552				retval += printf(" %s ", name);
553			else if (printed > 0)
554				retval += printf(",");
555			printed++;
556			retval += printf(format, rle->start);
557			if (rle->count > 1) {
558				retval += printf("-");
559				retval += printf(format,
560						 rle->start + rle->count - 1);
561			}
562		} else if (i > 3) {
563			/* check the first few regardless */
564			break;
565		}
566	}
567	return retval;
568}
569
570static int
571isa_print_all_resources(device_t dev)
572{
573	struct	isa_device *idev = DEVTOISA(dev);
574	struct resource_list *rl = &idev->id_resources;
575	int retval = 0;
576
577	if (SLIST_FIRST(rl) || device_get_flags(dev))
578		retval += printf(" at");
579
580	retval += isa_print_resources(rl, "port", SYS_RES_IOPORT,
581				      ISA_NPORT, "%#lx");
582	retval += isa_print_resources(rl, "iomem", SYS_RES_MEMORY,
583				      ISA_NMEM, "%#lx");
584	retval += isa_print_resources(rl, "irq", SYS_RES_IRQ,
585				      ISA_NIRQ, "%ld");
586	retval += isa_print_resources(rl, "drq", SYS_RES_DRQ,
587				      ISA_NDRQ, "%ld");
588	if (device_get_flags(dev))
589		retval += printf(" flags %#x", device_get_flags(dev));
590
591	return retval;
592}
593
594static int
595isa_print_child(device_t bus, device_t dev)
596{
597	int retval = 0;
598
599	retval += bus_print_child_header(bus, dev);
600	retval += isa_print_all_resources(dev);
601	retval += bus_print_child_footer(bus, dev);
602
603	return (retval);
604}
605
606static void
607isa_probe_nomatch(device_t dev, device_t child)
608{
609	if (bootverbose) {
610		bus_print_child_header(dev, child);
611		printf(" failed to probe");
612		isa_print_all_resources(child);
613		bus_print_child_footer(dev, child);
614	}
615
616	return;
617}
618
619static int
620isa_read_ivar(device_t bus, device_t dev, int index, uintptr_t * result)
621{
622	struct isa_device* idev = DEVTOISA(dev);
623	struct resource_list *rl = &idev->id_resources;
624	struct resource_list_entry *rle;
625
626	switch (index) {
627	case ISA_IVAR_PORT_0:
628		rle = resource_list_find(rl, SYS_RES_IOPORT, 0);
629		if (rle)
630			*result = rle->start;
631		else
632			*result = -1;
633		break;
634
635	case ISA_IVAR_PORT_1:
636		rle = resource_list_find(rl, SYS_RES_IOPORT, 1);
637		if (rle)
638			*result = rle->start;
639		else
640			*result = -1;
641		break;
642
643	case ISA_IVAR_PORTSIZE_0:
644		rle = resource_list_find(rl, SYS_RES_IOPORT, 0);
645		if (rle)
646			*result = rle->count;
647		else
648			*result = 0;
649		break;
650
651	case ISA_IVAR_PORTSIZE_1:
652		rle = resource_list_find(rl, SYS_RES_IOPORT, 1);
653		if (rle)
654			*result = rle->count;
655		else
656			*result = 0;
657		break;
658
659	case ISA_IVAR_MADDR_0:
660		rle = resource_list_find(rl, SYS_RES_MEMORY, 0);
661		if (rle)
662			*result = rle->start;
663		else
664			*result = -1;
665		break;
666
667	case ISA_IVAR_MADDR_1:
668		rle = resource_list_find(rl, SYS_RES_MEMORY, 1);
669		if (rle)
670			*result = rle->start;
671		else
672			*result = -1;
673		break;
674
675	case ISA_IVAR_MSIZE_0:
676		rle = resource_list_find(rl, SYS_RES_MEMORY, 0);
677		if (rle)
678			*result = rle->count;
679		else
680			*result = 0;
681		break;
682
683	case ISA_IVAR_MSIZE_1:
684		rle = resource_list_find(rl, SYS_RES_MEMORY, 1);
685		if (rle)
686			*result = rle->count;
687		else
688			*result = 0;
689		break;
690
691	case ISA_IVAR_IRQ_0:
692		rle = resource_list_find(rl, SYS_RES_IRQ, 0);
693		if (rle)
694			*result = rle->start;
695		else
696			*result = -1;
697		break;
698
699	case ISA_IVAR_IRQ_1:
700		rle = resource_list_find(rl, SYS_RES_IRQ, 1);
701		if (rle)
702			*result = rle->start;
703		else
704			*result = -1;
705		break;
706
707	case ISA_IVAR_DRQ_0:
708		rle = resource_list_find(rl, SYS_RES_DRQ, 0);
709		if (rle)
710			*result = rle->start;
711		else
712			*result = -1;
713		break;
714
715	case ISA_IVAR_DRQ_1:
716		rle = resource_list_find(rl, SYS_RES_DRQ, 1);
717		if (rle)
718			*result = rle->start;
719		else
720			*result = -1;
721		break;
722
723	case ISA_IVAR_VENDORID:
724		*result = idev->id_vendorid;
725		break;
726
727	case ISA_IVAR_SERIAL:
728		*result = idev->id_serial;
729		break;
730
731	case ISA_IVAR_LOGICALID:
732		*result = idev->id_logicalid;
733		break;
734
735	case ISA_IVAR_COMPATID:
736		*result = idev->id_compatid;
737		break;
738
739	default:
740		return ENOENT;
741	}
742
743	return 0;
744}
745
746static int
747isa_write_ivar(device_t bus, device_t dev,
748	       int index, uintptr_t value)
749{
750	struct isa_device* idev = DEVTOISA(dev);
751
752	switch (index) {
753	case ISA_IVAR_PORT_0:
754	case ISA_IVAR_PORT_1:
755	case ISA_IVAR_PORTSIZE_0:
756	case ISA_IVAR_PORTSIZE_1:
757	case ISA_IVAR_MADDR_0:
758	case ISA_IVAR_MADDR_1:
759	case ISA_IVAR_MSIZE_0:
760	case ISA_IVAR_MSIZE_1:
761	case ISA_IVAR_IRQ_0:
762	case ISA_IVAR_IRQ_1:
763	case ISA_IVAR_DRQ_0:
764	case ISA_IVAR_DRQ_1:
765		return EINVAL;
766
767	case ISA_IVAR_VENDORID:
768		idev->id_vendorid = value;
769		break;
770
771	case ISA_IVAR_SERIAL:
772		idev->id_serial = value;
773		break;
774
775	case ISA_IVAR_LOGICALID:
776		idev->id_logicalid = value;
777		break;
778
779	case ISA_IVAR_COMPATID:
780		idev->id_compatid = value;
781		break;
782
783	default:
784		return (ENOENT);
785	}
786
787	return (0);
788}
789
790/*
791 * Free any resources which the driver missed or which we were holding for
792 * it (see isa_probe_children).
793 */
794static void
795isa_child_detached(device_t dev, device_t child)
796{
797	struct isa_device* idev = DEVTOISA(child);
798	struct resource_list *rl = &idev->id_resources;
799	struct resource_list_entry *rle;
800
801	if (TAILQ_FIRST(&idev->id_configs)) {
802		/*
803		 * Claim any unallocated resources to keep other
804		 * devices from using them.
805		 */
806		SLIST_FOREACH(rle, rl, link) {
807			if (!rle->res) {
808				int rid = rle->rid;
809				resource_list_alloc(rl, dev, child,
810						    rle->type,
811						    &rid, 0, ~0, 1, 0);
812			}
813		}
814	}
815}
816
817static void
818isa_driver_added(device_t dev, driver_t *driver)
819{
820	device_t *children;
821	int nchildren, i;
822
823	/*
824	 * Don't do anything if drivers are dynamically
825	 * added during autoconfiguration (cf. ymf724).
826	 * since that would end up calling identify
827	 * twice.
828	 */
829	if (!isa_running)
830		return;
831
832	DEVICE_IDENTIFY(driver, dev);
833	if (device_get_children(dev, &children, &nchildren))
834		return;
835
836	for (i = 0; i < nchildren; i++) {
837		device_t child = children[i];
838		struct isa_device *idev = DEVTOISA(child);
839		struct resource_list *rl = &idev->id_resources;
840		struct resource_list_entry *rle;
841
842		if (device_get_state(child) != DS_NOTPRESENT)
843			continue;
844		if (!device_is_enabled(child))
845			continue;
846
847		/*
848		 * Free resources which we were holding on behalf of
849		 * the device.
850		 */
851		SLIST_FOREACH(rle, &idev->id_resources, link) {
852			if (rle->res)
853				resource_list_release(rl, dev, child,
854						      rle->type,
855						      rle->rid,
856						      rle->res);
857		}
858
859		if (TAILQ_FIRST(&idev->id_configs))
860			if (!isa_assign_resources(child))
861				continue;
862
863		device_probe_and_attach(child);
864
865		if (TAILQ_FIRST(&idev->id_configs)) {
866			/*
867			 * Claim any unallocated resources to keep other
868			 * devices from using them.
869			 */
870			SLIST_FOREACH(rle, rl, link) {
871				if (!rle->res) {
872					int rid = rle->rid;
873					resource_list_alloc(rl, dev, child,
874							    rle->type,
875							    &rid, 0, ~0, 1, 0);
876				}
877			}
878		}
879	}
880
881	free(children, M_TEMP);
882}
883
884static int
885isa_set_resource(device_t dev, device_t child, int type, int rid,
886		 u_long start, u_long count)
887{
888	struct isa_device* idev = DEVTOISA(child);
889	struct resource_list *rl = &idev->id_resources;
890
891	if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY
892	    && type != SYS_RES_IRQ && type != SYS_RES_DRQ)
893		return EINVAL;
894	if (rid < 0)
895		return EINVAL;
896	if (type == SYS_RES_IOPORT && rid >= ISA_NPORT)
897		return EINVAL;
898	if (type == SYS_RES_MEMORY && rid >= ISA_NMEM)
899		return EINVAL;
900	if (type == SYS_RES_IRQ && rid >= ISA_NIRQ)
901		return EINVAL;
902	if (type == SYS_RES_DRQ && rid >= ISA_NDRQ)
903		return EINVAL;
904
905	resource_list_add(rl, type, rid, start, start + count - 1, count);
906
907	return 0;
908}
909
910static struct resource_list *
911isa_get_resource_list (device_t dev, device_t child)
912{
913	struct isa_device* idev = DEVTOISA(child);
914	struct resource_list *rl = &idev->id_resources;
915
916	if (!rl)
917		return (NULL);
918
919	return (rl);
920}
921
922static int
923isa_add_config(device_t dev, device_t child,
924	       int priority, struct isa_config *config)
925{
926	struct isa_device* idev = DEVTOISA(child);
927	struct isa_config_entry *newice, *ice;
928
929	newice = malloc(sizeof *ice, M_DEVBUF, M_NOWAIT);
930	if (!newice)
931		return ENOMEM;
932
933	newice->ice_priority = priority;
934	newice->ice_config = *config;
935
936	TAILQ_FOREACH(ice, &idev->id_configs, ice_link) {
937		if (ice->ice_priority > priority)
938			break;
939	}
940	if (ice)
941		TAILQ_INSERT_BEFORE(ice, newice, ice_link);
942	else
943		TAILQ_INSERT_TAIL(&idev->id_configs, newice, ice_link);
944
945	return 0;
946}
947
948static void
949isa_set_config_callback(device_t dev, device_t child,
950			isa_config_cb *fn, void *arg)
951{
952	struct isa_device* idev = DEVTOISA(child);
953
954	idev->id_config_cb = fn;
955	idev->id_config_arg = arg;
956}
957
958static int
959isa_pnp_probe(device_t dev, device_t child, struct isa_pnp_id *ids)
960{
961	struct isa_device* idev = DEVTOISA(child);
962
963	if (!idev->id_vendorid)
964		return ENOENT;
965
966	while (ids->ip_id) {
967		/*
968		 * Really ought to support >1 compat id per device.
969		 */
970		if (idev->id_logicalid == ids->ip_id
971		    || idev->id_compatid == ids->ip_id) {
972			if (ids->ip_desc)
973				device_set_desc(child, ids->ip_desc);
974			return 0;
975		}
976		ids++;
977	}
978
979	return ENXIO;
980}
981
982static device_method_t isa_methods[] = {
983	/* Device interface */
984	DEVMETHOD(device_probe,		isa_probe),
985	DEVMETHOD(device_attach,	isa_attach),
986	DEVMETHOD(device_detach,	bus_generic_detach),
987	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
988	DEVMETHOD(device_suspend,	bus_generic_suspend),
989	DEVMETHOD(device_resume,	bus_generic_resume),
990
991	/* Bus interface */
992	DEVMETHOD(bus_add_child,	isa_add_child),
993	DEVMETHOD(bus_print_child,	isa_print_child),
994	DEVMETHOD(bus_probe_nomatch,	isa_probe_nomatch),
995	DEVMETHOD(bus_read_ivar,	isa_read_ivar),
996	DEVMETHOD(bus_write_ivar,	isa_write_ivar),
997	DEVMETHOD(bus_child_detached,	isa_child_detached),
998	DEVMETHOD(bus_driver_added,	isa_driver_added),
999	DEVMETHOD(bus_setup_intr,	isa_setup_intr),
1000	DEVMETHOD(bus_teardown_intr,	isa_teardown_intr),
1001
1002	DEVMETHOD(bus_get_resource_list,isa_get_resource_list),
1003	DEVMETHOD(bus_alloc_resource,	isa_alloc_resource),
1004	DEVMETHOD(bus_release_resource,	isa_release_resource),
1005	DEVMETHOD(bus_set_resource,	isa_set_resource),
1006	DEVMETHOD(bus_get_resource,	bus_generic_rl_get_resource),
1007	DEVMETHOD(bus_delete_resource,	bus_generic_rl_delete_resource),
1008	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
1009	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
1010
1011	/* ISA interface */
1012	DEVMETHOD(isa_add_config,	isa_add_config),
1013	DEVMETHOD(isa_set_config_callback, isa_set_config_callback),
1014	DEVMETHOD(isa_pnp_probe,	isa_pnp_probe),
1015
1016	{ 0, 0 }
1017};
1018
1019static driver_t isa_driver = {
1020	"isa",
1021	isa_methods,
1022	1,			/* no softc */
1023};
1024
1025/*
1026 * ISA can be attached to a PCI-ISA bridge or directly to the nexus.
1027 */
1028DRIVER_MODULE(isa, isab, isa_driver, isa_devclass, 0, 0);
1029#ifdef __i386__
1030DRIVER_MODULE(isa, nexus, isa_driver, isa_devclass, 0, 0);
1031#endif
1032