1 1.22 chs /* $NetBSD: agp_via.c,v 1.22 2019/11/10 21:16:36 chs Exp $ */ 2 1.1 fvdl 3 1.1 fvdl /*- 4 1.1 fvdl * Copyright (c) 2000 Doug Rabson 5 1.1 fvdl * All rights reserved. 6 1.1 fvdl * 7 1.1 fvdl * Redistribution and use in source and binary forms, with or without 8 1.1 fvdl * modification, are permitted provided that the following conditions 9 1.1 fvdl * are met: 10 1.1 fvdl * 1. Redistributions of source code must retain the above copyright 11 1.1 fvdl * notice, this list of conditions and the following disclaimer. 12 1.1 fvdl * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 fvdl * notice, this list of conditions and the following disclaimer in the 14 1.1 fvdl * documentation and/or other materials provided with the distribution. 15 1.1 fvdl * 16 1.1 fvdl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 1.1 fvdl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 1.1 fvdl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 1.1 fvdl * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 1.1 fvdl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 1.1 fvdl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 1.1 fvdl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 1.1 fvdl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 1.1 fvdl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 1.1 fvdl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 1.1 fvdl * SUCH DAMAGE. 27 1.1 fvdl * 28 1.1 fvdl * $FreeBSD: src/sys/pci/agp_via.c,v 1.3 2001/07/05 21:28:47 jhb Exp $ 29 1.1 fvdl */ 30 1.3 lukem 31 1.3 lukem #include <sys/cdefs.h> 32 1.22 chs __KERNEL_RCSID(0, "$NetBSD: agp_via.c,v 1.22 2019/11/10 21:16:36 chs Exp $"); 33 1.1 fvdl 34 1.1 fvdl #include <sys/param.h> 35 1.1 fvdl #include <sys/systm.h> 36 1.1 fvdl #include <sys/malloc.h> 37 1.1 fvdl #include <sys/kernel.h> 38 1.1 fvdl #include <sys/proc.h> 39 1.1 fvdl #include <sys/conf.h> 40 1.1 fvdl #include <sys/device.h> 41 1.1 fvdl #include <sys/agpio.h> 42 1.1 fvdl 43 1.1 fvdl #include <dev/pci/pcivar.h> 44 1.1 fvdl #include <dev/pci/pcireg.h> 45 1.1 fvdl #include <dev/pci/agpvar.h> 46 1.1 fvdl #include <dev/pci/agpreg.h> 47 1.12 jmcneill #include <dev/pci/pcidevs.h> 48 1.1 fvdl 49 1.14 ad #include <sys/bus.h> 50 1.1 fvdl 51 1.1 fvdl static u_int32_t agp_via_get_aperture(struct agp_softc *); 52 1.1 fvdl static int agp_via_set_aperture(struct agp_softc *, u_int32_t); 53 1.1 fvdl static int agp_via_bind_page(struct agp_softc *, off_t, bus_addr_t); 54 1.1 fvdl static int agp_via_unbind_page(struct agp_softc *, off_t); 55 1.1 fvdl static void agp_via_flush_tlb(struct agp_softc *); 56 1.1 fvdl 57 1.7 thorpej static struct agp_methods agp_via_methods = { 58 1.1 fvdl agp_via_get_aperture, 59 1.1 fvdl agp_via_set_aperture, 60 1.1 fvdl agp_via_bind_page, 61 1.1 fvdl agp_via_unbind_page, 62 1.1 fvdl agp_via_flush_tlb, 63 1.1 fvdl agp_generic_enable, 64 1.1 fvdl agp_generic_alloc_memory, 65 1.1 fvdl agp_generic_free_memory, 66 1.1 fvdl agp_generic_bind_memory, 67 1.1 fvdl agp_generic_unbind_memory, 68 1.1 fvdl }; 69 1.1 fvdl 70 1.1 fvdl struct agp_via_softc { 71 1.1 fvdl u_int32_t initial_aperture; /* aperture size at startup */ 72 1.1 fvdl struct agp_gatt *gatt; 73 1.12 jmcneill int *regs; 74 1.12 jmcneill }; 75 1.12 jmcneill 76 1.12 jmcneill #define REG_GARTCTRL 0 77 1.12 jmcneill #define REG_APSIZE 1 78 1.12 jmcneill #define REG_ATTBASE 2 79 1.12 jmcneill 80 1.12 jmcneill static int via_v2_regs[] = 81 1.12 jmcneill { AGP_VIA_GARTCTRL, AGP_VIA_APSIZE, AGP_VIA_ATTBASE }; 82 1.12 jmcneill static int via_v3_regs[] = 83 1.12 jmcneill { AGP3_VIA_GARTCTRL, AGP3_VIA_APSIZE, AGP3_VIA_ATTBASE }; 84 1.12 jmcneill 85 1.1 fvdl int 86 1.17 freza agp_via_attach(device_t parent, device_t self, void *aux) 87 1.1 fvdl { 88 1.1 fvdl struct pci_attach_args *pa = aux; 89 1.17 freza struct agp_softc *sc = device_private(self); 90 1.1 fvdl struct agp_via_softc *asc; 91 1.1 fvdl struct agp_gatt *gatt; 92 1.13 jmcneill pcireg_t agpsel, capval; 93 1.1 fvdl 94 1.22 chs asc = malloc(sizeof *asc, M_AGP, M_WAITOK|M_ZERO); 95 1.1 fvdl sc->as_chipc = asc; 96 1.1 fvdl sc->as_methods = &agp_via_methods; 97 1.1 fvdl pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_AGP, &sc->as_capoff, 98 1.13 jmcneill &capval); 99 1.1 fvdl 100 1.13 jmcneill if (PCI_CAP_AGP_MAJOR(capval) >= 3) { 101 1.13 jmcneill agpsel = pci_conf_read(pa->pa_pc, pa->pa_tag, AGP_VIA_AGPSEL); 102 1.16 joerg if ((agpsel & (1 << 9)) == 0) { 103 1.13 jmcneill asc->regs = via_v3_regs; 104 1.20 jmcneill aprint_debug(" (v3)"); 105 1.13 jmcneill } else { 106 1.13 jmcneill asc->regs = via_v2_regs; 107 1.20 jmcneill aprint_debug(" (v2 compat mode)"); 108 1.12 jmcneill } 109 1.13 jmcneill } else { 110 1.13 jmcneill asc->regs = via_v2_regs; 111 1.20 jmcneill aprint_debug(" (v2)"); 112 1.12 jmcneill } 113 1.12 jmcneill 114 1.9 christos if (agp_map_aperture(pa, sc, AGP_APBASE) != 0) { 115 1.5 thorpej aprint_error(": can't map aperture\n"); 116 1.1 fvdl free(asc, M_AGP); 117 1.1 fvdl return ENXIO; 118 1.1 fvdl } 119 1.1 fvdl 120 1.1 fvdl asc->initial_aperture = AGP_GET_APERTURE(sc); 121 1.1 fvdl 122 1.1 fvdl for (;;) { 123 1.1 fvdl gatt = agp_alloc_gatt(sc); 124 1.1 fvdl if (gatt) 125 1.1 fvdl break; 126 1.1 fvdl 127 1.1 fvdl /* 128 1.1 fvdl * Probably contigmalloc failure. Try reducing the 129 1.1 fvdl * aperture so that the gatt size reduces. 130 1.1 fvdl */ 131 1.1 fvdl if (AGP_SET_APERTURE(sc, AGP_GET_APERTURE(sc) / 2)) { 132 1.1 fvdl agp_generic_detach(sc); 133 1.5 thorpej aprint_error(": can't set aperture size\n"); 134 1.1 fvdl return ENOMEM; 135 1.1 fvdl } 136 1.1 fvdl } 137 1.1 fvdl asc->gatt = gatt; 138 1.1 fvdl 139 1.12 jmcneill if (asc->regs == via_v2_regs) { 140 1.12 jmcneill /* Install the gatt. */ 141 1.12 jmcneill pci_conf_write(pa->pa_pc, pa->pa_tag, asc->regs[REG_ATTBASE], 142 1.12 jmcneill gatt->ag_physical | 3); 143 1.12 jmcneill /* Enable the aperture. */ 144 1.12 jmcneill pci_conf_write(pa->pa_pc, pa->pa_tag, asc->regs[REG_GARTCTRL], 145 1.12 jmcneill 0x0000000f); 146 1.12 jmcneill } else { 147 1.12 jmcneill pcireg_t gartctrl; 148 1.12 jmcneill /* Install the gatt. */ 149 1.12 jmcneill pci_conf_write(pa->pa_pc, pa->pa_tag, asc->regs[REG_ATTBASE], 150 1.12 jmcneill gatt->ag_physical); 151 1.12 jmcneill /* Enable the aperture. */ 152 1.12 jmcneill gartctrl = pci_conf_read(pa->pa_pc, pa->pa_tag, 153 1.21 jmcneill asc->regs[REG_GARTCTRL]); 154 1.12 jmcneill pci_conf_write(pa->pa_pc, pa->pa_tag, asc->regs[REG_GARTCTRL], 155 1.12 jmcneill gartctrl | (3 << 7)); 156 1.12 jmcneill } 157 1.1 fvdl 158 1.1 fvdl return 0; 159 1.1 fvdl } 160 1.1 fvdl 161 1.1 fvdl #if 0 162 1.1 fvdl static int 163 1.1 fvdl agp_via_detach(struct agp_softc *sc) 164 1.1 fvdl { 165 1.1 fvdl struct agp_via_softc *asc = sc->as_chipc; 166 1.1 fvdl int error; 167 1.1 fvdl 168 1.1 fvdl error = agp_generic_detach(sc); 169 1.1 fvdl if (error) 170 1.1 fvdl return error; 171 1.1 fvdl 172 1.12 jmcneill pci_conf_write(sc->as_pc, sc->as_tag, asc->regs[REG_GARTCTRL], 0); 173 1.12 jmcneill pci_conf_write(sc->as_pc, sc->as_tag, asc->regs[REG_ATTBASE], 0); 174 1.1 fvdl AGP_SET_APERTURE(sc, asc->initial_aperture); 175 1.1 fvdl agp_free_gatt(sc, asc->gatt); 176 1.1 fvdl 177 1.1 fvdl return 0; 178 1.1 fvdl } 179 1.1 fvdl #endif 180 1.1 fvdl 181 1.1 fvdl static u_int32_t 182 1.1 fvdl agp_via_get_aperture(struct agp_softc *sc) 183 1.1 fvdl { 184 1.12 jmcneill struct agp_via_softc *asc = sc->as_chipc; 185 1.1 fvdl u_int32_t apsize; 186 1.1 fvdl 187 1.21 jmcneill if (asc->regs == via_v2_regs) { 188 1.21 jmcneill apsize = pci_conf_read(sc->as_pc, sc->as_tag, 189 1.21 jmcneill asc->regs[REG_APSIZE]) & 0xff; 190 1.1 fvdl 191 1.21 jmcneill /* 192 1.21 jmcneill * The size is determined by the number of low bits of 193 1.21 jmcneill * register APBASE which are forced to zero. The low 20 bits 194 1.21 jmcneill * are always forced to zero and each zero bit in the apsize 195 1.21 jmcneill * field just read forces the corresponding bit in the 27:20 196 1.21 jmcneill * to be zero. We calculate the aperture size accordingly. 197 1.21 jmcneill */ 198 1.21 jmcneill return (((apsize ^ 0xff) << 20) | ((1 << 20) - 1)) + 1; 199 1.21 jmcneill } else { 200 1.21 jmcneill apsize = pci_conf_read(sc->as_pc, sc->as_tag, 201 1.21 jmcneill asc->regs[REG_APSIZE]) & 0xfff; 202 1.21 jmcneill switch (apsize) { 203 1.21 jmcneill case 0x800: 204 1.21 jmcneill return 0x80000000; 205 1.21 jmcneill case 0xc00: 206 1.21 jmcneill return 0x40000000; 207 1.21 jmcneill case 0xe00: 208 1.21 jmcneill return 0x20000000; 209 1.21 jmcneill case 0xf00: 210 1.21 jmcneill return 0x10000000; 211 1.21 jmcneill case 0xf20: 212 1.21 jmcneill return 0x08000000; 213 1.21 jmcneill case 0xf30: 214 1.21 jmcneill return 0x04000000; 215 1.21 jmcneill case 0xf38: 216 1.21 jmcneill return 0x02000000; 217 1.21 jmcneill case 0xf3c: 218 1.21 jmcneill return 0x01000000; 219 1.21 jmcneill case 0xf3e: 220 1.21 jmcneill return 0x00800000; 221 1.21 jmcneill case 0xf3f: 222 1.21 jmcneill return 0x00400000; 223 1.21 jmcneill default: 224 1.21 jmcneill aprint_error_dev(sc->as_dev, 225 1.21 jmcneill "invalid aperture setting 0x%x\n", apsize); 226 1.21 jmcneill return 0; 227 1.21 jmcneill } 228 1.21 jmcneill } 229 1.1 fvdl } 230 1.1 fvdl 231 1.1 fvdl static int 232 1.1 fvdl agp_via_set_aperture(struct agp_softc *sc, u_int32_t aperture) 233 1.1 fvdl { 234 1.12 jmcneill struct agp_via_softc *asc = sc->as_chipc; 235 1.21 jmcneill u_int32_t apsize, key; 236 1.1 fvdl pcireg_t reg; 237 1.1 fvdl 238 1.21 jmcneill if (asc->regs == via_v2_regs) { 239 1.21 jmcneill /* 240 1.21 jmcneill * Reverse the magic from get_aperture. 241 1.21 jmcneill */ 242 1.21 jmcneill apsize = ((aperture - 1) >> 20) ^ 0xff; 243 1.21 jmcneill 244 1.21 jmcneill /* 245 1.21 jmcneill * Double check for sanity. 246 1.21 jmcneill */ 247 1.21 jmcneill if ((((apsize ^ 0xff) << 20) | ((1 << 20) - 1)) + 1 != aperture) 248 1.21 jmcneill return EINVAL; 249 1.1 fvdl 250 1.21 jmcneill reg = pci_conf_read(sc->as_pc, sc->as_tag, 251 1.21 jmcneill asc->regs[REG_APSIZE]); 252 1.21 jmcneill reg &= ~0xff; 253 1.21 jmcneill reg |= apsize; 254 1.21 jmcneill pci_conf_write(sc->as_pc, sc->as_tag, 255 1.21 jmcneill asc->regs[REG_APSIZE], reg); 256 1.21 jmcneill } else { 257 1.21 jmcneill switch (aperture) { 258 1.21 jmcneill case 0x80000000: 259 1.21 jmcneill key = 0x800; 260 1.21 jmcneill break; 261 1.21 jmcneill case 0x40000000: 262 1.21 jmcneill key = 0xc00; 263 1.21 jmcneill break; 264 1.21 jmcneill case 0x20000000: 265 1.21 jmcneill key = 0xe00; 266 1.21 jmcneill break; 267 1.21 jmcneill case 0x10000000: 268 1.21 jmcneill key = 0xf00; 269 1.21 jmcneill break; 270 1.21 jmcneill case 0x08000000: 271 1.21 jmcneill key = 0xf20; 272 1.21 jmcneill break; 273 1.21 jmcneill case 0x04000000: 274 1.21 jmcneill key = 0xf30; 275 1.21 jmcneill break; 276 1.21 jmcneill case 0x02000000: 277 1.21 jmcneill key = 0xf38; 278 1.21 jmcneill break; 279 1.21 jmcneill case 0x01000000: 280 1.21 jmcneill key = 0xf3c; 281 1.21 jmcneill break; 282 1.21 jmcneill case 0x00800000: 283 1.21 jmcneill key = 0xf3e; 284 1.21 jmcneill break; 285 1.21 jmcneill case 0x00400000: 286 1.21 jmcneill key = 0xf3f; 287 1.21 jmcneill break; 288 1.21 jmcneill default: 289 1.21 jmcneill aprint_error_dev(sc->as_dev, 290 1.21 jmcneill "invalid aperture size (%dMB)\n", 291 1.21 jmcneill aperture / 1024 / 1024); 292 1.21 jmcneill return EINVAL; 293 1.21 jmcneill } 294 1.21 jmcneill reg = pci_conf_read(sc->as_pc, sc->as_tag, asc->regs[REG_APSIZE]); 295 1.21 jmcneill reg &= ~0xfff; 296 1.21 jmcneill reg |= key; 297 1.21 jmcneill pci_conf_write(sc->as_pc, sc->as_tag, asc->regs[REG_APSIZE], reg); 298 1.21 jmcneill } 299 1.1 fvdl 300 1.1 fvdl return 0; 301 1.1 fvdl } 302 1.1 fvdl 303 1.1 fvdl static int 304 1.1 fvdl agp_via_bind_page(struct agp_softc *sc, off_t offset, bus_addr_t physical) 305 1.1 fvdl { 306 1.1 fvdl struct agp_via_softc *asc = sc->as_chipc; 307 1.1 fvdl 308 1.1 fvdl if (offset < 0 || offset >= (asc->gatt->ag_entries << AGP_PAGE_SHIFT)) 309 1.1 fvdl return EINVAL; 310 1.1 fvdl 311 1.1 fvdl asc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical; 312 1.1 fvdl return 0; 313 1.1 fvdl } 314 1.1 fvdl 315 1.1 fvdl static int 316 1.1 fvdl agp_via_unbind_page(struct agp_softc *sc, off_t offset) 317 1.1 fvdl { 318 1.1 fvdl struct agp_via_softc *asc = sc->as_chipc; 319 1.1 fvdl 320 1.1 fvdl if (offset < 0 || offset >= (asc->gatt->ag_entries << AGP_PAGE_SHIFT)) 321 1.1 fvdl return EINVAL; 322 1.1 fvdl 323 1.1 fvdl asc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0; 324 1.1 fvdl return 0; 325 1.1 fvdl } 326 1.1 fvdl 327 1.1 fvdl static void 328 1.1 fvdl agp_via_flush_tlb(struct agp_softc *sc) 329 1.1 fvdl { 330 1.12 jmcneill struct agp_via_softc *asc = sc->as_chipc; 331 1.12 jmcneill pcireg_t gartctrl; 332 1.12 jmcneill 333 1.12 jmcneill if (asc->regs == via_v2_regs) { 334 1.12 jmcneill pci_conf_write(sc->as_pc, sc->as_tag, asc->regs[REG_GARTCTRL], 335 1.12 jmcneill 0x8f); 336 1.12 jmcneill pci_conf_write(sc->as_pc, sc->as_tag, asc->regs[REG_GARTCTRL], 337 1.12 jmcneill 0x0f); 338 1.12 jmcneill } else { 339 1.12 jmcneill gartctrl = pci_conf_read(sc->as_pc, sc->as_tag, 340 1.12 jmcneill asc->regs[REG_GARTCTRL]); 341 1.12 jmcneill pci_conf_write(sc->as_pc, sc->as_tag, asc->regs[REG_GARTCTRL], 342 1.12 jmcneill gartctrl & ~(1 << 7)); 343 1.12 jmcneill pci_conf_write(sc->as_pc, sc->as_tag, asc->regs[REG_GARTCTRL], 344 1.12 jmcneill gartctrl); 345 1.12 jmcneill } 346 1.1 fvdl } 347