1 /* $OpenBSD: agp_sis.c,v 1.10 2008/11/09 22:54:01 oga Exp $ */ 2 /* $NetBSD: agp_sis.c,v 1.2 2001/09/15 00:25:00 thorpej Exp $ */ 3 4 /*- 5 * Copyright (c) 2000 Doug Rabson 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_sis.c,v 1.3 2001/07/05 21:28:47 jhb Exp $ 30 */ 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/malloc.h> 35 #include <sys/kernel.h> 36 #include <sys/lock.h> 37 #include <sys/proc.h> 38 #include <sys/conf.h> 39 #include <sys/device.h> 40 #include <sys/agpio.h> 41 42 #include <dev/pci/pcivar.h> 43 #include <dev/pci/pcireg.h> 44 #include <dev/pci/pcidevs.h> 45 #include <dev/pci/vga_pcivar.h> 46 #include <dev/pci/agpvar.h> 47 #include <dev/pci/agpreg.h> 48 49 #include <machine/bus.h> 50 51 struct agp_sis_softc { 52 struct device dev; 53 struct agp_softc *agpdev; 54 struct agp_gatt *gatt; 55 pci_chipset_tag_t ssc_pc; 56 pcitag_t ssc_tag; 57 bus_size_t initial_aperture; 58 }; 59 60 void agp_sis_attach(struct device *, struct device *, void *); 61 int agp_sis_probe(struct device *, void *, void *); 62 bus_size_t agp_sis_get_aperture(void *); 63 int agp_sis_set_aperture(void *, bus_size_t); 64 int agp_sis_bind_page(void *, off_t, bus_addr_t); 65 int agp_sis_unbind_page(void *, off_t); 66 void agp_sis_flush_tlb(void *); 67 68 struct cfattach sisagp_ca = { 69 sizeof(struct agp_sis_softc), agp_sis_probe, agp_sis_attach 70 }; 71 72 struct cfdriver sisagp_cd = { 73 NULL, "sisagp", DV_DULL 74 }; 75 76 const struct agp_methods agp_sis_methods = { 77 agp_sis_get_aperture, 78 agp_sis_bind_page, 79 agp_sis_unbind_page, 80 agp_sis_flush_tlb, 81 }; 82 83 int 84 agp_sis_probe(struct device *parent, void *match, void *aux) 85 { 86 struct agp_attach_args *aa = aux; 87 struct pci_attach_args *pa = aa->aa_pa; 88 89 /* Must be a pchb, don't attach to iommu-style agp devs */ 90 if (agpbus_probe(aa) == 1 && PCI_VENDOR(pa->pa_id) == PCI_VENDOR_SIS && 91 PCI_PRODUCT(pa->pa_id) != PCI_PRODUCT_SIS_755 && 92 PCI_PRODUCT(pa->pa_id) != PCI_PRODUCT_SIS_760) 93 return (1); 94 return (0); 95 } 96 97 98 void 99 agp_sis_attach(struct device *parent, struct device *self, void *aux) 100 { 101 struct agp_sis_softc *ssc = (struct agp_sis_softc *)self; 102 struct agp_attach_args *aa = aux; 103 struct pci_attach_args *pa = aa->aa_pa; 104 struct agp_gatt *gatt; 105 pcireg_t reg; 106 107 ssc->ssc_pc = pa->pa_pc; 108 ssc->ssc_tag = pa->pa_tag; 109 ssc->initial_aperture = agp_sis_get_aperture(ssc); 110 111 for (;;) { 112 bus_size_t size = agp_sis_get_aperture(ssc); 113 gatt = agp_alloc_gatt(pa->pa_dmat, size); 114 if (gatt != NULL) 115 break; 116 117 /* 118 * Probably failed to alloc congigious memory. Try reducing the 119 * aperture so that the gatt size reduces. 120 */ 121 if (agp_sis_set_aperture(ssc, size / 2)) { 122 printf("can't set aperture size\n"); 123 return; 124 } 125 } 126 ssc->gatt = gatt; 127 128 /* Install the gatt. */ 129 pci_conf_write(ssc->ssc_pc, ssc->ssc_tag, AGP_SIS_ATTBASE, 130 gatt->ag_physical); 131 132 /* Enable the aperture and auto-tlb-inval */ 133 reg = pci_conf_read(ssc->ssc_pc, ssc->ssc_tag, AGP_SIS_WINCTRL); 134 reg |= (0x05 << 24) | 3; 135 pci_conf_write(ssc->ssc_pc, ssc->ssc_tag, AGP_SIS_WINCTRL, reg); 136 137 ssc->agpdev = (struct agp_softc *)agp_attach_bus(pa, &agp_sis_methods, 138 AGP_APBASE, PCI_MAPREG_TYPE_MEM, &ssc->dev); 139 return; 140 } 141 142 #if 0 143 int 144 agp_sis_detach(struct agp_softc *sc) 145 { 146 struct agp_sis_softc *ssc = sc->sc_chipc; 147 pcireg_t reg; 148 int error; 149 150 error = agp_generic_detach(sc); 151 if (error) 152 return (error); 153 154 reg = pci_conf_read(sc->sc_pc, sc->sc_pcitag, AGP_SIS_WINCTRL); 155 reg &= ~3; 156 reg &= 0x00ffffff; 157 pci_conf_write(sc->sc_pc, sc->sc_pcitag, AGP_SIS_WINCTRL, reg); 158 159 /* Put the aperture back the way it started. */ 160 AGP_SET_APERTURE(sc, ssc->initial_aperture); 161 162 agp_free_gatt(sc, ssc->gatt); 163 return (0); 164 } 165 #endif 166 167 bus_size_t 168 agp_sis_get_aperture(void *sc) 169 { 170 struct agp_sis_softc *ssc = sc; 171 int gws; 172 173 /* 174 * The aperture size is equal to 4M<<gws. 175 */ 176 gws = (pci_conf_read(ssc->ssc_pc, ssc->ssc_tag, 177 AGP_SIS_WINCTRL)&0x70) >> 4; 178 return ((4 * 1024 * 1024) << gws); 179 } 180 181 int 182 agp_sis_set_aperture(void *sc, bus_size_t aperture) 183 { 184 struct agp_sis_softc *ssc = sc; 185 int gws; 186 pcireg_t reg; 187 188 /* 189 * Check for a power of two and make sure its within the 190 * programmable range. 191 */ 192 if (aperture & (aperture - 1) 193 || aperture < 4*1024*1024 194 || aperture > 256*1024*1024) 195 return (EINVAL); 196 197 gws = ffs(aperture / 4*1024*1024) - 1; 198 199 reg = pci_conf_read(ssc->ssc_pc, ssc->ssc_tag, AGP_SIS_WINCTRL); 200 reg &= ~0x00000070; 201 reg |= gws << 4; 202 pci_conf_write(ssc->ssc_pc, ssc->ssc_tag, AGP_SIS_WINCTRL, reg); 203 204 return (0); 205 } 206 207 int 208 agp_sis_bind_page(void *sc, off_t offset, bus_addr_t physical) 209 { 210 struct agp_sis_softc *ssc = sc; 211 212 if (offset < 0 || offset >= (ssc->gatt->ag_entries << AGP_PAGE_SHIFT)) 213 return (EINVAL); 214 215 ssc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical; 216 return (0); 217 } 218 219 int 220 agp_sis_unbind_page(void *sc, off_t offset) 221 { 222 struct agp_sis_softc *ssc = sc; 223 224 if (offset < 0 || offset >= (ssc->gatt->ag_entries << AGP_PAGE_SHIFT)) 225 return (EINVAL); 226 227 ssc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0; 228 return (0); 229 } 230 231 void 232 agp_sis_flush_tlb(void *sc) 233 { 234 struct agp_sis_softc *ssc = sc; 235 pcireg_t reg; 236 237 reg = pci_conf_read(ssc->ssc_pc, ssc->ssc_tag, AGP_SIS_TLBFLUSH); 238 reg &= 0xffffff00; 239 reg |= 0x02; 240 pci_conf_write(ssc->ssc_pc, ssc->ssc_tag, AGP_SIS_TLBFLUSH, reg); 241 } 242