1 /* $NetBSD: isa.c,v 1.142 2025/10/17 16:56:00 thorpej 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.142 2025/10/17 16:56:00 thorpej 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 int isaprint(void *, const char *); 62 63 CFATTACH_DECL2_NEW(isa, sizeof(struct isa_softc), 64 isamatch, isaattach, isadetach, NULL, isarescan, NULL); 65 66 int isasearch(device_t, cfdata_t, const int *, void *); 67 68 static int isa_slotcount = -1; /* -1 == don't know how many */ 69 70 int 71 isamatch(device_t parent, cfdata_t cf, void *aux) 72 { 73 /* XXX check other indicators */ 74 75 return (1); 76 } 77 78 void 79 isaattach(device_t parent, device_t self, void *aux) 80 { 81 struct isa_softc *sc = device_private(self); 82 struct isabus_attach_args *iba = aux; 83 static const int wildcard[ISACF_NLOCS] = { 84 [ISACF_PORT] = ISACF_PORT_DEFAULT, 85 [ISACF_SIZE] = ISACF_SIZE_DEFAULT, 86 [ISACF_IOMEM] = ISACF_IOMEM_DEFAULT, 87 [ISACF_IOSIZ] = ISACF_IOSIZ_DEFAULT, 88 [ISACF_IRQ] = ISACF_IRQ_DEFAULT, 89 [ISACF_DRQ] = ISACF_DRQ_DEFAULT, 90 [ISACF_DRQ2] = ISACF_DRQ2_DEFAULT, 91 }; 92 93 sc->sc_dev = self; 94 95 isa_attach_hook(parent, self, iba); 96 aprint_naive("\n"); 97 aprint_normal("\n"); 98 99 sc->sc_iot = iba->iba_iot; 100 sc->sc_memt = iba->iba_memt; 101 sc->sc_dmat = iba->iba_dmat; 102 sc->sc_ic = iba->iba_ic; 103 104 #if NISAPNP > 0 105 /* 106 * Reset isapnp cards that the bios configured for us 107 */ 108 isapnp_isa_attach_hook(sc); 109 #endif 110 111 #if NISADMA > 0 112 /* 113 * Initialize our DMA state. 114 */ 115 isa_dmainit(sc->sc_ic, sc->sc_iot, sc->sc_dmat, self); 116 #endif 117 118 /* Attach all indirect-config children. */ 119 isarescan(self, NULL, wildcard); 120 121 if (!pmf_device_register(self, NULL, NULL)) 122 aprint_error_dev(self, "couldn't establish power handler\n"); 123 } 124 125 int 126 isadetach(device_t self, int flags) 127 { 128 struct isa_softc *sc = device_private(self); 129 int rc; 130 131 if ((rc = config_detach_children(self, flags)) != 0) 132 return rc; 133 134 pmf_device_deregister(self); 135 136 #if NISADMA > 0 137 isa_dmadestroy(sc->sc_ic); 138 #endif 139 isa_detach_hook(sc->sc_ic, self); 140 141 return 0; 142 } 143 144 int 145 isarescan(device_t self, const char *ifattr, const int *locators) 146 { 147 int locs[ISACF_NLOCS]; 148 149 if (device_getprop_bool(self, "no-legacy-devices")) { 150 aprint_debug_dev(self, "platform reports no legacy devices\n"); 151 return 0; 152 } 153 154 memcpy(locs, locators, sizeof(locs)); 155 156 /* 157 * XXX Bus independent code calling this function does not 158 * know the locator default values. It assumes "-1" for now. 159 * (should be made available by "config" one day) 160 * So fixup where the "-1" is not correct. 161 */ 162 if (locs[ISACF_SIZE] == -1) 163 locs[ISACF_SIZE] = ISACF_SIZE_DEFAULT; 164 if (locs[ISACF_IOSIZ] == -1) 165 locs[ISACF_IOSIZ] = ISACF_IOSIZ_DEFAULT; 166 167 config_search(self, NULL, 168 CFARGS(.search = isasearch, 169 .locators = locs)); 170 return (0); 171 } 172 173 static int 174 checkattachargs(struct isa_attach_args *ia, const int *loc) 175 { 176 int i; 177 178 if (ia->ia_nio == 0) { 179 if (loc[ISACF_PORT] != ISACF_PORT_DEFAULT) 180 return (0); 181 } else { 182 if (loc[ISACF_PORT] != ISACF_PORT_DEFAULT && 183 loc[ISACF_PORT] != ia->ia_io[0].ir_addr) 184 return (0); 185 } 186 187 if (ia->ia_niomem == 0) { 188 if (loc[ISACF_IOMEM] != ISACF_IOMEM_DEFAULT) 189 return (0); 190 } else { 191 if (loc[ISACF_IOMEM] != ISACF_IOMEM_DEFAULT && 192 loc[ISACF_IOMEM] != ia->ia_iomem[0].ir_addr) 193 return (0); 194 } 195 196 if (ia->ia_nirq == 0) { 197 if (loc[ISACF_IRQ] != ISACF_IRQ_DEFAULT) 198 return (0); 199 } else { 200 if (loc[ISACF_IRQ] != ISACF_IRQ_DEFAULT && 201 loc[ISACF_IRQ] != ia->ia_irq[0].ir_irq) 202 return (0); 203 } 204 205 if (ia->ia_ndrq == 0) { 206 if (loc[ISACF_DRQ] != ISACF_DRQ_DEFAULT) 207 return (0); 208 if (loc[ISACF_DRQ2] != ISACF_DRQ2_DEFAULT) 209 return (0); 210 } else { 211 for (i = 0; i < 2; i++) { 212 if (i == ia->ia_ndrq) 213 break; 214 if (loc[ISACF_DRQ + i] != ISACF_DRQ_DEFAULT && 215 loc[ISACF_DRQ + i] != ia->ia_drq[i].ir_drq) 216 return (0); 217 } 218 for (; i < 2; i++) { 219 if (loc[ISACF_DRQ + i] != ISACF_DRQ_DEFAULT) 220 return (0); 221 } 222 } 223 224 return (1); 225 } 226 227 int 228 isaprint(void *aux, const char *isa) 229 { 230 struct isa_attach_args *ia = aux; 231 const char *sep; 232 int i; 233 234 /* 235 * This block of code only fires if we have a direct-config'd 236 * device for which there is no driver match. 237 */ 238 if (isa != NULL) { 239 struct isa_pnpname *ipn; 240 241 if (ia->ia_pnpname != NULL) 242 aprint_normal("%s", ia->ia_pnpname); 243 if ((ipn = ia->ia_pnpcompatnames) != NULL) { 244 aprint_normal(" ("); /* ) */ 245 for (sep = ""; ipn != NULL; 246 ipn = ipn->ipn_next, sep = " ") { 247 aprint_normal("%s%s", sep, ipn->ipn_name); 248 } 249 /* ( */ aprint_normal(")"); 250 } 251 aprint_normal(" at %s", isa); 252 } 253 254 if (ia->ia_nio) { 255 sep = ""; 256 aprint_normal(" port "); 257 for (i = 0; i < ia->ia_nio; i++) { 258 if (ia->ia_io[i].ir_size == 0) 259 continue; 260 aprint_normal("%s0x%x", sep, ia->ia_io[i].ir_addr); 261 if (ia->ia_io[i].ir_size > 1) 262 aprint_normal("-0x%x", ia->ia_io[i].ir_addr + 263 ia->ia_io[i].ir_size - 1); 264 sep = ","; 265 } 266 } 267 268 if (ia->ia_niomem) { 269 sep = ""; 270 aprint_normal(" iomem "); 271 for (i = 0; i < ia->ia_niomem; i++) { 272 if (ia->ia_iomem[i].ir_size == 0) 273 continue; 274 aprint_normal("%s0x%x", sep, ia->ia_iomem[i].ir_addr); 275 if (ia->ia_iomem[i].ir_size > 1) 276 aprint_normal("-0x%x", ia->ia_iomem[i].ir_addr + 277 ia->ia_iomem[i].ir_size - 1); 278 sep = ","; 279 } 280 } 281 282 if (ia->ia_nirq) { 283 sep = ""; 284 aprint_normal(" irq "); 285 for (i = 0; i < ia->ia_nirq; i++) { 286 if (ia->ia_irq[i].ir_irq == ISACF_IRQ_DEFAULT) 287 continue; 288 aprint_normal("%s%d", sep, ia->ia_irq[i].ir_irq); 289 sep = ","; 290 } 291 } 292 293 if (ia->ia_ndrq) { 294 sep = ""; 295 aprint_normal(" drq "); 296 for (i = 0; i < ia->ia_ndrq; i++) { 297 if (ia->ia_drq[i].ir_drq == ISACF_DRQ_DEFAULT) 298 continue; 299 aprint_normal("%s%d", sep, ia->ia_drq[i].ir_drq); 300 sep = ","; 301 } 302 } 303 304 return (UNCONF); 305 } 306 307 int 308 isasearch(device_t parent, cfdata_t cf, const int *slocs, void *aux) 309 { 310 struct isa_io res_io[1]; 311 struct isa_iomem res_mem[1]; 312 struct isa_irq res_irq[1]; 313 struct isa_drq res_drq[2]; 314 struct isa_softc *sc = device_private(parent); 315 struct isa_attach_args ia; 316 int flocs[ISACF_NLOCS]; 317 int tryagain; 318 319 do { 320 ia.ia_pnpname = NULL; 321 ia.ia_pnpcompatnames = NULL; 322 323 res_io[0].ir_addr = cf->cf_loc[ISACF_PORT]; 324 res_io[0].ir_size = 0; 325 326 res_mem[0].ir_addr = cf->cf_loc[ISACF_IOMEM]; 327 res_mem[0].ir_size = cf->cf_loc[ISACF_IOSIZ]; 328 329 res_irq[0].ir_irq = 330 cf->cf_loc[ISACF_IRQ] == 2 ? 9 : cf->cf_loc[ISACF_IRQ]; 331 332 res_drq[0].ir_drq = cf->cf_loc[ISACF_DRQ]; 333 res_drq[1].ir_drq = cf->cf_loc[ISACF_DRQ2]; 334 335 ia.ia_iot = sc->sc_iot; 336 ia.ia_memt = sc->sc_memt; 337 ia.ia_dmat = sc->sc_dmat; 338 ia.ia_ic = sc->sc_ic; 339 340 ia.ia_io = res_io; 341 ia.ia_nio = 1; 342 343 ia.ia_iomem = res_mem; 344 ia.ia_niomem = 1; 345 346 ia.ia_irq = res_irq; 347 ia.ia_nirq = 1; 348 349 ia.ia_drq = res_drq; 350 ia.ia_ndrq = 2; 351 352 if (!checkattachargs(&ia, slocs)) 353 return (0); 354 355 tryagain = 0; 356 if (config_probe(parent, cf, &ia)) { 357 /* 358 * This is not necessary for detach, but might 359 * still be useful to collect device information. 360 */ 361 flocs[ISACF_PORT] = ia.ia_io[0].ir_addr; 362 flocs[ISACF_SIZE] = ia.ia_io[0].ir_size; 363 flocs[ISACF_IOMEM] = ia.ia_iomem[0].ir_addr; 364 flocs[ISACF_IOSIZ] = ia.ia_iomem[0].ir_size; 365 flocs[ISACF_IRQ] = ia.ia_irq[0].ir_irq; 366 flocs[ISACF_DRQ] = ia.ia_drq[0].ir_drq; 367 flocs[ISACF_DRQ2] = ia.ia_drq[1].ir_drq; 368 config_attach(parent, cf, &ia, isaprint, 369 CFARGS(.locators = flocs)); 370 tryagain = (cf->cf_fstate == FSTATE_STAR); 371 } 372 } while (tryagain); 373 374 return (0); 375 } 376 377 const char * 378 isa_intr_typename(int type) 379 { 380 381 switch (type) { 382 case IST_NONE: 383 return ("none"); 384 case IST_PULSE: 385 return ("pulsed"); 386 case IST_EDGE: 387 return ("edge-triggered"); 388 case IST_LEVEL: 389 return ("level-triggered"); 390 default: 391 panic("isa_intr_typename: invalid type %d", type); 392 } 393 } 394 395 int 396 isa_get_slotcount(void) 397 { 398 399 return isa_slotcount; 400 } 401 402 void 403 isa_set_slotcount(int arg) 404 { 405 406 isa_slotcount = arg; 407 } 408