1 /* $NetBSD: agp_i810.c,v 1.16 2003/03/24 09:12:55 drochner Exp $ */ 2 3 /*- 4 * Copyright (c) 2000 Doug Rabson 5 * Copyright (c) 2000 Ruslan Ermilov 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $FreeBSD: src/sys/pci/agp_i810.c,v 1.4 2001/07/05 21:28:47 jhb Exp $ 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: agp_i810.c,v 1.16 2003/03/24 09:12:55 drochner Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/malloc.h> 38 #include <sys/kernel.h> 39 #include <sys/lock.h> 40 #include <sys/proc.h> 41 #include <sys/device.h> 42 #include <sys/conf.h> 43 44 #include <uvm/uvm_extern.h> 45 46 #include <dev/pci/pcivar.h> 47 #include <dev/pci/pcireg.h> 48 #include <dev/pci/pcidevs.h> 49 #include <dev/pci/agpvar.h> 50 #include <dev/pci/agpreg.h> 51 52 #include <sys/agpio.h> 53 54 #include <machine/bus.h> 55 56 #define READ1(off) bus_space_read_1(isc->bst, isc->bsh, off) 57 #define READ4(off) bus_space_read_4(isc->bst, isc->bsh, off) 58 #define WRITE4(off,v) bus_space_write_4(isc->bst, isc->bsh, off, v) 59 60 #define CHIP_I810 0 /* i810/i815 */ 61 #define CHIP_I830 1 /* i830/i845 */ 62 63 struct agp_i810_softc { 64 u_int32_t initial_aperture; /* aperture size at startup */ 65 struct agp_gatt *gatt; 66 int chiptype; /* i810-like or i830 */ 67 u_int32_t dcache_size; /* i810 only */ 68 u_int32_t stolen; /* number of i830/845 gtt entries 69 for stolen memory */ 70 bus_space_tag_t bst; /* bus_space tag */ 71 bus_space_handle_t bsh; /* bus_space handle */ 72 struct pci_attach_args vga_pa; 73 }; 74 75 static u_int32_t agp_i810_get_aperture(struct agp_softc *); 76 static int agp_i810_set_aperture(struct agp_softc *, u_int32_t); 77 static int agp_i810_bind_page(struct agp_softc *, off_t, bus_addr_t); 78 static int agp_i810_unbind_page(struct agp_softc *, off_t); 79 static void agp_i810_flush_tlb(struct agp_softc *); 80 static int agp_i810_enable(struct agp_softc *, u_int32_t mode); 81 static struct agp_memory *agp_i810_alloc_memory(struct agp_softc *, int, 82 vsize_t); 83 static int agp_i810_free_memory(struct agp_softc *, struct agp_memory *); 84 static int agp_i810_bind_memory(struct agp_softc *, struct agp_memory *, off_t); 85 static int agp_i810_unbind_memory(struct agp_softc *, struct agp_memory *); 86 87 struct agp_methods agp_i810_methods = { 88 agp_i810_get_aperture, 89 agp_i810_set_aperture, 90 agp_i810_bind_page, 91 agp_i810_unbind_page, 92 agp_i810_flush_tlb, 93 agp_i810_enable, 94 agp_i810_alloc_memory, 95 agp_i810_free_memory, 96 agp_i810_bind_memory, 97 agp_i810_unbind_memory, 98 }; 99 100 /* XXXthorpej -- duplicated code (see arch/i386/pci/pchb.c) */ 101 static int 102 agp_i810_vgamatch(struct pci_attach_args *pa) 103 { 104 105 if (PCI_CLASS(pa->pa_class) != PCI_CLASS_DISPLAY || 106 PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_DISPLAY_VGA) 107 return (0); 108 109 switch (PCI_PRODUCT(pa->pa_id)) { 110 case PCI_PRODUCT_INTEL_82810_GC: 111 case PCI_PRODUCT_INTEL_82810_DC100_GC: 112 case PCI_PRODUCT_INTEL_82810E_GC: 113 case PCI_PRODUCT_INTEL_82815_FULL_GRAPH: 114 case PCI_PRODUCT_INTEL_82830MP_IV: 115 case PCI_PRODUCT_INTEL_82845G_IGD: 116 return (1); 117 } 118 119 return (0); 120 } 121 122 int 123 agp_i810_attach(struct device *parent, struct device *self, void *aux) 124 { 125 struct agp_softc *sc = (void *)self; 126 struct agp_i810_softc *isc; 127 struct agp_gatt *gatt; 128 int error; 129 130 isc = malloc(sizeof *isc, M_AGP, M_NOWAIT|M_ZERO); 131 if (isc == NULL) { 132 aprint_error(": can't allocate chipset-specific softc\n"); 133 return ENOMEM; 134 } 135 sc->as_chipc = isc; 136 sc->as_methods = &agp_i810_methods; 137 138 if (pci_find_device(&isc->vga_pa, agp_i810_vgamatch) == 0) { 139 aprint_error(": can't find internal VGA device config space\n"); 140 free(isc, M_AGP); 141 return ENOENT; 142 } 143 144 /* XXXfvdl */ 145 sc->as_dmat = isc->vga_pa.pa_dmat; 146 147 error = agp_map_aperture(&isc->vga_pa, sc); 148 if (error != 0) { 149 aprint_error(": can't map aperture\n"); 150 free(isc, M_AGP); 151 return error; 152 } 153 154 switch (PCI_PRODUCT(isc->vga_pa.pa_id)) { 155 case PCI_PRODUCT_INTEL_82810_GC: 156 case PCI_PRODUCT_INTEL_82810_DC100_GC: 157 case PCI_PRODUCT_INTEL_82810E_GC: 158 case PCI_PRODUCT_INTEL_82815_FULL_GRAPH: 159 isc->chiptype = CHIP_I810; 160 break; 161 case PCI_PRODUCT_INTEL_82830MP_IV: 162 case PCI_PRODUCT_INTEL_82845G_IGD: 163 isc->chiptype = CHIP_I830; 164 break; 165 } 166 167 error = pci_mapreg_map(&isc->vga_pa, AGP_I810_MMADR, 168 PCI_MAPREG_TYPE_MEM, 0, &isc->bst, &isc->bsh, NULL, NULL); 169 if (error != 0) { 170 aprint_error(": can't map mmadr registers\n"); 171 return error; 172 } 173 174 isc->initial_aperture = AGP_GET_APERTURE(sc); 175 176 gatt = malloc(sizeof(struct agp_gatt), M_AGP, M_NOWAIT); 177 if (!gatt) { 178 agp_generic_detach(sc); 179 return ENOMEM; 180 } 181 isc->gatt = gatt; 182 183 gatt->ag_entries = AGP_GET_APERTURE(sc) >> AGP_PAGE_SHIFT; 184 185 if (isc->chiptype == CHIP_I810) { 186 int dummyseg; 187 /* Some i810s have on-chip memory called dcache */ 188 if (READ1(AGP_I810_DRT) & AGP_I810_DRT_POPULATED) 189 isc->dcache_size = 4 * 1024 * 1024; 190 else 191 isc->dcache_size = 0; 192 193 /* According to the specs the gatt on the i810 must be 64k */ 194 if (agp_alloc_dmamem(sc->as_dmat, 64 * 1024, 195 0, &gatt->ag_dmamap, (caddr_t *)&gatt->ag_virtual, 196 &gatt->ag_physical, &gatt->ag_dmaseg, 1, &dummyseg) != 0) { 197 free(gatt, M_AGP); 198 agp_generic_detach(sc); 199 return ENOMEM; 200 } 201 202 gatt->ag_size = gatt->ag_entries * sizeof(u_int32_t); 203 memset(gatt->ag_virtual, 0, gatt->ag_size); 204 205 agp_flush_cache(); 206 /* Install the GATT. */ 207 WRITE4(AGP_I810_PGTBL_CTL, gatt->ag_physical | 1); 208 } else { 209 /* The i830 automatically initializes the 128k gatt on boot. */ 210 pcireg_t reg; 211 u_int32_t pgtblctl; 212 u_int16_t gcc1; 213 214 reg = pci_conf_read(sc->as_pc, sc->as_tag, AGP_I830_GCC0); 215 gcc1 = (u_int16_t)(reg >> 16); 216 switch (gcc1 & AGP_I830_GCC1_GMS) { 217 case AGP_I830_GCC1_GMS_STOLEN_512: 218 isc->stolen = (512 - 132) * 1024 / 4096; 219 break; 220 case AGP_I830_GCC1_GMS_STOLEN_1024: 221 isc->stolen = (1024 - 132) * 1024 / 4096; 222 break; 223 case AGP_I830_GCC1_GMS_STOLEN_8192: 224 isc->stolen = (8192 - 132) * 1024 / 4096; 225 break; 226 default: 227 isc->stolen = 0; 228 aprint_error( 229 ": unknown memory configuration, disabling\n"); 230 agp_generic_detach(sc); 231 return EINVAL; 232 } 233 if (isc->stolen > 0) { 234 aprint_error(": detected %dk stolen memory\n", 235 isc->stolen * 4); 236 } 237 aprint_error("%s: aperture size is %dM\n", sc->as_dev.dv_xname, 238 isc->initial_aperture / 1024 / 1024); 239 240 /* GATT address is already in there, make sure it's enabled */ 241 pgtblctl = READ4(AGP_I810_PGTBL_CTL); 242 pgtblctl |= 1; 243 WRITE4(AGP_I810_PGTBL_CTL, pgtblctl); 244 245 gatt->ag_physical = pgtblctl & ~1; 246 } 247 248 /* 249 * Make sure the chipset can see everything. 250 */ 251 agp_flush_cache(); 252 253 return 0; 254 } 255 256 #if 0 257 static int 258 agp_i810_detach(struct agp_softc *sc) 259 { 260 int error; 261 struct agp_i810_softc *isc = sc->as_chipc; 262 263 error = agp_generic_detach(sc); 264 if (error) 265 return error; 266 267 /* Clear the GATT base. */ 268 if (sc->chiptype == CHIP_I810) { 269 WRITE4(AGP_I810_PGTBL_CTL, 0); 270 } else { 271 unsigned int pgtblctl; 272 pgtblctl = READ4(AGP_I810_PGTBL_CTL); 273 pgtblctl &= ~1; 274 WRITE4(AGP_I810_PGTBL_CTL, pgtblctl); 275 } 276 277 /* Put the aperture back the way it started. */ 278 AGP_SET_APERTURE(sc, isc->initial_aperture); 279 280 if (sc->chiptype == CHIP_I810) { 281 agp_free_dmamem(sc->as_dmat, gatt->ag_size, gatt->ag_dmamap, 282 (caddr_t)gatt->ag_virtual, &gatt->ag_dmaseg, 1); 283 } 284 free(sc->gatt, M_AGP); 285 286 return 0; 287 } 288 #endif 289 290 static u_int32_t 291 agp_i810_get_aperture(struct agp_softc *sc) 292 { 293 struct agp_i810_softc *isc = sc->as_chipc; 294 pcireg_t reg; 295 296 if (isc->chiptype == CHIP_I810) { 297 u_int16_t miscc; 298 299 reg = pci_conf_read(sc->as_pc, sc->as_tag, AGP_I810_SMRAM); 300 miscc = (u_int16_t)(reg >> 16); 301 if ((miscc & AGP_I810_MISCC_WINSIZE) == 302 AGP_I810_MISCC_WINSIZE_32) 303 return 32 * 1024 * 1024; 304 else 305 return 64 * 1024 * 1024; 306 } else { /* I830 */ 307 u_int16_t gcc1; 308 309 reg = pci_conf_read(sc->as_pc, sc->as_tag, AGP_I830_GCC0); 310 gcc1 = (u_int16_t)(reg >> 16); 311 if ((gcc1 & AGP_I830_GCC1_GMASIZE) == AGP_I830_GCC1_GMASIZE_64) 312 return 64 * 1024 * 1024; 313 else 314 return 128 * 1024 * 1024; 315 } 316 } 317 318 static int 319 agp_i810_set_aperture(struct agp_softc *sc, u_int32_t aperture) 320 { 321 struct agp_i810_softc *isc = sc->as_chipc; 322 pcireg_t reg; 323 324 if (isc->chiptype == CHIP_I810) { 325 u_int16_t miscc; 326 327 /* 328 * Double check for sanity. 329 */ 330 if (aperture != (32 * 1024 * 1024) && 331 aperture != (64 * 1024 * 1024)) { 332 printf("%s: bad aperture size %d\n", 333 sc->as_dev.dv_xname, aperture); 334 return EINVAL; 335 } 336 337 reg = pci_conf_read(sc->as_pc, sc->as_tag, AGP_I810_SMRAM); 338 miscc = (u_int16_t)(reg >> 16); 339 miscc &= ~AGP_I810_MISCC_WINSIZE; 340 if (aperture == 32 * 1024 * 1024) 341 miscc |= AGP_I810_MISCC_WINSIZE_32; 342 else 343 miscc |= AGP_I810_MISCC_WINSIZE_64; 344 345 reg &= 0x0000ffff; 346 reg |= ((pcireg_t)miscc) << 16; 347 pci_conf_write(sc->as_pc, sc->as_tag, AGP_I810_SMRAM, reg); 348 } else { /* I830 */ 349 u_int16_t gcc1; 350 351 if (aperture != (64 * 1024 * 1024) && 352 aperture != (128 * 1024 * 1024)) { 353 printf("%s: bad aperture size %d\n", 354 sc->as_dev.dv_xname, aperture); 355 return EINVAL; 356 } 357 reg = pci_conf_read(sc->as_pc, sc->as_tag, AGP_I830_GCC0); 358 gcc1 = (u_int16_t)(reg >> 16); 359 gcc1 &= ~AGP_I830_GCC1_GMASIZE; 360 if (aperture == 64 * 1024 * 1024) 361 gcc1 |= AGP_I830_GCC1_GMASIZE_64; 362 else 363 gcc1 |= AGP_I830_GCC1_GMASIZE_128; 364 365 reg &= 0x0000ffff; 366 reg |= ((pcireg_t)gcc1) << 16; 367 pci_conf_write(sc->as_pc, sc->as_tag, AGP_I830_GCC0, reg); 368 } 369 370 return 0; 371 } 372 373 static int 374 agp_i810_bind_page(struct agp_softc *sc, off_t offset, bus_addr_t physical) 375 { 376 struct agp_i810_softc *isc = sc->as_chipc; 377 378 if (offset < 0 || offset >= (isc->gatt->ag_entries << AGP_PAGE_SHIFT)) { 379 #ifdef DEBUG 380 printf("%s: failed: offset 0x%08x, shift %d, entries %d\n", 381 sc->as_dev.dv_xname, (int)offset, AGP_PAGE_SHIFT, 382 isc->gatt->ag_entries); 383 #endif 384 return EINVAL; 385 } 386 387 if (isc->chiptype == CHIP_I810) { 388 if ((offset >> AGP_PAGE_SHIFT) < isc->stolen) { 389 #ifdef DEBUG 390 printf("%s: trying to bind into stolen memory", 391 sc->as_dev.dv_xname); 392 #endif 393 return EINVAL; 394 } 395 } 396 397 WRITE4(AGP_I810_GTT + (u_int32_t)(offset >> AGP_PAGE_SHIFT) * 4, 398 physical | 1); 399 return 0; 400 } 401 402 static int 403 agp_i810_unbind_page(struct agp_softc *sc, off_t offset) 404 { 405 struct agp_i810_softc *isc = sc->as_chipc; 406 407 if (offset < 0 || offset >= (isc->gatt->ag_entries << AGP_PAGE_SHIFT)) 408 return EINVAL; 409 410 if (isc->chiptype == CHIP_I830 ) { 411 if ((offset >> AGP_PAGE_SHIFT) < isc->stolen) { 412 #ifdef DEBUG 413 printf("%s: trying to unbind from stolen memory", 414 sc->as_dev.dv_xname); 415 #endif 416 return EINVAL; 417 } 418 } 419 420 WRITE4(AGP_I810_GTT + (u_int32_t)(offset >> AGP_PAGE_SHIFT) * 4, 0); 421 return 0; 422 } 423 424 /* 425 * Writing via memory mapped registers already flushes all TLBs. 426 */ 427 static void 428 agp_i810_flush_tlb(struct agp_softc *sc) 429 { 430 } 431 432 static int 433 agp_i810_enable(struct agp_softc *sc, u_int32_t mode) 434 { 435 436 return 0; 437 } 438 439 static struct agp_memory * 440 agp_i810_alloc_memory(struct agp_softc *sc, int type, vsize_t size) 441 { 442 struct agp_i810_softc *isc = sc->as_chipc; 443 struct agp_memory *mem; 444 445 if ((size & (AGP_PAGE_SIZE - 1)) != 0) 446 return 0; 447 448 if (sc->as_allocated + size > sc->as_maxmem) 449 return 0; 450 451 if (type == 1) { 452 /* 453 * Mapping local DRAM into GATT. 454 */ 455 if (isc->chiptype == CHIP_I830 ) 456 return 0; 457 if (size != isc->dcache_size) 458 return 0; 459 } else if (type == 2) { 460 /* 461 * Bogus mapping of a single page for the hardware cursor. 462 */ 463 if (size != AGP_PAGE_SIZE) 464 return 0; 465 } 466 467 mem = malloc(sizeof *mem, M_AGP, M_WAITOK|M_ZERO); 468 if (mem == NULL) 469 return NULL; 470 mem->am_id = sc->as_nextid++; 471 mem->am_size = size; 472 mem->am_type = type; 473 474 if (type == 2) { 475 /* 476 * Allocate and wire down the page now so that we can 477 * get its physical address. 478 */ 479 mem->am_dmaseg = malloc(sizeof *mem->am_dmaseg, M_AGP, 480 M_WAITOK); 481 if (mem->am_dmaseg == NULL) { 482 free(mem, M_AGP); 483 return NULL; 484 } 485 if (agp_alloc_dmamem(sc->as_dmat, size, 0, 486 &mem->am_dmamap, &mem->am_virtual, &mem->am_physical, 487 mem->am_dmaseg, 1, &mem->am_nseg) != 0) { 488 free(mem->am_dmaseg, M_AGP); 489 free(mem, M_AGP); 490 return NULL; 491 } 492 } else if (type != 1) { 493 if (bus_dmamap_create(sc->as_dmat, size, size / PAGE_SIZE + 1, 494 size, 0, BUS_DMA_NOWAIT, 495 &mem->am_dmamap) != 0) { 496 free(mem, M_AGP); 497 return NULL; 498 } 499 } 500 501 TAILQ_INSERT_TAIL(&sc->as_memory, mem, am_link); 502 sc->as_allocated += size; 503 504 return mem; 505 } 506 507 static int 508 agp_i810_free_memory(struct agp_softc *sc, struct agp_memory *mem) 509 { 510 if (mem->am_is_bound) 511 return EBUSY; 512 513 if (mem->am_type == 2) { 514 agp_free_dmamem(sc->as_dmat, mem->am_size, mem->am_dmamap, 515 mem->am_virtual, mem->am_dmaseg, mem->am_nseg); 516 free(mem->am_dmaseg, M_AGP); 517 } 518 519 sc->as_allocated -= mem->am_size; 520 TAILQ_REMOVE(&sc->as_memory, mem, am_link); 521 free(mem, M_AGP); 522 return 0; 523 } 524 525 static int 526 agp_i810_bind_memory(struct agp_softc *sc, struct agp_memory *mem, 527 off_t offset) 528 { 529 struct agp_i810_softc *isc = sc->as_chipc; 530 u_int32_t regval, i; 531 532 /* 533 * XXX evil hack: the PGTBL_CTL appearently gets overwritten by the 534 * X server for mysterious reasons which leads to crashes if we write 535 * to the GTT through the MMIO window. 536 * Until the issue is solved, simply restore it. 537 */ 538 regval = bus_space_read_4(isc->bst, isc->bsh, AGP_I810_PGTBL_CTL); 539 if (regval != (isc->gatt->ag_physical | 1)) { 540 printf("agp_i810_bind_memory: PGTBL_CTL is 0x%x - fixing\n", 541 regval); 542 bus_space_write_4(isc->bst, isc->bsh, AGP_I810_PGTBL_CTL, 543 isc->gatt->ag_physical | 1); 544 } 545 546 if (mem->am_type == 2) { 547 WRITE4(AGP_I810_GTT + (u_int32_t)(offset >> AGP_PAGE_SHIFT) * 4, 548 mem->am_physical | 1); 549 mem->am_offset = offset; 550 mem->am_is_bound = 1; 551 return 0; 552 } 553 554 if (mem->am_type != 1) 555 return agp_generic_bind_memory(sc, mem, offset); 556 557 if (isc->chiptype == CHIP_I830) 558 return EINVAL; 559 560 for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) { 561 WRITE4(AGP_I810_GTT + (u_int32_t)(offset >> AGP_PAGE_SHIFT) * 4, 562 i | 3); 563 } 564 mem->am_is_bound = 1; 565 return 0; 566 } 567 568 static int 569 agp_i810_unbind_memory(struct agp_softc *sc, struct agp_memory *mem) 570 { 571 struct agp_i810_softc *isc = sc->as_chipc; 572 u_int32_t i; 573 574 if (mem->am_type == 2) { 575 WRITE4(AGP_I810_GTT + 576 (u_int32_t)(mem->am_offset >> AGP_PAGE_SHIFT) * 4, 577 0); 578 mem->am_offset = 0; 579 mem->am_is_bound = 0; 580 return 0; 581 } 582 583 if (mem->am_type != 1) 584 return agp_generic_unbind_memory(sc, mem); 585 586 if (isc->chiptype == CHIP_I830) 587 return EINVAL; 588 589 for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) 590 WRITE4(AGP_I810_GTT + (i >> AGP_PAGE_SHIFT) * 4, 0); 591 mem->am_is_bound = 0; 592 return 0; 593 } 594