1 /* $NetBSD: pci_machdep_ofw.c,v 1.23 2026/06/27 13:31:03 rkujawa Exp $ */ 2 3 /*- 4 * Copyright (c) 2007 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 /* 33 * Generic OFW routines for pci_machdep 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: pci_machdep_ofw.c,v 1.23 2026/06/27 13:31:03 rkujawa Exp $"); 38 39 #include <sys/types.h> 40 #include <sys/param.h> 41 #include <sys/time.h> 42 #include <sys/systm.h> 43 #include <sys/errno.h> 44 #include <sys/device.h> 45 #include <sys/kmem.h> 46 #include <sys/bus.h> 47 #include <sys/intr.h> 48 49 #include <uvm/uvm_extern.h> 50 51 #include <machine/autoconf.h> 52 #include <machine/pio.h> 53 54 #include <dev/pci/pcivar.h> 55 #include <dev/pci/pcireg.h> 56 #include <dev/pci/ppbreg.h> 57 #include <dev/pci/pcidevs.h> 58 #include <dev/pci/pciconf.h> 59 60 #include <dev/ofw/openfirm.h> 61 #include <dev/ofw/ofw_pci.h> 62 63 pcitag_t genppc_pci_indirect_make_tag(void *, int, int, int); 64 void genppc_pci_indirect_decompose_tag(void *, pcitag_t, int *, int *, int *); 65 66 ofw_pic_node_t picnodes[8]; 67 int nrofpics = 0; 68 69 int 70 genofw_find_picnode(int node) 71 { 72 int i; 73 74 for (i = 0; i < 8; i++) 75 if (node == picnodes[i].node) 76 return i; 77 return -1; 78 } 79 80 void 81 genofw_find_ofpics(int startnode) 82 { 83 int node, iparent, child, iranges[6], irgot=0, i; 84 uint32_t reg[12]; 85 char name[32]; 86 87 for (node = startnode; node; node = OF_peer(node)) { 88 if ((child = OF_child(node)) != 0) 89 genofw_find_ofpics(child); 90 memset(name, 0, sizeof(name)); 91 if (OF_getprop(node, "name", name, sizeof(name)) == -1) 92 continue; 93 if (strncmp(name, "interrupt-controller", 20) == 0) 94 goto foundic; 95 96 if (OF_getprop(node, "interrupt-controller", name, 97 sizeof(name)) > -1) 98 goto foundic; 99 100 /* 101 * The MPC5200/MPC5200B SIU interrupt controller advertises 102 * itself only through its "compatible" property. 103 */ 104 if (OF_getprop(node, "compatible", name, sizeof(name)) > 0 && 105 (strcmp(name, "mpc5200-pic") == 0 || 106 strcmp(name, "mpc5200b-pic") == 0)) 107 goto foundic; 108 109 if (OF_getprop(node, "device_type", name, sizeof(name)) == -1) 110 continue; 111 if (strncmp(name, "interrupt-controller", 20) == 0) 112 goto foundic; 113 114 /* if we didn't find one, skip to the next */ 115 continue; 116 foundic: 117 picnodes[nrofpics].node = node; 118 if (OF_getprop(node, "interrupt-parent", &iparent, 119 sizeof(iparent)) == sizeof(iparent)) 120 picnodes[nrofpics].parent = iparent; 121 if (OF_getprop(node, "#interrupt-cells", &iparent, 122 sizeof(iparent)) == sizeof(iparent)) 123 picnodes[nrofpics].cells = iparent; 124 else 125 picnodes[nrofpics].cells = 1; 126 127 picnodes[nrofpics].intrs = 0; 128 irgot = OF_getprop(node, "interrupt-ranges", iranges, 129 sizeof(int)*6); /* XXX is this ok? */ 130 if (irgot >= sizeof(int)) { 131 for (i=0; i < irgot/4; i++) 132 if (!picnodes[nrofpics].intrs) 133 picnodes[nrofpics].intrs = iranges[i]; 134 } 135 136 irgot = OF_getprop(node, "reg", reg, sizeof(reg)); 137 138 if (!picnodes[nrofpics].intrs) 139 picnodes[nrofpics].intrs = 16; 140 141 if (nrofpics > 0) 142 picnodes[nrofpics].offset = picnodes[nrofpics-1].offset 143 + picnodes[nrofpics-1].intrs; 144 else 145 picnodes[nrofpics].offset = 0; 146 OF_getprop(node, "device_type", name, sizeof(name)); 147 if (strcmp(name, "open-pic") == 0) 148 picnodes[nrofpics].type = PICNODE_TYPE_OPENPIC; 149 if (strcmp(name, "interrupt-controller") == 0) { 150 OF_getprop(node, "compatible", name, sizeof(name)); 151 if (strcmp(name, "heathrow") == 0) 152 picnodes[nrofpics].type = PICNODE_TYPE_HEATHROW; 153 if (strcmp(name, "pnpPNP,0") == 0) 154 picnodes[nrofpics].type = PICNODE_TYPE_8259; 155 if (strcmp(name, "chrp,iic") == 0) { 156 picnodes[nrofpics].type = PICNODE_TYPE_8259; 157 if (irgot >= 9 * sizeof(uint32_t) && 158 reg[7] == 0x4d0) 159 picnodes[nrofpics].type = 160 PICNODE_TYPE_IVR; 161 } 162 } 163 if (strlen(name) == 0) { 164 /* probably a Pegasos, assume 8259 */ 165 picnodes[nrofpics].type = PICNODE_TYPE_8259; 166 } 167 /* 168 * The MPC5200/MPC5200B SIU interrupt controller advertises 169 * itself via its "compatible" property. 170 */ 171 if (OF_getprop(node, "compatible", name, sizeof(name)) > 0 && 172 (strcmp(name, "mpc5200-pic") == 0 || 173 strcmp(name, "mpc5200b-pic") == 0)) 174 picnodes[nrofpics].type = PICNODE_TYPE_MPC5200; 175 nrofpics++; 176 } 177 } 178 179 /* Fix up the various picnode offsets */ 180 void 181 genofw_fixup_picnode_offsets(void) 182 { 183 int i, curoff; 184 185 curoff=0; 186 187 for (i=0; i < nrofpics; i++) { 188 if (picnodes[i].type == PICNODE_TYPE_8259 || 189 picnodes[i].type == PICNODE_TYPE_IVR) { 190 picnodes[i].offset = 0; 191 curoff = picnodes[i].intrs; 192 } 193 } 194 for (i=0; i < nrofpics; i++) { 195 /* now skip the 8259 */ 196 if (picnodes[i].type == PICNODE_TYPE_8259) 197 continue; 198 if (picnodes[i].type == PICNODE_TYPE_IVR) 199 continue; 200 201 picnodes[i].offset = curoff; 202 curoff += picnodes[i].intrs; 203 } 204 } 205 206 /* we are given a pci devnode, and dig from there */ 207 void 208 genofw_setup_pciintr_map(void *v, struct genppc_pci_chipset_businfo *pbi, 209 int pcinode) 210 { 211 int node; 212 u_int32_t map[160]; 213 int parent, len; 214 int curdev, foundirqs=0; 215 int i, reclen, nrofpcidevs=0; 216 u_int32_t acells, icells, pcells; 217 prop_dictionary_t dict; 218 prop_dictionary_t sub=0; 219 pci_chipset_tag_t pc = (pci_chipset_tag_t)v; 220 221 len = OF_getprop(pcinode, "interrupt-map", map, sizeof(map)); 222 if (len == -1) 223 goto nomap; 224 225 if (OF_getprop(pcinode, "#address-cells", &acells, 226 sizeof(acells)) == -1) 227 acells = 1; 228 if (OF_getprop(pcinode, "#interrupt-cells", &icells, 229 sizeof(icells)) == -1) 230 icells = 1; 231 232 parent = map[acells+icells]; 233 if (OF_getprop(parent, "#interrupt-cells", &pcells, 234 sizeof(pcells)) == -1) 235 pcells = 1; 236 237 reclen = acells+pcells+icells+1; 238 nrofpcidevs = len / (reclen * sizeof(int)); 239 240 dict = prop_dictionary_create_with_capacity(nrofpcidevs*2); 241 KASSERT(dict != NULL); 242 243 curdev = -1; 244 prop_dictionary_set(pbi->pbi_properties, "ofw-pci-intrmap", dict); 245 for (i = 0; i < nrofpcidevs; i++) { 246 prop_number_t intr_num; 247 int dev, pin, pic, func; 248 char key[20]; 249 250 pic = genofw_find_picnode(map[i*reclen + acells + icells]); 251 KASSERT(pic != -1); 252 dev = (map[i*reclen] >> 8) / 0x8; 253 func = (map[i*reclen] >> 8) % 0x8; 254 if (curdev != dev) 255 sub = prop_dictionary_create_with_capacity(4); 256 pin = map[i*reclen + acells]; 257 intr_num = prop_number_create_integer(map[i*reclen + acells + icells + 1] + picnodes[pic].offset); 258 snprintf(key, sizeof(key), "pin-%c", 'A' + (pin-1)); 259 prop_dictionary_set(sub, key, intr_num); 260 prop_object_release(intr_num); 261 /* should we care about level? */ 262 263 snprintf(key, sizeof(key), "devfunc-%d", dev*0x8 + func); 264 prop_dictionary_set(dict, key, sub); 265 if (curdev != dev) { 266 prop_object_release(sub); 267 curdev = dev; 268 } 269 } 270 /* the mapping is complete */ 271 prop_object_release(dict); 272 aprint_debug("%s\n", prop_dictionary_externalize(pbi->pbi_properties)); 273 return; 274 275 nomap: 276 /* so, we have one of those annoying machines that doesn't provide 277 * a nice simple map of interrupts. We get to do this the hard 278 * way instead. Lucky us. 279 */ 280 for (node = OF_child(pcinode), nrofpcidevs=0; node; 281 node = OF_peer(node)) 282 nrofpcidevs++; 283 dict = prop_dictionary_create_with_capacity(nrofpcidevs*2); 284 KASSERT(dict != NULL); 285 prop_dictionary_set(pbi->pbi_properties, "ofw-pci-intrmap", dict); 286 287 for (node = OF_child(pcinode); node; node = OF_peer(node)) { 288 uint32_t irqs[4], reg[5]; 289 prop_number_t intr_num; 290 int dev, pin, func; 291 char key[20]; 292 293 /* walk the bus looking for pci devices and map them */ 294 if (OF_getprop(node, "AAPL,interrupts", irqs, 4) > 0) { 295 dev = 0; 296 if (OF_getprop(node, "reg", reg, 5) > 0) { 297 dev = ((reg[0] & 0x0000ff00) >> 8) / 0x8; 298 func = ((reg[0] & 0x0000ff00) >> 8) % 0x8; 299 } else if (OF_getprop(node, "assigned-addresses", 300 reg, 5) > 0) { 301 dev = ((reg[0] & 0x0000ff00) >> 8) / 0x8; 302 func = ((reg[0] & 0x0000ff00) >> 8) % 0x8; 303 } 304 if (dev == 0) { 305 aprint_error("cannot figure out device num " 306 "for node 0x%x\n", node); 307 continue; 308 } 309 sub = prop_dictionary_create_with_capacity(4); 310 if (OF_getprop(node, "interrupts", &pin, 4) < 0) 311 pin = 1; 312 intr_num = prop_number_create_integer(irqs[0]); 313 snprintf(key, sizeof(key), "pin-%c", 'A' + (pin-1)); 314 prop_dictionary_set(sub, key, intr_num); 315 prop_object_release(intr_num); 316 snprintf(key, sizeof(key), "devfunc-%d", dev*0x8 + func); 317 prop_dictionary_set(dict, key, sub); 318 prop_object_release(sub); 319 foundirqs++; 320 } 321 } 322 if (foundirqs) 323 return; 324 325 /* 326 * If we got this far, we have a super-annoying OFW. 327 * They didn't bother to fill in any interrupt properties anywhere, 328 * so we pray that they filled in the ones on the pci devices. 329 */ 330 for (node = OF_child(pcinode); node; node = OF_peer(node)) { 331 uint32_t reg[5], irq; 332 prop_number_t intr_num; 333 pcitag_t tag; 334 int dev, pin, func; 335 char key[20]; 336 337 if (OF_getprop(node, "reg", reg, 5) > 0) { 338 dev = ((reg[0] & 0x0000ff00) >> 8) / 0x8; 339 func = ((reg[0] & 0x0000ff00) >> 8) % 0x8; 340 341 tag = pci_make_tag(pc, pc->pc_bus, dev, func); 342 irq = PCI_INTERRUPT_LINE(pci_conf_read(pc, tag, 343 PCI_INTERRUPT_REG)); 344 if (irq == 255) 345 irq = 0; 346 347 sub = prop_dictionary_create_with_capacity(4); 348 if (OF_getprop(node, "interrupts", &pin, 4) < 0) 349 pin = 1; 350 intr_num = prop_number_create_integer(irq); 351 snprintf(key, sizeof(key), "pin-%c", 'A' + (pin-1)); 352 prop_dictionary_set(sub, key, intr_num); 353 prop_object_release(intr_num); 354 snprintf(key, sizeof(key), "devfunc-%d", dev*0x8 + func); 355 prop_dictionary_set(dict, key, sub); 356 prop_object_release(sub); 357 } 358 } 359 aprint_debug("%s\n", prop_dictionary_externalize(pbi->pbi_properties)); 360 } 361 362 int 363 genofw_find_node_by_devfunc(int startnode, int bus, int dev, int func) 364 { 365 int node, sz, p=0; 366 uint32_t reg; 367 368 for (node = startnode; node; node = p) { 369 sz = OF_getprop(node, "reg", ®, sizeof(reg)); 370 if (sz != sizeof(reg)) 371 continue; 372 if (OFW_PCI_PHYS_HI_BUS(reg) == bus && 373 OFW_PCI_PHYS_HI_DEVICE(reg) == dev && 374 OFW_PCI_PHYS_HI_FUNCTION(reg) == func) 375 return node; 376 if ((p = OF_child(node))) 377 continue; 378 while (node) { 379 if ((p = OF_peer(node))) 380 break; 381 node = OF_parent(node); 382 } 383 } 384 /* couldn't find it */ 385 return -1; 386 } 387 388 int 389 genofw_pci_intr_map(const struct pci_attach_args *pa, pci_intr_handle_t *ihp) 390 { 391 struct genppc_pci_chipset_businfo *pbi; 392 prop_dictionary_t dict, devsub; 393 prop_object_t pinsub; 394 prop_number_t pbus; 395 int busno, pin, line, dev, origdev, func, i; 396 char key[20]; 397 398 pin = pa->pa_intrpin; 399 line = pa->pa_intrline; 400 busno = pa->pa_bus; 401 origdev = dev = pa->pa_device; 402 func = pa->pa_function; 403 i = 0; 404 405 pbi = SIMPLEQ_FIRST(&pa->pa_pc->pc_pbi); 406 while (busno--) 407 pbi = SIMPLEQ_NEXT(pbi, next); 408 KASSERT(pbi != NULL); 409 410 dict = prop_dictionary_get(pbi->pbi_properties, "ofw-pci-intrmap"); 411 412 if (dict != NULL) 413 i = prop_dictionary_count(dict); 414 415 if (dict == NULL || i == 0) { 416 /* We have an unmapped bus, now it gets hard */ 417 pbus = prop_dictionary_get(pbi->pbi_properties, 418 "ofw-pcibus-parent"); 419 if (pbus == NULL) 420 goto bad; 421 busno = prop_number_integer_value(pbus); 422 pbus = prop_dictionary_get(pbi->pbi_properties, 423 "ofw-pcibus-rawdevnum"); 424 dev = prop_number_integer_value(pbus); 425 426 /* now that we know the parent bus, we need to find its pbi */ 427 pbi = SIMPLEQ_FIRST(&pa->pa_pc->pc_pbi); 428 while (busno--) 429 pbi = SIMPLEQ_NEXT(pbi, next); 430 KASSERT(pbi != NULL); 431 432 /* swizzle the pin */ 433 pin = ((pin + origdev - 1) & 3) + 1; 434 435 /* now we have the pbi, ask for dict again */ 436 dict = prop_dictionary_get(pbi->pbi_properties, 437 "ofw-pci-intrmap"); 438 if (dict == NULL) 439 goto bad; 440 } 441 442 /* No IRQ used. */ 443 if (pin == 0) 444 goto bad; 445 if (pin > 4) { 446 aprint_error("pci_intr_map: bad interrupt pin %d\n", pin); 447 goto bad; 448 } 449 450 snprintf(key, sizeof(key), "devfunc-%d", dev*0x8 + func); 451 devsub = prop_dictionary_get(dict, key); 452 if (devsub == NULL) 453 goto bad; 454 snprintf(key, sizeof(key), "pin-%c", 'A' + (pin-1)); 455 pinsub = prop_dictionary_get(devsub, key); 456 if (pinsub == NULL) 457 goto bad; 458 line = prop_number_integer_value(pinsub); 459 460 if (line == 0 || line == 255) { 461 aprint_error("pci_intr_map: no mapping for pin %c\n",'@' + pin); 462 goto bad; 463 } 464 465 *ihp = line; 466 return 0; 467 468 bad: 469 *ihp = -1; 470 return 1; 471 } 472 473 int 474 genofw_pci_conf_hook(void *v, int bus, int dev, int func, pcireg_t id) 475 { 476 pci_chipset_tag_t pct = v; 477 struct genppc_pci_chipset_businfo *pbi; 478 prop_number_t pbus; 479 pcitag_t tag; 480 pcireg_t class; 481 int node; 482 483 /* We have already mapped MPIC's if we have them, so leave them alone */ 484 if (PCI_VENDOR(id) == PCI_VENDOR_IBM && 485 PCI_PRODUCT(id) == PCI_PRODUCT_IBM_MPIC2) 486 return 0; 487 488 if (PCI_VENDOR(id) == PCI_VENDOR_IBM && 489 PCI_PRODUCT(id) == PCI_PRODUCT_IBM_MPIC) 490 return 0; 491 492 /* I highly doubt there are any CHRP ravens, but just in case */ 493 if (PCI_VENDOR(id) == PCI_VENDOR_MOT && 494 PCI_PRODUCT(id) == PCI_PRODUCT_MOT_RAVEN) 495 return (PCI_CONF_ALL & ~PCI_CONF_MAP_MEM); 496 497 /* 498 * Pegasos2 specific stuff. 499 */ 500 if (strncmp(model_name, "Pegasos2", 8) == 0) { 501 502 /* never reconfigure the MV64361 host bridge */ 503 if (PCI_VENDOR(id) == PCI_VENDOR_MARVELL && 504 PCI_PRODUCT(id) == PCI_PRODUCT_MARVELL_MV64360) 505 return 0; 506 507 /* we want to leave viaide(4) alone */ 508 if (PCI_VENDOR(id) == PCI_VENDOR_VIATECH && 509 PCI_PRODUCT(id) == PCI_PRODUCT_VIATECH_VT82C586A_IDE) 510 return 0; 511 512 /* leave the audio IO alone */ 513 if (PCI_VENDOR(id) == PCI_VENDOR_VIATECH && 514 PCI_PRODUCT(id) == PCI_PRODUCT_VIATECH_VT82C686A_AC97) 515 return (PCI_CONF_ALL & ~PCI_CONF_MAP_IO); 516 517 } 518 519 tag = pci_make_tag(pct, bus, dev, func); 520 class = pci_conf_read(pct, tag, PCI_CLASS_REG); 521 522 /* leave video cards alone */ 523 if (PCI_CLASS(class) == PCI_CLASS_DISPLAY) 524 return 0; 525 526 /* NOTE, all device specific stuff must be above this line */ 527 /* don't do this on the primary host bridge */ 528 if (bus == 0 && dev == 0 && func == 0) 529 return PCI_CONF_DEFAULT; 530 531 /* 532 * PCI bridges have special needs. We need to discover where they 533 * came from, and wire them appropriately. 534 */ 535 if (PCI_CLASS(class) == PCI_CLASS_BRIDGE && 536 PCI_SUBCLASS(class) == PCI_SUBCLASS_BRIDGE_PCI) { 537 pbi = kmem_alloc(sizeof(*pbi), KM_SLEEP); 538 pbi->pbi_properties = prop_dictionary_create(); 539 KASSERT(pbi->pbi_properties != NULL); 540 node = genofw_find_node_by_devfunc(pct->pc_node, bus, dev, 541 func); 542 if (node == -1) { 543 aprint_error("Cannot find node for device " 544 "bus %d dev %d func %d\n", bus, dev, func); 545 prop_object_release(pbi->pbi_properties); 546 kmem_free(pbi, sizeof(*pbi)); 547 return (PCI_CONF_DEFAULT); 548 } 549 genofw_setup_pciintr_map((void *)pct, pbi, node); 550 551 /* record the parent bus, and the parent device number */ 552 pbus = prop_number_create_integer(bus); 553 prop_dictionary_set(pbi->pbi_properties, "ofw-pcibus-parent", 554 pbus); 555 prop_object_release(pbus); 556 pbus = prop_number_create_integer(dev); 557 prop_dictionary_set(pbi->pbi_properties, "ofw-pcibus-rawdevnum", 558 pbus); 559 prop_object_release(pbus); 560 561 SIMPLEQ_INSERT_TAIL(&pct->pc_pbi, pbi, next); 562 } 563 564 return (PCI_CONF_DEFAULT); 565 } 566