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