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