1 /* $NetBSD: agp_i810.c,v 1.8 2001/09/20 20:00:16 fvdl 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/param.h> 33 #include <sys/systm.h> 34 #include <sys/malloc.h> 35 #include <sys/kernel.h> 36 #include <sys/lock.h> 37 #include <sys/proc.h> 38 #include <sys/device.h> 39 #include <sys/conf.h> 40 41 #include <uvm/uvm_extern.h> 42 43 #include <dev/pci/pcivar.h> 44 #include <dev/pci/pcireg.h> 45 #include <dev/pci/pcidevs.h> 46 #include <dev/pci/agpvar.h> 47 #include <dev/pci/agpreg.h> 48 49 #include <sys/agpio.h> 50 51 #include <machine/bus.h> 52 53 #define READ1(off) bus_space_read_1(isc->bst, isc->bsh, off) 54 #define WRITE4(off,v) bus_space_write_4(isc->bst, isc->bsh, off, v) 55 56 struct agp_i810_softc { 57 u_int32_t initial_aperture; /* aperture size at startup */ 58 struct agp_gatt *gatt; 59 u_int32_t dcache_size; 60 bus_space_tag_t bst; /* bus_space tag */ 61 bus_space_handle_t bsh; /* bus_space handle */ 62 struct pci_attach_args vga_pa; 63 }; 64 65 static u_int32_t agp_i810_get_aperture(struct agp_softc *); 66 static int agp_i810_set_aperture(struct agp_softc *, u_int32_t); 67 static int agp_i810_bind_page(struct agp_softc *, off_t, bus_addr_t); 68 static int agp_i810_unbind_page(struct agp_softc *, off_t); 69 static void agp_i810_flush_tlb(struct agp_softc *); 70 static int agp_i810_enable(struct agp_softc *, u_int32_t mode); 71 static struct agp_memory *agp_i810_alloc_memory(struct agp_softc *, int, 72 vsize_t); 73 static int agp_i810_free_memory(struct agp_softc *, struct agp_memory *); 74 static int agp_i810_bind_memory(struct agp_softc *, struct agp_memory *, off_t); 75 static int agp_i810_unbind_memory(struct agp_softc *, struct agp_memory *); 76 77 struct agp_methods agp_i810_methods = { 78 agp_i810_get_aperture, 79 agp_i810_set_aperture, 80 agp_i810_bind_page, 81 agp_i810_unbind_page, 82 agp_i810_flush_tlb, 83 agp_i810_enable, 84 agp_i810_alloc_memory, 85 agp_i810_free_memory, 86 agp_i810_bind_memory, 87 agp_i810_unbind_memory, 88 }; 89 90 /* XXXthorpej -- duplicated code (see arch/i386/pci/pchb.c) */ 91 static int 92 agp_i810_vgamatch(struct pci_attach_args *pa) 93 { 94 95 if (PCI_CLASS(pa->pa_class) != PCI_CLASS_DISPLAY || 96 PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_DISPLAY_VGA) 97 return (0); 98 99 switch (PCI_PRODUCT(pa->pa_id)) { 100 case PCI_PRODUCT_INTEL_82810_GC: 101 case PCI_PRODUCT_INTEL_82810_DC100_GC: 102 case PCI_PRODUCT_INTEL_82810E_GC: 103 case PCI_PRODUCT_INTEL_82815_FULL_GRAPH: 104 return (1); 105 } 106 107 return (0); 108 } 109 110 int 111 agp_i810_attach(struct device *parent, struct device *self, void *aux) 112 { 113 struct agp_softc *sc = (void *)self; 114 struct agp_i810_softc *isc; 115 struct agp_gatt *gatt; 116 int error; 117 118 isc = malloc(sizeof *isc, M_AGP, M_NOWAIT); 119 if (isc == NULL) { 120 printf(": can't allocate chipset-specific softc\n"); 121 return ENOMEM; 122 } 123 memset(isc, 0, sizeof *isc); 124 sc->as_chipc = isc; 125 sc->as_methods = &agp_i810_methods; 126 127 if (pci_find_device(&isc->vga_pa, agp_i810_vgamatch) == 0) { 128 printf(": can't find internal VGA device config space\n"); 129 free(isc, M_AGP); 130 return ENOENT; 131 } 132 133 /* XXXfvdl */ 134 sc->as_dmat = isc->vga_pa.pa_dmat; 135 136 error = agp_map_aperture(&isc->vga_pa, sc); 137 if (error != 0) { 138 printf(": can't map aperture\n"); 139 free(isc, M_AGP); 140 return error; 141 } 142 143 error = pci_mapreg_map(&isc->vga_pa, AGP_I810_MMADR, 144 PCI_MAPREG_TYPE_MEM, 0, &isc->bst, &isc->bsh, NULL, NULL); 145 if (error != 0) { 146 printf(": can't map mmadr registers\n"); 147 return error; 148 } 149 150 isc->initial_aperture = AGP_GET_APERTURE(sc); 151 152 if (READ1(AGP_I810_DRT) & AGP_I810_DRT_POPULATED) 153 isc->dcache_size = 4 * 1024 * 1024; 154 else 155 isc->dcache_size = 0; 156 157 for (;;) { 158 gatt = agp_alloc_gatt(sc); 159 if (gatt) 160 break; 161 162 /* 163 * Probably contigmalloc failure. Try reducing the 164 * aperture so that the gatt size reduces. 165 */ 166 if (AGP_SET_APERTURE(sc, AGP_GET_APERTURE(sc) / 2)) { 167 agp_generic_detach(sc); 168 return ENOMEM; 169 } 170 } 171 isc->gatt = gatt; 172 173 /* Install the GATT. */ 174 WRITE4(AGP_I810_PGTBL_CTL, gatt->ag_physical | 1); 175 176 /* 177 * Make sure the chipset can see everything. 178 */ 179 agp_flush_cache(); 180 181 return 0; 182 } 183 184 #if 0 185 static int 186 agp_i810_detach(struct agp_softc *sc) 187 { 188 int error; 189 struct agp_i810_softc *isc = sc->as_chipc; 190 191 error = agp_generic_detach(sc); 192 if (error) 193 return error; 194 195 /* Clear the GATT base. */ 196 WRITE4(AGP_I810_PGTBL_CTL, 0); 197 198 /* Put the aperture back the way it started. */ 199 AGP_SET_APERTURE(sc, isc->initial_aperture); 200 201 agp_free_gatt(sc, isc->gatt); 202 203 return 0; 204 } 205 #endif 206 207 static u_int32_t 208 agp_i810_get_aperture(struct agp_softc *sc) 209 { 210 u_int16_t miscc; 211 212 miscc = pci_conf_read(sc->as_pc, sc->as_tag, AGP_I810_SMRAM) >> 16; 213 if ((miscc & AGP_I810_MISCC_WINSIZE) == AGP_I810_MISCC_WINSIZE_32) 214 return 32 * 1024 * 1024; 215 else 216 return 64 * 1024 * 1024; 217 } 218 219 static int 220 agp_i810_set_aperture(struct agp_softc *sc, u_int32_t aperture) 221 { 222 pcireg_t reg, miscc; 223 224 /* 225 * Double check for sanity. 226 */ 227 if (aperture != 32 * 1024 * 1024 && aperture != 64 * 1024 * 1024) { 228 printf("%s: bad aperture size %d\n", sc->as_dev.dv_xname, 229 aperture); 230 return EINVAL; 231 } 232 233 reg = pci_conf_read(sc->as_pc, sc->as_tag, AGP_I810_SMRAM); 234 miscc = reg >> 16; 235 miscc &= ~AGP_I810_MISCC_WINSIZE; 236 if (aperture == 32 * 1024 * 1024) 237 miscc |= AGP_I810_MISCC_WINSIZE_32; 238 else 239 miscc |= AGP_I810_MISCC_WINSIZE_64; 240 241 reg &= 0x0000ffff; 242 reg |= (miscc << 16); 243 pci_conf_write(sc->as_pc, sc->as_tag, AGP_I810_SMRAM, miscc); 244 245 return 0; 246 } 247 248 static int 249 agp_i810_bind_page(struct agp_softc *sc, off_t offset, bus_addr_t physical) 250 { 251 struct agp_i810_softc *isc = sc->as_chipc; 252 253 if (offset < 0 || offset >= (isc->gatt->ag_entries << AGP_PAGE_SHIFT)) 254 return EINVAL; 255 256 WRITE4(AGP_I810_GTT + (u_int32_t)(offset >> AGP_PAGE_SHIFT) * 4, 257 physical | 1); 258 return 0; 259 } 260 261 static int 262 agp_i810_unbind_page(struct agp_softc *sc, off_t offset) 263 { 264 struct agp_i810_softc *isc = sc->as_chipc; 265 266 if (offset < 0 || offset >= (isc->gatt->ag_entries << AGP_PAGE_SHIFT)) 267 return EINVAL; 268 269 WRITE4(AGP_I810_GTT + (u_int32_t)(offset >> AGP_PAGE_SHIFT) * 4, 0); 270 return 0; 271 } 272 273 /* 274 * Writing via memory mapped registers already flushes all TLBs. 275 */ 276 static void 277 agp_i810_flush_tlb(struct agp_softc *sc) 278 { 279 } 280 281 static int 282 agp_i810_enable(struct agp_softc *sc, u_int32_t mode) 283 { 284 285 return 0; 286 } 287 288 static struct agp_memory * 289 agp_i810_alloc_memory(struct agp_softc *sc, int type, vsize_t size) 290 { 291 struct agp_i810_softc *isc = sc->as_chipc; 292 struct agp_memory *mem; 293 294 if ((size & (AGP_PAGE_SIZE - 1)) != 0) 295 return 0; 296 297 if (sc->as_allocated + size > sc->as_maxmem) 298 return 0; 299 300 if (type == 1) { 301 /* 302 * Mapping local DRAM into GATT. 303 */ 304 if (size != isc->dcache_size) 305 return 0; 306 } else if (type == 2) { 307 /* 308 * Bogus mapping of a single page for the hardware cursor. 309 */ 310 if (size != AGP_PAGE_SIZE) 311 return 0; 312 } 313 314 mem = malloc(sizeof *mem, M_AGP, M_WAITOK); 315 if (mem == NULL) 316 return NULL; 317 memset(mem, 0, sizeof *mem); 318 mem->am_id = sc->as_nextid++; 319 mem->am_size = size; 320 mem->am_type = type; 321 322 if (type == 2) { 323 /* 324 * Allocate and wire down the page now so that we can 325 * get its physical address. 326 */ 327 mem->am_dmaseg = malloc(sizeof *mem->am_dmaseg, M_AGP, 328 M_WAITOK); 329 if (mem->am_dmaseg == NULL) { 330 free(mem, M_AGP); 331 return NULL; 332 } 333 if (agp_alloc_dmamem(sc->as_dmat, size, 0, 334 &mem->am_dmamap, &mem->am_virtual, &mem->am_physical, 335 mem->am_dmaseg, 1, &mem->am_nseg) != 0) { 336 free(mem->am_dmaseg, M_AGP); 337 free(mem, M_AGP); 338 return NULL; 339 } 340 } else if (type != 1) { 341 if (bus_dmamap_create(sc->as_dmat, size, size / PAGE_SIZE + 1, 342 size, 0, BUS_DMA_NOWAIT, 343 &mem->am_dmamap) != 0) { 344 free(mem, M_AGP); 345 return NULL; 346 } 347 } 348 349 TAILQ_INSERT_TAIL(&sc->as_memory, mem, am_link); 350 sc->as_allocated += size; 351 352 return mem; 353 } 354 355 static int 356 agp_i810_free_memory(struct agp_softc *sc, struct agp_memory *mem) 357 { 358 if (mem->am_is_bound) 359 return EBUSY; 360 361 if (mem->am_type == 2) { 362 agp_free_dmamem(sc->as_dmat, mem->am_size, mem->am_dmamap, 363 mem->am_virtual, mem->am_dmaseg, mem->am_nseg); 364 free(mem->am_dmaseg, M_AGP); 365 } 366 367 sc->as_allocated -= mem->am_size; 368 TAILQ_REMOVE(&sc->as_memory, mem, am_link); 369 free(mem, M_AGP); 370 return 0; 371 } 372 373 static int 374 agp_i810_bind_memory(struct agp_softc *sc, struct agp_memory *mem, 375 off_t offset) 376 { 377 struct agp_i810_softc *isc = sc->as_chipc; 378 u_int32_t regval, i; 379 380 /* 381 * XXX evil hack: the PGTBL_CTL appearently gets overwritten by the 382 * X server for mysterious reasons which leads to crashes if we write 383 * to the GTT through the MMIO window. 384 * Until the issue is solved, simply restore it. 385 */ 386 regval = bus_space_read_4(isc->bst, isc->bsh, AGP_I810_PGTBL_CTL); 387 if (regval != (isc->gatt->ag_physical | 1)) { 388 printf("agp_i810_bind_memory: PGTBL_CTL is 0x%x - fixing\n", 389 regval); 390 bus_space_write_4(isc->bst, isc->bsh, AGP_I810_PGTBL_CTL, 391 isc->gatt->ag_physical | 1); 392 } 393 394 if (mem->am_type == 2) { 395 WRITE4(AGP_I810_GTT + (u_int32_t)(offset >> AGP_PAGE_SHIFT) * 4, 396 mem->am_physical | 1); 397 mem->am_offset = offset; 398 mem->am_is_bound = 1; 399 return 0; 400 } 401 402 if (mem->am_type != 1) 403 return agp_generic_bind_memory(sc, mem, offset); 404 405 for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) { 406 WRITE4(AGP_I810_GTT + (u_int32_t)(offset >> AGP_PAGE_SHIFT) * 4, 407 i | 3); 408 } 409 410 return 0; 411 } 412 413 static int 414 agp_i810_unbind_memory(struct agp_softc *sc, struct agp_memory *mem) 415 { 416 struct agp_i810_softc *isc = sc->as_chipc; 417 u_int32_t i; 418 419 if (mem->am_type == 2) { 420 WRITE4(AGP_I810_GTT + 421 (u_int32_t)(mem->am_offset >> AGP_PAGE_SHIFT) * 4, 422 0); 423 mem->am_offset = 0; 424 mem->am_is_bound = 0; 425 return 0; 426 } 427 428 if (mem->am_type != 1) 429 return agp_generic_unbind_memory(sc, mem); 430 431 for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) 432 WRITE4(AGP_I810_GTT + (i >> AGP_PAGE_SHIFT) * 4, 0); 433 434 return 0; 435 } 436