isa.c revision 1.110.6.1 1 /* $NetBSD: isa.c,v 1.110.6.1 2002/04/06 16:12:08 eeh Exp $ */
2
3 /*-
4 * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Charles M. Hannum; by Jason R. Thorpe of Wasabi Systems, Inc.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #include <sys/cdefs.h>
40 __KERNEL_RCSID(0, "$NetBSD: isa.c,v 1.110.6.1 2002/04/06 16:12:08 eeh Exp $");
41
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/kernel.h>
45 #include <sys/malloc.h>
46 #include <sys/device.h>
47 #include <sys/properties.h>
48
49 #include <machine/intr.h>
50
51 #include <dev/isa/isareg.h>
52 #include <dev/isa/isavar.h>
53 #include <dev/isa/isadmareg.h>
54
55 #include "isadma.h"
56
57 #include "isapnp.h"
58 #if NISAPNP > 0
59 #include <dev/isapnp/isapnpreg.h>
60 #include <dev/isapnp/isapnpvar.h>
61 #endif
62
63 int isamatch(struct device *, struct cfdata *, void *);
64 void isaattach(struct device *, struct device *, void *);
65 int isaprint(void *, const char *);
66
67 struct cfattach isa_ca = {
68 sizeof(struct isa_softc), isamatch, isaattach
69 };
70
71 void isa_attach_knowndevs(struct isa_softc *);
72 void isa_free_knowndevs(struct isa_softc *);
73
74 int isasubmatch(struct device *, struct cfdata *, void *);
75 int isasearch(struct device *, struct cfdata *, void *);
76
77 int
78 isamatch(struct device *parent, struct cfdata *cf, void *aux)
79 {
80 struct isabus_attach_args *iba = aux;
81
82 if (strcmp(iba->iba_busname, cf->cf_driver->cd_name))
83 return (0);
84
85 /* XXX check other indicators */
86
87 return (1);
88 }
89
90 void
91 isaattach(struct device *parent, struct device *self, void *aux)
92 {
93 struct isa_softc *sc = (struct isa_softc *)DEV_PRIVATE(self);
94 struct isabus_attach_args *iba = aux;
95
96 sc->sc_devp = self;
97
98 TAILQ_INIT(&sc->sc_knowndevs);
99 sc->sc_dynamicdevs = 0;
100
101 isa_attach_hook(parent, self, iba);
102 printf("\n");
103
104 /* XXX Add code to fetch known-devices. */
105
106 sc->sc_iot = iba->iba_iot;
107 dev_setprop(self, "io-tag", &sc->sc_iot, sizeof(sc->sc_iot),
108 PROP_INT, 0);
109 sc->sc_memt = iba->iba_memt;
110 dev_setprop(self, "mem-tag", &sc->sc_memt, sizeof(sc->sc_memt),
111 PROP_INT, 0);
112 sc->sc_dmat = iba->iba_dmat;
113 dev_setprop(self, "dma-tag", &sc->sc_dmat, sizeof(sc->sc_dmat),
114 PROP_INT, 0);
115 sc->sc_ic = iba->iba_ic;
116 dev_setprop(self, "chipset-tag", &sc->sc_ic, sizeof(sc->sc_ic),
117 PROP_INT, 0);
118
119 #if NISAPNP > 0
120 /*
121 * Reset isapnp cards that the bios configured for us
122 */
123 isapnp_isa_attach_hook(sc);
124 #endif
125
126 #if NISADMA > 0
127 /*
128 * Initialize our DMA state.
129 */
130 isa_dmainit(sc->sc_ic, sc->sc_iot, sc->sc_dmat, self);
131 #endif
132
133 /* Attach all direct-config children. */
134 isa_attach_knowndevs(sc);
135
136 /*
137 * If we don't support dynamic hello/goodbye of devices,
138 * then free the knowndevs info now.
139 */
140 if (sc->sc_dynamicdevs == 0)
141 isa_free_knowndevs(sc);
142
143 /* Attach all indrect-config children. */
144 config_search(isasearch, self, NULL);
145 }
146
147 void
148 isa_attach_knowndevs(struct isa_softc *sc)
149 {
150 struct isa_attach_args ia;
151 struct isa_knowndev *ik;
152 struct device *child;
153
154 if (TAILQ_EMPTY(&sc->sc_knowndevs))
155 return;
156
157 TAILQ_FOREACH(ik, &sc->sc_knowndevs, ik_list) {
158 if (ik->ik_claimed != NULL)
159 continue;
160
161 if ((child = dev_config_create(sc->sc_devp, 0)) == NULL)
162 panic("isa_attach_knowndevs: cannot allocate dev");
163
164 ia.ia_iot = sc->sc_iot;
165 ia.ia_memt = sc->sc_memt;
166 ia.ia_dmat = sc->sc_dmat;
167 ia.ia_ic = sc->sc_ic;
168
169 ia.ia_pnpname = ik->ik_pnpname;
170 if (ik->ik_pnpname) {
171 dev_setprop(child, "PNP-name", ik->ik_pnpname,
172 strlen(ik->ik_pnpname), PROP_STRING, 0);
173 }
174 ia.ia_pnpcompatnames = ik->ik_pnpcompatnames;
175 if (ik->ik_pnpcompatnames) {
176 struct isa_pnpname *ipn;
177 char buf[256];
178 int i = 0;
179
180 /*
181 * Yeech. We need to concatenate all these
182 * annoying strings into one big one and then
183 * create a property with it. Good thing nothing
184 * currently uses this feature. Hope 255 bytes is
185 * enough.
186 */
187
188 for (ipn = ik->ik_pnpcompatnames; ipn != NULL;
189 ipn = ipn->ipn_next) {
190 strcpy(&buf[i], ipn->ipn_name);
191 i += strlen(ipn->ipn_name);
192 buf[i++] = 0;
193 if (i > 256)
194 panic("isa_attach_knowndevs: compat");
195 }
196 dev_setprop(child, "PNP-compat", buf, i, PROP_STRING,
197 0);
198 }
199
200 ia.ia_io = ik->ik_io;
201 ia.ia_nio = ik->ik_nio;
202 if (ik->ik_nio) {
203 if (ik->ik_io[0].ir_addr != ISACF_PORT_DEFAULT) {
204 dev_setprop(child, "port",
205 &ik->ik_io[0].ir_addr, sizeof(int),
206 PROP_INT, 0);
207 }
208 if (ik->ik_io[0].ir_size != ISACF_SIZE_DEFAULT) {
209 dev_setprop(child, "size",
210 &ik->ik_io[0].ir_size, sizeof(int),
211 PROP_INT, 0);
212 }
213 }
214
215 ia.ia_iomem = ik->ik_iomem;
216 ia.ia_niomem = ik->ik_niomem;
217 if (ik->ik_niomem) {
218 if (ik->ik_iomem[0].ir_addr != ISACF_IOMEM_DEFAULT) {
219 dev_setprop(child, "iomem",
220 &ik->ik_iomem[0].ir_addr, sizeof(int),
221 PROP_INT, 0);
222 }
223 if (ik->ik_iomem[0].ir_size != ISACF_IOSIZ_DEFAULT) {
224 dev_setprop(child, "iosiz",
225 &ik->ik_iomem[0].ir_size, sizeof(int),
226 PROP_INT, 0);
227 }
228 }
229
230 ia.ia_irq = ik->ik_irq;
231 ia.ia_nirq = ik->ik_nirq;
232 if (ik->ik_nirq) {
233 if (ik->ik_irq[0].ir_irq != ISACF_IRQ_DEFAULT) {
234 dev_setprop(child, "irq",
235 &ik->ik_irq[0].ir_irq, sizeof(int),
236 PROP_INT, 0);
237 }
238 }
239
240 ia.ia_drq = ik->ik_drq;
241 ia.ia_ndrq = ik->ik_ndrq;
242 if (ik->ik_ndrq > 0 &&
243 ik->ik_drq[0].ir_drq != ISACF_DRQ_DEFAULT) {
244 dev_setprop(child, "drq",
245 &ik->ik_drq[0].ir_drq, sizeof(int),
246 PROP_INT, 0);
247 }
248 if (ik->ik_ndrq > 1 &&
249 ik->ik_drq[1].ir_drq != ISACF_DRQ2_DEFAULT) {
250 dev_setprop(child, "drq2",
251 &ik->ik_drq[1].ir_drq, sizeof(int),
252 PROP_INT, 0);
253 }
254
255 ia.ia_aux = NULL;
256
257 ik->ik_claimed = config_found_sad(sc->sc_devp, &ia,
258 isaprint, isasubmatch, child);
259 }
260 }
261
262 void
263 isa_free_knowndevs(struct isa_softc *sc)
264 {
265 struct isa_knowndev *ik;
266 struct isa_pnpname *ipn;
267
268 #define FREEIT(x) if (x != NULL) free(x, M_DEVBUF)
269
270 while ((ik = TAILQ_FIRST(&sc->sc_knowndevs)) != NULL) {
271 TAILQ_REMOVE(&sc->sc_knowndevs, ik, ik_list);
272 FREEIT(ik->ik_pnpname);
273 while ((ipn = ik->ik_pnpcompatnames) != NULL) {
274 ik->ik_pnpcompatnames = ipn->ipn_next;
275 free(ipn->ipn_name, M_DEVBUF);
276 free(ipn, M_DEVBUF);
277 }
278 FREEIT(ik->ik_io);
279 FREEIT(ik->ik_iomem);
280 FREEIT(ik->ik_irq);
281 FREEIT(ik->ik_drq);
282 free(ik, M_DEVBUF);
283 }
284
285 #undef FREEIT
286 }
287
288 int
289 isasubmatch(struct device *parent, struct cfdata *cf, void *aux)
290 {
291 struct isa_attach_args *ia = aux;
292 int i;
293
294 if (ia->ia_nio == 0) {
295 if (cf->cf_iobase != ISACF_PORT_DEFAULT)
296 return (0);
297 } else {
298 if (cf->cf_iobase != ISACF_PORT_DEFAULT &&
299 cf->cf_iobase != ia->ia_io[0].ir_addr)
300 return (0);
301 }
302
303 if (ia->ia_niomem == 0) {
304 if (cf->cf_maddr != ISACF_IOMEM_DEFAULT)
305 return (0);
306 } else {
307 if (cf->cf_maddr != ISACF_IOMEM_DEFAULT &&
308 cf->cf_maddr != ia->ia_iomem[0].ir_addr)
309 return (0);
310 }
311
312 if (ia->ia_nirq == 0) {
313 if (cf->cf_irq != ISACF_IRQ_DEFAULT)
314 return (0);
315 } else {
316 if (cf->cf_irq != ISACF_IRQ_DEFAULT &&
317 cf->cf_irq != ia->ia_irq[0].ir_irq)
318 return (0);
319 }
320
321 if (ia->ia_ndrq == 0) {
322 if (cf->cf_drq != ISACF_DRQ_DEFAULT)
323 return (0);
324 if (cf->cf_drq2 != ISACF_DRQ_DEFAULT)
325 return (0);
326 } else {
327 for (i = 0; i < 2; i++) {
328 if (i == ia->ia_ndrq)
329 break;
330 if (cf->cf_loc[ISACF_DRQ + i] != ISACF_DRQ_DEFAULT &&
331 cf->cf_loc[ISACF_DRQ + i] != ia->ia_drq[i].ir_drq)
332 return (0);
333 }
334 for (; i < 2; i++) {
335 if (cf->cf_loc[ISACF_DRQ + i] != ISACF_DRQ_DEFAULT)
336 return (0);
337 }
338 }
339
340 return ((*cf->cf_attach->ca_match)(parent, cf, aux));
341 }
342
343 int
344 isaprint(void *aux, const char *isa)
345 {
346 struct isa_attach_args *ia = aux;
347 const char *sep;
348 int i;
349
350 /*
351 * This block of code only fires if we have a direct-config'd
352 * device for which there is no driver match.
353 */
354 if (isa != NULL) {
355 struct isa_pnpname *ipn;
356
357 if (ia->ia_pnpname != NULL)
358 printf("%s", ia->ia_pnpname);
359 if ((ipn = ia->ia_pnpcompatnames) != NULL) {
360 printf(" ("); /* ) */
361 for (sep = ""; ipn != NULL;
362 ipn = ipn->ipn_next, sep = " ") {
363 printf("%s%s", sep, ipn->ipn_name);
364 }
365 /* ( */ printf(")");
366 }
367 printf(" at %s", isa);
368 }
369
370 if (ia->ia_nio) {
371 sep = "";
372 printf(" port ");
373 for (i = 0; i < ia->ia_nio; i++) {
374 if (ia->ia_io[i].ir_size == 0)
375 continue;
376 printf("%s0x%x", sep, ia->ia_io[i].ir_addr);
377 if (ia->ia_io[i].ir_size > 1)
378 printf("-0x%x", ia->ia_io[i].ir_addr +
379 ia->ia_io[i].ir_size - 1);
380 sep = ",";
381 }
382 }
383
384 if (ia->ia_niomem) {
385 sep = "";
386 printf(" iomem ");
387 for (i = 0; i < ia->ia_niomem; i++) {
388 if (ia->ia_iomem[i].ir_size == 0)
389 continue;
390 printf("%s0x%x", sep, ia->ia_iomem[i].ir_addr);
391 if (ia->ia_iomem[i].ir_size > 1)
392 printf("-0x%x", ia->ia_iomem[i].ir_addr +
393 ia->ia_iomem[i].ir_size - 1);
394 sep = ",";
395 }
396 }
397
398 if (ia->ia_nirq) {
399 sep = "";
400 printf(" irq ");
401 for (i = 0; i < ia->ia_nirq; i++) {
402 if (ia->ia_irq[i].ir_irq == ISACF_IRQ_DEFAULT)
403 continue;
404 printf("%s%d", sep, ia->ia_irq[i].ir_irq);
405 sep = ",";
406 }
407 }
408
409 if (ia->ia_ndrq) {
410 sep = "";
411 printf(" drq ");
412 for (i = 0; i < ia->ia_ndrq; i++) {
413 if (ia->ia_drq[i].ir_drq == ISACF_DRQ_DEFAULT)
414 continue;
415 printf("%s%d", sep, ia->ia_drq[i].ir_drq);
416 sep = ",";
417 }
418 }
419
420 return (UNCONF);
421 }
422
423 int
424 isasearch(struct device *parent, struct cfdata *cf, void *aux)
425 {
426 struct device *child;
427 struct isa_io res_io[1];
428 struct isa_iomem res_mem[1];
429 struct isa_irq res_irq[1];
430 struct isa_drq res_drq[2];
431 struct isa_softc *sc = (struct isa_softc *)DEV_PRIVATE(parent);
432 struct isa_attach_args ia;
433 int tryagain = 1;
434
435 while (tryagain && (child = dev_config_create(parent, 0))) {
436 ia.ia_pnpname = NULL;
437 ia.ia_pnpcompatnames = NULL;
438
439 dev_setprop(child, "cd-name", cf->cf_driver->cd_name,
440 strlen(cf->cf_driver->cd_name) + 1, PROP_STRING, 0);
441
442 /*
443 * ISA uses a slightly different property protocol for
444 * locators. Instead of instantiating "loc-foo" properties
445 * for each locator, we will attach a "foo" property, but
446 * only if the locator is not wildcarded. When the device
447 * attaches, if it wants to record or override locator
448 * information, it creates appropriate properties, overriding
449 * existing properties created here.
450 */
451 res_io[0].ir_addr = cf->cf_loc[ISACF_PORT];
452 res_io[0].ir_size = 0;
453 if (cf->cf_loc[ISACF_PORT] != ISACF_PORT_DEFAULT) {
454 dev_setprop(child, cf->cf_locnames[ISACF_PORT],
455 &cf->cf_loc[ISACF_PORT], sizeof(int),
456 PROP_CONST|PROP_INT, 0);
457 }
458
459 res_mem[0].ir_addr = cf->cf_loc[ISACF_IOMEM];
460 if (cf->cf_loc[ISACF_IOMEM] != ISACF_IOMEM_DEFAULT) {
461 dev_setprop(child, cf->cf_locnames[ISACF_IOMEM],
462 &cf->cf_loc[ISACF_IOMEM], sizeof(int),
463 PROP_CONST|PROP_INT, 0);
464 }
465 res_mem[0].ir_size = cf->cf_loc[ISACF_IOSIZ];
466 if (cf->cf_loc[ISACF_IOSIZ] != ISACF_IOSIZ_DEFAULT) {
467 dev_setprop(child, cf->cf_locnames[ISACF_IOSIZ],
468 &cf->cf_loc[ISACF_IOSIZ], sizeof(int),
469 PROP_CONST|PROP_INT, 0);
470 }
471
472 res_irq[0].ir_irq =
473 cf->cf_loc[ISACF_IRQ] == 2 ? 9 : cf->cf_loc[ISACF_IRQ];
474 if (res_irq[0].ir_irq != ISACF_IRQ_DEFAULT) {
475 dev_setprop(child, cf->cf_locnames[ISACF_IRQ],
476 &res_irq[0].ir_irq, sizeof(int),
477 PROP_INT, 0);
478 }
479
480 res_drq[0].ir_drq = cf->cf_loc[ISACF_DRQ];
481 if (cf->cf_loc[ISACF_DRQ] != ISACF_DRQ_DEFAULT) {
482 dev_setprop(child, cf->cf_locnames[ISACF_DRQ],
483 &cf->cf_loc[ISACF_DRQ], sizeof(int),
484 PROP_CONST|PROP_INT, 0);
485 }
486 res_drq[1].ir_drq = cf->cf_loc[ISACF_DRQ2];
487 if (cf->cf_loc[ISACF_DRQ2] != ISACF_DRQ2_DEFAULT) {
488 dev_setprop(child, cf->cf_locnames[ISACF_DRQ2],
489 &cf->cf_loc[ISACF_DRQ2], sizeof(int),
490 PROP_CONST|PROP_INT, 0);
491 }
492
493 ia.ia_iot = sc->sc_iot;
494 ia.ia_memt = sc->sc_memt;
495 ia.ia_dmat = sc->sc_dmat;
496 ia.ia_ic = sc->sc_ic;
497
498 ia.ia_io = res_io;
499 ia.ia_nio = 1;
500
501 ia.ia_iomem = res_mem;
502 ia.ia_niomem = 1;
503
504 ia.ia_irq = res_irq;
505 ia.ia_nirq = 1;
506
507 ia.ia_drq = res_drq;
508 ia.ia_ndrq = 2;
509
510 tryagain = 0;
511
512 /*
513 * Find out if this is a new driver or old driver and call
514 * the match function in the correct way.
515 */
516 if ((ssize_t)cf->cf_attach->ca_devsize < 0) {
517 DEV_PRIVATE(child) = &ia;
518 if ((*cf->cf_attach->ca_match)(parent, cf, child) > 0) {
519 config_attach_ad(parent, cf, &ia,
520 isaprint, child);
521 tryagain = (cf->cf_fstate == FSTATE_STAR);
522 } else {
523 /* Destroy unused device node. */
524 config_detach(child, 0);
525 }
526 } else if ((*cf->cf_attach->ca_match)(parent, cf, &ia) > 0) {
527
528 /*
529 * Old style devices do not update locator
530 * properties, so we'll do that for them.
531 */
532 if (res_io[0].ir_addr != cf->cf_loc[ISACF_PORT] &&
533 cf->cf_loc[ISACF_PORT] != ISACF_PORT_DEFAULT) {
534 dev_setprop(child, cf->cf_locnames[ISACF_PORT],
535 &res_io[0].ir_addr, sizeof(int),
536 PROP_INT, 0);
537 }
538 if (res_io[0].ir_size != 0) {
539 dev_setprop(child, cf->cf_locnames[ISACF_SIZE],
540 &res_io[0].ir_size, sizeof(int),
541 PROP_INT, 0);
542 }
543
544 if (res_mem[0].ir_addr != cf->cf_loc[ISACF_IOMEM] &&
545 cf->cf_loc[ISACF_IOMEM] !=
546 ISACF_IOMEM_DEFAULT) {
547 dev_setprop(child, cf->cf_locnames[ISACF_IOMEM],
548 &res_mem[0].ir_addr, sizeof(int),
549 PROP_INT, 0);
550 }
551
552 if (res_mem[0].ir_addr != cf->cf_loc[ISACF_IOSIZ] &&
553 cf->cf_loc[ISACF_IOSIZ] !=
554 ISACF_IOSIZ_DEFAULT) {
555 dev_setprop(child, cf->cf_locnames[ISACF_IOSIZ],
556 &res_mem[0].ir_size, sizeof(int),
557 PROP_INT, 0);
558 }
559
560 /*
561 * Since this value may have been twiddled by us
562 * earlier we cannot determine if the value was
563 * untouched by our child.
564 */
565 if (res_irq[0].ir_irq != cf->cf_loc[ISACF_IRQ]) {
566 dev_setprop(child, cf->cf_locnames[ISACF_IRQ],
567 &res_irq[0].ir_irq, sizeof(int),
568 PROP_INT, 0);
569 }
570
571 if (res_drq[0].ir_drq != cf->cf_loc[ISACF_DRQ] &&
572 cf->cf_loc[ISACF_DRQ] != ISACF_DRQ_DEFAULT) {
573 dev_setprop(child, cf->cf_locnames[ISACF_DRQ],
574 &res_drq[0].ir_drq, sizeof(int),
575 PROP_INT, 0);
576 }
577 if (res_drq[0].ir_drq != cf->cf_loc[ISACF_DRQ2] &&
578 cf->cf_loc[ISACF_DRQ2] != ISACF_DRQ2_DEFAULT) {
579 dev_setprop(child, cf->cf_locnames[ISACF_DRQ2],
580 &res_drq[0].ir_drq, sizeof(int),
581 PROP_INT, 0);
582 }
583
584 config_attach_ad(parent, cf, &ia, isaprint, child);
585 tryagain = (cf->cf_fstate == FSTATE_STAR);
586 } else {
587 /* Destroy unused device node. */
588 config_detach(child, 0);
589 }
590 }
591
592 return (0);
593 }
594
595 char *
596 isa_intr_typename(int type)
597 {
598
599 switch (type) {
600 case IST_NONE :
601 return ("none");
602 case IST_PULSE:
603 return ("pulsed");
604 case IST_EDGE:
605 return ("edge-triggered");
606 case IST_LEVEL:
607 return ("level-triggered");
608 default:
609 panic("isa_intr_typename: invalid type %d", type);
610 }
611 }
612