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