xref: /openbsd-src/sys/dev/pci/agp_via.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: agp_via.c,v 1.11 2009/01/04 20:47:35 grange Exp $	*/
2 /*	$NetBSD: agp_via.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_via.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 void	agp_via_attach(struct device *, struct device *, void *);
52 int	agp_via_probe(struct device *, void *, void *);
53 bus_size_t agp_via_get_aperture(void *);
54 int	agp_via_set_aperture(void *, bus_size_t);
55 int	agp_via_bind_page(void *, off_t, bus_addr_t);
56 int	agp_via_unbind_page(void *, off_t);
57 void	agp_via_flush_tlb(void *);
58 
59 const struct agp_methods agp_via_methods = {
60 	agp_via_get_aperture,
61 	agp_via_bind_page,
62 	agp_via_unbind_page,
63 	agp_via_flush_tlb,
64 };
65 
66 struct agp_via_softc {
67 	struct device		 dev;
68 	struct agp_softc	*agpdev;
69 	struct agp_gatt		*gatt;
70 	int			*regs;
71 	pci_chipset_tag_t	 vsc_pc;
72 	pcitag_t		 vsc_tag;
73 	bus_size_t		 initial_aperture;
74 };
75 
76 struct cfattach viaagp_ca = {
77         sizeof(struct agp_via_softc), agp_via_probe, agp_via_attach
78 };
79 
80 struct cfdriver viaagp_cd = {
81 	NULL, "viaagp", DV_DULL
82 };
83 
84 #define REG_GARTCTRL	0
85 #define REG_APSIZE	1
86 #define REG_ATTBASE	2
87 
88 int via_v2_regs[] =
89 	{ AGP_VIA_GARTCTRL, AGP_VIA_APSIZE, AGP_VIA_ATTBASE };
90 int via_v3_regs[] =
91 	{ AGP3_VIA_GARTCTRL, AGP3_VIA_APSIZE, AGP3_VIA_ATTBASE };
92 
93 int
94 agp_via_probe(struct device *parent, void *match, void *aux)
95 {
96 	struct agp_attach_args	*aa = aux;
97 	struct pci_attach_args	*pa = aa->aa_pa;
98 
99 	/* Must be a pchb don't attach to iommu-style agp devs */
100 	if (agpbus_probe(aa) == 1 &&
101 	    PCI_VENDOR(pa->pa_id) == PCI_VENDOR_VIATECH &&
102 	    PCI_PRODUCT(pa->pa_id) != PCI_PRODUCT_VIATECH_K8M800_0 &&
103 	    PCI_PRODUCT(pa->pa_id) != PCI_PRODUCT_VIATECH_K8T890_0 &&
104 	    PCI_PRODUCT(pa->pa_id) != PCI_PRODUCT_VIATECH_K8HTB_0 &&
105 	    PCI_PRODUCT(pa->pa_id) != PCI_PRODUCT_VIATECH_K8HTB)
106 		return (1);
107 	return (0);
108 }
109 
110 void
111 agp_via_attach(struct device *parent, struct device *self, void *aux)
112 {
113 	struct agp_via_softc	*asc = (struct agp_via_softc *)self;
114 	struct agp_attach_args	*aa = aux;
115 	struct pci_attach_args	*pa = aa->aa_pa;
116 	struct agp_gatt		*gatt;
117 	pcireg_t		 agpsel, capval;
118 
119 	asc->vsc_pc = pa->pa_pc;
120 	asc->vsc_tag = pa->pa_tag;
121 	pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_AGP, NULL, &capval);
122 
123 	if (AGP_CAPID_GET_MAJOR(capval) >= 3) {
124 		agpsel = pci_conf_read(pa->pa_pc, pa->pa_tag, AGP_VIA_AGPSEL);
125 		if ((agpsel & (1 << 1)) == 0) {
126 			asc->regs = via_v3_regs;
127 			printf(": v3");
128 		} else {
129 			asc->regs = via_v2_regs;
130 			printf(": v2 compat mode");
131 		}
132 	} else {
133 		asc->regs = via_v2_regs;
134 		printf(": v2");
135 	}
136 
137 
138 	asc->initial_aperture = agp_via_get_aperture(asc);
139 
140 	for (;;) {
141 		bus_size_t size = agp_via_get_aperture(asc);
142 		gatt = agp_alloc_gatt(pa->pa_dmat, size);
143 		if (gatt != NULL)
144 			break;
145 
146 		/*
147 		 * Probably failed to alloc congigious memory. Try reducing the
148 		 * aperture so that the gatt size reduces.
149 		 */
150 		if (agp_via_set_aperture(asc, size / 2)) {
151 			printf(", can't set aperture size\n");
152 			return;
153 		}
154 	}
155 	asc->gatt = gatt;
156 
157 	if (asc->regs == via_v2_regs) {
158 		/* Install the gatt. */
159 		pci_conf_write(pa->pa_pc, pa->pa_tag, asc->regs[REG_ATTBASE],
160 		    gatt->ag_physical | 3);
161 		/* Enable the aperture. */
162 		pci_conf_write(pa->pa_pc, pa->pa_tag, asc->regs[REG_GARTCTRL],
163 		    0x0000000f);
164 	} else {
165 		pcireg_t gartctrl;
166 		/* Install the gatt. */
167 		pci_conf_write(pa->pa_pc, pa->pa_tag, asc->regs[REG_ATTBASE],
168 		    gatt->ag_physical);
169 		/* Enable the aperture. */
170 		gartctrl = pci_conf_read(pa->pa_pc, pa->pa_tag,
171 		    asc->regs[REG_ATTBASE]);
172 		pci_conf_write(pa->pa_pc, pa->pa_tag, asc->regs[REG_GARTCTRL],
173 		    gartctrl | (3 << 7));
174 	}
175 	asc->agpdev = (struct agp_softc *)agp_attach_bus(pa, &agp_via_methods,
176 	    AGP_APBASE, PCI_MAPREG_TYPE_MEM, &asc->dev);
177 
178 	return;
179 }
180 
181 #if 0
182 int
183 agp_via_detach(struct agp_softc *sc)
184 {
185 	struct agp_via_softc *asc = sc->sc_chipc;
186 	int error;
187 
188 	error = agp_generic_detach(sc);
189 	if (error)
190 		return (error);
191 
192 	pci_conf_write(sc->as_pc, sc->as_tag, asc->regs[REG_GARTCTRL], 0);
193 	pci_conf_write(sc->as_pc, sc->as_tag, asc->regs[REG_ATTBASE], 0);
194 	AGP_SET_APERTURE(sc, asc->initial_aperture);
195 	agp_free_gatt(sc, asc->gatt);
196 
197 	return (0);
198 }
199 #endif
200 
201 bus_size_t
202 agp_via_get_aperture(void *sc)
203 {
204 	struct agp_via_softc	*vsc = sc;
205 	bus_size_t		 apsize;
206 
207 	apsize = pci_conf_read(vsc->vsc_pc, vsc->vsc_tag,
208 	    vsc->regs[REG_APSIZE]) & 0x1f;
209 
210 	/*
211 	 * The size is determined by the number of low bits of
212 	 * register APBASE which are forced to zero. The low 20 bits
213 	 * are always forced to zero and each zero bit in the apsize
214 	 * field just read forces the corresponding bit in the 27:20
215 	 * to be zero. We calculate the aperture size accordingly.
216 	 */
217 	return ((((apsize ^ 0xff) << 20) | ((1 << 20) - 1)) + 1);
218 }
219 
220 int
221 agp_via_set_aperture(void *sc, bus_size_t aperture)
222 {
223 	struct agp_via_softc	*vsc = sc;
224 	bus_size_t		 apsize;
225 	pcireg_t		 reg;
226 
227 	/*
228 	 * Reverse the magic from get_aperture.
229 	 */
230 	apsize = ((aperture - 1) >> 20) ^ 0xff;
231 
232 	/*
233 	 * Double check for sanity.
234 	 */
235 	if ((((apsize ^ 0xff) << 20) | ((1 << 20) - 1)) + 1 != aperture)
236 		return (EINVAL);
237 
238 	reg = pci_conf_read(vsc->vsc_pc, vsc->vsc_tag, vsc->regs[REG_APSIZE]);
239 	reg &= ~0xff;
240 	reg |= apsize;
241 	pci_conf_write(vsc->vsc_pc, vsc->vsc_tag, vsc->regs[REG_APSIZE], reg);
242 
243 	return (0);
244 }
245 
246 int
247 agp_via_bind_page(void *sc, off_t offset, bus_addr_t physical)
248 {
249 	struct agp_via_softc *vsc = sc;
250 
251 	if (offset < 0 || offset >= (vsc->gatt->ag_entries << AGP_PAGE_SHIFT))
252 		return (EINVAL);
253 
254 	vsc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical;
255 	return (0);
256 }
257 
258 int
259 agp_via_unbind_page(void *sc, off_t offset)
260 {
261 	struct agp_via_softc *vsc = sc;
262 
263 	if (offset < 0 || offset >= (vsc->gatt->ag_entries << AGP_PAGE_SHIFT))
264 		return (EINVAL);
265 
266 	vsc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0;
267 	return (0);
268 }
269 
270 void
271 agp_via_flush_tlb(void *sc)
272 {
273 	struct agp_via_softc *vsc = sc;
274 	pcireg_t gartctrl;
275 
276 	if (vsc->regs == via_v2_regs) {
277 		pci_conf_write(vsc->vsc_pc, vsc->vsc_tag,
278 		    vsc->regs[REG_GARTCTRL], 0x8f);
279 		pci_conf_write(vsc->vsc_pc, vsc->vsc_tag,
280 		    vsc->regs[REG_GARTCTRL], 0x0f);
281 	} else {
282 		gartctrl = pci_conf_read(vsc->vsc_pc, vsc->vsc_tag,
283 		    vsc->regs[REG_GARTCTRL]);
284 		pci_conf_write(vsc->vsc_pc, vsc->vsc_tag,
285 		    vsc->regs[REG_GARTCTRL], gartctrl & ~(1 << 7));
286 		pci_conf_write(vsc->vsc_pc, vsc->vsc_tag,
287 		    vsc->regs[REG_GARTCTRL], gartctrl);
288 	}
289 }
290