Annotation of sys/arch/macppc/pci/pci_addr_fixup.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: pci_addr_fixup.c,v 1.11 2006/07/14 05:46:54 deraadt Exp $ */
! 2: /* $NetBSD: pci_addr_fixup.c,v 1.7 2000/08/03 20:10:45 nathanw Exp $ */
! 3:
! 4: /*-
! 5: * Copyright (c) 2000 UCHIYAMA Yasushi. All rights reserved.
! 6: *
! 7: * Redistribution and use in source and binary forms, with or without
! 8: * modification, are permitted provided that the following conditions
! 9: * are met:
! 10: * 1. Redistributions of source code must retain the above copyright
! 11: * notice, this list of conditions and the following disclaimer.
! 12: * 2. Redistributions in binary form must reproduce the above copyright
! 13: * notice, this list of conditions and the following disclaimer in the
! 14: * documentation and/or other materials provided with the distribution.
! 15: * 3. The name of the author may not be used to endorse or promote products
! 16: * derived from this software without specific prior written permission.
! 17: *
! 18: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
! 19: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
! 20: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
! 21: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
! 22: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
! 23: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
! 24: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
! 25: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! 26: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
! 27: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 28: */
! 29:
! 30: #include <sys/param.h>
! 31: #include <sys/systm.h>
! 32: #include <sys/malloc.h>
! 33: #include <sys/kernel.h>
! 34: #include <sys/device.h>
! 35: #include <sys/extent.h>
! 36:
! 37: #include <uvm/uvm_param.h>
! 38: #include <machine/bus.h>
! 39:
! 40: #include <dev/pci/pcireg.h>
! 41: #include <dev/pci/pcivar.h>
! 42: #include <dev/pci/pcidevs.h>
! 43:
! 44: #include <macppc/pci/pcibrvar.h>
! 45:
! 46: typedef int (*pciaddr_resource_manage_func_t)(struct pcibr_softc *,
! 47: pci_chipset_tag_t, pcitag_t, int, struct extent *, int, bus_addr_t *,
! 48: bus_size_t);
! 49: void pciaddr_resource_manage(struct pcibr_softc *,
! 50: pci_chipset_tag_t, pcitag_t, pciaddr_resource_manage_func_t);
! 51: void pciaddr_resource_reserve(struct pcibr_softc *,
! 52: pci_chipset_tag_t, pcitag_t);
! 53: void pciaddr_resource_reserve_disabled(struct pcibr_softc *,
! 54: pci_chipset_tag_t, pcitag_t);
! 55: int pciaddr_do_resource_reserve(struct pcibr_softc *,
! 56: pci_chipset_tag_t, pcitag_t, int, struct extent *, int,
! 57: bus_addr_t *, bus_size_t);
! 58: int pciaddr_do_resource_reserve_disabled(struct pcibr_softc *,
! 59: pci_chipset_tag_t, pcitag_t, int, struct extent *, int,
! 60: bus_addr_t *, bus_size_t);
! 61: void pciaddr_resource_allocate(struct pcibr_softc *,
! 62: pci_chipset_tag_t, pcitag_t);
! 63: int pciaddr_do_resource_allocate(struct pcibr_softc *,
! 64: pci_chipset_tag_t, pcitag_t, int, struct extent *, int, bus_addr_t *,
! 65: bus_size_t);
! 66: bus_addr_t pciaddr_ioaddr(u_int32_t);
! 67: void pciaddr_print_devid(pci_chipset_tag_t, pcitag_t);
! 68:
! 69: int pciaddr_device_is_agp(pci_chipset_tag_t, pcitag_t);
! 70:
! 71: void pci_device_foreach(struct pcibr_softc *sc, pci_chipset_tag_t pc,
! 72: int maxbus,
! 73: void (*func)(struct pcibr_softc *, pci_chipset_tag_t, pcitag_t));
! 74:
! 75: #define PCIADDR_MEM_START 0x0
! 76: #define PCIADDR_MEM_END 0xffffffff
! 77: #define PCIADDR_PORT_START 0x0
! 78: #define PCIADDR_PORT_END 0xffff
! 79:
! 80: int pcibr_flags = 0;
! 81: #define PCIBR_VERBOSE 1
! 82: #define PCIBR_ADDR_FIXUP 2
! 83:
! 84: #define PCIBIOS_PRINTV(x) if (pcibr_flags & PCIBR_VERBOSE) \
! 85: printf x
! 86:
! 87: void
! 88: pci_addr_fixup(struct pcibr_softc *sc, pci_chipset_tag_t pc, int maxbus)
! 89: {
! 90: const char *verbose_header =
! 91: "[%s]-----------------------\n"
! 92: " device vendor product\n"
! 93: " register space address size\n"
! 94: "--------------------------------------------\n";
! 95: const char *verbose_footer =
! 96: "--------------------------[%3d devices bogus]\n";
! 97:
! 98: sc->extent_mem = extent_create("PCI I/O memory space",
! 99: sc->sc_membus_space.bus_base,
! 100: sc->sc_membus_space.bus_base + sc->sc_membus_space.bus_size,
! 101: M_DEVBUF, 0, 0, EX_NOWAIT);
! 102: KASSERT(sc->extent_mem);
! 103: sc->extent_port = extent_create("PCI I/O port space",
! 104: PCIADDR_PORT_START, PCIADDR_PORT_END,
! 105: M_DEVBUF, 0, 0, EX_NOWAIT);
! 106: KASSERT(sc->extent_port);
! 107:
! 108: /*
! 109: * 1. check & reserve system BIOS setting.
! 110: */
! 111: PCIBIOS_PRINTV((verbose_header, "System BIOS Setting"));
! 112: pci_device_foreach(sc, pc, maxbus, pciaddr_resource_reserve);
! 113: pci_device_foreach(sc, pc, maxbus, pciaddr_resource_reserve_disabled);
! 114: PCIBIOS_PRINTV((verbose_footer, sc->nbogus));
! 115:
! 116: {
! 117: struct extent_region *rp;
! 118: struct extent *ex = sc->extent_mem;
! 119: for (rp = LIST_FIRST(&ex->ex_regions);
! 120: rp; rp = LIST_NEXT(rp, er_link)) {
! 121: }
! 122: }
! 123: {
! 124: struct extent_region *rp;
! 125: struct extent *ex = sc->extent_port;
! 126: for (rp = LIST_FIRST(&ex->ex_regions);
! 127: rp; rp = LIST_NEXT(rp, er_link)) {
! 128: }
! 129: }
! 130:
! 131: /*
! 132: * 4. do fixup
! 133: */
! 134: PCIBIOS_PRINTV((verbose_header, "PCIBIOS fixup stage"));
! 135: sc->nbogus = 0;
! 136: pci_device_foreach(sc, pc, maxbus, pciaddr_resource_allocate);
! 137: PCIBIOS_PRINTV((verbose_footer, sc->nbogus));
! 138:
! 139: }
! 140:
! 141: void
! 142: pciaddr_resource_reserve(struct pcibr_softc *sc, pci_chipset_tag_t pc,
! 143: pcitag_t tag)
! 144: {
! 145: if (pcibr_flags & PCIBR_VERBOSE)
! 146: pciaddr_print_devid(pc, tag);
! 147: pciaddr_resource_manage(sc, pc, tag, pciaddr_do_resource_reserve);
! 148: }
! 149:
! 150: void
! 151: pciaddr_resource_reserve_disabled(struct pcibr_softc *sc,
! 152: pci_chipset_tag_t pc, pcitag_t tag)
! 153: {
! 154: if (pcibr_flags & PCIBR_VERBOSE)
! 155: pciaddr_print_devid(pc, tag);
! 156: pciaddr_resource_manage(sc, pc, tag,
! 157: pciaddr_do_resource_reserve_disabled);
! 158: }
! 159:
! 160: void
! 161: pciaddr_resource_allocate(struct pcibr_softc *sc, pci_chipset_tag_t pc,
! 162: pcitag_t tag)
! 163: {
! 164: if (pcibr_flags & PCIBR_VERBOSE)
! 165: pciaddr_print_devid(pc, tag);
! 166: pciaddr_resource_manage(sc, pc, tag, pciaddr_do_resource_allocate);
! 167: }
! 168:
! 169: void
! 170: pciaddr_resource_manage(struct pcibr_softc *sc, pci_chipset_tag_t pc,
! 171: pcitag_t tag, pciaddr_resource_manage_func_t func)
! 172: {
! 173: struct extent *ex;
! 174: pcireg_t val, mask;
! 175: bus_addr_t addr;
! 176: bus_size_t size;
! 177: int error, mapreg, type, reg_start, reg_end, width;
! 178:
! 179: val = pci_conf_read(pc, tag, PCI_BHLC_REG);
! 180: switch (PCI_HDRTYPE_TYPE(val)) {
! 181: default:
! 182: printf("WARNING: unknown PCI device header.\n");
! 183: sc->nbogus++;
! 184: return;
! 185: case 0:
! 186: reg_start = PCI_MAPREG_START;
! 187: reg_end = PCI_MAPREG_END;
! 188: break;
! 189: case 1: /* PCI-PCI bridge */
! 190: reg_start = PCI_MAPREG_START;
! 191: reg_end = PCI_MAPREG_PPB_END;
! 192: break;
! 193: case 2: /* PCI-CardBus bridge */
! 194: reg_start = PCI_MAPREG_START;
! 195: reg_end = PCI_MAPREG_PCB_END;
! 196: break;
! 197: }
! 198: error = 0;
! 199:
! 200: for (mapreg = reg_start; mapreg < reg_end; mapreg += width) {
! 201: /* inquire PCI device bus space requirement */
! 202: val = pci_conf_read(pc, tag, mapreg);
! 203: pci_conf_write(pc, tag, mapreg, ~0);
! 204:
! 205: mask = pci_conf_read(pc, tag, mapreg);
! 206: pci_conf_write(pc, tag, mapreg, val);
! 207:
! 208: type = PCI_MAPREG_TYPE(val);
! 209: width = 4;
! 210: if (type == PCI_MAPREG_TYPE_MEM) {
! 211: if (PCI_MAPREG_MEM_TYPE(val) ==
! 212: PCI_MAPREG_MEM_TYPE_64BIT) {
! 213: /* XXX We could examine the upper 32 bits
! 214: * XXX of the BAR here, but we are totally
! 215: * XXX unprepared to handle a non-zero value,
! 216: * XXX either here or anywhere else in
! 217: * XXX i386-land.
! 218: * XXX So just arrange to not look at the
! 219: * XXX upper 32 bits, lest we misinterpret
! 220: * XXX it as a 32-bit BAR set to zero.
! 221: */
! 222: width = 8;
! 223: }
! 224: addr = PCI_MAPREG_MEM_ADDR(val);
! 225: size = PCI_MAPREG_MEM_SIZE(mask);
! 226: ex = sc->extent_mem;
! 227: } else {
! 228: /* XXX some devices give 32bit value */
! 229: addr = PCI_MAPREG_IO_ADDR(val) & PCIADDR_PORT_END;
! 230: size = PCI_MAPREG_IO_SIZE(mask);
! 231: ex = sc->extent_port;
! 232: }
! 233:
! 234: if (!size) /* unused register */
! 235: continue;
! 236:
! 237: /* reservation/allocation phase */
! 238: error += (*func) (sc, pc, tag, mapreg, ex, type, &addr, size);
! 239:
! 240: PCIBIOS_PRINTV(("\t%02xh %s 0x%08x 0x%08x\n",
! 241: mapreg, type ? "port" : "mem ",
! 242: (unsigned int)addr, (unsigned int)size));
! 243: }
! 244:
! 245: if (error)
! 246: sc->nbogus++;
! 247:
! 248: PCIBIOS_PRINTV(("\t\t[%s]\n", error ? "NG" : "OK"));
! 249: }
! 250:
! 251: int
! 252: pciaddr_do_resource_allocate(struct pcibr_softc *sc, pci_chipset_tag_t pc,
! 253: pcitag_t tag, int mapreg, struct extent *ex, int type, bus_addr_t *addr,
! 254: bus_size_t size)
! 255: {
! 256: bus_addr_t start;
! 257: int error;
! 258:
! 259: if (*addr) /* no need to allocate */
! 260: return (0);
! 261:
! 262: /* XXX Don't allocate if device is AGP device to avoid conflict. */
! 263: if (pciaddr_device_is_agp(pc, tag))
! 264: return (0);
! 265:
! 266: start = (type == PCI_MAPREG_TYPE_MEM ? sc->sc_membus_space.bus_base
! 267: : PCIADDR_PORT_START);
! 268: if (start < ex->ex_start || start + size - 1 >= ex->ex_end) {
! 269: PCIBIOS_PRINTV(("No available resources. fixup failed\n"));
! 270: return (1);
! 271: }
! 272: error = extent_alloc_subregion(ex, start, ex->ex_end, size, size, 0, 0,
! 273: EX_FAST|EX_NOWAIT|EX_MALLOCOK, addr);
! 274: if (error) {
! 275: PCIBIOS_PRINTV(("No available resources. fixup failed\n"));
! 276: return (1);
! 277: }
! 278:
! 279: /* write new address to PCI device configuration header */
! 280: pci_conf_write(pc, tag, mapreg, *addr);
! 281: /* check */
! 282: if (pcibr_flags & PCIBR_VERBOSE) {
! 283: printf("pci_addr_fixup: ");
! 284: pciaddr_print_devid(pc, tag);
! 285: }
! 286:
! 287: if (pciaddr_ioaddr(pci_conf_read(pc, tag, mapreg)) != *addr) {
! 288: pci_conf_write(pc, tag, mapreg, 0); /* clear */
! 289: printf("fixup failed. (new address=%#lx)\n", *addr);
! 290: return (1);
! 291: }
! 292: if (pcibr_flags & PCIBR_VERBOSE)
! 293: printf("new address 0x%08lx\n", *addr);
! 294:
! 295: return (0);
! 296: }
! 297:
! 298: int
! 299: pciaddr_do_resource_reserve(struct pcibr_softc *sc, pci_chipset_tag_t pc,
! 300: pcitag_t tag, int mapreg, struct extent *ex, int type, bus_addr_t *addr,
! 301: bus_size_t size)
! 302: {
! 303: pcireg_t val;
! 304: int error;
! 305:
! 306: if (*addr == 0)
! 307: return (0);
! 308:
! 309: val = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
! 310: if (type == PCI_MAPREG_TYPE_MEM &&
! 311: (val & PCI_COMMAND_MEM_ENABLE) != PCI_COMMAND_MEM_ENABLE)
! 312: return (0);
! 313: if (type == PCI_MAPREG_TYPE_IO &&
! 314: (val & PCI_COMMAND_IO_ENABLE) != PCI_COMMAND_IO_ENABLE)
! 315: return (0);
! 316:
! 317: error = extent_alloc_region(ex, *addr, size, EX_NOWAIT | EX_MALLOCOK);
! 318: if (error) {
! 319: PCIBIOS_PRINTV(("Resource conflict.\n"));
! 320: pci_conf_write(pc, tag, mapreg, 0); /* clear */
! 321: return (1);
! 322: }
! 323:
! 324: return (0);
! 325: }
! 326:
! 327: int
! 328: pciaddr_do_resource_reserve_disabled(struct pcibr_softc *sc,
! 329: pci_chipset_tag_t pc, pcitag_t tag, int mapreg, struct extent *ex,
! 330: int type, bus_addr_t *addr, bus_size_t size)
! 331: {
! 332: pcireg_t val;
! 333: int error;
! 334:
! 335: if (*addr == 0)
! 336: return (0);
! 337:
! 338: val = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
! 339: if (type == PCI_MAPREG_TYPE_MEM &&
! 340: (val & PCI_COMMAND_MEM_ENABLE) == PCI_COMMAND_MEM_ENABLE)
! 341: return (0);
! 342: if (type == PCI_MAPREG_TYPE_IO &&
! 343: (val & PCI_COMMAND_IO_ENABLE) == PCI_COMMAND_IO_ENABLE)
! 344: return (0);
! 345:
! 346: error = extent_alloc_region(ex, *addr, size, EX_NOWAIT | EX_MALLOCOK);
! 347: if (error) {
! 348: PCIBIOS_PRINTV(("Resource conflict.\n"));
! 349: pci_conf_write(pc, tag, mapreg, 0); /* clear */
! 350: return (1);
! 351: }
! 352:
! 353: return (0);
! 354: }
! 355:
! 356: bus_addr_t
! 357: pciaddr_ioaddr(u_int32_t val)
! 358: {
! 359: return ((PCI_MAPREG_TYPE(val) == PCI_MAPREG_TYPE_MEM)
! 360: ? PCI_MAPREG_MEM_ADDR(val)
! 361: : (PCI_MAPREG_IO_ADDR(val) & PCIADDR_PORT_END));
! 362: }
! 363:
! 364: void
! 365: pciaddr_print_devid(pci_chipset_tag_t pc, pcitag_t tag)
! 366: {
! 367: int bus, device, function;
! 368: pcireg_t id;
! 369:
! 370: id = pci_conf_read(pc, tag, PCI_ID_REG);
! 371: pci_decompose_tag(pc, tag, &bus, &device, &function);
! 372: printf("%03d:%02d:%d %04x:%04x\n", bus, device, function,
! 373: PCI_VENDOR(id), PCI_PRODUCT(id));
! 374: }
! 375:
! 376: int
! 377: pciaddr_device_is_agp(pci_chipset_tag_t pc, pcitag_t tag)
! 378: {
! 379: pcireg_t class, status, rval;
! 380: int off;
! 381:
! 382: /* Check AGP device. */
! 383: class = pci_conf_read(pc, tag, PCI_CLASS_REG);
! 384: if (PCI_CLASS(class) == PCI_CLASS_DISPLAY) {
! 385: status = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
! 386: if (status & PCI_STATUS_CAPLIST_SUPPORT) {
! 387: rval = pci_conf_read(pc, tag, PCI_CAPLISTPTR_REG);
! 388: for (off = PCI_CAPLIST_PTR(rval);
! 389: off != 0;
! 390: off = PCI_CAPLIST_NEXT(rval) ) {
! 391: rval = pci_conf_read(pc, tag, off);
! 392: if (PCI_CAPLIST_CAP(rval) == PCI_CAP_AGP)
! 393: return (1);
! 394: }
! 395: }
! 396: }
! 397: return (0);
! 398: }
! 399:
! 400:
! 401: struct extent *
! 402: pciaddr_search(int mem_port, struct device *parent, bus_addr_t *startp,
! 403: bus_size_t size)
! 404: {
! 405: struct pcibr_softc *sc;
! 406:
! 407: /* find the bridge, 'mpcpcibr' */
! 408:
! 409: sc = NULL;
! 410: while (parent != NULL) {
! 411: if (strncmp("mpcpcibr", parent->dv_xname, 8) == 0) {
! 412: sc = (void *)parent;
! 413: break;
! 414: }
! 415: parent = parent->dv_parent;
! 416: }
! 417:
! 418: if (sc && !(pcibr_flags & PCIBR_ADDR_FIXUP)) {
! 419: struct extent_region *rp;
! 420: struct extent *ex = mem_port? sc->extent_mem : sc->extent_port;
! 421:
! 422: /* Search the PCI I/O memory space extent for free
! 423: * space that will accommodate size. Remember that the
! 424: * extent stores allocated space and we're searching
! 425: * for the gaps.
! 426: *
! 427: * If we're at the end or the gap between this region
! 428: * and the next region big enough, then we're done
! 429: */
! 430: *startp = ex->ex_start;
! 431: rp = LIST_FIRST(&ex->ex_regions);
! 432:
! 433: for (rp = LIST_FIRST(&ex->ex_regions);
! 434: rp && *startp + size > rp->er_start;
! 435: rp = LIST_NEXT(rp, er_link)) {
! 436: bus_addr_t new_start;
! 437:
! 438: new_start = (rp->er_end - 1 + size) & ~(size - 1);
! 439: if (new_start > *startp)
! 440: *startp = new_start;
! 441: }
! 442:
! 443: return (ex);
! 444: }
! 445:
! 446: return (NULL);
! 447: }
! 448:
! 449:
! 450: void
! 451: pci_device_foreach(struct pcibr_softc *sc, pci_chipset_tag_t pc, int maxbus,
! 452: void (*func)(struct pcibr_softc *, pci_chipset_tag_t, pcitag_t))
! 453: {
! 454: const struct pci_quirkdata *qd;
! 455: int bus, device, function, maxdevs, nfuncs;
! 456: pcireg_t id, bhlcr;
! 457: pcitag_t tag;
! 458:
! 459: for (bus = 0; bus <= maxbus; bus++) {
! 460: maxdevs = pci_bus_maxdevs(pc, bus);
! 461: for (device = 0; device < maxdevs; device++) {
! 462: tag = pci_make_tag(pc, bus, device, 0);
! 463: id = pci_conf_read(pc, tag, PCI_ID_REG);
! 464:
! 465: /* Invalid vendor ID value? */
! 466: if (PCI_VENDOR(id) == PCI_VENDOR_INVALID)
! 467: continue;
! 468: /* XXX Not invalid, but we've done this ~forever. */
! 469: if (PCI_VENDOR(id) == 0)
! 470: continue;
! 471:
! 472: qd = pci_lookup_quirkdata(PCI_VENDOR(id),
! 473: PCI_PRODUCT(id));
! 474:
! 475: bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG);
! 476: if (PCI_HDRTYPE_MULTIFN(bhlcr) ||
! 477: (qd != NULL &&
! 478: (qd->quirks & PCI_QUIRK_MULTIFUNCTION) != 0))
! 479: nfuncs = 8;
! 480: else
! 481: nfuncs = 1;
! 482:
! 483: for (function = 0; function < nfuncs; function++) {
! 484: tag = pci_make_tag(pc, bus, device, function);
! 485: id = pci_conf_read(pc, tag, PCI_ID_REG);
! 486:
! 487: /* Invalid vendor ID value? */
! 488: if (PCI_VENDOR(id) == PCI_VENDOR_INVALID)
! 489: continue;
! 490: /*
! 491: * XXX Not invalid, but we've done this
! 492: * ~forever.
! 493: */
! 494: if (PCI_VENDOR(id) == 0)
! 495: continue;
! 496: (*func)(sc, pc, tag);
! 497: }
! 498: }
! 499: }
! 500: }
CVSweb