xref: /freebsd/sys/dev/agp/agp_amd.c (revision 1b6c76a2fe091c74f08427e6c870851025a9cf67)
1 /*-
2  * Copyright (c) 2000 Doug Rabson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  *	$FreeBSD$
27  */
28 
29 #include "opt_bus.h"
30 #include "opt_pci.h"
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/malloc.h>
35 #include <sys/kernel.h>
36 #include <sys/bus.h>
37 #include <sys/lock.h>
38 #include <sys/mutex.h>
39 
40 #include <pci/pcivar.h>
41 #include <pci/pcireg.h>
42 #include <pci/agppriv.h>
43 #include <pci/agpreg.h>
44 
45 #include <vm/vm.h>
46 #include <vm/vm_object.h>
47 #include <vm/pmap.h>
48 #include <machine/bus.h>
49 #include <machine/resource.h>
50 #include <sys/rman.h>
51 
52 MALLOC_DECLARE(M_AGP);
53 
54 #define READ2(off)	bus_space_read_2(sc->bst, sc->bsh, off)
55 #define READ4(off)	bus_space_read_4(sc->bst, sc->bsh, off)
56 #define WRITE2(off,v)	bus_space_write_2(sc->bst, sc->bsh, off, v)
57 #define WRITE4(off,v)	bus_space_write_4(sc->bst, sc->bsh, off, v)
58 
59 struct agp_amd_gatt {
60 	u_int32_t	ag_entries;
61 	u_int32_t      *ag_vdir;	/* virtual address of page dir */
62 	vm_offset_t	ag_pdir;	/* physical address of page dir */
63 	u_int32_t      *ag_virtual;	/* virtual address of gatt */
64 };
65 
66 struct agp_amd_softc {
67 	struct agp_softc agp;
68 	struct resource *regs;	/* memory mapped control registers */
69 	bus_space_tag_t bst;	/* bus_space tag */
70 	bus_space_handle_t bsh;	/* bus_space handle */
71 	u_int32_t	initial_aperture; /* aperture size at startup */
72 	struct agp_amd_gatt *gatt;
73 };
74 
75 static struct agp_amd_gatt *
76 agp_amd_alloc_gatt(device_t dev)
77 {
78 	u_int32_t apsize = AGP_GET_APERTURE(dev);
79 	u_int32_t entries = apsize >> AGP_PAGE_SHIFT;
80 	struct agp_amd_gatt *gatt;
81 	int i, npages;
82 
83 	if (bootverbose)
84 		device_printf(dev,
85 			      "allocating GATT for aperture of size %dM\n",
86 			      apsize / (1024*1024));
87 
88 	gatt = malloc(sizeof(struct agp_amd_gatt), M_AGP, M_NOWAIT);
89 	if (!gatt)
90 		return 0;
91 
92 	/*
93 	 * The AMD751 uses a page directory to map a non-contiguous
94 	 * gatt so we don't need to use contigmalloc.
95 	 */
96 	gatt->ag_entries = entries;
97 	gatt->ag_virtual = malloc(entries * sizeof(u_int32_t),
98 				  M_AGP, M_NOWAIT);
99 	if (!gatt->ag_virtual) {
100 		if (bootverbose)
101 			device_printf(dev, "allocation failed\n");
102 		free(gatt, M_AGP);
103 		return 0;
104 	}
105 	bzero(gatt->ag_virtual, entries * sizeof(u_int32_t));
106 
107 	/*
108 	 * Allocate the page directory.
109 	 */
110 	gatt->ag_vdir = malloc(AGP_PAGE_SIZE, M_AGP, M_NOWAIT);
111 	if (!gatt->ag_vdir) {
112 		if (bootverbose)
113 			device_printf(dev,
114 				      "failed to allocate page directory\n");
115 		free(gatt->ag_virtual, M_AGP);
116 		free(gatt, M_AGP);
117 		return 0;
118 	}
119 	bzero(gatt->ag_vdir, AGP_PAGE_SIZE);
120 	gatt->ag_pdir = vtophys((vm_offset_t) gatt->ag_vdir);
121 	gatt->ag_pdir = vtophys(gatt->ag_virtual);
122 
123 	/*
124 	 * Map the pages of the GATT into the page directory.
125 	 */
126 	npages = ((entries * sizeof(u_int32_t) + AGP_PAGE_SIZE - 1)
127 		  >> AGP_PAGE_SHIFT);
128 	for (i = 0; i < npages; i++) {
129 		vm_offset_t va;
130 		vm_offset_t pa;
131 
132 		va = ((vm_offset_t) gatt->ag_virtual) + i * AGP_PAGE_SIZE;
133 		pa = vtophys(va);
134 		gatt->ag_vdir[i] = pa | 1;
135 	}
136 
137 	/*
138 	 * Make sure the chipset can see everything.
139 	 */
140 	agp_flush_cache();
141 
142 	return gatt;
143 }
144 
145 static void
146 agp_amd_free_gatt(struct agp_amd_gatt *gatt)
147 {
148 	free(gatt->ag_virtual, M_AGP);
149 	free(gatt->ag_vdir, M_AGP);
150 	free(gatt, M_AGP);
151 }
152 
153 static const char*
154 agp_amd_match(device_t dev)
155 {
156 	if (pci_get_class(dev) != PCIC_BRIDGE
157 	    || pci_get_subclass(dev) != PCIS_BRIDGE_HOST)
158 		return NULL;
159 
160 	if (agp_find_caps(dev) == 0)
161 		return NULL;
162 
163 	switch (pci_get_devid(dev)) {
164 	case 0x70061022:
165 		return ("AMD 751 host to AGP bridge");
166 	};
167 
168 	return NULL;
169 }
170 
171 static int
172 agp_amd_probe(device_t dev)
173 {
174 	const char *desc;
175 
176 	desc = agp_amd_match(dev);
177 	if (desc) {
178 		device_verbose(dev);
179 		device_set_desc(dev, desc);
180 		return 0;
181 	}
182 
183 	return ENXIO;
184 }
185 
186 static int
187 agp_amd_attach(device_t dev)
188 {
189 	struct agp_amd_softc *sc = device_get_softc(dev);
190 	struct agp_amd_gatt *gatt;
191 	int error, rid;
192 
193 	error = agp_generic_attach(dev);
194 	if (error)
195 		return error;
196 
197 	rid = AGP_AMD751_REGISTERS;
198 	sc->regs = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
199 				      0, ~0, 1, RF_ACTIVE);
200 	if (!sc->regs) {
201 		agp_generic_detach(dev);
202 		return ENOMEM;
203 	}
204 
205 	sc->bst = rman_get_bustag(sc->regs);
206 	sc->bsh = rman_get_bushandle(sc->regs);
207 
208 	sc->initial_aperture = AGP_GET_APERTURE(dev);
209 
210 	for (;;) {
211 		gatt = agp_amd_alloc_gatt(dev);
212 		if (gatt)
213 			break;
214 
215 		/*
216 		 * Probably contigmalloc failure. Try reducing the
217 		 * aperture so that the gatt size reduces.
218 		 */
219 		if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2))
220 			return ENOMEM;
221 	}
222 	sc->gatt = gatt;
223 
224 	/* Install the gatt. */
225 	WRITE4(AGP_AMD751_ATTBASE, gatt->ag_pdir);
226 
227 	/* Enable synchronisation between host and agp. */
228 	pci_write_config(dev,
229 			 AGP_AMD751_MODECTRL,
230 			 AGP_AMD751_MODECTRL_SYNEN, 1);
231 
232 	/* Set indexing mode for two-level and enable page dir cache */
233 	pci_write_config(dev,
234 			 AGP_AMD751_MODECTRL2,
235 			 AGP_AMD751_MODECTRL2_GPDCE, 1);
236 
237 	/* Enable the TLB and flush */
238 	WRITE2(AGP_AMD751_STATUS,
239 	       READ2(AGP_AMD751_STATUS) | AGP_AMD751_STATUS_GCE);
240 	AGP_FLUSH_TLB(dev);
241 
242 	return 0;
243 }
244 
245 static int
246 agp_amd_detach(device_t dev)
247 {
248 	struct agp_amd_softc *sc = device_get_softc(dev);
249 	int error;
250 
251 	error = agp_generic_detach(dev);
252 	if (error)
253 		return error;
254 
255 	/* Disable the TLB.. */
256 	WRITE2(AGP_AMD751_STATUS,
257 	       READ2(AGP_AMD751_STATUS) & ~AGP_AMD751_STATUS_GCE);
258 
259 	/* Disable host-agp sync */
260 	pci_write_config(dev, AGP_AMD751_MODECTRL, 0x00, 1);
261 
262 	/* Clear the GATT base */
263 	WRITE4(AGP_AMD751_ATTBASE, 0);
264 
265 	/* Put the aperture back the way it started. */
266 	AGP_SET_APERTURE(dev, sc->initial_aperture);
267 
268 	agp_amd_free_gatt(sc->gatt);
269 
270 	bus_release_resource(dev, SYS_RES_MEMORY,
271 			     AGP_AMD751_REGISTERS, sc->regs);
272 
273 	return 0;
274 }
275 
276 static u_int32_t
277 agp_amd_get_aperture(device_t dev)
278 {
279 	int vas;
280 
281 	/*
282 	 * The aperture size is equal to 32M<<vas.
283 	 */
284 	vas = (pci_read_config(dev, AGP_AMD751_APCTRL, 1) & 0x06) >> 1;
285 	return (32*1024*1024) << vas;
286 }
287 
288 static int
289 agp_amd_set_aperture(device_t dev, u_int32_t aperture)
290 {
291 	int vas;
292 
293 	/*
294 	 * Check for a power of two and make sure its within the
295 	 * programmable range.
296 	 */
297 	if (aperture & (aperture - 1)
298 	    || aperture < 32*1024*1024
299 	    || aperture > 2U*1024*1024*1024)
300 		return EINVAL;
301 
302 	vas = ffs(aperture / 32*1024*1024) - 1;
303 
304 	pci_write_config(dev, AGP_AMD751_APCTRL,
305 			 ((pci_read_config(dev, AGP_AMD751_APCTRL, 1) & ~0x06)
306 			  | vas << 1), 1);
307 
308 	return 0;
309 }
310 
311 static int
312 agp_amd_bind_page(device_t dev, int offset, vm_offset_t physical)
313 {
314 	struct agp_amd_softc *sc = device_get_softc(dev);
315 
316 	if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT))
317 		return EINVAL;
318 
319 	sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical | 1;
320 	return 0;
321 }
322 
323 static int
324 agp_amd_unbind_page(device_t dev, int offset)
325 {
326 	struct agp_amd_softc *sc = device_get_softc(dev);
327 
328 	if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT))
329 		return EINVAL;
330 
331 	sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0;
332 	return 0;
333 }
334 
335 static void
336 agp_amd_flush_tlb(device_t dev)
337 {
338 	struct agp_amd_softc *sc = device_get_softc(dev);
339 
340 	/* Set the cache invalidate bit and wait for the chipset to clear */
341 	WRITE4(AGP_AMD751_TLBCTRL, 1);
342 	do {
343 		DELAY(1);
344 	} while (READ4(AGP_AMD751_TLBCTRL));
345 }
346 
347 static device_method_t agp_amd_methods[] = {
348 	/* Device interface */
349 	DEVMETHOD(device_probe,		agp_amd_probe),
350 	DEVMETHOD(device_attach,	agp_amd_attach),
351 	DEVMETHOD(device_detach,	agp_amd_detach),
352 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
353 	DEVMETHOD(device_suspend,	bus_generic_suspend),
354 	DEVMETHOD(device_resume,	bus_generic_resume),
355 
356 	/* AGP interface */
357 	DEVMETHOD(agp_get_aperture,	agp_amd_get_aperture),
358 	DEVMETHOD(agp_set_aperture,	agp_amd_set_aperture),
359 	DEVMETHOD(agp_bind_page,	agp_amd_bind_page),
360 	DEVMETHOD(agp_unbind_page,	agp_amd_unbind_page),
361 	DEVMETHOD(agp_flush_tlb,	agp_amd_flush_tlb),
362 	DEVMETHOD(agp_enable,		agp_generic_enable),
363 	DEVMETHOD(agp_alloc_memory,	agp_generic_alloc_memory),
364 	DEVMETHOD(agp_free_memory,	agp_generic_free_memory),
365 	DEVMETHOD(agp_bind_memory,	agp_generic_bind_memory),
366 	DEVMETHOD(agp_unbind_memory,	agp_generic_unbind_memory),
367 
368 	{ 0, 0 }
369 };
370 
371 static driver_t agp_amd_driver = {
372 	"agp",
373 	agp_amd_methods,
374 	sizeof(struct agp_amd_softc),
375 };
376 
377 static devclass_t agp_devclass;
378 
379 DRIVER_MODULE(agp_amd, pci, agp_amd_driver, agp_devclass, 0, 0);
380