xref: /freebsd/sys/dev/agp/agp_i810.c (revision b601c69bdbe8755d26570261d7fd4c02ee4eff74)
1 /*-
2  * Copyright (c) 2000 Doug Rabson
3  * Copyright (c) 2000 Ruslan Ermilov
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  *	$FreeBSD$
28  */
29 
30 #include "opt_bus.h"
31 #include "opt_pci.h"
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/malloc.h>
36 #include <sys/kernel.h>
37 #include <sys/bus.h>
38 #include <sys/lock.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/vm_page.h>
48 #include <vm/vm_pageout.h>
49 #include <vm/pmap.h>
50 
51 #include <machine/bus.h>
52 #include <machine/resource.h>
53 #include <sys/rman.h>
54 
55 MALLOC_DECLARE(M_AGP);
56 
57 #define READ1(off)	bus_space_read_1(sc->bst, sc->bsh, off)
58 #define WRITE4(off,v)	bus_space_write_4(sc->bst, sc->bsh, off, v)
59 
60 struct agp_i810_softc {
61 	struct agp_softc agp;
62 	u_int32_t initial_aperture;	/* aperture size at startup */
63 	struct agp_gatt *gatt;
64 	u_int32_t dcache_size;
65 	device_t bdev;			/* bridge device */
66 	struct resource *regs;		/* memory mapped GC registers */
67 	bus_space_tag_t bst;		/* bus_space tag */
68 	bus_space_handle_t bsh;		/* bus_space handle */
69 };
70 
71 static const char*
72 agp_i810_match(device_t dev)
73 {
74 	if (pci_get_class(dev) != PCIC_DISPLAY
75 	    || pci_get_subclass(dev) != PCIS_DISPLAY_VGA)
76 		return NULL;
77 
78 	switch (pci_get_devid(dev)) {
79 	case 0x71218086:
80 		return ("Intel 82810 (i810 GMCH) SVGA controller");
81 
82 	case 0x71238086:
83 		return ("Intel 82810-DC100 (i810-DC100 GMCH) SVGA controller");
84 
85 	case 0x71258086:
86 		return ("Intel 82810E (i810E GMCH) SVGA controller");
87 	};
88 
89 	return NULL;
90 }
91 
92 /*
93  * Find bridge device.
94  */
95 static device_t
96 agp_i810_find_bridge(device_t dev)
97 {
98 	device_t *children, child;
99 	int nchildren, i;
100 	u_int32_t devid;
101 
102 	/*
103 	 * XXX assume that the bridge device's ID is one minus the vga ID.
104 	 */
105 	devid = pci_get_devid(dev) - 0x10000;
106 	if (device_get_children(device_get_parent(dev), &children, &nchildren))
107 		return 0;
108 
109 	for (i = 0; i < nchildren; i++) {
110 		child = children[i];
111 
112 		if (pci_get_devid(child) == devid) {
113 			free(children, M_TEMP);
114 			return child;
115 		}
116 	}
117 	free(children, M_TEMP);
118 	return 0;
119 }
120 
121 static int
122 agp_i810_probe(device_t dev)
123 {
124 	const char *desc;
125 
126 	desc = agp_i810_match(dev);
127 	if (desc) {
128 		device_t bdev;
129 		u_int8_t smram;
130 
131 		bdev = agp_i810_find_bridge(dev);
132 		if (!bdev) {
133 			if (bootverbose)
134 				printf("I810: can't find bridge device\n");
135 			return ENXIO;
136 		}
137 
138 		smram = pci_read_config(bdev, AGP_I810_SMRAM, 1);
139 		if ((smram & AGP_I810_SMRAM_GMS)
140 		    == AGP_I810_SMRAM_GMS_DISABLED) {
141 			if (bootverbose)
142 				printf("I810: disabled, not probing\n");
143 			return ENXIO;
144 		}
145 
146 		device_verbose(dev);
147 		device_set_desc(dev, desc);
148 		return 0;
149 	}
150 
151 	return ENXIO;
152 }
153 
154 static int
155 agp_i810_attach(device_t dev)
156 {
157 	struct agp_i810_softc *sc = device_get_softc(dev);
158 	struct agp_gatt *gatt;
159 	int error, rid;
160 
161 	sc->bdev = agp_i810_find_bridge(dev);
162 	if (!sc->bdev)
163 		return ENOENT;
164 
165 	error = agp_generic_attach(dev);
166 	if (error)
167 		return error;
168 
169 	rid = AGP_I810_MMADR;
170 	sc->regs = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
171 				      0, ~0, 1, RF_ACTIVE);
172 	if (!sc->regs) {
173 		agp_generic_detach(dev);
174 		return ENOMEM;
175 	}
176 	sc->bst = rman_get_bustag(sc->regs);
177 	sc->bsh = rman_get_bushandle(sc->regs);
178 
179 	sc->initial_aperture = AGP_GET_APERTURE(dev);
180 
181 	if (READ1(AGP_I810_DRT) & AGP_I810_DRT_POPULATED)
182 		sc->dcache_size = 4 * 1024 * 1024;
183 	else
184 		sc->dcache_size = 0;
185 
186 	for (;;) {
187 		gatt = agp_alloc_gatt(dev);
188 		if (gatt)
189 			break;
190 
191 		/*
192 		 * Probably contigmalloc failure. Try reducing the
193 		 * aperture so that the gatt size reduces.
194 		 */
195 		if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2)) {
196 			agp_generic_detach(dev);
197 			return ENOMEM;
198 		}
199 	}
200 	sc->gatt = gatt;
201 
202 	/* Install the GATT. */
203 	WRITE4(AGP_I810_PGTBL_CTL, gatt->ag_physical | 1);
204 
205 	/*
206 	 * Make sure the chipset can see everything.
207 	 */
208 	agp_flush_cache();
209 
210 	return 0;
211 }
212 
213 static int
214 agp_i810_detach(device_t dev)
215 {
216 	struct agp_i810_softc *sc = device_get_softc(dev);
217 	int error;
218 
219 	error = agp_generic_detach(dev);
220 	if (error)
221 		return error;
222 
223 	/* Clear the GATT base. */
224 	WRITE4(AGP_I810_PGTBL_CTL, 0);
225 
226 	/* Put the aperture back the way it started. */
227 	AGP_SET_APERTURE(dev, sc->initial_aperture);
228 
229 	agp_free_gatt(sc->gatt);
230 
231 	bus_release_resource(dev, SYS_RES_MEMORY,
232 			     AGP_I810_MMADR, sc->regs);
233 
234 	return 0;
235 }
236 
237 static u_int32_t
238 agp_i810_get_aperture(device_t dev)
239 {
240 	struct agp_i810_softc *sc = device_get_softc(dev);
241 	u_int16_t miscc;
242 
243 	miscc = pci_read_config(sc->bdev, AGP_I810_MISCC, 2);
244 	if ((miscc & AGP_I810_MISCC_WINSIZE) == AGP_I810_MISCC_WINSIZE_32)
245 		return 32 * 1024 * 1024;
246 	else
247 		return 64 * 1024 * 1024;
248 }
249 
250 static int
251 agp_i810_set_aperture(device_t dev, u_int32_t aperture)
252 {
253 	struct agp_i810_softc *sc = device_get_softc(dev);
254 	u_int16_t miscc;
255 
256 	/*
257 	 * Double check for sanity.
258 	 */
259 	if (aperture != 32 * 1024 * 1024 && aperture != 64 * 1024 * 1024) {
260 		device_printf(dev, "bad aperture size %d\n", aperture);
261 		return EINVAL;
262 	}
263 
264 	miscc = pci_read_config(sc->bdev, AGP_I810_MISCC, 2);
265 	miscc &= ~AGP_I810_MISCC_WINSIZE;
266 	if (aperture == 32 * 1024 * 1024)
267 		miscc |= AGP_I810_MISCC_WINSIZE_32;
268 	else
269 		miscc |= AGP_I810_MISCC_WINSIZE_64;
270 
271 	pci_write_config(sc->bdev, AGP_I810_MISCC, miscc, 2);
272 
273 	return 0;
274 }
275 
276 static int
277 agp_i810_bind_page(device_t dev, int offset, vm_offset_t physical)
278 {
279 	struct agp_i810_softc *sc = device_get_softc(dev);
280 
281 	if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT))
282 		return EINVAL;
283 
284 	WRITE4(AGP_I810_GTT + (offset >> AGP_PAGE_SHIFT) * 4, physical | 1);
285 	return 0;
286 }
287 
288 static int
289 agp_i810_unbind_page(device_t dev, int offset)
290 {
291 	struct agp_i810_softc *sc = device_get_softc(dev);
292 
293 	if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT))
294 		return EINVAL;
295 
296 	WRITE4(AGP_I810_GTT + (offset >> AGP_PAGE_SHIFT) * 4, 0);
297 	return 0;
298 }
299 
300 /*
301  * Writing via memory mapped registers already flushes all TLBs.
302  */
303 static void
304 agp_i810_flush_tlb(device_t dev)
305 {
306 }
307 
308 static int
309 agp_i810_enable(device_t dev, u_int32_t mode)
310 {
311 
312 	return 0;
313 }
314 
315 static struct agp_memory *
316 agp_i810_alloc_memory(device_t dev, int type, vm_size_t size)
317 {
318 	struct agp_i810_softc *sc = device_get_softc(dev);
319 	struct agp_memory *mem;
320 
321 	if ((size & (AGP_PAGE_SIZE - 1)) != 0)
322 		return 0;
323 
324 	if (sc->agp.as_allocated + size > sc->agp.as_maxmem)
325 		return 0;
326 
327 	if (type == 1) {
328 		/*
329 		 * Mapping local DRAM into GATT.
330 		 */
331 		if (size != sc->dcache_size)
332 			return 0;
333 	} else if (type == 2) {
334 		/*
335 		 * Bogus mapping of a single page for the hardware cursor.
336 		 */
337 		if (size != AGP_PAGE_SIZE)
338 			return 0;
339 	}
340 
341 	mem = malloc(sizeof *mem, M_AGP, M_WAITOK);
342 	mem->am_id = sc->agp.as_nextid++;
343 	mem->am_size = size;
344 	mem->am_type = type;
345 	if (type != 1)
346 		mem->am_obj = vm_object_allocate(OBJT_DEFAULT,
347 						 atop(round_page(size)));
348 	else
349 		mem->am_obj = 0;
350 
351 	if (type == 2) {
352 		/*
353 		 * Allocate and wire down the page now so that we can
354 		 * get its physical address.
355 		 */
356 		vm_page_t m;
357 		m = vm_page_grab(mem->am_obj, 0, VM_ALLOC_ZERO|VM_ALLOC_RETRY);
358 		vm_page_wire(m);
359 		mem->am_physical = VM_PAGE_TO_PHYS(m);
360 		vm_page_wakeup(m);
361 	} else {
362 		mem->am_physical = 0;
363 	}
364 
365 	mem->am_offset = 0;
366 	mem->am_is_bound = 0;
367 	TAILQ_INSERT_TAIL(&sc->agp.as_memory, mem, am_link);
368 	sc->agp.as_allocated += size;
369 
370 	return mem;
371 }
372 
373 static int
374 agp_i810_free_memory(device_t dev, struct agp_memory *mem)
375 {
376 	struct agp_i810_softc *sc = device_get_softc(dev);
377 
378 	if (mem->am_is_bound)
379 		return EBUSY;
380 
381 	if (mem->am_type == 2) {
382 		/*
383 		 * Unwire the page which we wired in alloc_memory.
384 		 */
385 		vm_page_t m = vm_page_lookup(mem->am_obj, 0);
386 		vm_page_unwire(m, 0);
387 	}
388 
389 	sc->agp.as_allocated -= mem->am_size;
390 	TAILQ_REMOVE(&sc->agp.as_memory, mem, am_link);
391 	if (mem->am_obj)
392 		vm_object_deallocate(mem->am_obj);
393 	free(mem, M_AGP);
394 	return 0;
395 }
396 
397 static int
398 agp_i810_bind_memory(device_t dev, struct agp_memory *mem,
399 		     vm_offset_t offset)
400 {
401 	struct agp_i810_softc *sc = device_get_softc(dev);
402 	vm_offset_t i;
403 
404 	if (mem->am_type != 1)
405 		return agp_generic_bind_memory(dev, mem, offset);
406 
407 	for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) {
408 		WRITE4(AGP_I810_GTT + (offset >> AGP_PAGE_SHIFT) * 4,
409 		       i | 3);
410 	}
411 
412 	return 0;
413 }
414 
415 static int
416 agp_i810_unbind_memory(device_t dev, struct agp_memory *mem)
417 {
418 	struct agp_i810_softc *sc = device_get_softc(dev);
419 	vm_offset_t i;
420 
421 	if (mem->am_type != 1)
422 		return agp_generic_unbind_memory(dev, mem);
423 
424 	for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE)
425 		WRITE4(AGP_I810_GTT + (i >> AGP_PAGE_SHIFT) * 4, 0);
426 
427 	return 0;
428 }
429 
430 static device_method_t agp_i810_methods[] = {
431 	/* Device interface */
432 	DEVMETHOD(device_probe,		agp_i810_probe),
433 	DEVMETHOD(device_attach,	agp_i810_attach),
434 	DEVMETHOD(device_detach,	agp_i810_detach),
435 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
436 	DEVMETHOD(device_suspend,	bus_generic_suspend),
437 	DEVMETHOD(device_resume,	bus_generic_resume),
438 
439 	/* AGP interface */
440 	DEVMETHOD(agp_get_aperture,	agp_i810_get_aperture),
441 	DEVMETHOD(agp_set_aperture,	agp_i810_set_aperture),
442 	DEVMETHOD(agp_bind_page,	agp_i810_bind_page),
443 	DEVMETHOD(agp_unbind_page,	agp_i810_unbind_page),
444 	DEVMETHOD(agp_flush_tlb,	agp_i810_flush_tlb),
445 	DEVMETHOD(agp_enable,		agp_i810_enable),
446 	DEVMETHOD(agp_alloc_memory,	agp_i810_alloc_memory),
447 	DEVMETHOD(agp_free_memory,	agp_i810_free_memory),
448 	DEVMETHOD(agp_bind_memory,	agp_i810_bind_memory),
449 	DEVMETHOD(agp_unbind_memory,	agp_i810_unbind_memory),
450 
451 	{ 0, 0 }
452 };
453 
454 static driver_t agp_i810_driver = {
455 	"agp",
456 	agp_i810_methods,
457 	sizeof(struct agp_i810_softc),
458 };
459 
460 static devclass_t agp_devclass;
461 
462 DRIVER_MODULE(agp_i810, pci, agp_i810_driver, agp_devclass, 0, 0);
463