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