1 /* $OpenBSD: pci_addr_fixup.c,v 1.22 2010/07/02 16:11:19 jsg 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