1 /* $NetBSD: agp_via.c,v 1.22 2019/11/10 21:16:36 chs 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.22 2019/11/10 21:16:36 chs Exp $"); 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/malloc.h> 37 #include <sys/kernel.h> 38 #include <sys/proc.h> 39 #include <sys/conf.h> 40 #include <sys/device.h> 41 #include <sys/agpio.h> 42 43 #include <dev/pci/pcivar.h> 44 #include <dev/pci/pcireg.h> 45 #include <dev/pci/agpvar.h> 46 #include <dev/pci/agpreg.h> 47 #include <dev/pci/pcidevs.h> 48 49 #include <sys/bus.h> 50 51 static u_int32_t agp_via_get_aperture(struct agp_softc *); 52 static int agp_via_set_aperture(struct agp_softc *, u_int32_t); 53 static int agp_via_bind_page(struct agp_softc *, off_t, bus_addr_t); 54 static int agp_via_unbind_page(struct agp_softc *, off_t); 55 static void agp_via_flush_tlb(struct agp_softc *); 56 57 static struct agp_methods agp_via_methods = { 58 agp_via_get_aperture, 59 agp_via_set_aperture, 60 agp_via_bind_page, 61 agp_via_unbind_page, 62 agp_via_flush_tlb, 63 agp_generic_enable, 64 agp_generic_alloc_memory, 65 agp_generic_free_memory, 66 agp_generic_bind_memory, 67 agp_generic_unbind_memory, 68 }; 69 70 struct agp_via_softc { 71 u_int32_t initial_aperture; /* aperture size at startup */ 72 struct agp_gatt *gatt; 73 int *regs; 74 }; 75 76 #define REG_GARTCTRL 0 77 #define REG_APSIZE 1 78 #define REG_ATTBASE 2 79 80 static int via_v2_regs[] = 81 { AGP_VIA_GARTCTRL, AGP_VIA_APSIZE, AGP_VIA_ATTBASE }; 82 static int via_v3_regs[] = 83 { AGP3_VIA_GARTCTRL, AGP3_VIA_APSIZE, AGP3_VIA_ATTBASE }; 84 85 int 86 agp_via_attach(device_t parent, device_t self, void *aux) 87 { 88 struct pci_attach_args *pa = aux; 89 struct agp_softc *sc = device_private(self); 90 struct agp_via_softc *asc; 91 struct agp_gatt *gatt; 92 pcireg_t agpsel, capval; 93 94 asc = malloc(sizeof *asc, M_AGP, M_WAITOK|M_ZERO); 95 sc->as_chipc = asc; 96 sc->as_methods = &agp_via_methods; 97 pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_AGP, &sc->as_capoff, 98 &capval); 99 100 if (PCI_CAP_AGP_MAJOR(capval) >= 3) { 101 agpsel = pci_conf_read(pa->pa_pc, pa->pa_tag, AGP_VIA_AGPSEL); 102 if ((agpsel & (1 << 9)) == 0) { 103 asc->regs = via_v3_regs; 104 aprint_debug(" (v3)"); 105 } else { 106 asc->regs = via_v2_regs; 107 aprint_debug(" (v2 compat mode)"); 108 } 109 } else { 110 asc->regs = via_v2_regs; 111 aprint_debug(" (v2)"); 112 } 113 114 if (agp_map_aperture(pa, sc, AGP_APBASE) != 0) { 115 aprint_error(": can't map aperture\n"); 116 free(asc, M_AGP); 117 return ENXIO; 118 } 119 120 asc->initial_aperture = AGP_GET_APERTURE(sc); 121 122 for (;;) { 123 gatt = agp_alloc_gatt(sc); 124 if (gatt) 125 break; 126 127 /* 128 * Probably contigmalloc failure. Try reducing the 129 * aperture so that the gatt size reduces. 130 */ 131 if (AGP_SET_APERTURE(sc, AGP_GET_APERTURE(sc) / 2)) { 132 agp_generic_detach(sc); 133 aprint_error(": can't set aperture size\n"); 134 return ENOMEM; 135 } 136 } 137 asc->gatt = gatt; 138 139 if (asc->regs == via_v2_regs) { 140 /* Install the gatt. */ 141 pci_conf_write(pa->pa_pc, pa->pa_tag, asc->regs[REG_ATTBASE], 142 gatt->ag_physical | 3); 143 /* Enable the aperture. */ 144 pci_conf_write(pa->pa_pc, pa->pa_tag, asc->regs[REG_GARTCTRL], 145 0x0000000f); 146 } else { 147 pcireg_t gartctrl; 148 /* Install the gatt. */ 149 pci_conf_write(pa->pa_pc, pa->pa_tag, asc->regs[REG_ATTBASE], 150 gatt->ag_physical); 151 /* Enable the aperture. */ 152 gartctrl = pci_conf_read(pa->pa_pc, pa->pa_tag, 153 asc->regs[REG_GARTCTRL]); 154 pci_conf_write(pa->pa_pc, pa->pa_tag, asc->regs[REG_GARTCTRL], 155 gartctrl | (3 << 7)); 156 } 157 158 return 0; 159 } 160 161 #if 0 162 static int 163 agp_via_detach(struct agp_softc *sc) 164 { 165 struct agp_via_softc *asc = sc->as_chipc; 166 int error; 167 168 error = agp_generic_detach(sc); 169 if (error) 170 return error; 171 172 pci_conf_write(sc->as_pc, sc->as_tag, asc->regs[REG_GARTCTRL], 0); 173 pci_conf_write(sc->as_pc, sc->as_tag, asc->regs[REG_ATTBASE], 0); 174 AGP_SET_APERTURE(sc, asc->initial_aperture); 175 agp_free_gatt(sc, asc->gatt); 176 177 return 0; 178 } 179 #endif 180 181 static u_int32_t 182 agp_via_get_aperture(struct agp_softc *sc) 183 { 184 struct agp_via_softc *asc = sc->as_chipc; 185 u_int32_t apsize; 186 187 if (asc->regs == via_v2_regs) { 188 apsize = pci_conf_read(sc->as_pc, sc->as_tag, 189 asc->regs[REG_APSIZE]) & 0xff; 190 191 /* 192 * The size is determined by the number of low bits of 193 * register APBASE which are forced to zero. The low 20 bits 194 * are always forced to zero and each zero bit in the apsize 195 * field just read forces the corresponding bit in the 27:20 196 * to be zero. We calculate the aperture size accordingly. 197 */ 198 return (((apsize ^ 0xff) << 20) | ((1 << 20) - 1)) + 1; 199 } else { 200 apsize = pci_conf_read(sc->as_pc, sc->as_tag, 201 asc->regs[REG_APSIZE]) & 0xfff; 202 switch (apsize) { 203 case 0x800: 204 return 0x80000000; 205 case 0xc00: 206 return 0x40000000; 207 case 0xe00: 208 return 0x20000000; 209 case 0xf00: 210 return 0x10000000; 211 case 0xf20: 212 return 0x08000000; 213 case 0xf30: 214 return 0x04000000; 215 case 0xf38: 216 return 0x02000000; 217 case 0xf3c: 218 return 0x01000000; 219 case 0xf3e: 220 return 0x00800000; 221 case 0xf3f: 222 return 0x00400000; 223 default: 224 aprint_error_dev(sc->as_dev, 225 "invalid aperture setting 0x%x\n", apsize); 226 return 0; 227 } 228 } 229 } 230 231 static int 232 agp_via_set_aperture(struct agp_softc *sc, u_int32_t aperture) 233 { 234 struct agp_via_softc *asc = sc->as_chipc; 235 u_int32_t apsize, key; 236 pcireg_t reg; 237 238 if (asc->regs == via_v2_regs) { 239 /* 240 * Reverse the magic from get_aperture. 241 */ 242 apsize = ((aperture - 1) >> 20) ^ 0xff; 243 244 /* 245 * Double check for sanity. 246 */ 247 if ((((apsize ^ 0xff) << 20) | ((1 << 20) - 1)) + 1 != aperture) 248 return EINVAL; 249 250 reg = pci_conf_read(sc->as_pc, sc->as_tag, 251 asc->regs[REG_APSIZE]); 252 reg &= ~0xff; 253 reg |= apsize; 254 pci_conf_write(sc->as_pc, sc->as_tag, 255 asc->regs[REG_APSIZE], reg); 256 } else { 257 switch (aperture) { 258 case 0x80000000: 259 key = 0x800; 260 break; 261 case 0x40000000: 262 key = 0xc00; 263 break; 264 case 0x20000000: 265 key = 0xe00; 266 break; 267 case 0x10000000: 268 key = 0xf00; 269 break; 270 case 0x08000000: 271 key = 0xf20; 272 break; 273 case 0x04000000: 274 key = 0xf30; 275 break; 276 case 0x02000000: 277 key = 0xf38; 278 break; 279 case 0x01000000: 280 key = 0xf3c; 281 break; 282 case 0x00800000: 283 key = 0xf3e; 284 break; 285 case 0x00400000: 286 key = 0xf3f; 287 break; 288 default: 289 aprint_error_dev(sc->as_dev, 290 "invalid aperture size (%dMB)\n", 291 aperture / 1024 / 1024); 292 return EINVAL; 293 } 294 reg = pci_conf_read(sc->as_pc, sc->as_tag, asc->regs[REG_APSIZE]); 295 reg &= ~0xfff; 296 reg |= key; 297 pci_conf_write(sc->as_pc, sc->as_tag, asc->regs[REG_APSIZE], reg); 298 } 299 300 return 0; 301 } 302 303 static int 304 agp_via_bind_page(struct agp_softc *sc, off_t offset, bus_addr_t physical) 305 { 306 struct agp_via_softc *asc = sc->as_chipc; 307 308 if (offset < 0 || offset >= (asc->gatt->ag_entries << AGP_PAGE_SHIFT)) 309 return EINVAL; 310 311 asc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical; 312 return 0; 313 } 314 315 static int 316 agp_via_unbind_page(struct agp_softc *sc, off_t offset) 317 { 318 struct agp_via_softc *asc = sc->as_chipc; 319 320 if (offset < 0 || offset >= (asc->gatt->ag_entries << AGP_PAGE_SHIFT)) 321 return EINVAL; 322 323 asc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0; 324 return 0; 325 } 326 327 static void 328 agp_via_flush_tlb(struct agp_softc *sc) 329 { 330 struct agp_via_softc *asc = sc->as_chipc; 331 pcireg_t gartctrl; 332 333 if (asc->regs == via_v2_regs) { 334 pci_conf_write(sc->as_pc, sc->as_tag, asc->regs[REG_GARTCTRL], 335 0x8f); 336 pci_conf_write(sc->as_pc, sc->as_tag, asc->regs[REG_GARTCTRL], 337 0x0f); 338 } else { 339 gartctrl = pci_conf_read(sc->as_pc, sc->as_tag, 340 asc->regs[REG_GARTCTRL]); 341 pci_conf_write(sc->as_pc, sc->as_tag, asc->regs[REG_GARTCTRL], 342 gartctrl & ~(1 << 7)); 343 pci_conf_write(sc->as_pc, sc->as_tag, asc->regs[REG_GARTCTRL], 344 gartctrl); 345 } 346 } 347