Annotation of sys/arch/i386/pci/pci_addr_fixup.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: pci_addr_fixup.c,v 1.21 2007/02/20 21:15:01 tom 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_extern.h>
! 38:
! 39: #include <machine/bus.h>
! 40:
! 41: #include <dev/pci/pcireg.h>
! 42: #include <dev/pci/pcivar.h>
! 43: #include <dev/pci/pcidevs.h>
! 44:
! 45: #include <i386/pci/pcibiosvar.h>
! 46:
! 47: typedef int (*pciaddr_resource_manage_func_t)(struct pcibios_softc *, pci_chipset_tag_t, pcitag_t, int,
! 48: struct extent *, int, bus_addr_t *, bus_size_t);
! 49: void pciaddr_resource_manage(struct pcibios_softc *,
! 50: pci_chipset_tag_t, pcitag_t, pciaddr_resource_manage_func_t);
! 51: void pciaddr_resource_reserve(struct pcibios_softc *,
! 52: pci_chipset_tag_t, pcitag_t);
! 53: void pciaddr_resource_reserve_disabled(struct pcibios_softc *,
! 54: pci_chipset_tag_t, pcitag_t);
! 55: int pciaddr_do_resource_reserve(struct pcibios_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 pcibios_softc *,
! 59: pci_chipset_tag_t, pcitag_t, int, struct extent *, int, u_long *,
! 60: bus_size_t);
! 61: void pciaddr_resource_allocate(struct pcibios_softc *,
! 62: pci_chipset_tag_t, pcitag_t);
! 63: int pciaddr_do_resource_allocate(struct pcibios_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: #define PCIADDR_MEM_START 0x0
! 72: #define PCIADDR_MEM_END 0xffffffff
! 73: #define PCIADDR_PORT_START 0x0
! 74: #define PCIADDR_PORT_END 0xffff
! 75:
! 76: /* for ISA devices */
! 77: #define PCIADDR_ISAPORT_RESERVE 0x5800 /* empirical value */
! 78: #define PCIADDR_ISAMEM_RESERVE (16 * 1024 * 1024)
! 79:
! 80: void
! 81: pci_addr_fixup(struct pcibios_softc *sc, pci_chipset_tag_t pc, int maxbus)
! 82: {
! 83: extern paddr_t avail_end;
! 84: const char *verbose_header =
! 85: "[%s]-----------------------\n"
! 86: " device vendor product\n"
! 87: " register space address size\n"
! 88: "--------------------------------------------\n";
! 89: const char *verbose_footer =
! 90: "--------------------------[%3d devices bogus]\n";
! 91:
! 92: const struct {
! 93: bus_addr_t start;
! 94: bus_size_t size;
! 95: char *name;
! 96: } system_reserve [] = {
! 97: { 0xfec00000, 0x100000, "I/O APIC" },
! 98: { 0xfee00000, 0x100000, "Local APIC" },
! 99: { 0xfffe0000, 0x20000, "BIOS PROM" },
! 100: { 0, 0, 0 }, /* terminator */
! 101: }, *srp;
! 102: paddr_t start;
! 103: int error;
! 104:
! 105: sc->extent_mem = extent_create("PCI I/O memory space",
! 106: PCIADDR_MEM_START, PCIADDR_MEM_END, M_DEVBUF, 0, 0, EX_NOWAIT);
! 107: KASSERT(sc->extent_mem);
! 108: sc->extent_port = extent_create("PCI I/O port space",
! 109: PCIADDR_PORT_START, PCIADDR_PORT_END, M_DEVBUF, 0, 0, EX_NOWAIT);
! 110: KASSERT(sc->extent_port);
! 111:
! 112: /*
! 113: * 1. check & reserve system BIOS setting.
! 114: */
! 115: PCIBIOS_PRINTV((verbose_header, "System BIOS Setting"));
! 116: pci_device_foreach(sc, pc, maxbus, pciaddr_resource_reserve);
! 117: pci_device_foreach(sc, pc, maxbus, pciaddr_resource_reserve_disabled);
! 118: PCIBIOS_PRINTV((verbose_footer, sc->nbogus));
! 119:
! 120: /*
! 121: * 2. reserve non-PCI area.
! 122: */
! 123: for (srp = system_reserve; srp->size; srp++) {
! 124: error = extent_alloc_region(sc->extent_mem, srp->start,
! 125: srp->size, EX_NOWAIT| EX_MALLOCOK);
! 126: if (error != 0)
! 127: printf("WARNING: can't reserve area for %s.\n",
! 128: srp->name);
! 129: }
! 130:
! 131: /*
! 132: * 3. determine allocation space
! 133: */
! 134: start = round_page(avail_end + 1);
! 135: if (start < PCIADDR_ISAMEM_RESERVE)
! 136: start = PCIADDR_ISAMEM_RESERVE;
! 137: sc->mem_alloc_start = (start + 0x100000 + 1) & ~(0x100000 - 1);
! 138: sc->port_alloc_start = PCIADDR_ISAPORT_RESERVE;
! 139: PCIBIOS_PRINTV((" Physical memory end: 0x%08x\n PCI memory mapped I/O "
! 140: "space start: 0x%08x\n", avail_end, sc->mem_alloc_start));
! 141:
! 142: /*
! 143: * 4. do fixup
! 144: */
! 145: PCIBIOS_PRINTV((verbose_header, "PCIBIOS fixup stage"));
! 146: sc->nbogus = 0;
! 147: pci_device_foreach(sc, pc, maxbus, pciaddr_resource_allocate);
! 148: PCIBIOS_PRINTV((verbose_footer, sc->nbogus));
! 149:
! 150: }
! 151:
! 152: void
! 153: pciaddr_resource_reserve(struct pcibios_softc *sc, pci_chipset_tag_t pc,
! 154: pcitag_t tag)
! 155: {
! 156: if (pcibios_flags & PCIBIOS_VERBOSE)
! 157: pciaddr_print_devid(pc, tag);
! 158: pciaddr_resource_manage(sc, pc, tag, pciaddr_do_resource_reserve);
! 159: }
! 160:
! 161: void
! 162: pciaddr_resource_reserve_disabled(struct pcibios_softc *sc,
! 163: pci_chipset_tag_t pc, pcitag_t tag)
! 164: {
! 165: if (pcibios_flags & PCIBIOS_VERBOSE)
! 166: pciaddr_print_devid(pc, tag);
! 167: pciaddr_resource_manage(sc, pc, tag,
! 168: pciaddr_do_resource_reserve_disabled);
! 169: }
! 170:
! 171: void
! 172: pciaddr_resource_allocate(struct pcibios_softc *sc, pci_chipset_tag_t pc,
! 173: pcitag_t tag)
! 174: {
! 175: if (pcibios_flags & PCIBIOS_VERBOSE)
! 176: pciaddr_print_devid(pc, tag);
! 177: pciaddr_resource_manage(sc, pc, tag, pciaddr_do_resource_allocate);
! 178: }
! 179:
! 180: void
! 181: pciaddr_resource_manage(struct pcibios_softc *sc, pci_chipset_tag_t pc,
! 182: pcitag_t tag, pciaddr_resource_manage_func_t func)
! 183: {
! 184: struct extent *ex;
! 185: pcireg_t val, mask;
! 186: bus_addr_t addr;
! 187: bus_size_t size;
! 188: int error, mapreg, type, reg_start, reg_end, width;
! 189:
! 190: val = pci_conf_read(pc, tag, PCI_BHLC_REG);
! 191: switch (PCI_HDRTYPE_TYPE(val)) {
! 192: default:
! 193: printf("WARNING: unknown PCI device header 0x%x.\n",
! 194: PCI_HDRTYPE_TYPE(val));
! 195: sc->nbogus++;
! 196: return;
! 197: case 0:
! 198: reg_start = PCI_MAPREG_START;
! 199: reg_end = PCI_MAPREG_END;
! 200: break;
! 201: case 1: /* PCI-PCI bridge */
! 202: reg_start = PCI_MAPREG_START;
! 203: reg_end = PCI_MAPREG_PPB_END;
! 204: break;
! 205: case 2: /* PCI-CardBus bridge */
! 206: reg_start = PCI_MAPREG_START;
! 207: reg_end = PCI_MAPREG_PCB_END;
! 208: break;
! 209: }
! 210: error = 0;
! 211:
! 212: for (mapreg = reg_start; mapreg < reg_end; mapreg += width) {
! 213: /* inquire PCI device bus space requirement */
! 214: val = pci_conf_read(pc, tag, mapreg);
! 215: pci_conf_write(pc, tag, mapreg, ~0);
! 216:
! 217: mask = pci_conf_read(pc, tag, mapreg);
! 218: pci_conf_write(pc, tag, mapreg, val);
! 219:
! 220: type = PCI_MAPREG_TYPE(val);
! 221: width = 4;
! 222: if (type == PCI_MAPREG_TYPE_MEM) {
! 223: if (PCI_MAPREG_MEM_TYPE(val) ==
! 224: PCI_MAPREG_MEM_TYPE_64BIT) {
! 225: /* XXX We could examine the upper 32 bits
! 226: * XXX of the BAR here, but we are totally
! 227: * XXX unprepared to handle a non-zero value,
! 228: * XXX either here or anywhere else in
! 229: * XXX i386-land.
! 230: * XXX So just arrange to not look at the
! 231: * XXX upper 32 bits, lest we misinterpret
! 232: * XXX it as a 32-bit BAR set to zero.
! 233: */
! 234: width = 8;
! 235: }
! 236: addr = PCI_MAPREG_MEM_ADDR(val);
! 237: size = PCI_MAPREG_MEM_SIZE(mask);
! 238: ex = sc->extent_mem;
! 239: } else {
! 240: /* XXX some devices give 32bit value */
! 241: addr = PCI_MAPREG_IO_ADDR(val) & PCIADDR_PORT_END;
! 242: size = PCI_MAPREG_IO_SIZE(mask);
! 243: ex = sc->extent_port;
! 244: }
! 245:
! 246: if (!size) /* unused register */
! 247: continue;
! 248:
! 249: /* reservation/allocation phase */
! 250: error += (*func) (sc, pc, tag, mapreg, ex, type, &addr, size);
! 251:
! 252: PCIBIOS_PRINTV(("\t%02xh %s 0x%08x 0x%08x\n",
! 253: mapreg, type ? "port" : "mem ",
! 254: (unsigned int)addr, (unsigned int)size));
! 255: }
! 256:
! 257: if (error)
! 258: sc->nbogus++;
! 259:
! 260: PCIBIOS_PRINTV(("\t\t[%s]\n", error ? "NG" : "OK"));
! 261: }
! 262:
! 263: int
! 264: pciaddr_do_resource_allocate(struct pcibios_softc *sc, pci_chipset_tag_t pc,
! 265: pcitag_t tag, int mapreg, struct extent *ex, int type, bus_addr_t *addr,
! 266: bus_size_t size)
! 267: {
! 268: bus_addr_t start;
! 269: int error;
! 270:
! 271: if (*addr) /* no need to allocate */
! 272: return (0);
! 273:
! 274: /* XXX Don't allocate if device is AGP device to avoid conflict. */
! 275: if (pciaddr_device_is_agp(pc, tag))
! 276: return (0);
! 277:
! 278: start = (type == PCI_MAPREG_TYPE_MEM ? sc->mem_alloc_start
! 279: : sc->port_alloc_start);
! 280: if (start < ex->ex_start || start + size - 1 >= ex->ex_end) {
! 281: PCIBIOS_PRINTV(("No available resources. fixup failed\n"));
! 282: return (1);
! 283: }
! 284: error = extent_alloc_subregion(ex, start, ex->ex_end, size, size, 0, 0,
! 285: EX_FAST|EX_NOWAIT|EX_MALLOCOK, addr);
! 286: if (error) {
! 287: PCIBIOS_PRINTV(("No available resources. fixup failed\n"));
! 288: return (1);
! 289: }
! 290:
! 291: /* write new address to PCI device configuration header */
! 292: pci_conf_write(pc, tag, mapreg, *addr);
! 293: /* check */
! 294: if (pcibios_flags & PCIBIOS_VERBOSE) {
! 295: printf("pci_addr_fixup: ");
! 296: pciaddr_print_devid(pc, tag);
! 297: }
! 298:
! 299: if (pciaddr_ioaddr(pci_conf_read(pc, tag, mapreg)) != *addr) {
! 300: pci_conf_write(pc, tag, mapreg, 0); /* clear */
! 301: PCIBIOS_PRINTV(("fixup failed. (new address=%#x)\n", *addr));
! 302: return (1);
! 303: }
! 304: PCIBIOS_PRINTV(("new address 0x%08x\n", *addr));
! 305:
! 306: return (0);
! 307: }
! 308:
! 309: int
! 310: pciaddr_do_resource_reserve(struct pcibios_softc *sc, pci_chipset_tag_t pc,
! 311: pcitag_t tag, int mapreg, struct extent *ex, int type, bus_addr_t *addr,
! 312: bus_size_t size)
! 313: {
! 314: pcireg_t val;
! 315: int error;
! 316:
! 317: if (*addr == 0)
! 318: return (0);
! 319:
! 320: val = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
! 321: if (type == PCI_MAPREG_TYPE_MEM &&
! 322: (val & PCI_COMMAND_MEM_ENABLE) != PCI_COMMAND_MEM_ENABLE)
! 323: return (0);
! 324: if (type == PCI_MAPREG_TYPE_IO &&
! 325: (val & PCI_COMMAND_IO_ENABLE) != PCI_COMMAND_IO_ENABLE)
! 326: return (0);
! 327:
! 328: error = extent_alloc_region(ex, *addr, size, EX_NOWAIT | EX_MALLOCOK);
! 329: if (error) {
! 330: PCIBIOS_PRINTV(("Resource conflict.\n"));
! 331: pci_conf_write(pc, tag, mapreg, 0); /* clear */
! 332: return (1);
! 333: }
! 334:
! 335: return (0);
! 336: }
! 337:
! 338: int
! 339: pciaddr_do_resource_reserve_disabled(struct pcibios_softc *sc,
! 340: pci_chipset_tag_t pc, pcitag_t tag, int mapreg, struct extent *ex, int type,
! 341: u_long *addr, bus_size_t size)
! 342: {
! 343: pcireg_t val;
! 344: int error;
! 345:
! 346: if (*addr == 0)
! 347: return (0);
! 348:
! 349: val = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
! 350: if (type == PCI_MAPREG_TYPE_MEM &&
! 351: (val & PCI_COMMAND_MEM_ENABLE) == PCI_COMMAND_MEM_ENABLE)
! 352: return (0);
! 353: if (type == PCI_MAPREG_TYPE_IO &&
! 354: (val & PCI_COMMAND_IO_ENABLE) == PCI_COMMAND_IO_ENABLE)
! 355: return (0);
! 356:
! 357: PCIBIOS_PRINTV(("disabled %s space at addr 0x%x size 0x%x\n",
! 358: type == PCI_MAPREG_TYPE_MEM ? "mem" : "io", *addr, size));
! 359:
! 360: error = extent_alloc_region(ex, *addr, size, EX_NOWAIT | EX_MALLOCOK);
! 361: if (error) {
! 362: PCIBIOS_PRINTV(("Resource conflict.\n"));
! 363: pci_conf_write(pc, tag, mapreg, 0); /* clear */
! 364: return (1);
! 365: }
! 366:
! 367: return (0);
! 368: }
! 369:
! 370: bus_addr_t
! 371: pciaddr_ioaddr(u_int32_t val)
! 372: {
! 373: return ((PCI_MAPREG_TYPE(val) == PCI_MAPREG_TYPE_MEM)
! 374: ? PCI_MAPREG_MEM_ADDR(val)
! 375: : (PCI_MAPREG_IO_ADDR(val) & PCIADDR_PORT_END));
! 376: }
! 377:
! 378: void
! 379: pciaddr_print_devid(pci_chipset_tag_t pc, pcitag_t tag)
! 380: {
! 381: int bus, device, function;
! 382: pcireg_t id;
! 383:
! 384: id = pci_conf_read(pc, tag, PCI_ID_REG);
! 385: pci_decompose_tag(pc, tag, &bus, &device, &function);
! 386: printf("%03d:%02d:%d %04x:%04x\n", bus, device, function,
! 387: PCI_VENDOR(id), PCI_PRODUCT(id));
! 388: }
! 389:
! 390: int
! 391: pciaddr_device_is_agp(pci_chipset_tag_t pc, pcitag_t tag)
! 392: {
! 393: pcireg_t class, status, rval;
! 394: int off;
! 395:
! 396: /* Check AGP device. */
! 397: class = pci_conf_read(pc, tag, PCI_CLASS_REG);
! 398: if (PCI_CLASS(class) == PCI_CLASS_DISPLAY) {
! 399: status = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
! 400: if (status & PCI_STATUS_CAPLIST_SUPPORT) {
! 401: rval = pci_conf_read(pc, tag, PCI_CAPLISTPTR_REG);
! 402: for (off = PCI_CAPLIST_PTR(rval);
! 403: off != 0;
! 404: off = PCI_CAPLIST_NEXT(rval) ) {
! 405: rval = pci_conf_read(pc, tag, off);
! 406: if (PCI_CAPLIST_CAP(rval) == PCI_CAP_AGP)
! 407: return (1);
! 408: }
! 409: }
! 410: }
! 411: return (0);
! 412: }
! 413:
! 414:
! 415: struct extent *
! 416: pciaddr_search(int mem_port, bus_addr_t *startp, bus_size_t size)
! 417: {
! 418: extern struct cfdriver pcibios_cd;
! 419: struct pcibios_softc *sc;
! 420:
! 421: sc = (struct pcibios_softc *)device_lookup(&pcibios_cd, 0);
! 422: if (sc && !(pcibios_flags & PCIBIOS_ADDR_FIXUP)) {
! 423: struct extent_region *rp;
! 424: struct extent *ex = mem_port? sc->extent_mem : sc->extent_port;
! 425:
! 426: /* Search the PCI I/O memory space extent for free
! 427: * space that will accommodate size. Remember that the
! 428: * extent stores allocated space and we're searching
! 429: * for the gaps.
! 430: *
! 431: * If we're at the end or the gap between this region
! 432: * and the next region big enough, then we're done
! 433: */
! 434: for (rp = LIST_FIRST(&ex->ex_regions);
! 435: rp && *startp + size > rp->er_start;
! 436: rp = LIST_NEXT(rp, er_link)) {
! 437: bus_addr_t new_start;
! 438:
! 439: new_start = (rp->er_end - 1 + size) & ~(size - 1);
! 440: if (new_start > *startp)
! 441: *startp = new_start;
! 442: }
! 443:
! 444: return (ex);
! 445: }
! 446:
! 447: return (NULL);
! 448: }
CVSweb