1 /* $NetBSD: pnpbus.c,v 1.11 2011/07/01 16:55:42 dyoung 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 * 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: pnpbus.c,v 1.11 2011/07/01 16:55:42 dyoung Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/device.h> 38 #include <sys/extent.h> 39 #include <sys/malloc.h> 40 41 #include <sys/bus.h> 42 #include <machine/pio.h> 43 #include <machine/intr.h> 44 #include <machine/platform.h> 45 #include <machine/residual.h> 46 #include <machine/pnp.h> 47 #include <machine/isa_machdep.h> 48 #include <machine/chpidpnp.h> 49 50 #include <dev/isa/isareg.h> 51 52 #include <prep/pnpbus/pnpbusvar.h> 53 54 #include "isadma.h" 55 56 static int pnpbus_match(device_t, cfdata_t, void *); 57 static void pnpbus_attach(device_t, device_t, void *); 58 static int pnpbus_print(void *, const char *); 59 static int pnpbus_search(device_t, cfdata_t, const int *, void *); 60 61 CFATTACH_DECL_NEW(pnpbus, sizeof(struct pnpbus_softc), 62 pnpbus_match, pnpbus_attach, NULL, NULL); 63 64 struct pnpbus_softc *pnpbus_softc; 65 extern struct cfdriver pnpbus_cd; 66 67 static int 68 pnpbus_match(device_t parent, cfdata_t cf, void *aux) 69 { 70 struct pnpbus_attach_args *paa = aux; 71 72 if (paa->paa_name != NULL && strcmp(paa->paa_name, "pnpbus") == 0) 73 return 1; 74 return 0; 75 } 76 77 static void 78 pnpbus_attach(device_t parent, device_t self, void *aux) 79 { 80 struct pnpbus_softc *sc = device_private(self); 81 struct pnpbus_attach_args *paa = aux; 82 83 aprint_normal("\n"); 84 85 pnpbus_softc = sc; 86 sc->sc_dev = self; 87 sc->sc_ic = paa->paa_ic; 88 sc->sc_iot = paa->paa_iot; 89 sc->sc_memt = paa->paa_memt; 90 sc->sc_dmat = paa->paa_dmat; 91 92 #if NISADMA > 0 93 isa_dmainit(sc->sc_ic, sc->sc_iot, sc->sc_dmat, self); 94 #endif 95 96 (void)config_search_ia(pnpbus_search, self, "pnpbus", aux); 97 } 98 99 static int 100 pnp_newirq(void *v, struct pnpresources *r, int size) 101 { 102 struct _S4_Pack *p = v; 103 struct pnpbus_irq *irq; 104 105 irq = malloc(sizeof(struct pnpbus_irq), M_DEVBUF, M_NOWAIT); 106 107 irq->mask = le16dec(&p->IRQMask[0]); 108 109 if (size > 2) 110 irq->flags = p->IRQInfo; 111 else 112 irq->flags = 0x1; 113 114 SIMPLEQ_INSERT_TAIL(&r->irq, irq, next); 115 r->numirq++; 116 117 return 0; 118 } 119 120 static int 121 pnp_newdma(void *v, struct pnpresources *r, int size) 122 { 123 struct _S5_Pack *p = v; 124 struct pnpbus_dma *dma; 125 126 dma = malloc(sizeof(struct pnpbus_dma), M_DEVBUF, M_NOWAIT); 127 128 dma->mask = le16dec(&p->DMAMask); 129 if (size > 2) 130 dma->flags = p->DMAInfo; 131 else 132 dma->flags = 0x01; 133 134 SIMPLEQ_INSERT_TAIL(&r->dma, dma, next); 135 r->numdma++; 136 137 return 0; 138 } 139 140 static int 141 pnp_newioport(void *v, struct pnpresources *r, int size) 142 { 143 struct _S8_Pack *p = v; 144 struct pnpbus_io *io; 145 uint16_t mask; 146 147 io = malloc(sizeof(struct pnpbus_io), M_DEVBUF, M_NOWAIT); 148 mask = p->IOInfo & ISAAddr16bit ? 0xffff : 0x03ff; 149 io->minbase = (p->RangeMin[0] | (p->RangeMin[1] << 8)) & mask; 150 io->maxbase = (p->RangeMax[0] | (p->RangeMax[1] << 8)) & mask; 151 io->align = p->IOAlign; 152 io->len = p->IONum; 153 io->flags = p->IOInfo; 154 155 SIMPLEQ_INSERT_TAIL(&r->io, io, next); 156 r->numio++; 157 158 return 0; 159 } 160 161 static int 162 pnp_newfixedioport(void *v, struct pnpresources *r, int size) 163 { 164 struct _S9_Pack *p = v; 165 struct pnpbus_io *io; 166 167 io = malloc(sizeof(struct pnpbus_io), M_DEVBUF, M_NOWAIT); 168 io->minbase = (p->Range[0] | (p->Range[1] << 8)) & 0x3ff; 169 io->len = p->IONum; 170 io->maxbase = -1; 171 io->flags = 0; 172 io->align = 1; 173 174 SIMPLEQ_INSERT_TAIL(&r->io, io, next); 175 r->numio++; 176 177 return 0; 178 } 179 180 static int 181 pnp_newiomem(void *v, struct pnpresources *r, int size) 182 { 183 struct pnpbus_mem *mem; 184 struct _L1_Pack *pack = v; 185 186 if (pack->Count0 >= 0x9) { 187 mem = malloc(sizeof(struct pnpbus_mem), M_DEVBUF, M_NOWAIT); 188 mem->minbase = (pack->Data[2] << 16) | (pack->Data[1] << 8); 189 mem->maxbase = (pack->Data[4] << 16) | (pack->Data[3] << 8); 190 mem->align = (pack->Data[6] << 8) | pack->Data[5]; 191 mem->len = (pack->Data[8] << 16) | (pack->Data[7] << 8); 192 mem->flags = pack->Data[0]; 193 SIMPLEQ_INSERT_TAIL(&r->iomem, mem, next); 194 r->numiomem++; 195 return 0; 196 } 197 return -1; 198 } 199 200 static int 201 pnp_newaddr(void *v, struct pnpresources *r, int size) 202 { 203 struct pnpbus_io *io; 204 struct pnpbus_mem *mem; 205 struct _L4_Pack *pack = v; 206 struct _L4_PPCPack *p = &pack->L4_Data.L4_PPCPack; 207 208 if (p->PPCData[0] == 1) {/* type IO */ 209 io = malloc(sizeof(struct pnpbus_io), M_DEVBUF, M_NOWAIT); 210 io->minbase = (uint16_t)le64dec(&p->PPCData[4]); 211 io->maxbase = -1; 212 io->align = p->PPCData[1]; 213 io->len = (uint16_t)le64dec(&p->PPCData[12]); 214 io->flags = 0; 215 SIMPLEQ_INSERT_TAIL(&r->io, io, next); 216 r->numio++; 217 218 return 0; 219 } else if (p->PPCData[0] == 2) { 220 mem = malloc(sizeof(struct pnpbus_mem), M_DEVBUF, M_NOWAIT); 221 mem->minbase = (uint32_t)le64dec(&p->PPCData[4]); 222 mem->maxbase = -1; 223 mem->align = p->PPCData[1]; 224 mem->len = (uint32_t)le64dec(&p->PPCData[12]); 225 mem->flags = 0; 226 SIMPLEQ_INSERT_TAIL(&r->mem, mem, next); 227 r->nummem++; 228 229 return 0; 230 } else 231 return -1; 232 } 233 234 static int 235 pnp_newcompatid(void *v, struct pnpresources *r, int size) 236 { 237 struct _S3_Pack *p = v; 238 struct pnpbus_compatid *id; 239 uint32_t cid; 240 241 id = malloc(sizeof(*id), M_DEVBUF, M_NOWAIT); 242 cid = le32dec(p->CompatId); 243 pnp_devid_to_string(cid, id->idstr); 244 id->next = r->compatids; 245 r->compatids = id; 246 247 return 0; 248 } 249 250 /* 251 * Call if match succeeds. This way we don't allocate lots of ram 252 * for structures we never use if the device isn't attached. 253 */ 254 255 int 256 pnpbus_scan(struct pnpbus_dev_attach_args *pna, PPC_DEVICE *dev) 257 { 258 struct pnpresources *r = &pna->pna_res; 259 uint32_t l; 260 uint8_t *p, *q; 261 void *v; 262 int tag, size, item; 263 264 l = be32toh(dev->AllocatedOffset); 265 p = res->DevicePnPHeap + l; 266 267 if (p == NULL) 268 return -1; 269 270 for (; p[0] != END_TAG; p += size) { 271 tag = *p; 272 v = p; 273 if (tag_type(p[0]) == PNP_SMALL) { 274 size = tag_small_count(tag) + 1; 275 item = tag_small_item_name(tag); 276 switch (item) { 277 case IRQFormat: 278 pnp_newirq(v, r, size); 279 break; 280 case DMAFormat: 281 pnp_newdma(v, r, size); 282 break; 283 case IOPort: 284 pnp_newioport(v, r, size); 285 break; 286 case FixedIOPort: 287 pnp_newfixedioport(v, r, size); 288 break; 289 } 290 } else { 291 struct _L4_Pack *pack = v; 292 struct _L4_PPCPack *pa = &pack->L4_Data.L4_PPCPack; 293 294 q = p; 295 size = (q[1] | (q[2] << 8)) + 3 /* tag + length */; 296 item = tag_large_item_name(tag); 297 if (item == LargeVendorItem && 298 pa->Type == LV_GenericAddress) 299 pnp_newaddr(v, r, size); 300 else if (item == MemoryRange) 301 pnp_newiomem(v, r, size); 302 } 303 } 304 305 /* scan for compatid's */ 306 307 l = be32toh(dev->CompatibleOffset); 308 p = res->DevicePnPHeap + l; 309 310 if (p == NULL) 311 return -1; 312 313 for (; p[0] != END_TAG; p += size) { 314 tag = *p; 315 v = p; 316 if (tag_type(p[0]) == PNP_SMALL) { 317 size = tag_small_count(tag) + 1; 318 item = tag_small_item_name(tag); 319 switch (item) { 320 case CompatibleDevice: 321 pnp_newcompatid(v, r, size); 322 break; 323 } 324 } else { 325 q = p; 326 size = (q[1] | (q[2] << 8)) + 3 /* tag + length */; 327 } 328 } 329 return 0; 330 } 331 332 /* 333 * Setup the basic pna structure. 334 */ 335 336 static void 337 pnp_getpna(struct pnpbus_dev_attach_args *pna, struct pnpbus_attach_args *paa, 338 PPC_DEVICE *dev) 339 { 340 DEVICE_ID *id = &dev->DeviceId; 341 struct pnpresources *r = &pna->pna_res; 342 ChipIDPack *pack; 343 uint32_t l; 344 uint8_t *p; 345 void *v; 346 int tag, size, item; 347 348 l = be32toh(dev->AllocatedOffset); 349 p = res->DevicePnPHeap + l; 350 351 pna->pna_iot = paa->paa_iot; 352 pna->pna_memt = paa->paa_memt; 353 pna->pna_ic = paa->paa_ic; 354 pna->pna_dmat = paa->paa_dmat; 355 pnp_devid_to_string(id->DevId, pna->pna_devid); 356 pna->basetype = id->BaseType; 357 pna->subtype = id->SubType; 358 pna->interface = id->Interface; 359 pna->pna_ppc_dev = dev; 360 memset(r, 0, sizeof(*r)); 361 SIMPLEQ_INIT(&r->mem); 362 SIMPLEQ_INIT(&r->io); 363 SIMPLEQ_INIT(&r->irq); 364 SIMPLEQ_INIT(&r->dma); 365 SIMPLEQ_INIT(&r->iomem); 366 if (p == NULL) 367 return; 368 /* otherwise, we start looking for chipid's */ 369 for (; p[0] != END_TAG; p += size) { 370 tag = *p; 371 v = p; 372 if (tag_type(p[0]) == PNP_SMALL) { 373 size = tag_small_count(tag) + 1; 374 item = tag_small_item_name(tag); 375 if (item != SmallVendorItem || p[1] != 1) 376 continue; 377 pack = v; 378 pna->chipid = le16dec(&pack->Name[0]); 379 pna->chipmfg0 = pack->VendorID0; 380 pna->chipmfg1 = pack->VendorID1; 381 break; 382 } else { 383 /* Large */ 384 size = (p[1] | (p[2] << 8)) + 3 /* tag + length */; 385 } 386 } 387 } 388 389 static int 390 pnpbus_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux) 391 { 392 struct pnpbus_dev_attach_args pna; 393 struct pnpbus_attach_args *paa = aux; 394 PPC_DEVICE *ppc_dev; 395 int i; 396 uint32_t ndev; 397 398 ndev = be32toh(res->ActualNumDevices); 399 ppc_dev = res->Devices; 400 401 for (i = 0; i < ((ndev > MAX_DEVICES) ? MAX_DEVICES : ndev); i++) { 402 pnp_getpna(&pna, paa, &ppc_dev[i]); 403 if (config_match(parent, cf, &pna) > 0) 404 config_attach(parent, cf, &pna, pnpbus_print); 405 } 406 407 return 0; 408 } 409 410 static void 411 pnpbus_printres(struct pnpresources *r) 412 { 413 struct pnpbus_io *io; 414 struct pnpbus_mem *mem; 415 struct pnpbus_irq *irq; 416 struct pnpbus_dma *dma; 417 int p = 0; 418 419 if (!SIMPLEQ_EMPTY(&r->mem)) { 420 aprint_normal("mem"); 421 SIMPLEQ_FOREACH(mem, &r->mem, next) { 422 aprint_normal(" 0x%x", mem->minbase); 423 if (mem->len > 1) 424 aprint_normal("-0x%x", 425 mem->minbase + mem->len - 1); 426 } 427 p++; 428 } 429 if (!SIMPLEQ_EMPTY(&r->io)) { 430 if (p++) 431 aprint_normal(", "); 432 aprint_normal("port"); 433 SIMPLEQ_FOREACH(io, &r->io, next) { 434 aprint_normal(" 0x%x", io->minbase); 435 if (io->len > 1) 436 aprint_normal("-0x%x", 437 io->minbase + io->len - 1); 438 } 439 } 440 if (!SIMPLEQ_EMPTY(&r->iomem)) { 441 if (p++) 442 aprint_normal(", "); 443 aprint_normal("iomem"); 444 SIMPLEQ_FOREACH(mem, &r->iomem, next) { 445 aprint_normal(" 0x%x", mem->minbase); 446 if (mem->len > 1) 447 aprint_normal("-0x%x", 448 mem->minbase + mem->len - 1); 449 } 450 p++; 451 } 452 if (!SIMPLEQ_EMPTY(&r->irq)) { 453 if (p++) 454 aprint_normal(", "); 455 aprint_normal("irq"); 456 SIMPLEQ_FOREACH(irq, &r->irq, next) { 457 aprint_normal(" %d", ffs(irq->mask) - 1); 458 } 459 } 460 if (!SIMPLEQ_EMPTY(&r->dma)) { 461 if (p++) 462 aprint_normal(", "); 463 aprint_normal("DMA"); 464 SIMPLEQ_FOREACH(dma, &r->dma, next) { 465 aprint_normal(" %d", ffs(dma->mask) - 1); 466 } 467 } 468 } 469 470 void 471 pnpbus_print_devres(struct pnpbus_dev_attach_args *pna) 472 { 473 aprint_normal(": "); 474 pnpbus_printres(&pna->pna_res); 475 } 476 477 static int 478 pnpbus_print(void *args, const char *name) 479 { 480 struct pnpbus_dev_attach_args *pna = args; 481 482 pnpbus_print_devres(pna); 483 return (UNCONF); 484 } 485 486 /* 487 * Set up an interrupt handler to start being called. 488 */ 489 void * 490 pnpbus_intr_establish(int idx, int level, int tover, int (*ih_fun)(void *), 491 void *ih_arg, struct pnpresources *r) 492 { 493 struct pnpbus_irq *irq; 494 int irqnum, type; 495 496 if (idx >= r->numirq) 497 return 0; 498 499 irq = SIMPLEQ_FIRST(&r->irq); 500 while (idx--) 501 irq = SIMPLEQ_NEXT(irq, next); 502 503 irqnum = ffs(irq->mask) - 1; 504 type = (irq->flags & 0x0c) ? IST_LEVEL : IST_EDGE; 505 if (tover != IST_PNP) 506 type = tover; 507 508 return (void *)intr_establish(irqnum, type, level, ih_fun, ih_arg); 509 } 510 511 /* 512 * Deregister an interrupt handler. 513 */ 514 void 515 pnpbus_intr_disestablish(void *arg) 516 { 517 518 intr_disestablish(arg); 519 } 520 521 int 522 pnpbus_getirqnum(struct pnpresources *r, int idx, int *irqp, int *istp) 523 { 524 struct pnpbus_irq *irq; 525 526 if (idx >= r->numirq) 527 return EINVAL; 528 529 irq = SIMPLEQ_FIRST(&r->irq); 530 while (idx--) 531 irq = SIMPLEQ_NEXT(irq, next); 532 533 if (irqp != NULL) 534 *irqp = ffs(irq->mask) - 1; 535 if (istp != NULL) 536 *istp = (irq->flags &0x0c) ? IST_LEVEL : IST_EDGE; 537 return 0; 538 } 539 540 int 541 pnpbus_getdmachan(struct pnpresources *r, int idx, int *chanp) 542 { 543 struct pnpbus_dma *dma; 544 545 if (idx >= r->numdma) 546 return EINVAL; 547 548 dma = SIMPLEQ_FIRST(&r->dma); 549 while (idx--) 550 dma = SIMPLEQ_NEXT(dma, next); 551 552 if (chanp != NULL) 553 *chanp = ffs(dma->mask) - 1; 554 return 0; 555 } 556 557 int 558 pnpbus_getioport(struct pnpresources *r, int idx, int *basep, int *sizep) 559 { 560 struct pnpbus_io *io; 561 562 if (idx >= r->numio) 563 return EINVAL; 564 565 io = SIMPLEQ_FIRST(&r->io); 566 while (idx--) 567 io = SIMPLEQ_NEXT(io, next); 568 569 if (basep) 570 *basep = io->minbase; 571 if (sizep) 572 *sizep = io->len; 573 return 0; 574 } 575 576 int 577 pnpbus_io_map(struct pnpresources *r, int idx, bus_space_tag_t *tagp, 578 bus_space_handle_t *hdlp) 579 { 580 struct pnpbus_io *io; 581 582 if (idx >= r->numio) 583 return EINVAL; 584 585 io = SIMPLEQ_FIRST(&r->io); 586 while (idx--) 587 io = SIMPLEQ_NEXT(io, next); 588 589 *tagp = &genppc_isa_io_space_tag; 590 return (bus_space_map(&genppc_isa_io_space_tag, io->minbase, io->len, 591 0, hdlp)); 592 } 593 594 void 595 pnpbus_io_unmap(struct pnpresources *r, int idx, bus_space_tag_t tag, 596 bus_space_handle_t hdl) 597 { 598 struct pnpbus_io *io; 599 600 if (idx >= r->numio) 601 return; 602 603 io = SIMPLEQ_FIRST(&r->io); 604 while (idx--) 605 io = SIMPLEQ_NEXT(io, next); 606 607 bus_space_unmap(tag, hdl, io->len); 608 } 609 610 int 611 pnpbus_getiomem(struct pnpresources *r, int idx, int *basep, int *sizep) 612 { 613 struct pnpbus_mem *mem; 614 615 if (idx >= r->numiomem) 616 return EINVAL; 617 618 mem = SIMPLEQ_FIRST(&r->iomem); 619 while (idx--) 620 mem = SIMPLEQ_NEXT(mem, next); 621 622 if (basep) 623 *basep = mem->minbase; 624 if (sizep) 625 *sizep = mem->len; 626 return 0; 627 } 628 629 int 630 pnpbus_iomem_map(struct pnpresources *r, int idx, bus_space_tag_t *tagp, 631 bus_space_handle_t *hdlp) 632 { 633 struct pnpbus_mem *mem; 634 635 if (idx >= r->numiomem) 636 return EINVAL; 637 638 mem = SIMPLEQ_FIRST(&r->iomem); 639 while (idx--) 640 mem = SIMPLEQ_NEXT(mem, next); 641 642 *tagp = &genppc_isa_mem_space_tag; 643 return (bus_space_map(&genppc_isa_mem_space_tag, mem->minbase, mem->len, 644 0, hdlp)); 645 } 646 647 void 648 pnpbus_iomem_unmap(struct pnpresources *r, int idx, bus_space_tag_t tag, 649 bus_space_handle_t hdl) 650 { 651 struct pnpbus_mem *mem; 652 653 if (idx >= r->numiomem) 654 return; 655 656 mem = SIMPLEQ_FIRST(&r->mem); 657 while (idx--) 658 mem = SIMPLEQ_NEXT(mem, next); 659 660 bus_space_unmap(tag, hdl, mem->len); 661 } 662