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