1 /* $NetBSD: agp_i810.c,v 1.13 2002/08/11 12:36:21 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.13 2002/08/11 12:36:21 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 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 /*case PCI_PRODUCT_INTEL_82830MP_IV: XXX needs somewhat different driver */ 108 return (1); 109 } 110 111 return (0); 112 } 113 114 int 115 agp_i810_attach(struct device *parent, struct device *self, void *aux) 116 { 117 struct agp_softc *sc = (void *)self; 118 struct agp_i810_softc *isc; 119 struct agp_gatt *gatt; 120 int error; 121 122 isc = malloc(sizeof *isc, M_AGP, M_NOWAIT|M_ZERO); 123 if (isc == NULL) { 124 printf(": can't allocate chipset-specific softc\n"); 125 return ENOMEM; 126 } 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|M_ZERO); 318 if (mem == NULL) 319 return NULL; 320 mem->am_id = sc->as_nextid++; 321 mem->am_size = size; 322 mem->am_type = type; 323 324 if (type == 2) { 325 /* 326 * Allocate and wire down the page now so that we can 327 * get its physical address. 328 */ 329 mem->am_dmaseg = malloc(sizeof *mem->am_dmaseg, M_AGP, 330 M_WAITOK); 331 if (mem->am_dmaseg == NULL) { 332 free(mem, M_AGP); 333 return NULL; 334 } 335 if (agp_alloc_dmamem(sc->as_dmat, size, 0, 336 &mem->am_dmamap, &mem->am_virtual, &mem->am_physical, 337 mem->am_dmaseg, 1, &mem->am_nseg) != 0) { 338 free(mem->am_dmaseg, M_AGP); 339 free(mem, M_AGP); 340 return NULL; 341 } 342 } else if (type != 1) { 343 if (bus_dmamap_create(sc->as_dmat, size, size / PAGE_SIZE + 1, 344 size, 0, BUS_DMA_NOWAIT, 345 &mem->am_dmamap) != 0) { 346 free(mem, M_AGP); 347 return NULL; 348 } 349 } 350 351 TAILQ_INSERT_TAIL(&sc->as_memory, mem, am_link); 352 sc->as_allocated += size; 353 354 return mem; 355 } 356 357 static int 358 agp_i810_free_memory(struct agp_softc *sc, struct agp_memory *mem) 359 { 360 if (mem->am_is_bound) 361 return EBUSY; 362 363 if (mem->am_type == 2) { 364 agp_free_dmamem(sc->as_dmat, mem->am_size, mem->am_dmamap, 365 mem->am_virtual, mem->am_dmaseg, mem->am_nseg); 366 free(mem->am_dmaseg, M_AGP); 367 } 368 369 sc->as_allocated -= mem->am_size; 370 TAILQ_REMOVE(&sc->as_memory, mem, am_link); 371 free(mem, M_AGP); 372 return 0; 373 } 374 375 static int 376 agp_i810_bind_memory(struct agp_softc *sc, struct agp_memory *mem, 377 off_t offset) 378 { 379 struct agp_i810_softc *isc = sc->as_chipc; 380 u_int32_t regval, i; 381 382 /* 383 * XXX evil hack: the PGTBL_CTL appearently gets overwritten by the 384 * X server for mysterious reasons which leads to crashes if we write 385 * to the GTT through the MMIO window. 386 * Until the issue is solved, simply restore it. 387 */ 388 regval = bus_space_read_4(isc->bst, isc->bsh, AGP_I810_PGTBL_CTL); 389 if (regval != (isc->gatt->ag_physical | 1)) { 390 printf("agp_i810_bind_memory: PGTBL_CTL is 0x%x - fixing\n", 391 regval); 392 bus_space_write_4(isc->bst, isc->bsh, AGP_I810_PGTBL_CTL, 393 isc->gatt->ag_physical | 1); 394 } 395 396 if (mem->am_type == 2) { 397 WRITE4(AGP_I810_GTT + (u_int32_t)(offset >> AGP_PAGE_SHIFT) * 4, 398 mem->am_physical | 1); 399 mem->am_offset = offset; 400 mem->am_is_bound = 1; 401 return 0; 402 } 403 404 if (mem->am_type != 1) 405 return agp_generic_bind_memory(sc, mem, offset); 406 407 for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) { 408 WRITE4(AGP_I810_GTT + (u_int32_t)(offset >> AGP_PAGE_SHIFT) * 4, 409 i | 3); 410 } 411 mem->am_is_bound = 1; 412 return 0; 413 } 414 415 static int 416 agp_i810_unbind_memory(struct agp_softc *sc, struct agp_memory *mem) 417 { 418 struct agp_i810_softc *isc = sc->as_chipc; 419 u_int32_t i; 420 421 if (mem->am_type == 2) { 422 WRITE4(AGP_I810_GTT + 423 (u_int32_t)(mem->am_offset >> AGP_PAGE_SHIFT) * 4, 424 0); 425 mem->am_offset = 0; 426 mem->am_is_bound = 0; 427 return 0; 428 } 429 430 if (mem->am_type != 1) 431 return agp_generic_unbind_memory(sc, mem); 432 433 for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) 434 WRITE4(AGP_I810_GTT + (i >> AGP_PAGE_SHIFT) * 4, 0); 435 mem->am_is_bound = 0; 436 return 0; 437 } 438