1 | /* $NetBSD: isa.c,v 1.138 2010/08/21 17:08:15 jmcneill Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 1998, 2001, 2008 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 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | #include <sys/cdefs.h> |
33 | __KERNEL_RCSID(0, "$NetBSD: isa.c,v 1.138 2010/08/21 17:08:15 jmcneill Exp $" ); |
34 | |
35 | #include <sys/param.h> |
36 | #include <sys/systm.h> |
37 | #include <sys/kernel.h> |
38 | #include <sys/malloc.h> |
39 | #include <sys/device.h> |
40 | |
41 | #include <sys/intr.h> |
42 | |
43 | #include <dev/isa/isareg.h> |
44 | #include <dev/isa/isavar.h> |
45 | #include <dev/isa/isadmareg.h> |
46 | |
47 | #include "isadma.h" |
48 | |
49 | #include "isapnp.h" |
50 | #if NISAPNP > 0 |
51 | #include <dev/isapnp/isapnpreg.h> |
52 | #include <dev/isapnp/isapnpvar.h> |
53 | #endif |
54 | |
55 | #include "locators.h" |
56 | |
57 | int isamatch(device_t, cfdata_t, void *); |
58 | void isaattach(device_t, device_t, void *); |
59 | int isadetach(device_t, int); |
60 | int isarescan(device_t, const char *, const int *); |
61 | void isachilddetached(device_t, device_t); |
62 | int isaprint(void *, const char *); |
63 | |
64 | CFATTACH_DECL2_NEW(isa, sizeof(struct isa_softc), |
65 | isamatch, isaattach, isadetach, NULL, isarescan, isachilddetached); |
66 | |
67 | void isa_attach_knowndevs(struct isa_softc *); |
68 | void isa_free_knowndevs(struct isa_softc *); |
69 | |
70 | int isasubmatch(device_t, cfdata_t, const int *, void *); |
71 | int isasearch(device_t, cfdata_t, const int *, void *); |
72 | |
73 | static int isa_slotcount = -1; /* -1 == don't know how many */ |
74 | |
75 | int |
76 | isamatch(device_t parent, cfdata_t cf, void *aux) |
77 | { |
78 | /* XXX check other indicators */ |
79 | |
80 | return (1); |
81 | } |
82 | |
83 | void |
84 | isaattach(device_t parent, device_t self, void *aux) |
85 | { |
86 | struct isa_softc *sc = device_private(self); |
87 | struct isabus_attach_args *iba = aux; |
88 | static const int wildcard[ISACF_NLOCS] = { |
89 | ISACF_PORT_DEFAULT, ISACF_SIZE_DEFAULT, |
90 | ISACF_IOMEM_DEFAULT, ISACF_IOSIZ_DEFAULT, |
91 | ISACF_IRQ_DEFAULT, ISACF_DRQ_DEFAULT, ISACF_DRQ2_DEFAULT |
92 | }; |
93 | |
94 | TAILQ_INIT(&sc->sc_knowndevs); |
95 | sc->sc_dynamicdevs = 0; |
96 | |
97 | sc->sc_dev = self; |
98 | |
99 | isa_attach_hook(parent, self, iba); |
100 | aprint_naive("\n" ); |
101 | aprint_normal("\n" ); |
102 | |
103 | /* XXX Add code to fetch known-devices. */ |
104 | |
105 | sc->sc_iot = iba->iba_iot; |
106 | sc->sc_memt = iba->iba_memt; |
107 | sc->sc_dmat = iba->iba_dmat; |
108 | sc->sc_ic = iba->iba_ic; |
109 | |
110 | #if NISAPNP > 0 |
111 | /* |
112 | * Reset isapnp cards that the bios configured for us |
113 | */ |
114 | isapnp_isa_attach_hook(sc); |
115 | #endif |
116 | |
117 | #if NISADMA > 0 |
118 | /* |
119 | * Initialize our DMA state. |
120 | */ |
121 | isa_dmainit(sc->sc_ic, sc->sc_iot, sc->sc_dmat, self); |
122 | #endif |
123 | |
124 | /* Attach all direct-config children. */ |
125 | isa_attach_knowndevs(sc); |
126 | |
127 | /* |
128 | * If we don't support dynamic hello/goodbye of devices, |
129 | * then free the knowndevs info now. |
130 | */ |
131 | if (sc->sc_dynamicdevs == 0) |
132 | isa_free_knowndevs(sc); |
133 | |
134 | /* Attach all indirect-config children. */ |
135 | isarescan(self, "isa" , wildcard); |
136 | |
137 | if (!pmf_device_register(self, NULL, NULL)) |
138 | aprint_error_dev(self, "couldn't establish power handler\n" ); |
139 | } |
140 | |
141 | int |
142 | isadetach(device_t self, int flags) |
143 | { |
144 | struct isa_softc *sc = device_private(self); |
145 | int rc; |
146 | |
147 | if ((rc = config_detach_children(self, flags)) != 0) |
148 | return rc; |
149 | |
150 | pmf_device_deregister(self); |
151 | |
152 | isa_free_knowndevs(sc); |
153 | |
154 | #if NISADMA > 0 |
155 | isa_dmadestroy(sc->sc_ic); |
156 | #endif |
157 | isa_detach_hook(sc->sc_ic, self); |
158 | |
159 | return 0; |
160 | } |
161 | |
162 | int |
163 | isarescan(device_t self, const char *ifattr, const int *locators) |
164 | { |
165 | prop_dictionary_t dict; |
166 | int locs[ISACF_NLOCS]; |
167 | bool no_legacy_devices = false; |
168 | |
169 | dict = device_properties(self); |
170 | if (prop_dictionary_get_bool(dict, "no-legacy-devices" , |
171 | &no_legacy_devices) == true) { |
172 | aprint_debug_dev(self, "platform reports no legacy devices\n" ); |
173 | return 0; |
174 | } |
175 | |
176 | memcpy(locs, locators, sizeof(locs)); |
177 | |
178 | /* |
179 | * XXX Bus independent code calling this function does not |
180 | * know the locator default values. It assumes "-1" for now. |
181 | * (should be made available by "config" one day) |
182 | * So fixup where the "-1" is not correct. |
183 | */ |
184 | if (locs[ISACF_SIZE] == -1) |
185 | locs[ISACF_SIZE] = ISACF_SIZE_DEFAULT; |
186 | if (locs[ISACF_IOSIZ] == -1) |
187 | locs[ISACF_IOSIZ] = ISACF_IOSIZ_DEFAULT; |
188 | |
189 | config_search_loc(isasearch, self, ifattr, locs, NULL); |
190 | return (0); |
191 | } |
192 | |
193 | void |
194 | isachilddetached(device_t self, device_t child) |
195 | { |
196 | struct isa_knowndev *ik; |
197 | struct isa_softc *sc = device_private(self); |
198 | |
199 | TAILQ_FOREACH(ik, &sc->sc_knowndevs, ik_list) { |
200 | if (ik->ik_claimed == child) |
201 | ik->ik_claimed = NULL; |
202 | } |
203 | } |
204 | |
205 | void |
206 | isa_attach_knowndevs(struct isa_softc *sc) |
207 | { |
208 | struct isa_attach_args ia; |
209 | struct isa_knowndev *ik; |
210 | |
211 | if (TAILQ_EMPTY(&sc->sc_knowndevs)) |
212 | return; |
213 | |
214 | TAILQ_FOREACH(ik, &sc->sc_knowndevs, ik_list) { |
215 | if (ik->ik_claimed != NULL) |
216 | continue; |
217 | |
218 | ia.ia_iot = sc->sc_iot; |
219 | ia.ia_memt = sc->sc_memt; |
220 | ia.ia_dmat = sc->sc_dmat; |
221 | ia.ia_ic = sc->sc_ic; |
222 | |
223 | ia.ia_pnpname = ik->ik_pnpname; |
224 | ia.ia_pnpcompatnames = ik->ik_pnpcompatnames; |
225 | |
226 | ia.ia_io = ik->ik_io; |
227 | ia.ia_nio = ik->ik_nio; |
228 | |
229 | ia.ia_iomem = ik->ik_iomem; |
230 | ia.ia_niomem = ik->ik_niomem; |
231 | |
232 | ia.ia_irq = ik->ik_irq; |
233 | ia.ia_nirq = ik->ik_nirq; |
234 | |
235 | ia.ia_drq = ik->ik_drq; |
236 | ia.ia_ndrq = ik->ik_ndrq; |
237 | |
238 | ia.ia_aux = NULL; |
239 | |
240 | /* XXX should setup locator array */ |
241 | |
242 | ik->ik_claimed = config_found_sm_loc(sc->sc_dev, |
243 | "isa" , 0, &ia, isaprint, isasubmatch); |
244 | } |
245 | } |
246 | |
247 | void |
248 | isa_free_knowndevs(struct isa_softc *sc) |
249 | { |
250 | struct isa_knowndev *ik; |
251 | struct isa_pnpname *ipn; |
252 | |
253 | #define FREEIT(x) if (x != NULL) free(x, M_DEVBUF) |
254 | |
255 | while ((ik = TAILQ_FIRST(&sc->sc_knowndevs)) != NULL) { |
256 | TAILQ_REMOVE(&sc->sc_knowndevs, ik, ik_list); |
257 | FREEIT(ik->ik_pnpname); |
258 | while ((ipn = ik->ik_pnpcompatnames) != NULL) { |
259 | ik->ik_pnpcompatnames = ipn->ipn_next; |
260 | free(ipn->ipn_name, M_DEVBUF); |
261 | free(ipn, M_DEVBUF); |
262 | } |
263 | FREEIT(ik->ik_io); |
264 | FREEIT(ik->ik_iomem); |
265 | FREEIT(ik->ik_irq); |
266 | FREEIT(ik->ik_drq); |
267 | free(ik, M_DEVBUF); |
268 | } |
269 | |
270 | #undef FREEIT |
271 | } |
272 | |
273 | static int |
274 | checkattachargs(struct isa_attach_args *ia, const int *loc) |
275 | { |
276 | int i; |
277 | |
278 | if (ia->ia_nio == 0) { |
279 | if (loc[ISACF_PORT] != ISACF_PORT_DEFAULT) |
280 | return (0); |
281 | } else { |
282 | if (loc[ISACF_PORT] != ISACF_PORT_DEFAULT && |
283 | loc[ISACF_PORT] != ia->ia_io[0].ir_addr) |
284 | return (0); |
285 | } |
286 | |
287 | if (ia->ia_niomem == 0) { |
288 | if (loc[ISACF_IOMEM] != ISACF_IOMEM_DEFAULT) |
289 | return (0); |
290 | } else { |
291 | if (loc[ISACF_IOMEM] != ISACF_IOMEM_DEFAULT && |
292 | loc[ISACF_IOMEM] != ia->ia_iomem[0].ir_addr) |
293 | return (0); |
294 | } |
295 | |
296 | if (ia->ia_nirq == 0) { |
297 | if (loc[ISACF_IRQ] != ISACF_IRQ_DEFAULT) |
298 | return (0); |
299 | } else { |
300 | if (loc[ISACF_IRQ] != ISACF_IRQ_DEFAULT && |
301 | loc[ISACF_IRQ] != ia->ia_irq[0].ir_irq) |
302 | return (0); |
303 | } |
304 | |
305 | if (ia->ia_ndrq == 0) { |
306 | if (loc[ISACF_DRQ] != ISACF_DRQ_DEFAULT) |
307 | return (0); |
308 | if (loc[ISACF_DRQ2] != ISACF_DRQ2_DEFAULT) |
309 | return (0); |
310 | } else { |
311 | for (i = 0; i < 2; i++) { |
312 | if (i == ia->ia_ndrq) |
313 | break; |
314 | if (loc[ISACF_DRQ + i] != ISACF_DRQ_DEFAULT && |
315 | loc[ISACF_DRQ + i] != ia->ia_drq[i].ir_drq) |
316 | return (0); |
317 | } |
318 | for (; i < 2; i++) { |
319 | if (loc[ISACF_DRQ + i] != ISACF_DRQ_DEFAULT) |
320 | return (0); |
321 | } |
322 | } |
323 | |
324 | return (1); |
325 | } |
326 | |
327 | int |
328 | isasubmatch(device_t parent, cfdata_t cf, const int *ldesc, void *aux) |
329 | { |
330 | struct isa_attach_args *ia = aux; |
331 | |
332 | if (!checkattachargs(ia, cf->cf_loc)) |
333 | return (0); |
334 | |
335 | return (config_match(parent, cf, aux)); |
336 | } |
337 | |
338 | int |
339 | isaprint(void *aux, const char *isa) |
340 | { |
341 | struct isa_attach_args *ia = aux; |
342 | const char *sep; |
343 | int i; |
344 | |
345 | /* |
346 | * This block of code only fires if we have a direct-config'd |
347 | * device for which there is no driver match. |
348 | */ |
349 | if (isa != NULL) { |
350 | struct isa_pnpname *ipn; |
351 | |
352 | if (ia->ia_pnpname != NULL) |
353 | aprint_normal("%s" , ia->ia_pnpname); |
354 | if ((ipn = ia->ia_pnpcompatnames) != NULL) { |
355 | aprint_normal(" (" ); /* ) */ |
356 | for (sep = "" ; ipn != NULL; |
357 | ipn = ipn->ipn_next, sep = " " ) { |
358 | aprint_normal("%s%s" , sep, ipn->ipn_name); |
359 | } |
360 | /* ( */ aprint_normal(")" ); |
361 | } |
362 | aprint_normal(" at %s" , isa); |
363 | } |
364 | |
365 | if (ia->ia_nio) { |
366 | sep = "" ; |
367 | aprint_normal(" port " ); |
368 | for (i = 0; i < ia->ia_nio; i++) { |
369 | if (ia->ia_io[i].ir_size == 0) |
370 | continue; |
371 | aprint_normal("%s0x%x" , sep, ia->ia_io[i].ir_addr); |
372 | if (ia->ia_io[i].ir_size > 1) |
373 | aprint_normal("-0x%x" , ia->ia_io[i].ir_addr + |
374 | ia->ia_io[i].ir_size - 1); |
375 | sep = "," ; |
376 | } |
377 | } |
378 | |
379 | if (ia->ia_niomem) { |
380 | sep = "" ; |
381 | aprint_normal(" iomem " ); |
382 | for (i = 0; i < ia->ia_niomem; i++) { |
383 | if (ia->ia_iomem[i].ir_size == 0) |
384 | continue; |
385 | aprint_normal("%s0x%x" , sep, ia->ia_iomem[i].ir_addr); |
386 | if (ia->ia_iomem[i].ir_size > 1) |
387 | aprint_normal("-0x%x" , ia->ia_iomem[i].ir_addr + |
388 | ia->ia_iomem[i].ir_size - 1); |
389 | sep = "," ; |
390 | } |
391 | } |
392 | |
393 | if (ia->ia_nirq) { |
394 | sep = "" ; |
395 | aprint_normal(" irq " ); |
396 | for (i = 0; i < ia->ia_nirq; i++) { |
397 | if (ia->ia_irq[i].ir_irq == ISACF_IRQ_DEFAULT) |
398 | continue; |
399 | aprint_normal("%s%d" , sep, ia->ia_irq[i].ir_irq); |
400 | sep = "," ; |
401 | } |
402 | } |
403 | |
404 | if (ia->ia_ndrq) { |
405 | sep = "" ; |
406 | aprint_normal(" drq " ); |
407 | for (i = 0; i < ia->ia_ndrq; i++) { |
408 | if (ia->ia_drq[i].ir_drq == ISACF_DRQ_DEFAULT) |
409 | continue; |
410 | aprint_normal("%s%d" , sep, ia->ia_drq[i].ir_drq); |
411 | sep = "," ; |
412 | } |
413 | } |
414 | |
415 | return (UNCONF); |
416 | } |
417 | |
418 | int |
419 | isasearch(device_t parent, cfdata_t cf, const int *slocs, void *aux) |
420 | { |
421 | struct isa_io res_io[1]; |
422 | struct isa_iomem res_mem[1]; |
423 | struct isa_irq res_irq[1]; |
424 | struct isa_drq res_drq[2]; |
425 | struct isa_softc *sc = device_private(parent); |
426 | struct isa_attach_args ia; |
427 | int flocs[ISACF_NLOCS]; |
428 | int tryagain; |
429 | |
430 | do { |
431 | ia.ia_pnpname = NULL; |
432 | ia.ia_pnpcompatnames = NULL; |
433 | |
434 | res_io[0].ir_addr = cf->cf_loc[ISACF_PORT]; |
435 | res_io[0].ir_size = 0; |
436 | |
437 | res_mem[0].ir_addr = cf->cf_loc[ISACF_IOMEM]; |
438 | res_mem[0].ir_size = cf->cf_loc[ISACF_IOSIZ]; |
439 | |
440 | res_irq[0].ir_irq = |
441 | cf->cf_loc[ISACF_IRQ] == 2 ? 9 : cf->cf_loc[ISACF_IRQ]; |
442 | |
443 | res_drq[0].ir_drq = cf->cf_loc[ISACF_DRQ]; |
444 | res_drq[1].ir_drq = cf->cf_loc[ISACF_DRQ2]; |
445 | |
446 | ia.ia_iot = sc->sc_iot; |
447 | ia.ia_memt = sc->sc_memt; |
448 | ia.ia_dmat = sc->sc_dmat; |
449 | ia.ia_ic = sc->sc_ic; |
450 | |
451 | ia.ia_io = res_io; |
452 | ia.ia_nio = 1; |
453 | |
454 | ia.ia_iomem = res_mem; |
455 | ia.ia_niomem = 1; |
456 | |
457 | ia.ia_irq = res_irq; |
458 | ia.ia_nirq = 1; |
459 | |
460 | ia.ia_drq = res_drq; |
461 | ia.ia_ndrq = 2; |
462 | |
463 | if (!checkattachargs(&ia, slocs)) |
464 | return (0); |
465 | |
466 | tryagain = 0; |
467 | if (config_match(parent, cf, &ia) > 0) { |
468 | /* |
469 | * This is not necessary for detach, but might |
470 | * still be useful to collect device information. |
471 | */ |
472 | flocs[ISACF_PORT] = ia.ia_io[0].ir_addr; |
473 | flocs[ISACF_SIZE] = ia.ia_io[0].ir_size; |
474 | flocs[ISACF_IOMEM] = ia.ia_iomem[0].ir_addr; |
475 | flocs[ISACF_IOSIZ] = ia.ia_iomem[0].ir_size; |
476 | flocs[ISACF_IRQ] = ia.ia_irq[0].ir_irq; |
477 | flocs[ISACF_DRQ] = ia.ia_drq[0].ir_drq; |
478 | flocs[ISACF_DRQ2] = ia.ia_drq[1].ir_drq; |
479 | config_attach_loc(parent, cf, flocs, &ia, isaprint); |
480 | tryagain = (cf->cf_fstate == FSTATE_STAR); |
481 | } |
482 | } while (tryagain); |
483 | |
484 | return (0); |
485 | } |
486 | |
487 | const char * |
488 | isa_intr_typename(int type) |
489 | { |
490 | |
491 | switch (type) { |
492 | case IST_NONE: |
493 | return ("none" ); |
494 | case IST_PULSE: |
495 | return ("pulsed" ); |
496 | case IST_EDGE: |
497 | return ("edge-triggered" ); |
498 | case IST_LEVEL: |
499 | return ("level-triggered" ); |
500 | default: |
501 | panic("isa_intr_typename: invalid type %d" , type); |
502 | } |
503 | } |
504 | |
505 | int |
506 | isa_get_slotcount(void) |
507 | { |
508 | |
509 | return isa_slotcount; |
510 | } |
511 | |
512 | void |
513 | isa_set_slotcount(int arg) |
514 | { |
515 | |
516 | isa_slotcount = arg; |
517 | } |
518 | |