agp_i810.c revision 103272
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: head/sys/dev/agp/agp_i810.c 103272 2002-09-13 04:17:28Z anholt $
28 */
29
30/*
31 * Fixes for 830/845G support: David Dawes <dawes@xfree86.org>
32 */
33
34#include "opt_bus.h"
35#include "opt_pci.h"
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/malloc.h>
40#include <sys/kernel.h>
41#include <sys/bus.h>
42#include <sys/lock.h>
43#include <sys/lockmgr.h>
44#include <sys/mutex.h>
45#include <sys/proc.h>
46
47#include <pci/pcivar.h>
48#include <pci/pcireg.h>
49#include <pci/agppriv.h>
50#include <pci/agpreg.h>
51
52#include <vm/vm.h>
53#include <vm/vm_object.h>
54#include <vm/vm_page.h>
55#include <vm/vm_pageout.h>
56#include <vm/pmap.h>
57
58#include <machine/bus.h>
59#include <machine/resource.h>
60#include <sys/rman.h>
61
62MALLOC_DECLARE(M_AGP);
63
64#define READ1(off)	bus_space_read_1(sc->bst, sc->bsh, off)
65#define READ4(off)	bus_space_read_4(sc->bst, sc->bsh, off)
66#define WRITE4(off,v)	bus_space_write_4(sc->bst, sc->bsh, off, v)
67
68#define CHIP_I810 0	/* i810/i815 */
69#define CHIP_I830 1	/* i830/i845 */
70
71struct agp_i810_softc {
72	struct agp_softc agp;
73	u_int32_t initial_aperture;	/* aperture size at startup */
74	struct agp_gatt *gatt;
75	int chiptype;			/* i810-like or i830 */
76	u_int32_t dcache_size;		/* i810 only */
77	u_int32_t stolen;		/* number of i830/845 gtt entries for stolen memory */
78	device_t bdev;			/* bridge device */
79	struct resource *regs;		/* memory mapped GC registers */
80	bus_space_tag_t bst;		/* bus_space tag */
81	bus_space_handle_t bsh;		/* bus_space handle */
82};
83
84static const char*
85agp_i810_match(device_t dev)
86{
87	if (pci_get_class(dev) != PCIC_DISPLAY
88	    || pci_get_subclass(dev) != PCIS_DISPLAY_VGA)
89		return NULL;
90
91	switch (pci_get_devid(dev)) {
92	case 0x71218086:
93		return ("Intel 82810 (i810 GMCH) SVGA controller");
94
95	case 0x71238086:
96		return ("Intel 82810-DC100 (i810-DC100 GMCH) SVGA controller");
97
98	case 0x71258086:
99		return ("Intel 82810E (i810E GMCH) SVGA controller");
100
101	case 0x11328086:
102		return ("Intel 82815 (i815 GMCH) SVGA controller");
103
104	case 0x35778086:
105		return ("Intel 82830 (i830M GMCH) SVGA controller");
106
107	case 0x25628086:
108		return ("Intel 82845 (i845 GMCH) SVGA controller");
109	};
110
111	return NULL;
112}
113
114/*
115 * Find bridge device.
116 */
117static device_t
118agp_i810_find_bridge(device_t dev)
119{
120	device_t *children, child;
121	int nchildren, i;
122	u_int32_t devid;
123
124	/*
125	 * Calculate bridge device's ID.
126	 */
127	devid = pci_get_devid(dev);
128	switch (devid) {
129	case 0x71218086:
130	case 0x71238086:
131	case 0x71258086:
132		devid -= 0x10000;
133		break;
134
135	case 0x11328086:
136	case 0x35778086:
137	case 0x25628086:
138		devid -= 0x20000;
139		break;
140	};
141	if (device_get_children(device_get_parent(dev), &children, &nchildren))
142		return 0;
143
144	for (i = 0; i < nchildren; i++) {
145		child = children[i];
146
147		if (pci_get_devid(child) == devid) {
148			free(children, M_TEMP);
149			return child;
150		}
151	}
152	free(children, M_TEMP);
153	return 0;
154}
155
156static int
157agp_i810_probe(device_t dev)
158{
159	const char *desc;
160
161	desc = agp_i810_match(dev);
162	if (desc) {
163		device_t bdev;
164		u_int8_t smram;
165		int devid = pci_get_devid(dev);
166
167		bdev = agp_i810_find_bridge(dev);
168		if (!bdev) {
169			if (bootverbose)
170				printf("I810: can't find bridge device\n");
171			return ENXIO;
172		}
173
174		/*
175		 * checking whether internal graphics device has been activated.
176		 */
177		if ( (devid != 0x35778086 ) &&
178		     (devid != 0x25628086 ) ) {
179			smram = pci_read_config(bdev, AGP_I810_SMRAM, 1);
180			if ((smram & AGP_I810_SMRAM_GMS)
181			    == AGP_I810_SMRAM_GMS_DISABLED) {
182				if (bootverbose)
183					printf("I810: disabled, not probing\n");
184				return ENXIO;
185			}
186		} else {	/* I830MG */
187			unsigned int gcc1;
188			gcc1 = pci_read_config(bdev, AGP_I830_GCC1, 1);
189			if ((gcc1 & AGP_I830_GCC1_DEV2) == AGP_I830_GCC1_DEV2_DISABLED) {
190				if (bootverbose)
191					printf("I830: disabled, not probing\n");
192					return ENXIO;
193			}
194		}
195
196		device_verbose(dev);
197		device_set_desc(dev, desc);
198		return 0;
199	}
200
201	return ENXIO;
202}
203
204static int
205agp_i810_attach(device_t dev)
206{
207	struct agp_i810_softc *sc = device_get_softc(dev);
208	struct agp_gatt *gatt;
209	int error, rid;
210
211	sc->bdev = agp_i810_find_bridge(dev);
212	if (!sc->bdev)
213		return ENOENT;
214
215	error = agp_generic_attach(dev);
216	if (error)
217		return error;
218
219	switch (pci_get_devid(dev)) {
220	case 0x71218086:
221	case 0x71238086:
222	case 0x71258086:
223	case 0x11328086:
224		sc->chiptype = CHIP_I810;
225	case 0x35778086:
226	case 0x25628086:
227		sc->chiptype = CHIP_I830;
228	};
229
230	/* Same for i810 and i830 */
231	rid = AGP_I810_MMADR;
232	sc->regs = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
233				      0, ~0, 1, RF_ACTIVE);
234	if (!sc->regs) {
235		agp_generic_detach(dev);
236		return ENOMEM;
237	}
238	sc->bst = rman_get_bustag(sc->regs);
239	sc->bsh = rman_get_bushandle(sc->regs);
240
241	sc->initial_aperture = AGP_GET_APERTURE(dev);
242
243	gatt = malloc( sizeof(struct agp_gatt), M_AGP, M_NOWAIT);
244	if (!gatt) {
245 		agp_generic_detach(dev);
246 		return ENOMEM;
247	}
248	sc->gatt = gatt;
249
250	gatt->ag_entries = AGP_GET_APERTURE(dev) >> AGP_PAGE_SHIFT;
251
252	if ( sc->chiptype == CHIP_I810 ) {
253		/* Some i810s have on-chip memory called dcache */
254		if (READ1(AGP_I810_DRT) & AGP_I810_DRT_POPULATED)
255			sc->dcache_size = 4 * 1024 * 1024;
256		else
257			sc->dcache_size = 0;
258
259		/* According to the specs the gatt on the i810 must be 64k */
260		gatt->ag_virtual = contigmalloc( 64 * 1024, M_AGP, 0,
261					0, ~0, PAGE_SIZE, 0);
262		if (!gatt->ag_virtual) {
263			if (bootverbose)
264				device_printf(dev, "contiguous allocation failed\n");
265			free(gatt, M_AGP);
266			agp_generic_detach(dev);
267			return ENOMEM;
268		}
269		bzero(gatt->ag_virtual, gatt->ag_entries * sizeof(u_int32_t));
270
271		gatt->ag_physical = vtophys((vm_offset_t) gatt->ag_virtual);
272		agp_flush_cache();
273		/* Install the GATT. */
274		WRITE4(AGP_I810_PGTBL_CTL, gatt->ag_physical | 1);
275	} else {
276		/* The i830 automatically initializes the 128k gatt on boot. */
277		unsigned int gcc1, pgtblctl;
278
279		gcc1 = pci_read_config(sc->bdev, AGP_I830_GCC1, 1);
280		switch (gcc1 & AGP_I830_GCC1_GMS) {
281			case AGP_I830_GCC1_GMS_STOLEN_512:
282				sc->stolen = (512 - 132) * 1024 / 4096;
283				break;
284			case AGP_I830_GCC1_GMS_STOLEN_1024:
285				sc->stolen = (1024 - 132) * 1024 / 4096;
286				break;
287			case AGP_I830_GCC1_GMS_STOLEN_8192:
288				sc->stolen = (8192 - 132) * 1024 / 4096;
289				break;
290			default:
291				sc->stolen = 0;
292				device_printf(dev, "unknown memory configuration, disabling\n");
293				agp_generic_detach(dev);
294				return EINVAL;
295		}
296		if (sc->stolen > 0)
297			device_printf(dev, "detected %dk stolen memory\n", sc->stolen * 4);
298		device_printf(dev, "aperture size is %dM\n", sc->initial_aperture / 1024 / 1024);
299
300		/* GATT address is already in there, make sure it's enabled */
301		pgtblctl = READ4(AGP_I810_PGTBL_CTL);
302#if 0
303		device_printf(dev, "PGTBL_CTL is 0x%08x\n", pgtblctl);
304#endif
305		pgtblctl |= 1;
306		WRITE4(AGP_I810_PGTBL_CTL, pgtblctl);
307
308		gatt->ag_physical = pgtblctl & ~1;
309	}
310
311	/*
312	 * Make sure the chipset can see everything.
313	 */
314	agp_flush_cache();
315
316	return 0;
317}
318
319static int
320agp_i810_detach(device_t dev)
321{
322	struct agp_i810_softc *sc = device_get_softc(dev);
323	int error;
324
325	error = agp_generic_detach(dev);
326	if (error)
327		return error;
328
329	/* Clear the GATT base. */
330	if ( sc->chiptype == CHIP_I810 ) {
331		WRITE4(AGP_I810_PGTBL_CTL, 0);
332	} else {
333		unsigned int pgtblctl;
334		pgtblctl = READ4(AGP_I810_PGTBL_CTL);
335		pgtblctl &= ~1;
336		WRITE4(AGP_I810_PGTBL_CTL, pgtblctl);
337	}
338
339	/* Put the aperture back the way it started. */
340	AGP_SET_APERTURE(dev, sc->initial_aperture);
341
342	if ( sc->chiptype == CHIP_I810 ) {
343		contigfree(sc->gatt->ag_virtual, 64 * 1024, M_AGP);
344	}
345	free(sc->gatt, M_AGP);
346
347	bus_release_resource(dev, SYS_RES_MEMORY,
348			     AGP_I810_MMADR, sc->regs);
349
350	return 0;
351}
352
353static u_int32_t
354agp_i810_get_aperture(device_t dev)
355{
356	struct agp_i810_softc *sc = device_get_softc(dev);
357
358	if ( sc->chiptype == CHIP_I810 ) {
359		u_int16_t miscc;
360		miscc = pci_read_config(sc->bdev, AGP_I810_MISCC, 2);
361		if ((miscc & AGP_I810_MISCC_WINSIZE) == AGP_I810_MISCC_WINSIZE_32)
362			return 32 * 1024 * 1024;
363		else
364			return 64 * 1024 * 1024;
365	} else {	/* I830 */
366		unsigned int gcc1;
367
368		gcc1 = pci_read_config(sc->bdev, AGP_I830_GCC1, 2);
369		if ((gcc1 & AGP_I830_GCC1_GMASIZE) == AGP_I830_GCC1_GMASIZE_64)
370			return 64 * 1024 * 1024;
371		else
372			return 128 * 1024 * 1024;
373	}
374}
375
376static int
377agp_i810_set_aperture(device_t dev, u_int32_t aperture)
378{
379	struct agp_i810_softc *sc = device_get_softc(dev);
380	u_int16_t miscc;
381
382	if ( sc->chiptype == CHIP_I810 ) {
383		/*
384		 * Double check for sanity.
385		 */
386		if (aperture != 32 * 1024 * 1024 && aperture != 64 * 1024 * 1024) {
387			device_printf(dev, "bad aperture size %d\n", aperture);
388			return EINVAL;
389		}
390
391		miscc = pci_read_config(sc->bdev, AGP_I810_MISCC, 2);
392		miscc &= ~AGP_I810_MISCC_WINSIZE;
393		if (aperture == 32 * 1024 * 1024)
394			miscc |= AGP_I810_MISCC_WINSIZE_32;
395		else
396			miscc |= AGP_I810_MISCC_WINSIZE_64;
397
398		pci_write_config(sc->bdev, AGP_I810_MISCC, miscc, 2);
399	} else {	/* I830 */
400		unsigned int gcc1;
401
402		if (aperture != 64 * 1024 * 1024 && aperture != 128 * 1024 * 1024) {
403			device_printf(dev, "bad aperture size %d\n", aperture);
404			return EINVAL;
405		}
406		gcc1 = pci_read_config(sc->bdev, AGP_I830_GCC1, 2);
407		gcc1 &= ~AGP_I830_GCC1_GMASIZE;
408		if (aperture == 64 * 1024 * 1024)
409			gcc1 |= AGP_I830_GCC1_GMASIZE_64;
410		else
411			gcc1 |= AGP_I830_GCC1_GMASIZE_128;
412
413		pci_write_config(sc->bdev, AGP_I830_GCC1, gcc1, 2);
414	}
415
416	return 0;
417}
418
419static int
420agp_i810_bind_page(device_t dev, int offset, vm_offset_t physical)
421{
422	struct agp_i810_softc *sc = device_get_softc(dev);
423
424	if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) {
425		device_printf(dev, "failed: offset is 0x%08x, shift is %d, entries is %d\n", offset, AGP_PAGE_SHIFT, sc->gatt->ag_entries);
426		return EINVAL;
427	}
428
429	if ( sc->chiptype == CHIP_I830 ) {
430		if ( (offset >> AGP_PAGE_SHIFT) < sc->stolen ) {
431			device_printf(dev, "trying to bind into stolen memory");
432			return EINVAL;
433		}
434	}
435
436	WRITE4(AGP_I810_GTT + (offset >> AGP_PAGE_SHIFT) * 4, physical | 1);
437	return 0;
438}
439
440static int
441agp_i810_unbind_page(device_t dev, int offset)
442{
443	struct agp_i810_softc *sc = device_get_softc(dev);
444
445	if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT))
446		return EINVAL;
447
448	if ( sc->chiptype == CHIP_I830 ) {
449		if ( (offset >> AGP_PAGE_SHIFT) < sc->stolen ) {
450			device_printf(dev, "trying to unbind from stolen memory");
451			return EINVAL;
452		}
453	}
454
455	WRITE4(AGP_I810_GTT + (offset >> AGP_PAGE_SHIFT) * 4, 0);
456	return 0;
457}
458
459/*
460 * Writing via memory mapped registers already flushes all TLBs.
461 */
462static void
463agp_i810_flush_tlb(device_t dev)
464{
465}
466
467static int
468agp_i810_enable(device_t dev, u_int32_t mode)
469{
470
471	return 0;
472}
473
474static struct agp_memory *
475agp_i810_alloc_memory(device_t dev, int type, vm_size_t size)
476{
477	struct agp_i810_softc *sc = device_get_softc(dev);
478	struct agp_memory *mem;
479
480	if ((size & (AGP_PAGE_SIZE - 1)) != 0)
481		return 0;
482
483	if (sc->agp.as_allocated + size > sc->agp.as_maxmem)
484		return 0;
485
486	if (type == 1) {
487		/*
488		 * Mapping local DRAM into GATT.
489		 */
490		if ( sc->chiptype == CHIP_I830 )
491			return 0;
492		if (size != sc->dcache_size)
493			return 0;
494	} else if (type == 2) {
495		/*
496		 * Bogus mapping of a single page for the hardware cursor.
497		 */
498		if (size != AGP_PAGE_SIZE)
499			return 0;
500	}
501
502	mem = malloc(sizeof *mem, M_AGP, M_WAITOK);
503	mem->am_id = sc->agp.as_nextid++;
504	mem->am_size = size;
505	mem->am_type = type;
506	if (type != 1)
507		mem->am_obj = vm_object_allocate(OBJT_DEFAULT,
508						 atop(round_page(size)));
509	else
510		mem->am_obj = 0;
511
512	if (type == 2) {
513		/*
514		 * Allocate and wire down the page now so that we can
515		 * get its physical address.
516		 */
517		vm_page_t m;
518		m = vm_page_grab(mem->am_obj, 0,
519		    VM_ALLOC_WIRED | VM_ALLOC_ZERO | VM_ALLOC_RETRY);
520		if ((m->flags & PG_ZERO) == 0)
521			pmap_zero_page(m);
522		vm_page_lock_queues();
523		mem->am_physical = VM_PAGE_TO_PHYS(m);
524		vm_page_wakeup(m);
525		vm_page_unlock_queues();
526	} else {
527		mem->am_physical = 0;
528	}
529
530	mem->am_offset = 0;
531	mem->am_is_bound = 0;
532	TAILQ_INSERT_TAIL(&sc->agp.as_memory, mem, am_link);
533	sc->agp.as_allocated += size;
534
535	return mem;
536}
537
538static int
539agp_i810_free_memory(device_t dev, struct agp_memory *mem)
540{
541	struct agp_i810_softc *sc = device_get_softc(dev);
542
543	if (mem->am_is_bound)
544		return EBUSY;
545
546	if (mem->am_type == 2) {
547		/*
548		 * Unwire the page which we wired in alloc_memory.
549		 */
550		vm_page_t m = vm_page_lookup(mem->am_obj, 0);
551		vm_page_lock_queues();
552		vm_page_unwire(m, 0);
553		vm_page_unlock_queues();
554	}
555
556	sc->agp.as_allocated -= mem->am_size;
557	TAILQ_REMOVE(&sc->agp.as_memory, mem, am_link);
558	if (mem->am_obj)
559		vm_object_deallocate(mem->am_obj);
560	free(mem, M_AGP);
561	return 0;
562}
563
564static int
565agp_i810_bind_memory(device_t dev, struct agp_memory *mem,
566		     vm_offset_t offset)
567{
568	struct agp_i810_softc *sc = device_get_softc(dev);
569	vm_offset_t i;
570
571	if (mem->am_type != 1)
572		return agp_generic_bind_memory(dev, mem, offset);
573
574	if ( sc->chiptype == CHIP_I830 )
575		return EINVAL;
576
577	for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) {
578		WRITE4(AGP_I810_GTT + (offset >> AGP_PAGE_SHIFT) * 4,
579		       i | 3);
580	}
581
582	return 0;
583}
584
585static int
586agp_i810_unbind_memory(device_t dev, struct agp_memory *mem)
587{
588	struct agp_i810_softc *sc = device_get_softc(dev);
589	vm_offset_t i;
590
591	if (mem->am_type != 1)
592		return agp_generic_unbind_memory(dev, mem);
593
594	if ( sc->chiptype == CHIP_I830 )
595		return EINVAL;
596
597	for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE)
598		WRITE4(AGP_I810_GTT + (i >> AGP_PAGE_SHIFT) * 4, 0);
599
600	return 0;
601}
602
603static device_method_t agp_i810_methods[] = {
604	/* Device interface */
605	DEVMETHOD(device_probe,		agp_i810_probe),
606	DEVMETHOD(device_attach,	agp_i810_attach),
607	DEVMETHOD(device_detach,	agp_i810_detach),
608	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
609	DEVMETHOD(device_suspend,	bus_generic_suspend),
610	DEVMETHOD(device_resume,	bus_generic_resume),
611
612	/* AGP interface */
613	DEVMETHOD(agp_get_aperture,	agp_i810_get_aperture),
614	DEVMETHOD(agp_set_aperture,	agp_i810_set_aperture),
615	DEVMETHOD(agp_bind_page,	agp_i810_bind_page),
616	DEVMETHOD(agp_unbind_page,	agp_i810_unbind_page),
617	DEVMETHOD(agp_flush_tlb,	agp_i810_flush_tlb),
618	DEVMETHOD(agp_enable,		agp_i810_enable),
619	DEVMETHOD(agp_alloc_memory,	agp_i810_alloc_memory),
620	DEVMETHOD(agp_free_memory,	agp_i810_free_memory),
621	DEVMETHOD(agp_bind_memory,	agp_i810_bind_memory),
622	DEVMETHOD(agp_unbind_memory,	agp_i810_unbind_memory),
623
624	{ 0, 0 }
625};
626
627static driver_t agp_i810_driver = {
628	"agp",
629	agp_i810_methods,
630	sizeof(struct agp_i810_softc),
631};
632
633static devclass_t agp_devclass;
634
635DRIVER_MODULE(agp_i810, pci, agp_i810_driver, agp_devclass, 0, 0);
636