1/* $NetBSD: pci_addr_fixup.c,v 1.8 2011/08/28 06:08:15 dyoung Exp $ */ 2 3/*- 4 * Copyright (c) 2000 UCHIYAMA Yasushi. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__KERNEL_RCSID(0, "$NetBSD: pci_addr_fixup.c,v 1.8 2011/08/28 06:08:15 dyoung Exp $"); 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/malloc.h> 35#include <sys/kernel.h> 36#include <sys/device.h> 37#include <sys/extent.h> 38 39#include <sys/bus.h> 40 41#include <dev/pci/pcireg.h> 42#include <dev/pci/pcivar.h> 43#include <dev/pci/pcidevs.h> 44 45#include <x86/pci/pci_addr_fixup.h> 46 47struct pciaddr pciaddr; 48 49static void pciaddr_resource_reserve(pci_chipset_tag_t, pcitag_t, void *); 50static int pciaddr_do_resource_reserve(pci_chipset_tag_t, pcitag_t, int, 51 void *, int, bus_addr_t *, bus_size_t); 52static void pciaddr_resource_allocate(pci_chipset_tag_t, pcitag_t, void *); 53static int pciaddr_do_resource_allocate(pci_chipset_tag_t, pcitag_t, int, 54 void *, int, bus_addr_t *, bus_size_t); 55static int device_is_agp(pci_chipset_tag_t, pcitag_t); 56 57#define PCIADDR_MEM_START 0x0 58#define PCIADDR_MEM_END 0xffffffff 59#define PCIADDR_PORT_START 0x0 60#define PCIADDR_PORT_END 0xffff 61 62/* for ISA devices */ 63#define PCIADDR_ISAPORT_RESERVE 0x5800 /* empirical value */ 64#define PCIADDR_ISAMEM_RESERVE (16 * 1024 * 1024) 65 66void 67pci_addr_fixup(pci_chipset_tag_t pc, int maxbus) 68{ 69 extern paddr_t avail_end; 70 const char *verbose_header = 71 "[%s]-----------------------\n" 72 " device vendor product\n" 73 " register space address size\n" 74 "--------------------------------------------\n"; 75 const char *verbose_footer = 76 "--------------------------[%3d devices bogus]\n"; 77 const struct { 78 bus_addr_t start; 79 bus_size_t size; 80 const char *name; 81 } system_reserve [] = { 82 { 0xfec00000, 0x100000, "I/O APIC" }, 83 { 0xfee00000, 0x100000, "Local APIC" }, 84 { 0xfffe0000, 0x20000, "BIOS PROM" }, 85 { 0, 0, 0 }, /* terminator */ 86 }, *srp; 87 paddr_t start; 88 int error; 89 90 pciaddr.extent_mem = extent_create("PCI I/O memory space", 91 PCIADDR_MEM_START, 92 PCIADDR_MEM_END, 93 0, 0, EX_NOWAIT); 94 KASSERT(pciaddr.extent_mem); 95 pciaddr.extent_port = extent_create("PCI I/O port space", 96 PCIADDR_PORT_START, 97 PCIADDR_PORT_END, 98 0, 0, EX_NOWAIT); 99 KASSERT(pciaddr.extent_port); 100 101 /* 102 * 1. check & reserve system BIOS setting. 103 */ 104 aprint_debug(verbose_header, "System BIOS Setting"); 105 pci_device_foreach(pc, maxbus, pciaddr_resource_reserve, NULL); 106 aprint_debug(verbose_footer, pciaddr.nbogus); 107 108 /* 109 * 2. reserve non-PCI area. 110 */ 111 for (srp = system_reserve; srp->size; srp++) { 112 error = extent_alloc_region(pciaddr.extent_mem, srp->start, 113 srp->size, 114 EX_NOWAIT| EX_MALLOCOK); 115 if (error != 0) { 116 aprint_error("WARNING: can't reserve area for %s.\n", 117 srp->name); 118 } 119 } 120 121 /* 122 * 3. determine allocation space 123 */ 124 start = x86_round_page(avail_end + 1); 125 if (start < PCIADDR_ISAMEM_RESERVE) 126 start = PCIADDR_ISAMEM_RESERVE; 127 pciaddr.mem_alloc_start = (start + 0x100000 + 1) & ~(0x100000 - 1); 128 pciaddr.port_alloc_start = PCIADDR_ISAPORT_RESERVE; 129 aprint_debug(" Physical memory end: 0x%08x\n PCI memory mapped I/O " 130 "space start: 0x%08x\n", (unsigned)avail_end, 131 (unsigned)pciaddr.mem_alloc_start); 132 133 if (pciaddr.nbogus == 0) 134 return; /* no need to fixup */ 135 136 /* 137 * 4. do fixup 138 */ 139 aprint_debug(verbose_header, "PCIBIOS fixup stage"); 140 pciaddr.nbogus = 0; 141 pci_device_foreach_min(pc, 0, maxbus, pciaddr_resource_allocate, NULL); 142 aprint_debug(verbose_footer, pciaddr.nbogus); 143 144} 145 146static void 147pciaddr_resource_reserve(pci_chipset_tag_t pc, pcitag_t tag, 148 void *context) 149{ 150 pciaddr_print_devid(pc, tag); 151 pciaddr_resource_manage(pc, tag, 152 pciaddr_do_resource_reserve, 153 &pciaddr); 154} 155 156static void 157pciaddr_resource_allocate(pci_chipset_tag_t pc, pcitag_t tag, 158 void *context) 159{ 160 pciaddr_print_devid(pc, tag); 161 pciaddr_resource_manage(pc, tag, 162 pciaddr_do_resource_allocate, 163 &pciaddr); 164} 165 166void 167pciaddr_resource_manage(pci_chipset_tag_t pc, pcitag_t tag, 168 pciaddr_resource_manage_func_t func, void *ctx) 169{ 170 pcireg_t val, mask; 171 bus_addr_t addr; 172 bus_size_t size; 173 int error, useport, usemem, mapreg, type, reg_start, reg_end, width; 174 175 val = pci_conf_read(pc, tag, PCI_BHLC_REG); 176 switch (PCI_HDRTYPE_TYPE(val)) { 177 default: 178 aprint_error("WARNING: unknown PCI device header."); 179 pciaddr.nbogus++; 180 return; 181 case PCI_HDRTYPE_DEVICE: 182 reg_start = PCI_MAPREG_START; 183 reg_end = PCI_MAPREG_END; 184 break; 185 case PCI_HDRTYPE_PPB: /* PCI-PCI bridge */ 186 reg_start = PCI_MAPREG_START; 187 reg_end = PCI_MAPREG_PPB_END; 188 break; 189 case PCI_HDRTYPE_PCB: /* PCI-CardBus bridge */ 190 reg_start = PCI_MAPREG_START; 191 reg_end = PCI_MAPREG_PCB_END; 192 break; 193 } 194 error = useport = usemem = 0; 195 196 for (mapreg = reg_start; mapreg < reg_end; mapreg += width) { 197 /* inquire PCI device bus space requirement */ 198 val = pci_conf_read(pc, tag, mapreg); 199 pci_conf_write(pc, tag, mapreg, ~0); 200 201 mask = pci_conf_read(pc, tag, mapreg); 202 pci_conf_write(pc, tag, mapreg, val); 203 204 type = PCI_MAPREG_TYPE(val); 205 width = 4; 206 if (type == PCI_MAPREG_TYPE_MEM) { 207 if (PCI_MAPREG_MEM_TYPE(val) == 208 PCI_MAPREG_MEM_TYPE_64BIT) { 209 /* XXX We could examine the upper 32 bits 210 * XXX of the BAR here, but we are totally 211 * XXX unprepared to handle a non-zero value, 212 * XXX either here or anywhere else in 213 * XXX i386-land. 214 * XXX So just arrange to not look at the 215 * XXX upper 32 bits, lest we misinterpret 216 * XXX it as a 32-bit BAR set to zero. 217 */ 218 width = 8; 219 } 220 size = PCI_MAPREG_MEM_SIZE(mask); 221 } else { 222 size = PCI_MAPREG_IO_SIZE(mask); 223 } 224 addr = pciaddr_ioaddr(val); 225 226 if (size == 0) /* unused register */ 227 continue; 228 229 if (type == PCI_MAPREG_TYPE_MEM) 230 ++usemem; 231 else 232 ++useport; 233 234 /* reservation/allocation phase */ 235 error += (*func) (pc, tag, mapreg, ctx, type, &addr, size); 236 237 aprint_debug("\n\t%02xh %s 0x%08x 0x%08x", 238 mapreg, type ? "port" : "mem ", 239 (unsigned int)addr, (unsigned int)size); 240 } 241 242 /* enable/disable PCI device */ 243 val = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG); 244 if (error == 0) 245 val |= (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | 246 PCI_COMMAND_MASTER_ENABLE); 247 else 248 val &= ~(PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | 249 PCI_COMMAND_MASTER_ENABLE); 250 pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, val); 251 252 if (error != 0) 253 pciaddr.nbogus++; 254 255 aprint_debug("\n\t\t[%s]\n", error ? "NG" : "OK"); 256} 257 258static int 259pciaddr_do_resource_allocate(pci_chipset_tag_t pc, pcitag_t tag, 260 int mapreg, void *ctx, int type, bus_addr_t *addr, bus_size_t size) 261{ 262 struct pciaddr *pciaddrmap = (struct pciaddr *)ctx; 263 bus_addr_t start; 264 int error; 265 struct extent *ex; 266 267 if (*addr != 0) /* no need to allocate */ 268 return 0; 269 270 ex = (type == PCI_MAPREG_TYPE_MEM ? 271 pciaddrmap->extent_mem : pciaddrmap->extent_port); 272 273 /* XXX Don't allocate if device is AGP device to avoid conflict. */ 274 if (device_is_agp(pc, tag)) 275 return 0; 276 277 start = (type == PCI_MAPREG_TYPE_MEM ? 278 pciaddrmap->mem_alloc_start : pciaddrmap->port_alloc_start); 279 280 if (start < ex->ex_start || start + size - 1 >= ex->ex_end) { 281 aprint_debug("No available resources. fixup failed\n"); 282 return 1; 283 } 284 error = extent_alloc_subregion(ex, start, ex->ex_end, size, 285 size, 0, 286 EX_FAST|EX_NOWAIT|EX_MALLOCOK, 287 (u_long *)addr); 288 if (error) { 289 aprint_debug("No available resources. fixup failed\n"); 290 return 1; 291 } 292 293 /* write new address to PCI device configuration header */ 294 pci_conf_write(pc, tag, mapreg, *addr); 295 /* check */ 296 aprint_debug("pci_addr_fixup: "); 297 pciaddr_print_devid(pc, tag); 298 if (pciaddr_ioaddr(pci_conf_read(pc, tag, mapreg)) != *addr) { 299 pci_conf_write(pc, tag, mapreg, 0); /* clear */ 300 aprint_error("fixup failed. (new address=%#x)\n", (unsigned)*addr); 301 return 1; 302 } 303 aprint_debug("new address 0x%08x\n", (unsigned)*addr); 304 305 return 0; 306} 307 308int 309pciaddr_do_resource_reserve(pci_chipset_tag_t pc, pcitag_t tag, 310 int mapreg, void *ctx, int type, bus_addr_t *addr, bus_size_t size) 311{ 312 struct extent *ex; 313 struct pciaddr *pciaddrmap = (struct pciaddr *)ctx; 314 int error; 315 316 if (*addr == 0) 317 return 1; 318 319 ex = (type == PCI_MAPREG_TYPE_MEM ? 320 pciaddrmap->extent_mem : pciaddrmap->extent_port); 321 322 error = extent_alloc_region(ex, *addr, size, EX_NOWAIT| EX_MALLOCOK); 323 if (error) { 324 aprint_debug("Resource conflict.\n"); 325 pci_conf_write(pc, tag, mapreg, 0); /* clear */ 326 return 1; 327 } 328 329 return 0; 330} 331 332bus_addr_t 333pciaddr_ioaddr(uint32_t val) 334{ 335 return (PCI_MAPREG_TYPE(val) == PCI_MAPREG_TYPE_MEM) 336 ? PCI_MAPREG_MEM_ADDR(val) 337 : PCI_MAPREG_IO_ADDR(val); 338} 339 340void 341pciaddr_print_devid(pci_chipset_tag_t pc, pcitag_t tag) 342{ 343 int bus, device, function; 344 pcireg_t id; 345 346 id = pci_conf_read(pc, tag, PCI_ID_REG); 347 pci_decompose_tag(pc, tag, &bus, &device, &function); 348 aprint_debug("%03d:%02d:%d 0x%04x 0x%04x ", bus, device, function, 349 PCI_VENDOR(id), PCI_PRODUCT(id)); 350} 351 352static int 353device_is_agp(pci_chipset_tag_t pc, pcitag_t tag) 354{ 355 pcireg_t class, status, rval; 356 int off; 357 358 /* Check AGP device. */ 359 class = pci_conf_read(pc, tag, PCI_CLASS_REG); 360 if (PCI_CLASS(class) == PCI_CLASS_DISPLAY) { 361 status = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG); 362 if (status & PCI_STATUS_CAPLIST_SUPPORT) { 363 rval = pci_conf_read(pc, tag, PCI_CAPLISTPTR_REG); 364 for (off = PCI_CAPLIST_PTR(rval); 365 off != 0; 366 off = PCI_CAPLIST_NEXT(rval) ) { 367 rval = pci_conf_read(pc, tag, off); 368 if (PCI_CAPLIST_CAP(rval) == PCI_CAP_AGP) 369 return 1; 370 } 371 } 372 } 373 return 0; 374} 375