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