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