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