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