1 /*- 2 * Copyright (c) 2000 Doug Rabson 3 * Copyright (c) 2000 Ruslan Ermilov 4 * 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 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD$ 28 */ 29 30 #include "opt_bus.h" 31 #include "opt_pci.h" 32 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 #include <sys/malloc.h> 36 #include <sys/kernel.h> 37 #include <sys/bus.h> 38 #include <sys/lock.h> 39 40 #include <pci/pcivar.h> 41 #include <pci/pcireg.h> 42 #include <pci/agppriv.h> 43 #include <pci/agpreg.h> 44 45 #include <vm/vm.h> 46 #include <vm/vm_object.h> 47 #include <vm/vm_page.h> 48 #include <vm/vm_pageout.h> 49 #include <vm/pmap.h> 50 51 #include <machine/bus.h> 52 #include <machine/resource.h> 53 #include <sys/rman.h> 54 55 MALLOC_DECLARE(M_AGP); 56 57 #define READ1(off) bus_space_read_1(sc->bst, sc->bsh, off) 58 #define WRITE4(off,v) bus_space_write_4(sc->bst, sc->bsh, off, v) 59 60 struct agp_i810_softc { 61 struct agp_softc agp; 62 u_int32_t initial_aperture; /* aperture size at startup */ 63 struct agp_gatt *gatt; 64 u_int32_t dcache_size; 65 device_t bdev; /* bridge device */ 66 struct resource *regs; /* memory mapped GC registers */ 67 bus_space_tag_t bst; /* bus_space tag */ 68 bus_space_handle_t bsh; /* bus_space handle */ 69 }; 70 71 static const char* 72 agp_i810_match(device_t dev) 73 { 74 if (pci_get_class(dev) != PCIC_DISPLAY 75 || pci_get_subclass(dev) != PCIS_DISPLAY_VGA) 76 return NULL; 77 78 switch (pci_get_devid(dev)) { 79 case 0x71218086: 80 return ("Intel 82810 (i810 GMCH) SVGA controller"); 81 82 case 0x71238086: 83 return ("Intel 82810-DC100 (i810-DC100 GMCH) SVGA controller"); 84 85 case 0x71258086: 86 return ("Intel 82810E (i810E GMCH) SVGA controller"); 87 88 case 0x11328086: 89 return ("Intel 82815 (i815 GMCH) SVGA controller"); 90 }; 91 92 return NULL; 93 } 94 95 /* 96 * Find bridge device. 97 */ 98 static device_t 99 agp_i810_find_bridge(device_t dev) 100 { 101 device_t *children, child; 102 int nchildren, i; 103 u_int32_t devid; 104 105 /* 106 * Calculate bridge device's ID. 107 */ 108 devid = pci_get_devid(dev); 109 switch (devid) { 110 case 0x71218086: 111 case 0x71238086: 112 case 0x71258086: 113 devid -= 0x10000; 114 break; 115 116 case 0x11328086: 117 devid = 0x11308086; 118 break; 119 }; 120 if (device_get_children(device_get_parent(dev), &children, &nchildren)) 121 return 0; 122 123 for (i = 0; i < nchildren; i++) { 124 child = children[i]; 125 126 if (pci_get_devid(child) == devid) { 127 free(children, M_TEMP); 128 return child; 129 } 130 } 131 free(children, M_TEMP); 132 return 0; 133 } 134 135 static int 136 agp_i810_probe(device_t dev) 137 { 138 const char *desc; 139 140 desc = agp_i810_match(dev); 141 if (desc) { 142 device_t bdev; 143 u_int8_t smram; 144 145 bdev = agp_i810_find_bridge(dev); 146 if (!bdev) { 147 if (bootverbose) 148 printf("I810: can't find bridge device\n"); 149 return ENXIO; 150 } 151 152 smram = pci_read_config(bdev, AGP_I810_SMRAM, 1); 153 if ((smram & AGP_I810_SMRAM_GMS) 154 == AGP_I810_SMRAM_GMS_DISABLED) { 155 if (bootverbose) 156 printf("I810: disabled, not probing\n"); 157 return ENXIO; 158 } 159 160 device_verbose(dev); 161 device_set_desc(dev, desc); 162 return 0; 163 } 164 165 return ENXIO; 166 } 167 168 static int 169 agp_i810_attach(device_t dev) 170 { 171 struct agp_i810_softc *sc = device_get_softc(dev); 172 struct agp_gatt *gatt; 173 int error, rid; 174 175 sc->bdev = agp_i810_find_bridge(dev); 176 if (!sc->bdev) 177 return ENOENT; 178 179 error = agp_generic_attach(dev); 180 if (error) 181 return error; 182 183 rid = AGP_I810_MMADR; 184 sc->regs = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 185 0, ~0, 1, RF_ACTIVE); 186 if (!sc->regs) { 187 agp_generic_detach(dev); 188 return ENOMEM; 189 } 190 sc->bst = rman_get_bustag(sc->regs); 191 sc->bsh = rman_get_bushandle(sc->regs); 192 193 sc->initial_aperture = AGP_GET_APERTURE(dev); 194 195 if (READ1(AGP_I810_DRT) & AGP_I810_DRT_POPULATED) 196 sc->dcache_size = 4 * 1024 * 1024; 197 else 198 sc->dcache_size = 0; 199 200 for (;;) { 201 gatt = agp_alloc_gatt(dev); 202 if (gatt) 203 break; 204 205 /* 206 * Probably contigmalloc failure. Try reducing the 207 * aperture so that the gatt size reduces. 208 */ 209 if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2)) { 210 agp_generic_detach(dev); 211 return ENOMEM; 212 } 213 } 214 sc->gatt = gatt; 215 216 /* Install the GATT. */ 217 WRITE4(AGP_I810_PGTBL_CTL, gatt->ag_physical | 1); 218 219 /* 220 * Make sure the chipset can see everything. 221 */ 222 agp_flush_cache(); 223 224 return 0; 225 } 226 227 static int 228 agp_i810_detach(device_t dev) 229 { 230 struct agp_i810_softc *sc = device_get_softc(dev); 231 int error; 232 233 error = agp_generic_detach(dev); 234 if (error) 235 return error; 236 237 /* Clear the GATT base. */ 238 WRITE4(AGP_I810_PGTBL_CTL, 0); 239 240 /* Put the aperture back the way it started. */ 241 AGP_SET_APERTURE(dev, sc->initial_aperture); 242 243 agp_free_gatt(sc->gatt); 244 245 bus_release_resource(dev, SYS_RES_MEMORY, 246 AGP_I810_MMADR, sc->regs); 247 248 return 0; 249 } 250 251 static u_int32_t 252 agp_i810_get_aperture(device_t dev) 253 { 254 struct agp_i810_softc *sc = device_get_softc(dev); 255 u_int16_t miscc; 256 257 miscc = pci_read_config(sc->bdev, AGP_I810_MISCC, 2); 258 if ((miscc & AGP_I810_MISCC_WINSIZE) == AGP_I810_MISCC_WINSIZE_32) 259 return 32 * 1024 * 1024; 260 else 261 return 64 * 1024 * 1024; 262 } 263 264 static int 265 agp_i810_set_aperture(device_t dev, u_int32_t aperture) 266 { 267 struct agp_i810_softc *sc = device_get_softc(dev); 268 u_int16_t miscc; 269 270 /* 271 * Double check for sanity. 272 */ 273 if (aperture != 32 * 1024 * 1024 && aperture != 64 * 1024 * 1024) { 274 device_printf(dev, "bad aperture size %d\n", aperture); 275 return EINVAL; 276 } 277 278 miscc = pci_read_config(sc->bdev, AGP_I810_MISCC, 2); 279 miscc &= ~AGP_I810_MISCC_WINSIZE; 280 if (aperture == 32 * 1024 * 1024) 281 miscc |= AGP_I810_MISCC_WINSIZE_32; 282 else 283 miscc |= AGP_I810_MISCC_WINSIZE_64; 284 285 pci_write_config(sc->bdev, AGP_I810_MISCC, miscc, 2); 286 287 return 0; 288 } 289 290 static int 291 agp_i810_bind_page(device_t dev, int offset, vm_offset_t physical) 292 { 293 struct agp_i810_softc *sc = device_get_softc(dev); 294 295 if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) 296 return EINVAL; 297 298 WRITE4(AGP_I810_GTT + (offset >> AGP_PAGE_SHIFT) * 4, physical | 1); 299 return 0; 300 } 301 302 static int 303 agp_i810_unbind_page(device_t dev, int offset) 304 { 305 struct agp_i810_softc *sc = device_get_softc(dev); 306 307 if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) 308 return EINVAL; 309 310 WRITE4(AGP_I810_GTT + (offset >> AGP_PAGE_SHIFT) * 4, 0); 311 return 0; 312 } 313 314 /* 315 * Writing via memory mapped registers already flushes all TLBs. 316 */ 317 static void 318 agp_i810_flush_tlb(device_t dev) 319 { 320 } 321 322 static int 323 agp_i810_enable(device_t dev, u_int32_t mode) 324 { 325 326 return 0; 327 } 328 329 static struct agp_memory * 330 agp_i810_alloc_memory(device_t dev, int type, vm_size_t size) 331 { 332 struct agp_i810_softc *sc = device_get_softc(dev); 333 struct agp_memory *mem; 334 335 if ((size & (AGP_PAGE_SIZE - 1)) != 0) 336 return 0; 337 338 if (sc->agp.as_allocated + size > sc->agp.as_maxmem) 339 return 0; 340 341 if (type == 1) { 342 /* 343 * Mapping local DRAM into GATT. 344 */ 345 if (size != sc->dcache_size) 346 return 0; 347 } else if (type == 2) { 348 /* 349 * Bogus mapping of a single page for the hardware cursor. 350 */ 351 if (size != AGP_PAGE_SIZE) 352 return 0; 353 } 354 355 mem = malloc(sizeof *mem, M_AGP, M_WAITOK); 356 mem->am_id = sc->agp.as_nextid++; 357 mem->am_size = size; 358 mem->am_type = type; 359 if (type != 1) 360 mem->am_obj = vm_object_allocate(OBJT_DEFAULT, 361 atop(round_page(size))); 362 else 363 mem->am_obj = 0; 364 365 if (type == 2) { 366 /* 367 * Allocate and wire down the page now so that we can 368 * get its physical address. 369 */ 370 vm_page_t m; 371 m = vm_page_grab(mem->am_obj, 0, VM_ALLOC_ZERO|VM_ALLOC_RETRY); 372 vm_page_wire(m); 373 mem->am_physical = VM_PAGE_TO_PHYS(m); 374 vm_page_wakeup(m); 375 } else { 376 mem->am_physical = 0; 377 } 378 379 mem->am_offset = 0; 380 mem->am_is_bound = 0; 381 TAILQ_INSERT_TAIL(&sc->agp.as_memory, mem, am_link); 382 sc->agp.as_allocated += size; 383 384 return mem; 385 } 386 387 static int 388 agp_i810_free_memory(device_t dev, struct agp_memory *mem) 389 { 390 struct agp_i810_softc *sc = device_get_softc(dev); 391 392 if (mem->am_is_bound) 393 return EBUSY; 394 395 if (mem->am_type == 2) { 396 /* 397 * Unwire the page which we wired in alloc_memory. 398 */ 399 vm_page_t m = vm_page_lookup(mem->am_obj, 0); 400 vm_page_unwire(m, 0); 401 } 402 403 sc->agp.as_allocated -= mem->am_size; 404 TAILQ_REMOVE(&sc->agp.as_memory, mem, am_link); 405 if (mem->am_obj) 406 vm_object_deallocate(mem->am_obj); 407 free(mem, M_AGP); 408 return 0; 409 } 410 411 static int 412 agp_i810_bind_memory(device_t dev, struct agp_memory *mem, 413 vm_offset_t offset) 414 { 415 struct agp_i810_softc *sc = device_get_softc(dev); 416 vm_offset_t i; 417 418 if (mem->am_type != 1) 419 return agp_generic_bind_memory(dev, mem, offset); 420 421 for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) { 422 WRITE4(AGP_I810_GTT + (offset >> AGP_PAGE_SHIFT) * 4, 423 i | 3); 424 } 425 426 return 0; 427 } 428 429 static int 430 agp_i810_unbind_memory(device_t dev, struct agp_memory *mem) 431 { 432 struct agp_i810_softc *sc = device_get_softc(dev); 433 vm_offset_t i; 434 435 if (mem->am_type != 1) 436 return agp_generic_unbind_memory(dev, mem); 437 438 for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) 439 WRITE4(AGP_I810_GTT + (i >> AGP_PAGE_SHIFT) * 4, 0); 440 441 return 0; 442 } 443 444 static device_method_t agp_i810_methods[] = { 445 /* Device interface */ 446 DEVMETHOD(device_probe, agp_i810_probe), 447 DEVMETHOD(device_attach, agp_i810_attach), 448 DEVMETHOD(device_detach, agp_i810_detach), 449 DEVMETHOD(device_shutdown, bus_generic_shutdown), 450 DEVMETHOD(device_suspend, bus_generic_suspend), 451 DEVMETHOD(device_resume, bus_generic_resume), 452 453 /* AGP interface */ 454 DEVMETHOD(agp_get_aperture, agp_i810_get_aperture), 455 DEVMETHOD(agp_set_aperture, agp_i810_set_aperture), 456 DEVMETHOD(agp_bind_page, agp_i810_bind_page), 457 DEVMETHOD(agp_unbind_page, agp_i810_unbind_page), 458 DEVMETHOD(agp_flush_tlb, agp_i810_flush_tlb), 459 DEVMETHOD(agp_enable, agp_i810_enable), 460 DEVMETHOD(agp_alloc_memory, agp_i810_alloc_memory), 461 DEVMETHOD(agp_free_memory, agp_i810_free_memory), 462 DEVMETHOD(agp_bind_memory, agp_i810_bind_memory), 463 DEVMETHOD(agp_unbind_memory, agp_i810_unbind_memory), 464 465 { 0, 0 } 466 }; 467 468 static driver_t agp_i810_driver = { 469 "agp", 470 agp_i810_methods, 471 sizeof(struct agp_i810_softc), 472 }; 473 474 static devclass_t agp_devclass; 475 476 DRIVER_MODULE(agp_i810, pci, agp_i810_driver, agp_devclass, 0, 0); 477