1 /* $NetBSD: pci_machdep_ofw.c,v 1.6 2007/11/05 15:49:03 garbled 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 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * Generic OFW routines for pci_machdep 41 */ 42 43 #include <sys/cdefs.h> 44 __KERNEL_RCSID(0, "$NetBSD: pci_machdep_ofw.c,v 1.6 2007/11/05 15:49:03 garbled Exp $"); 45 46 #include <sys/types.h> 47 #include <sys/param.h> 48 #include <sys/time.h> 49 #include <sys/systm.h> 50 #include <sys/errno.h> 51 #include <sys/device.h> 52 #include <sys/malloc.h> 53 54 #include <uvm/uvm_extern.h> 55 56 #include <machine/bus.h> 57 58 #include <machine/autoconf.h> 59 #include <machine/pio.h> 60 #include <machine/intr.h> 61 62 #include <dev/pci/pcivar.h> 63 #include <dev/pci/pcireg.h> 64 #include <dev/pci/ppbreg.h> 65 #include <dev/pci/pcidevs.h> 66 #include <dev/pci/pciconf.h> 67 68 #include <dev/ofw/openfirm.h> 69 #include <dev/ofw/ofw_pci.h> 70 71 pcitag_t genppc_pci_indirect_make_tag(void *, int, int, int); 72 void genppc_pci_indirect_decompose_tag(void *, pcitag_t, int *, int *, int *); 73 74 ofw_pic_node_t picnodes[8]; 75 int nrofpics = 0; 76 77 int 78 genofw_find_picnode(int node) 79 { 80 int i; 81 82 for (i = 0; i < 8; i++) 83 if (node == picnodes[i].node) 84 return i; 85 return -1; 86 } 87 88 void 89 genofw_find_ofpics(int startnode) 90 { 91 int node, iparent, child, iranges[2]; 92 char name[32]; 93 94 for (node = startnode; node; node = OF_peer(node)) { 95 if ((child = OF_child(node)) != 0) 96 genofw_find_ofpics(child); 97 memset(name, 0, sizeof(name)); 98 if (OF_getprop(node, "name", name, sizeof(name)) == -1) 99 continue; 100 if (strncmp(name, "interrupt-controller", 20) == 0) 101 goto foundic; 102 103 if (OF_getprop(node, "interrupt-controller", name, 104 sizeof(name)) > -1) 105 goto foundic; 106 107 if (OF_getprop(node, "device_type", name, sizeof(name)) == -1) 108 continue; 109 if (strncmp(name, "interrupt-controller", 20) == 0) 110 goto foundic; 111 112 /* if we didn't find one, skip to the next */ 113 continue; 114 foundic: 115 picnodes[nrofpics].node = node; 116 if (OF_getprop(node, "interrupt-parent", &iparent, 117 sizeof(iparent)) == sizeof(iparent)) 118 picnodes[nrofpics].parent = iparent; 119 if (OF_getprop(node, "#interrupt-cells", &iparent, 120 sizeof(iparent)) == sizeof(iparent)) 121 picnodes[nrofpics].cells = iparent; 122 else 123 picnodes[nrofpics].cells = 1; 124 if (OF_getprop(node, "interrupt-ranges", iranges, 125 sizeof(int)*2) == sizeof(int)*2) 126 picnodes[nrofpics].intrs = iranges[1]; 127 else 128 picnodes[nrofpics].intrs = 16; 129 if (nrofpics > 0) 130 picnodes[nrofpics].offset = picnodes[nrofpics-1].offset 131 + picnodes[nrofpics-1].intrs; 132 else 133 picnodes[nrofpics].offset = 0; 134 OF_getprop(node, "device_type", name, sizeof(name)); 135 if (strcmp(name, "open-pic") == 0) 136 picnodes[nrofpics].type = PICNODE_TYPE_OPENPIC; 137 if (strcmp(name, "interrupt-controller") == 0) { 138 OF_getprop(node, "compatible", name, sizeof(name)); 139 if (strcmp(name, "heathrow") != 0) 140 picnodes[nrofpics].type = PICNODE_TYPE_HEATHROW; 141 if (strcmp(name, "chrp,iic") != 0) 142 picnodes[nrofpics].type = PICNODE_TYPE_8259; 143 } 144 if (strlen(name) == 0) { 145 /* probably a Pegasos, assume 8259 */ 146 picnodes[nrofpics].type = PICNODE_TYPE_8259; 147 } 148 nrofpics++; 149 } 150 } 151 152 /* Fix up the various picnode offsets */ 153 void 154 genofw_fixup_picnode_offsets(void) 155 { 156 int i, curoff; 157 158 curoff=0; 159 160 for (i=0; i < nrofpics; i++) { 161 if (picnodes[i].type == PICNODE_TYPE_8259) { 162 picnodes[i].offset = 0; 163 curoff = picnodes[i].intrs; 164 } 165 } 166 for (i=0; i < nrofpics; i++) { 167 /* now skip the 8259 */ 168 if (picnodes[i].type == PICNODE_TYPE_8259) 169 continue; 170 picnodes[i].offset = curoff; 171 curoff += picnodes[i].intrs; 172 } 173 } 174 175 /* we are given a pci devnode, and dig from there */ 176 void 177 genofw_setup_pciintr_map(void *v, struct genppc_pci_chipset_businfo *pbi, 178 int pcinode) 179 { 180 int node; 181 u_int32_t map[160]; 182 int parent, len; 183 int curdev, foundirqs=0; 184 int i, reclen, nrofpcidevs=0; 185 u_int32_t acells, icells, pcells; 186 prop_dictionary_t dict; 187 prop_dictionary_t sub=0; 188 pci_chipset_tag_t pc = (pci_chipset_tag_t)v; 189 190 len = OF_getprop(pcinode, "interrupt-map", map, sizeof(map)); 191 if (len == -1) 192 goto nomap; 193 194 if (OF_getprop(pcinode, "#address-cells", &acells, 195 sizeof(acells)) == -1) 196 acells = 1; 197 if (OF_getprop(pcinode, "#interrupt-cells", &icells, 198 sizeof(icells)) == -1) 199 icells = 1; 200 201 parent = map[acells+icells+1]; 202 if (OF_getprop(parent, "#interrupt-cells", &pcells, 203 sizeof(pcells)) == -1) 204 pcells = 1; 205 206 reclen = acells+pcells+icells+1; 207 nrofpcidevs = len / (reclen * sizeof(int)); 208 209 dict = prop_dictionary_create_with_capacity(nrofpcidevs*2); 210 KASSERT(dict != NULL); 211 212 curdev = -1; 213 prop_dictionary_set(pbi->pbi_properties, "ofw-pci-intrmap", dict); 214 for (i = 0; i < nrofpcidevs; i++) { 215 prop_number_t intr_num; 216 int dev, pin, pic; 217 char key[20]; 218 219 pic = genofw_find_picnode(map[i*reclen + acells + icells]); 220 KASSERT(pic != -1); 221 dev = (map[i*reclen] >> 8) / 0x8; 222 if (curdev != dev) 223 sub = prop_dictionary_create_with_capacity(4); 224 pin = map[i*reclen + acells]; 225 intr_num = prop_number_create_integer(map[i*reclen + acells + icells + 1] + picnodes[pic].offset); 226 sprintf(key, "pin-%c", 'A' + pin); 227 prop_dictionary_set(sub, key, intr_num); 228 prop_object_release(intr_num); 229 /* should we care about level? */ 230 231 sprintf(key, "devfunc-%d", dev); 232 prop_dictionary_set(dict, key, sub); 233 if (curdev != dev) { 234 prop_object_release(sub); 235 curdev = dev; 236 } 237 } 238 /* the mapping is complete */ 239 prop_object_release(dict); 240 return; 241 242 nomap: 243 /* so, we have one of those annoying machines that doesn't provide 244 * a nice simple map of interrupts. We get to do this the hard 245 * way instead. Lucky us. 246 */ 247 for (node = OF_child(pcinode), nrofpcidevs=0; node; 248 node = OF_peer(node)) 249 nrofpcidevs++; 250 dict = prop_dictionary_create_with_capacity(nrofpcidevs*2); 251 KASSERT(dict != NULL); 252 prop_dictionary_set(pbi->pbi_properties, "ofw-pci-intrmap", dict); 253 254 for (node = OF_child(pcinode); node; node = OF_peer(node)) { 255 uint32_t irqs[4], reg[5]; 256 prop_number_t intr_num; 257 int dev, pin; 258 char key[20]; 259 260 /* walk the bus looking for pci devices and map them */ 261 if (OF_getprop(node, "AAPL,interrupts", irqs, 4) > 0) { 262 dev = 0; 263 if (OF_getprop(node, "reg", reg, 5) > 0) 264 dev = ((reg[0] & 0x0000ff00) >> 8) / 0x8; 265 else if (OF_getprop(node, "assigned-addresses", 266 reg, 5) > 0) 267 dev = ((reg[0] & 0x0000ff00) >> 8) / 0x8; 268 if (dev == 0) { 269 aprint_error("cannot figure out device num " 270 "for node 0x%x\n", node); 271 continue; 272 } 273 sub = prop_dictionary_create_with_capacity(4); 274 if (OF_getprop(node, "interrupts", &pin, 4) < 0) 275 pin = 1; 276 intr_num = prop_number_create_integer(irqs[0]); 277 sprintf(key, "pin-%c", 'A' + pin); 278 prop_dictionary_set(sub, key, intr_num); 279 prop_object_release(intr_num); 280 sprintf(key, "devfunc-%d", dev); 281 prop_dictionary_set(dict, key, sub); 282 prop_object_release(sub); 283 foundirqs++; 284 } 285 } 286 if (foundirqs) 287 return; 288 289 /* 290 * If we got this far, we have a super-annoying OFW. 291 * They didn't bother to fill in any interrupt properties anywhere, 292 * so we pray that they filled in the ones on the pci devices. 293 */ 294 for (node = OF_child(pcinode); node; node = OF_peer(node)) { 295 uint32_t reg[5], irq; 296 prop_number_t intr_num; 297 pcitag_t tag; 298 int dev, pin, func; 299 char key[20]; 300 301 if (OF_getprop(node, "reg", reg, 5) > 0) { 302 dev = ((reg[0] & 0x0000ff00) >> 8) / 0x8; 303 func = ((reg[0] & 0x0000ff00) >> 8) % 0x8; 304 305 tag = pci_make_tag(pc, pc->pc_bus, dev, func); 306 irq = PCI_INTERRUPT_LINE(pci_conf_read(pc, tag, 307 PCI_INTERRUPT_REG)); 308 if (irq == 255) 309 irq = 0; 310 311 sub = prop_dictionary_create_with_capacity(4); 312 if (OF_getprop(node, "interrupts", &pin, 4) < 0) 313 pin = 1; 314 intr_num = prop_number_create_integer(irq); 315 sprintf(key, "pin-%c", 'A' + (pin-1)); 316 prop_dictionary_set(sub, key, intr_num); 317 prop_object_release(intr_num); 318 sprintf(key, "devfunc-%d", dev*0x8 + func); 319 prop_dictionary_set(dict, key, sub); 320 prop_object_release(sub); 321 } 322 } 323 aprint_debug("%s\n", prop_dictionary_externalize(pbi->pbi_properties)); 324 } 325 326 int 327 genofw_find_node_by_devfunc(int startnode, int bus, int dev, int func) 328 { 329 int node, sz, p=0; 330 uint32_t reg; 331 332 for (node = startnode; node; node = p) { 333 sz = OF_getprop(node, "reg", ®, sizeof(reg)); 334 if (sz != sizeof(reg)) 335 continue; 336 if (OFW_PCI_PHYS_HI_BUS(reg) == bus && 337 OFW_PCI_PHYS_HI_DEVICE(reg) == dev && 338 OFW_PCI_PHYS_HI_FUNCTION(reg) == func) 339 return node; 340 if ((p = OF_child(node))) 341 continue; 342 while (node) { 343 if ((p = OF_peer(node))) 344 break; 345 node = OF_parent(node); 346 } 347 } 348 /* couldn't find it */ 349 return -1; 350 } 351 352 int 353 genofw_pci_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp) 354 { 355 struct genppc_pci_chipset_businfo *pbi; 356 prop_dictionary_t dict, devsub; 357 prop_object_t pinsub; 358 prop_number_t pbus; 359 int busno, bus, pin, line, swiz, dev, origdev, func, i; 360 char key[20]; 361 362 pin = pa->pa_intrpin; 363 line = pa->pa_intrline; 364 bus = busno = pa->pa_bus; 365 swiz = pa->pa_intrswiz; 366 origdev = dev = pa->pa_device; 367 func = pa->pa_function; 368 i = 0; 369 370 pbi = SIMPLEQ_FIRST(&pa->pa_pc->pc_pbi); 371 while (busno--) 372 pbi = SIMPLEQ_NEXT(pbi, next); 373 KASSERT(pbi != NULL); 374 375 dict = prop_dictionary_get(pbi->pbi_properties, "ofw-pci-intrmap"); 376 377 if (dict != NULL) 378 i = prop_dictionary_count(dict); 379 380 if (dict == NULL || i == 0) { 381 /* We have an unmapped bus, now it gets hard */ 382 pbus = prop_dictionary_get(pbi->pbi_properties, 383 "ofw-pcibus-parent"); 384 if (pbus == NULL) 385 goto bad; 386 busno = prop_number_integer_value(pbus); 387 pbus = prop_dictionary_get(pbi->pbi_properties, 388 "ofw-pcibus-rawdevnum"); 389 dev = prop_number_integer_value(pbus); 390 391 /* now that we know the parent bus, we need to find it's pbi */ 392 pbi = SIMPLEQ_FIRST(&pa->pa_pc->pc_pbi); 393 while (busno--) 394 pbi = SIMPLEQ_NEXT(pbi, next); 395 KASSERT(pbi != NULL); 396 397 /* swizzle the pin */ 398 pin = ((pin + origdev - 1) & 3) + 1; 399 400 /* now we have the pbi, ask for dict again */ 401 dict = prop_dictionary_get(pbi->pbi_properties, 402 "ofw-pci-intrmap"); 403 if (dict == NULL) 404 goto bad; 405 } 406 407 /* No IRQ used. */ 408 if (pin == 0) 409 goto bad; 410 if (pin > 4) { 411 aprint_error("pci_intr_map: bad interrupt pin %d\n", pin); 412 goto bad; 413 } 414 415 sprintf(key, "devfunc-%d", dev*0x8 + func); 416 devsub = prop_dictionary_get(dict, key); 417 if (devsub == NULL) 418 goto bad; 419 sprintf(key, "pin-%c", 'A' + (pin-1)); 420 pinsub = prop_dictionary_get(devsub, key); 421 if (pinsub == NULL) 422 goto bad; 423 line = prop_number_integer_value(pinsub); 424 425 if (line == 0 || line == 255) { 426 aprint_error("pci_intr_map: no mapping for pin %c\n",'@' + pin); 427 goto bad; 428 } 429 430 *ihp = line; 431 return 0; 432 433 bad: 434 *ihp = -1; 435 return 1; 436 } 437 438 int 439 genofw_pci_conf_hook(pci_chipset_tag_t pct, int bus, int dev, int func, 440 pcireg_t id) 441 { 442 struct genppc_pci_chipset_businfo *pbi; 443 prop_number_t pbus; 444 pcitag_t tag; 445 pcireg_t class; 446 int node; 447 448 /* We have already mapped MPIC's if we have them, so leave them alone */ 449 if (PCI_VENDOR(id) == PCI_VENDOR_IBM && 450 PCI_PRODUCT(id) == PCI_PRODUCT_IBM_MPIC2) 451 return 0; 452 453 if (PCI_VENDOR(id) == PCI_VENDOR_IBM && 454 PCI_PRODUCT(id) == PCI_PRODUCT_IBM_MPIC) 455 return 0; 456 457 /* I highly doubt there are any CHRP ravens, but just in case */ 458 if (PCI_VENDOR(id) == PCI_VENDOR_MOT && 459 PCI_PRODUCT(id) == PCI_PRODUCT_MOT_RAVEN) 460 return (PCI_CONF_ALL & ~PCI_CONF_MAP_MEM); 461 462 /* NOTE, all device specific stuff must be above this line */ 463 /* don't do this on the primary host bridge */ 464 if (bus == 0 && dev == 0 && func == 0) 465 return PCI_CONF_DEFAULT; 466 467 tag = genppc_pci_indirect_make_tag(pct, bus, dev, func); 468 class = genppc_pci_indirect_conf_read(pct, tag, PCI_CLASS_REG); 469 470 /* 471 * PCI bridges have special needs. We need to discover where they 472 * came from, and wire them appropriately. 473 */ 474 if (PCI_CLASS(class) == PCI_CLASS_BRIDGE && 475 PCI_SUBCLASS(class) == PCI_SUBCLASS_BRIDGE_PCI) { 476 pbi = malloc(sizeof(struct genppc_pci_chipset_businfo), 477 M_DEVBUF, M_NOWAIT); 478 KASSERT(pbi != NULL); 479 pbi->pbi_properties = prop_dictionary_create(); 480 KASSERT(pbi->pbi_properties != NULL); 481 node = genofw_find_node_by_devfunc(pct->pc_node, bus, dev, 482 func); 483 if (node == -1) { 484 aprint_error("Cannot find node for device " 485 "bus %d dev %d func %d\n", bus, dev, func); 486 prop_object_release(pbi->pbi_properties); 487 free(pbi, M_DEVBUF); 488 return (PCI_CONF_DEFAULT); 489 } 490 genofw_setup_pciintr_map((void *)pct, pbi, node); 491 492 /* record the parent bus, and the parent device number */ 493 pbus = prop_number_create_integer(bus); 494 prop_dictionary_set(pbi->pbi_properties, "ofw-pcibus-parent", 495 pbus); 496 prop_object_release(pbus); 497 pbus = prop_number_create_integer(dev); 498 prop_dictionary_set(pbi->pbi_properties, "ofw-pcibus-rawdevnum", 499 pbus); 500 prop_object_release(pbus); 501 502 SIMPLEQ_INSERT_TAIL(&pct->pc_pbi, pbi, next); 503 } 504 505 return (PCI_CONF_DEFAULT); 506 } 507