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