isa_common.c revision 82553
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 82553 2001-08-30 09:14:28Z msmith $
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	default:
752		return ENOENT;
753	}
754
755	return 0;
756}
757
758static int
759isa_write_ivar(device_t bus, device_t dev,
760	       int index, uintptr_t value)
761{
762	struct isa_device* idev = DEVTOISA(dev);
763
764	switch (index) {
765	case ISA_IVAR_PORT_0:
766	case ISA_IVAR_PORT_1:
767	case ISA_IVAR_PORTSIZE_0:
768	case ISA_IVAR_PORTSIZE_1:
769	case ISA_IVAR_MADDR_0:
770	case ISA_IVAR_MADDR_1:
771	case ISA_IVAR_MSIZE_0:
772	case ISA_IVAR_MSIZE_1:
773	case ISA_IVAR_IRQ_0:
774	case ISA_IVAR_IRQ_1:
775	case ISA_IVAR_DRQ_0:
776	case ISA_IVAR_DRQ_1:
777		return EINVAL;
778
779	case ISA_IVAR_VENDORID:
780		idev->id_vendorid = value;
781		break;
782
783	case ISA_IVAR_SERIAL:
784		idev->id_serial = value;
785		break;
786
787	case ISA_IVAR_LOGICALID:
788		idev->id_logicalid = value;
789		break;
790
791	case ISA_IVAR_COMPATID:
792		idev->id_compatid = value;
793		break;
794
795	default:
796		return (ENOENT);
797	}
798
799	return (0);
800}
801
802/*
803 * Free any resources which the driver missed or which we were holding for
804 * it (see isa_probe_children).
805 */
806static void
807isa_child_detached(device_t dev, device_t child)
808{
809	struct isa_device* idev = DEVTOISA(child);
810	struct resource_list *rl = &idev->id_resources;
811	struct resource_list_entry *rle;
812
813	if (TAILQ_FIRST(&idev->id_configs)) {
814		/*
815		 * Claim any unallocated resources to keep other
816		 * devices from using them.
817		 */
818		SLIST_FOREACH(rle, rl, link) {
819			if (!rle->res) {
820				int rid = rle->rid;
821				resource_list_alloc(rl, dev, child,
822						    rle->type,
823						    &rid, 0, ~0, 1, 0);
824			}
825		}
826	}
827}
828
829static void
830isa_driver_added(device_t dev, driver_t *driver)
831{
832	device_t *children;
833	int nchildren, i;
834
835	/*
836	 * Don't do anything if drivers are dynamically
837	 * added during autoconfiguration (cf. ymf724).
838	 * since that would end up calling identify
839	 * twice.
840	 */
841	if (!isa_running)
842		return;
843
844	DEVICE_IDENTIFY(driver, dev);
845	if (device_get_children(dev, &children, &nchildren))
846		return;
847
848	for (i = 0; i < nchildren; i++) {
849		device_t child = children[i];
850		struct isa_device *idev = DEVTOISA(child);
851		struct resource_list *rl = &idev->id_resources;
852		struct resource_list_entry *rle;
853
854		if (device_get_state(child) != DS_NOTPRESENT)
855			continue;
856		if (!device_is_enabled(child))
857			continue;
858
859		/*
860		 * Free resources which we were holding on behalf of
861		 * the device.
862		 */
863		SLIST_FOREACH(rle, &idev->id_resources, link) {
864			if (rle->res)
865				resource_list_release(rl, dev, child,
866						      rle->type,
867						      rle->rid,
868						      rle->res);
869		}
870
871		if (TAILQ_FIRST(&idev->id_configs))
872			if (!isa_assign_resources(child))
873				continue;
874
875		device_probe_and_attach(child);
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
893	free(children, M_TEMP);
894}
895
896static int
897isa_set_resource(device_t dev, device_t child, int type, int rid,
898		 u_long start, u_long count)
899{
900	struct isa_device* idev = DEVTOISA(child);
901	struct resource_list *rl = &idev->id_resources;
902
903	if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY
904	    && type != SYS_RES_IRQ && type != SYS_RES_DRQ)
905		return EINVAL;
906	if (rid < 0)
907		return EINVAL;
908	if (type == SYS_RES_IOPORT && rid >= ISA_NPORT)
909		return EINVAL;
910	if (type == SYS_RES_MEMORY && rid >= ISA_NMEM)
911		return EINVAL;
912	if (type == SYS_RES_IRQ && rid >= ISA_NIRQ)
913		return EINVAL;
914	if (type == SYS_RES_DRQ && rid >= ISA_NDRQ)
915		return EINVAL;
916
917	resource_list_add(rl, type, rid, start, start + count - 1, count);
918
919	return 0;
920}
921
922static struct resource_list *
923isa_get_resource_list (device_t dev, device_t child)
924{
925	struct isa_device* idev = DEVTOISA(child);
926	struct resource_list *rl = &idev->id_resources;
927
928	if (!rl)
929		return (NULL);
930
931	return (rl);
932}
933
934static int
935isa_add_config(device_t dev, device_t child,
936	       int priority, struct isa_config *config)
937{
938	struct isa_device* idev = DEVTOISA(child);
939	struct isa_config_entry *newice, *ice;
940
941	newice = malloc(sizeof *ice, M_DEVBUF, M_NOWAIT);
942	if (!newice)
943		return ENOMEM;
944
945	newice->ice_priority = priority;
946	newice->ice_config = *config;
947
948	TAILQ_FOREACH(ice, &idev->id_configs, ice_link) {
949		if (ice->ice_priority > priority)
950			break;
951	}
952	if (ice)
953		TAILQ_INSERT_BEFORE(ice, newice, ice_link);
954	else
955		TAILQ_INSERT_TAIL(&idev->id_configs, newice, ice_link);
956
957	return 0;
958}
959
960static void
961isa_set_config_callback(device_t dev, device_t child,
962			isa_config_cb *fn, void *arg)
963{
964	struct isa_device* idev = DEVTOISA(child);
965
966	idev->id_config_cb = fn;
967	idev->id_config_arg = arg;
968}
969
970static int
971isa_pnp_probe(device_t dev, device_t child, struct isa_pnp_id *ids)
972{
973	struct isa_device* idev = DEVTOISA(child);
974
975	if (!idev->id_vendorid)
976		return ENOENT;
977
978	while (ids && ids->ip_id) {
979		/*
980		 * Really ought to support >1 compat id per device.
981		 */
982		if (idev->id_logicalid == ids->ip_id
983		    || idev->id_compatid == ids->ip_id) {
984			if (ids->ip_desc)
985				device_set_desc(child, ids->ip_desc);
986			return 0;
987		}
988		ids++;
989	}
990
991	return ENXIO;
992}
993
994static device_method_t isa_methods[] = {
995	/* Device interface */
996	DEVMETHOD(device_probe,		isa_probe),
997	DEVMETHOD(device_attach,	isa_attach),
998	DEVMETHOD(device_detach,	bus_generic_detach),
999	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
1000	DEVMETHOD(device_suspend,	bus_generic_suspend),
1001	DEVMETHOD(device_resume,	bus_generic_resume),
1002
1003	/* Bus interface */
1004	DEVMETHOD(bus_add_child,	isa_add_child),
1005	DEVMETHOD(bus_print_child,	isa_print_child),
1006	DEVMETHOD(bus_probe_nomatch,	isa_probe_nomatch),
1007	DEVMETHOD(bus_read_ivar,	isa_read_ivar),
1008	DEVMETHOD(bus_write_ivar,	isa_write_ivar),
1009	DEVMETHOD(bus_child_detached,	isa_child_detached),
1010	DEVMETHOD(bus_driver_added,	isa_driver_added),
1011	DEVMETHOD(bus_setup_intr,	isa_setup_intr),
1012	DEVMETHOD(bus_teardown_intr,	isa_teardown_intr),
1013
1014	DEVMETHOD(bus_get_resource_list,isa_get_resource_list),
1015	DEVMETHOD(bus_alloc_resource,	isa_alloc_resource),
1016	DEVMETHOD(bus_release_resource,	isa_release_resource),
1017	DEVMETHOD(bus_set_resource,	isa_set_resource),
1018	DEVMETHOD(bus_get_resource,	bus_generic_rl_get_resource),
1019	DEVMETHOD(bus_delete_resource,	bus_generic_rl_delete_resource),
1020	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
1021	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
1022
1023	/* ISA interface */
1024	DEVMETHOD(isa_add_config,	isa_add_config),
1025	DEVMETHOD(isa_set_config_callback, isa_set_config_callback),
1026	DEVMETHOD(isa_pnp_probe,	isa_pnp_probe),
1027
1028	{ 0, 0 }
1029};
1030
1031static driver_t isa_driver = {
1032	"isa",
1033	isa_methods,
1034	1,			/* no softc */
1035};
1036
1037/*
1038 * ISA can be attached to a PCI-ISA bridge or directly to the nexus.
1039 */
1040DRIVER_MODULE(isa, isab, isa_driver, isa_devclass, 0, 0);
1041DRIVER_MODULE(isa, eisab, isa_driver, isa_devclass, 0, 0);
1042#ifdef __i386__
1043DRIVER_MODULE(isa, nexus, isa_driver, isa_devclass, 0, 0);
1044#endif
1045