1 /* $NetBSD: pnpbus.c,v 1.2 2006/03/16 17:43:34 garbled Exp $ */ 2 3 /*- 4 * Copyright (c) 1998 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Tim Rightnour 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: pnpbus.c,v 1.2 2006/03/16 17:43:34 garbled Exp $"); 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/device.h> 45 #include <sys/extent.h> 46 #include <sys/malloc.h> 47 48 #include <machine/bus.h> 49 #include <machine/pio.h> 50 #include <machine/intr.h> 51 #include <machine/platform.h> 52 #include <machine/residual.h> 53 #include <machine/pnp.h> 54 #include <machine/isa_machdep.h> 55 56 #include <dev/isa/isareg.h> 57 58 #include <prep/pnpbus/pnpbusvar.h> 59 60 static int pnpbus_match(struct device *, struct cfdata *, void *); 61 static void pnpbus_attach(struct device *, struct device *, void *); 62 static int pnpbus_print(void *, const char *); 63 static int pnpbus_search(struct device *, struct cfdata *, 64 const int *, void *); 65 66 CFATTACH_DECL(pnpbus, sizeof(struct pnpbus_softc), 67 pnpbus_match, pnpbus_attach, NULL, NULL); 68 69 struct pnpbus_softc *pnpbus_softc; 70 extern struct cfdriver pnpbus_cd; 71 72 static int 73 pnpbus_match(struct device *parent, struct cfdata *cf, void *aux) 74 { 75 return 1; 76 } 77 78 static void 79 pnpbus_attach(struct device *parent, struct device *self, void *aux) 80 { 81 struct pnpbus_softc *sc = (struct pnpbus_softc *)self; 82 struct pnpbus_attach_args *paa = aux; 83 84 printf("\n"); 85 86 pnpbus_softc = sc; 87 sc->sc_ic = paa->paa_ic; 88 sc->sc_iot = paa->paa_iot; 89 sc->sc_memt = paa->paa_memt; 90 91 (void)config_search_ia(pnpbus_search, self, "pnpbus", aux); 92 } 93 94 static int 95 pnp_newirq(void *v, struct pnpresources *r, int size) 96 { 97 struct _S4_Pack *p = v; 98 struct pnpbus_irq *irq; 99 100 irq = malloc(sizeof(struct pnpbus_irq), M_DEVBUF, M_NOWAIT); 101 102 irq->mask = le16dec(&p->IRQMask[0]); 103 104 /* XXX default to level, not edge, based on the powerstack E0 */ 105 if (size > 2) 106 irq->flags = p->IRQInfo; 107 else 108 irq->flags = 0x0c; 109 110 SIMPLEQ_INSERT_TAIL(&r->irq, irq, next); 111 r->numirq++; 112 113 return 0; 114 } 115 116 static int 117 pnp_newdma(void *v, struct pnpresources *r, int size) 118 { 119 struct _S5_Pack *p = v; 120 struct pnpbus_dma *dma; 121 122 dma = malloc(sizeof(struct pnpbus_dma), M_DEVBUF, M_NOWAIT); 123 124 dma->mask = le16dec(&p->DMAMask); 125 if (size > 2) 126 dma->flags = p->DMAInfo; 127 else 128 dma->flags = 0x01; 129 130 SIMPLEQ_INSERT_TAIL(&r->dma, dma, next); 131 r->numdma++; 132 133 return 0; 134 } 135 136 static int 137 pnp_newioport(void *v, struct pnpresources *r, int size) 138 { 139 struct _S8_Pack *p = v; 140 struct pnpbus_io *io; 141 uint16_t mask; 142 143 io = malloc(sizeof(struct pnpbus_io), M_DEVBUF, M_NOWAIT); 144 mask = p->IOInfo & ISAAddr16bit ? 0xffff : 0x03ff; 145 io->minbase = (p->RangeMin[0] | (p->RangeMin[1] << 8)) & mask; 146 io->maxbase = (p->RangeMax[0] | (p->RangeMax[1] << 8)) & mask; 147 io->align = p->IOAlign; 148 io->len = p->IONum; 149 io->flags = p->IOInfo; 150 151 SIMPLEQ_INSERT_TAIL(&r->io, io, next); 152 r->numio++; 153 154 return 0; 155 } 156 157 static int 158 pnp_newfixedioport(void *v, struct pnpresources *r, int size) 159 { 160 struct _S9_Pack *p = v; 161 struct pnpbus_io *io; 162 163 io = malloc(sizeof(struct pnpbus_io), M_DEVBUF, M_NOWAIT); 164 io->minbase = (p->Range[0] | (p->Range[1] << 8)) & 0x3ff; 165 io->len = p->IONum; 166 io->maxbase = -1; 167 io->flags = 0; 168 io->align = 1; 169 170 SIMPLEQ_INSERT_TAIL(&r->io, io, next); 171 r->numio++; 172 173 return 0; 174 } 175 176 static int 177 pnp_newaddr(void *v, struct pnpresources *r, int size) 178 { 179 struct pnpbus_io *io; 180 struct pnpbus_mem *mem; 181 struct _L4_Pack *pack = v; 182 struct _L4_PPCPack *p = &pack->L4_Data.L4_PPCPack; 183 184 if (p->PPCData[0] == 1) {/* type IO */ 185 io = malloc(sizeof(struct pnpbus_io), M_DEVBUF, M_NOWAIT); 186 io->minbase = (uint16_t)le64dec(&p->PPCData[4]); 187 io->maxbase = -1; 188 io->align = p->PPCData[1]; 189 io->len = (uint16_t)le64dec(&p->PPCData[12]); 190 io->flags = 0; 191 SIMPLEQ_INSERT_TAIL(&r->io, io, next); 192 r->numio++; 193 194 return 0; 195 } else if (p->PPCData[0] == 2) { 196 mem = malloc(sizeof(struct pnpbus_mem), M_DEVBUF, M_NOWAIT); 197 mem->minbase = (uint32_t)le64dec(&p->PPCData[4]); 198 mem->maxbase = -1; 199 mem->align = p->PPCData[1]; 200 mem->len = (uint32_t)le64dec(&p->PPCData[12]); 201 mem->flags = 0; 202 SIMPLEQ_INSERT_TAIL(&r->mem, mem, next); 203 r->nummem++; 204 205 return 0; 206 } else 207 return -1; 208 } 209 210 static int 211 pnp_newcompatid(void *v, struct pnpresources *r, int size) 212 { 213 struct _S3_Pack *p = v; 214 struct pnpbus_compatid *id; 215 uint32_t cid; 216 217 id = malloc(sizeof(*id), M_DEVBUF, M_NOWAIT); 218 cid = le32dec(p->CompatId); 219 pnp_devid_to_string(cid, id->idstr); 220 id->next = r->compatids; 221 r->compatids = id; 222 223 return 0; 224 } 225 226 /* 227 * Call if match succeeds. This way we don't allocate lots of ram 228 * for structures we never use if the device isn't attached. 229 */ 230 231 int 232 pnpbus_scan(struct pnpbus_dev_attach_args *pna, PPC_DEVICE *dev) 233 { 234 struct pnpresources *r = &pna->pna_res; 235 uint32_t l; 236 uint8_t *p, *q; 237 void *v; 238 int tag, size, item; 239 240 l = be32toh(dev->AllocatedOffset); 241 p = res->DevicePnPHeap + l; 242 243 if (p == NULL) 244 return -1; 245 246 for (; p[0] != END_TAG; p += size) { 247 tag = *p; 248 v = p; 249 if (tag_type(p[0]) == PNP_SMALL) { 250 size = tag_small_count(tag) + 1; 251 item = tag_small_item_name(tag); 252 switch (item) { 253 case IRQFormat: 254 pnp_newirq(v, r, size); 255 break; 256 case DMAFormat: 257 pnp_newdma(v, r, size); 258 break; 259 case IOPort: 260 pnp_newioport(v, r, size); 261 break; 262 case FixedIOPort: 263 pnp_newfixedioport(v, r, size); 264 break; 265 } 266 } else { 267 struct _L4_Pack *pack = v; 268 struct _L4_PPCPack *pa = &pack->L4_Data.L4_PPCPack; 269 270 q = p; 271 size = (q[1] | (q[2] << 8)) + 3 /* tag + length */; 272 item = tag_large_item_name(tag); 273 if (item == LargeVendorItem && 274 pa->Type == LV_GenericAddress) 275 pnp_newaddr(v, r, size); 276 } 277 } 278 279 /* scan for compatid's */ 280 281 l = be32toh(dev->CompatibleOffset); 282 p = res->DevicePnPHeap + l; 283 284 if (p == NULL) 285 return -1; 286 287 for (; p[0] != END_TAG; p += size) { 288 tag = *p; 289 v = p; 290 if (tag_type(p[0]) == PNP_SMALL) { 291 size = tag_small_count(tag) + 1; 292 item = tag_small_item_name(tag); 293 switch (item) { 294 case CompatibleDevice: 295 pnp_newcompatid(v, r, size); 296 break; 297 } 298 } else { 299 q = p; 300 size = (q[1] | (q[2] << 8)) + 3 /* tag + length */; 301 } 302 } 303 return 0; 304 } 305 306 /* 307 * Setup the basic pna structure. 308 */ 309 310 static void 311 pnp_getpna(struct pnpbus_dev_attach_args *pna, struct pnpbus_attach_args *paa, 312 PPC_DEVICE *dev) 313 { 314 uint32_t l; 315 DEVICE_ID *id = &dev->DeviceId; 316 struct pnpresources *r = &pna->pna_res; 317 318 l = be32toh(dev->AllocatedOffset); 319 320 pna->pna_iot = paa->paa_iot; 321 pna->pna_memt = paa->paa_memt; 322 pna->pna_ic = paa->paa_ic; 323 pnp_devid_to_string(id->DevId, pna->pna_devid); 324 pna->pna_ppc_dev = dev; 325 memset(r, 0, sizeof(*r)); 326 SIMPLEQ_INIT(&r->mem); 327 SIMPLEQ_INIT(&r->io); 328 SIMPLEQ_INIT(&r->irq); 329 SIMPLEQ_INIT(&r->dma); 330 } 331 332 static int 333 pnpbus_search(struct device *parent, struct cfdata *cf, 334 const int *ldesc, void *aux) 335 { 336 struct pnpbus_dev_attach_args pna; 337 struct pnpbus_attach_args *paa = aux; 338 PPC_DEVICE *ppc_dev; 339 int i; 340 uint32_t ndev; 341 342 ndev = be32toh(res->ActualNumDevices); 343 ppc_dev = res->Devices; 344 345 for (i = 0; i < ((ndev > MAX_DEVICES) ? MAX_DEVICES : ndev); i++) { 346 pnp_getpna(&pna, paa, &ppc_dev[i]); 347 if (config_match(parent, cf, &pna) > 0) 348 config_attach(parent, cf, &pna, pnpbus_print); 349 } 350 351 return 0; 352 } 353 354 static void 355 pnpbus_printres(struct pnpresources *r) 356 { 357 struct pnpbus_io *io; 358 struct pnpbus_mem *mem; 359 struct pnpbus_irq *irq; 360 struct pnpbus_dma *dma; 361 int p = 0; 362 363 if (!SIMPLEQ_EMPTY(&r->mem)) { 364 aprint_normal("mem"); 365 SIMPLEQ_FOREACH(mem, &r->mem, next) { 366 aprint_normal(" 0x%x", mem->minbase); 367 if (mem->len > 1) 368 aprint_normal("-0x%x", 369 mem->minbase + mem->len - 1); 370 } 371 p++; 372 } 373 if (!SIMPLEQ_EMPTY(&r->io)) { 374 if (p++) 375 aprint_normal(", "); 376 aprint_normal("port"); 377 SIMPLEQ_FOREACH(io, &r->io, next) { 378 aprint_normal(" 0x%x", io->minbase); 379 if (io->len > 1) 380 aprint_normal("-0x%x", 381 io->minbase + io->len - 1); 382 } 383 } 384 if (!SIMPLEQ_EMPTY(&r->irq)) { 385 if (p++) 386 aprint_normal(", "); 387 aprint_normal("irq"); 388 SIMPLEQ_FOREACH(irq, &r->irq, next) { 389 aprint_normal(" %d", ffs(irq->mask) - 1); 390 } 391 } 392 if (!SIMPLEQ_EMPTY(&r->dma)) { 393 if (p++) 394 aprint_normal(", "); 395 aprint_normal("DMA"); 396 SIMPLEQ_FOREACH(dma, &r->dma, next) { 397 aprint_normal(" %d", ffs(dma->mask) - 1); 398 } 399 } 400 } 401 402 void 403 pnpbus_print_devres(struct pnpbus_dev_attach_args *pna) 404 { 405 aprint_normal(": "); 406 pnpbus_printres(&pna->pna_res); 407 aprint_normal("\n"); 408 } 409 410 static int 411 pnpbus_print(void *args, const char *name) 412 { 413 struct pnpbus_dev_attach_args *pna = args; 414 415 pnpbus_print_devres(pna); 416 return (UNCONF); 417 } 418 419 /* 420 * Set up an interrupt handler to start being called. 421 */ 422 void * 423 pnpbus_intr_establish(int idx, int level, int (*ih_fun)(void *), 424 void *ih_arg, struct pnpresources *r) 425 { 426 struct pnpbus_irq *irq; 427 int irqnum, type; 428 429 if (idx >= r->numirq) 430 return 0; 431 432 irq = SIMPLEQ_FIRST(&r->irq); 433 while (idx--) 434 irq = SIMPLEQ_NEXT(irq, next); 435 436 irqnum = ffs(irq->mask) - 1; 437 type = (irq->flags & 0x0c) ? IST_LEVEL : IST_EDGE; 438 439 return (void *)intr_establish(irqnum, type, level, ih_fun, ih_arg); 440 } 441 442 /* 443 * Deregister an interrupt handler. 444 */ 445 void 446 pnpbus_intr_disestablish(void *arg) 447 { 448 449 intr_disestablish(arg); 450 } 451 452 int 453 pnpbus_getirqnum(struct pnpresources *r, int idx, int *irqp, int *istp) 454 { 455 struct pnpbus_irq *irq; 456 457 if (idx >= r->numirq) 458 return EINVAL; 459 460 irq = SIMPLEQ_FIRST(&r->irq); 461 while (idx--) 462 irq = SIMPLEQ_NEXT(irq, next); 463 464 if (irqp != NULL) 465 *irqp = ffs(irq->mask) - 1; 466 if (istp != NULL) 467 *istp = (irq->flags &0x0c) ? IST_LEVEL : IST_EDGE; 468 return 0; 469 } 470 471 int 472 pnpbus_getdmachan(struct pnpresources *r, int idx, int *chanp) 473 { 474 struct pnpbus_dma *dma; 475 476 if (idx >= r->numdma) 477 return EINVAL; 478 479 dma = SIMPLEQ_FIRST(&r->dma); 480 while (idx--) 481 dma = SIMPLEQ_NEXT(dma, next); 482 483 if (chanp != NULL) 484 *chanp = ffs(dma->mask) - 1; 485 return 0; 486 } 487 488 int 489 pnpbus_getioport(struct pnpresources *r, int idx, int *basep, int *sizep) 490 { 491 struct pnpbus_io *io; 492 493 if (idx >= r->numio) 494 return EINVAL; 495 496 io = SIMPLEQ_FIRST(&r->io); 497 while (idx--) 498 io = SIMPLEQ_NEXT(io, next); 499 500 if (basep) 501 *basep = io->minbase; 502 if (sizep) 503 *sizep = io->len; 504 return 0; 505 } 506 507 int 508 pnpbus_io_map(struct pnpresources *r, int idx, bus_space_tag_t *tagp, 509 bus_space_handle_t *hdlp) 510 { 511 struct pnpbus_io *io; 512 513 if (idx >= r->numio) 514 return EINVAL; 515 516 io = SIMPLEQ_FIRST(&r->io); 517 while (idx--) 518 io = SIMPLEQ_NEXT(io, next); 519 520 *tagp = &prep_isa_io_space_tag; 521 return (bus_space_map(&prep_isa_io_space_tag, io->minbase, io->len, 522 0, hdlp)); 523 } 524 525 void 526 pnpbus_io_unmap(struct pnpresources *r, int idx, bus_space_tag_t tag, 527 bus_space_handle_t hdl) 528 { 529 struct pnpbus_io *io; 530 531 if (idx >= r->numio) 532 return; 533 534 io = SIMPLEQ_FIRST(&r->io); 535 while (idx--) 536 io = SIMPLEQ_NEXT(io, next); 537 538 bus_space_unmap(tag, hdl, io->len); 539 } 540