xref: /freebsd/sys/dev/agp/agp.c (revision 6b38974085215bcd787be0d138411ec57cf0096e)
159747216SDoug Rabson /*-
2718cf2ccSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3718cf2ccSPedro F. Giffuni  *
459747216SDoug Rabson  * Copyright (c) 2000 Doug Rabson
559747216SDoug Rabson  * All rights reserved.
659747216SDoug Rabson  *
759747216SDoug Rabson  * Redistribution and use in source and binary forms, with or without
859747216SDoug Rabson  * modification, are permitted provided that the following conditions
959747216SDoug Rabson  * are met:
1059747216SDoug Rabson  * 1. Redistributions of source code must retain the above copyright
1159747216SDoug Rabson  *    notice, this list of conditions and the following disclaimer.
1259747216SDoug Rabson  * 2. Redistributions in binary form must reproduce the above copyright
1359747216SDoug Rabson  *    notice, this list of conditions and the following disclaimer in the
1459747216SDoug Rabson  *    documentation and/or other materials provided with the distribution.
1559747216SDoug Rabson  *
1659747216SDoug Rabson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1759747216SDoug Rabson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1859747216SDoug Rabson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1959747216SDoug Rabson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2059747216SDoug Rabson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2159747216SDoug Rabson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2259747216SDoug Rabson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2359747216SDoug Rabson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2459747216SDoug Rabson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2559747216SDoug Rabson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2659747216SDoug Rabson  * SUCH DAMAGE.
2759747216SDoug Rabson  */
2859747216SDoug Rabson 
29f4636c59SDavid E. O'Brien #include <sys/cdefs.h>
30f4636c59SDavid E. O'Brien __FBSDID("$FreeBSD$");
31f4636c59SDavid E. O'Brien 
32c353491aSWojciech A. Koszek #include "opt_agp.h"
3359747216SDoug Rabson 
3459747216SDoug Rabson #include <sys/param.h>
3559747216SDoug Rabson #include <sys/systm.h>
3659747216SDoug Rabson #include <sys/malloc.h>
3759747216SDoug Rabson #include <sys/kernel.h>
38f11d01c3SPoul-Henning Kamp #include <sys/module.h>
3959747216SDoug Rabson #include <sys/bus.h>
4059747216SDoug Rabson #include <sys/conf.h>
4159747216SDoug Rabson #include <sys/ioccom.h>
4259747216SDoug Rabson #include <sys/agpio.h>
4359747216SDoug Rabson #include <sys/lock.h>
4423955314SAlfred Perlstein #include <sys/mutex.h>
451639f08bSJohn Baldwin #include <sys/proc.h>
4689f6b863SAttilio Rao #include <sys/rwlock.h>
4759747216SDoug Rabson 
48dbac8ff4SJohn Baldwin #include <dev/agp/agppriv.h>
49dbac8ff4SJohn Baldwin #include <dev/agp/agpvar.h>
50dbac8ff4SJohn Baldwin #include <dev/agp/agpreg.h>
5119b7ffd1SWarner Losh #include <dev/pci/pcivar.h>
5219b7ffd1SWarner Losh #include <dev/pci/pcireg.h>
5359747216SDoug Rabson 
5459747216SDoug Rabson #include <vm/vm.h>
554e612cddSTijl Coosemans #include <vm/vm_extern.h>
564e612cddSTijl Coosemans #include <vm/vm_kern.h>
571c771f92SKonstantin Belousov #include <vm/vm_param.h>
5859747216SDoug Rabson #include <vm/vm_object.h>
5959747216SDoug Rabson #include <vm/vm_page.h>
6059747216SDoug Rabson #include <vm/vm_pageout.h>
6159747216SDoug Rabson #include <vm/pmap.h>
6259747216SDoug Rabson 
6359747216SDoug Rabson #include <machine/bus.h>
6459747216SDoug Rabson #include <machine/resource.h>
6559747216SDoug Rabson #include <sys/rman.h>
6659747216SDoug Rabson 
6759747216SDoug Rabson MODULE_VERSION(agp, 1);
6859747216SDoug Rabson 
69315a1645SAssar Westerlund MALLOC_DEFINE(M_AGP, "agp", "AGP data structures");
7059747216SDoug Rabson 
7159747216SDoug Rabson 				/* agp_drv.c */
7259747216SDoug Rabson static d_open_t agp_open;
7359747216SDoug Rabson static d_close_t agp_close;
7459747216SDoug Rabson static d_ioctl_t agp_ioctl;
7559747216SDoug Rabson static d_mmap_t agp_mmap;
7659747216SDoug Rabson 
7759747216SDoug Rabson static struct cdevsw agp_cdevsw = {
78dc08ffecSPoul-Henning Kamp 	.d_version =	D_VERSION,
79dc08ffecSPoul-Henning Kamp 	.d_flags =	D_NEEDGIANT,
807ac40f5fSPoul-Henning Kamp 	.d_open =	agp_open,
817ac40f5fSPoul-Henning Kamp 	.d_close =	agp_close,
827ac40f5fSPoul-Henning Kamp 	.d_ioctl =	agp_ioctl,
837ac40f5fSPoul-Henning Kamp 	.d_mmap =	agp_mmap,
847ac40f5fSPoul-Henning Kamp 	.d_name =	"agp",
8559747216SDoug Rabson };
8659747216SDoug Rabson 
8759747216SDoug Rabson static devclass_t agp_devclass;
8859747216SDoug Rabson 
8959747216SDoug Rabson /* Helper functions for implementing chipset mini drivers. */
9059747216SDoug Rabson 
9159747216SDoug Rabson u_int8_t
9259747216SDoug Rabson agp_find_caps(device_t dev)
9359747216SDoug Rabson {
9404d6a912SJohn Baldwin 	int capreg;
9559747216SDoug Rabson 
963b0a4aefSJohn Baldwin 	if (pci_find_cap(dev, PCIY_AGP, &capreg) != 0)
9704d6a912SJohn Baldwin 		capreg = 0;
9804d6a912SJohn Baldwin 	return (capreg);
9959747216SDoug Rabson }
10059747216SDoug Rabson 
10159747216SDoug Rabson /*
10259747216SDoug Rabson  * Find an AGP display device (if any).
10359747216SDoug Rabson  */
10459747216SDoug Rabson static device_t
10559747216SDoug Rabson agp_find_display(void)
10659747216SDoug Rabson {
10759747216SDoug Rabson 	devclass_t pci = devclass_find("pci");
10859747216SDoug Rabson 	device_t bus, dev = 0;
10959747216SDoug Rabson 	device_t *kids;
11059747216SDoug Rabson 	int busnum, numkids, i;
11159747216SDoug Rabson 
11259747216SDoug Rabson 	for (busnum = 0; busnum < devclass_get_maxunit(pci); busnum++) {
11359747216SDoug Rabson 		bus = devclass_get_device(pci, busnum);
11459747216SDoug Rabson 		if (!bus)
11559747216SDoug Rabson 			continue;
116d444cd5fSWarner Losh 		if (device_get_children(bus, &kids, &numkids) != 0)
117d444cd5fSWarner Losh 			continue;
11859747216SDoug Rabson 		for (i = 0; i < numkids; i++) {
11959747216SDoug Rabson 			dev = kids[i];
12059747216SDoug Rabson 			if (pci_get_class(dev) == PCIC_DISPLAY
12159747216SDoug Rabson 			    && pci_get_subclass(dev) == PCIS_DISPLAY_VGA)
12259747216SDoug Rabson 				if (agp_find_caps(dev)) {
12359747216SDoug Rabson 					free(kids, M_TEMP);
12459747216SDoug Rabson 					return dev;
12559747216SDoug Rabson 				}
12659747216SDoug Rabson 
12759747216SDoug Rabson 		}
12859747216SDoug Rabson 		free(kids, M_TEMP);
12959747216SDoug Rabson 	}
13059747216SDoug Rabson 
13159747216SDoug Rabson 	return 0;
13259747216SDoug Rabson }
13359747216SDoug Rabson 
13459747216SDoug Rabson struct agp_gatt *
13559747216SDoug Rabson agp_alloc_gatt(device_t dev)
13659747216SDoug Rabson {
13759747216SDoug Rabson 	u_int32_t apsize = AGP_GET_APERTURE(dev);
13859747216SDoug Rabson 	u_int32_t entries = apsize >> AGP_PAGE_SHIFT;
13959747216SDoug Rabson 	struct agp_gatt *gatt;
14059747216SDoug Rabson 
14159747216SDoug Rabson 	if (bootverbose)
14259747216SDoug Rabson 		device_printf(dev,
14359747216SDoug Rabson 			      "allocating GATT for aperture of size %dM\n",
14459747216SDoug Rabson 			      apsize / (1024*1024));
14559747216SDoug Rabson 
146a6cb9d8eSEric Anholt 	if (entries == 0) {
147a6cb9d8eSEric Anholt 		device_printf(dev, "bad aperture size\n");
148a6cb9d8eSEric Anholt 		return NULL;
149a6cb9d8eSEric Anholt 	}
150a6cb9d8eSEric Anholt 
15159747216SDoug Rabson 	gatt = malloc(sizeof(struct agp_gatt), M_AGP, M_NOWAIT);
15259747216SDoug Rabson 	if (!gatt)
15359747216SDoug Rabson 		return 0;
15459747216SDoug Rabson 
15559747216SDoug Rabson 	gatt->ag_entries = entries;
15644d0efb2SAlan Cox 	gatt->ag_virtual = (void *)kmem_alloc_contig(entries *
15744d0efb2SAlan Cox 	    sizeof(u_int32_t), M_NOWAIT | M_ZERO, 0, ~0, PAGE_SIZE, 0,
15844d0efb2SAlan Cox 	    VM_MEMATTR_WRITE_COMBINING);
15959747216SDoug Rabson 	if (!gatt->ag_virtual) {
16059747216SDoug Rabson 		if (bootverbose)
16159747216SDoug Rabson 			device_printf(dev, "contiguous allocation failed\n");
16259747216SDoug Rabson 		free(gatt, M_AGP);
16359747216SDoug Rabson 		return 0;
16459747216SDoug Rabson 	}
16559747216SDoug Rabson 	gatt->ag_physical = vtophys((vm_offset_t) gatt->ag_virtual);
16659747216SDoug Rabson 
16759747216SDoug Rabson 	return gatt;
16859747216SDoug Rabson }
16959747216SDoug Rabson 
17059747216SDoug Rabson void
17159747216SDoug Rabson agp_free_gatt(struct agp_gatt *gatt)
17259747216SDoug Rabson {
17349bfa624SAlan Cox 	kmem_free((vm_offset_t)gatt->ag_virtual, gatt->ag_entries *
17449bfa624SAlan Cox 	    sizeof(u_int32_t));
17559747216SDoug Rabson 	free(gatt, M_AGP);
17659747216SDoug Rabson }
17759747216SDoug Rabson 
1787370bc77SSeigo Tanimura static u_int agp_max[][2] = {
17959747216SDoug Rabson 	{0,	0},
18059747216SDoug Rabson 	{32,	4},
18159747216SDoug Rabson 	{64,	28},
18259747216SDoug Rabson 	{128,	96},
18359747216SDoug Rabson 	{256,	204},
18459747216SDoug Rabson 	{512,	440},
18559747216SDoug Rabson 	{1024,	942},
18659747216SDoug Rabson 	{2048,	1920},
18759747216SDoug Rabson 	{4096,	3932}
18859747216SDoug Rabson };
1894ec642f1SPedro F. Giffuni #define	AGP_MAX_SIZE	nitems(agp_max)
19059747216SDoug Rabson 
191d450e052SEric Anholt /**
192d450e052SEric Anholt  * Sets the PCI resource which represents the AGP aperture.
193d450e052SEric Anholt  *
194d450e052SEric Anholt  * If not called, the default AGP aperture resource of AGP_APBASE will
195d450e052SEric Anholt  * be used.  Must be called before agp_generic_attach().
196d450e052SEric Anholt  */
197d450e052SEric Anholt void
198d450e052SEric Anholt agp_set_aperture_resource(device_t dev, int rid)
199d450e052SEric Anholt {
200d450e052SEric Anholt 	struct agp_softc *sc = device_get_softc(dev);
201d450e052SEric Anholt 
202d450e052SEric Anholt 	sc->as_aperture_rid = rid;
203d450e052SEric Anholt }
204d450e052SEric Anholt 
20559747216SDoug Rabson int
20659747216SDoug Rabson agp_generic_attach(device_t dev)
20759747216SDoug Rabson {
208437ea82cSMark Johnston 	struct make_dev_args mdargs;
20959747216SDoug Rabson 	struct agp_softc *sc = device_get_softc(dev);
210437ea82cSMark Johnston 	int error, i, unit;
2117370bc77SSeigo Tanimura 	u_int memsize;
21259747216SDoug Rabson 
21359747216SDoug Rabson 	/*
214d450e052SEric Anholt 	 * Find and map the aperture, RF_SHAREABLE for DRM but not RF_ACTIVE
215d450e052SEric Anholt 	 * because the kernel doesn't need to map it.
21659747216SDoug Rabson 	 */
21750fd2a5bSNathan Whitehorn 
21850fd2a5bSNathan Whitehorn 	if (sc->as_aperture_rid != -1) {
219d450e052SEric Anholt 		if (sc->as_aperture_rid == 0)
220d450e052SEric Anholt 			sc->as_aperture_rid = AGP_APBASE;
221d450e052SEric Anholt 
222d450e052SEric Anholt 		sc->as_aperture = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
223d450e052SEric Anholt 		    &sc->as_aperture_rid, RF_SHAREABLE);
22459747216SDoug Rabson 		if (!sc->as_aperture)
22559747216SDoug Rabson 			return ENOMEM;
22650fd2a5bSNathan Whitehorn 	}
22759747216SDoug Rabson 
22859747216SDoug Rabson 	/*
22959747216SDoug Rabson 	 * Work out an upper bound for agp memory allocation. This
23059747216SDoug Rabson 	 * uses a heurisitc table from the Linux driver.
23159747216SDoug Rabson 	 */
232e11379e9SMarcel Moolenaar 	memsize = ptoa(realmem) >> 20;
2334ec642f1SPedro F. Giffuni 	for (i = 0; i < AGP_MAX_SIZE; i++) {
23459747216SDoug Rabson 		if (memsize <= agp_max[i][0])
23559747216SDoug Rabson 			break;
23659747216SDoug Rabson 	}
2374ec642f1SPedro F. Giffuni 	if (i == AGP_MAX_SIZE)
2384ec642f1SPedro F. Giffuni 		i = AGP_MAX_SIZE - 1;
23959747216SDoug Rabson 	sc->as_maxmem = agp_max[i][1] << 20U;
24059747216SDoug Rabson 
24159747216SDoug Rabson 	/*
24259747216SDoug Rabson 	 * The lock is used to prevent re-entry to
24359747216SDoug Rabson 	 * agp_generic_bind_memory() since that function can sleep.
24459747216SDoug Rabson 	 */
245d8a821e8SMaxime Henrion 	mtx_init(&sc->as_lock, "agp lock", NULL, MTX_DEF);
24659747216SDoug Rabson 
24759747216SDoug Rabson 	/*
24859747216SDoug Rabson 	 * Initialise stuff for the userland device.
24959747216SDoug Rabson 	 */
25059747216SDoug Rabson 	agp_devclass = devclass_find("agp");
25159747216SDoug Rabson 	TAILQ_INIT(&sc->as_memory);
25259747216SDoug Rabson 	sc->as_nextid = 1;
25359747216SDoug Rabson 
254437ea82cSMark Johnston 	sc->as_devalias = NULL;
25559747216SDoug Rabson 
256437ea82cSMark Johnston 	make_dev_args_init(&mdargs);
257437ea82cSMark Johnston 	mdargs.mda_devsw = &agp_cdevsw;
258437ea82cSMark Johnston 	mdargs.mda_uid = UID_ROOT;
259437ea82cSMark Johnston 	mdargs.mda_gid = GID_WHEEL;
260437ea82cSMark Johnston 	mdargs.mda_mode = 0600;
261437ea82cSMark Johnston 	mdargs.mda_si_drv1 = sc;
262437ea82cSMark Johnston 	mdargs.mda_si_drv2 = NULL;
263437ea82cSMark Johnston 
264437ea82cSMark Johnston 	unit = device_get_unit(dev);
265437ea82cSMark Johnston 	error = make_dev_s(&mdargs, &sc->as_devnode, "agpgart%d", unit);
266437ea82cSMark Johnston 	if (error == 0) {
267437ea82cSMark Johnston 		/*
268437ea82cSMark Johnston 		 * Create an alias for the first device that shows up.
269437ea82cSMark Johnston 		 */
270437ea82cSMark Johnston 		if (unit == 0) {
271437ea82cSMark Johnston 			(void)make_dev_alias_p(MAKEDEV_CHECKNAME,
272437ea82cSMark Johnston 			    &sc->as_devalias, sc->as_devnode, "agpgart");
273437ea82cSMark Johnston 		}
274437ea82cSMark Johnston 	} else {
275437ea82cSMark Johnston 		agp_free_res(dev);
276437ea82cSMark Johnston 	}
277437ea82cSMark Johnston 
278437ea82cSMark Johnston 	return error;
27959747216SDoug Rabson }
28059747216SDoug Rabson 
281f82a1d49SJohn Baldwin void
282f82a1d49SJohn Baldwin agp_free_cdev(device_t dev)
28359747216SDoug Rabson {
28459747216SDoug Rabson 	struct agp_softc *sc = device_get_softc(dev);
2854c2546c1SJohn Baldwin 
2864c2546c1SJohn Baldwin 	destroy_dev(sc->as_devnode);
287437ea82cSMark Johnston 	if (sc->as_devalias != NULL)
288437ea82cSMark Johnston 		destroy_dev(sc->as_devalias);
289f82a1d49SJohn Baldwin }
290f82a1d49SJohn Baldwin 
291f82a1d49SJohn Baldwin void
292f82a1d49SJohn Baldwin agp_free_res(device_t dev)
293f82a1d49SJohn Baldwin {
294f82a1d49SJohn Baldwin 	struct agp_softc *sc = device_get_softc(dev);
295f82a1d49SJohn Baldwin 
29650fd2a5bSNathan Whitehorn 	if (sc->as_aperture != NULL)
297d450e052SEric Anholt 		bus_release_resource(dev, SYS_RES_MEMORY, sc->as_aperture_rid,
298d450e052SEric Anholt 		    sc->as_aperture);
299d8a821e8SMaxime Henrion 	mtx_destroy(&sc->as_lock);
300f82a1d49SJohn Baldwin }
301f82a1d49SJohn Baldwin 
302f82a1d49SJohn Baldwin int
303f82a1d49SJohn Baldwin agp_generic_detach(device_t dev)
304f82a1d49SJohn Baldwin {
305f82a1d49SJohn Baldwin 
306f82a1d49SJohn Baldwin 	agp_free_cdev(dev);
307f82a1d49SJohn Baldwin 	agp_free_res(dev);
30859747216SDoug Rabson 	return 0;
30959747216SDoug Rabson }
31059747216SDoug Rabson 
311d450e052SEric Anholt /**
312d450e052SEric Anholt  * Default AGP aperture size detection which simply returns the size of
313d450e052SEric Anholt  * the aperture's PCI resource.
314d450e052SEric Anholt  */
315cd6d5177SWarner Losh u_int32_t
316d450e052SEric Anholt agp_generic_get_aperture(device_t dev)
317d450e052SEric Anholt {
318d450e052SEric Anholt 	struct agp_softc *sc = device_get_softc(dev);
319d450e052SEric Anholt 
320d450e052SEric Anholt 	return rman_get_size(sc->as_aperture);
321d450e052SEric Anholt }
322d450e052SEric Anholt 
323d450e052SEric Anholt /**
324d450e052SEric Anholt  * Default AGP aperture size setting function, which simply doesn't allow
325d450e052SEric Anholt  * changes to resource size.
326d450e052SEric Anholt  */
327d450e052SEric Anholt int
328d450e052SEric Anholt agp_generic_set_aperture(device_t dev, u_int32_t aperture)
329d450e052SEric Anholt {
330d450e052SEric Anholt 	u_int32_t current_aperture;
331d450e052SEric Anholt 
332d450e052SEric Anholt 	current_aperture = AGP_GET_APERTURE(dev);
333d450e052SEric Anholt 	if (current_aperture != aperture)
334d450e052SEric Anholt 		return EINVAL;
335d450e052SEric Anholt 	else
336d450e052SEric Anholt 		return 0;
337d450e052SEric Anholt }
338d450e052SEric Anholt 
3399f9ccd20SJohn Baldwin /*
3409f9ccd20SJohn Baldwin  * This does the enable logic for v3, with the same topology
3419f9ccd20SJohn Baldwin  * restrictions as in place for v2 -- one bus, one device on the bus.
3429f9ccd20SJohn Baldwin  */
3439f9ccd20SJohn Baldwin static int
3449f9ccd20SJohn Baldwin agp_v3_enable(device_t dev, device_t mdev, u_int32_t mode)
34559747216SDoug Rabson {
34659747216SDoug Rabson 	u_int32_t tstatus, mstatus;
34759747216SDoug Rabson 	u_int32_t command;
3489f9ccd20SJohn Baldwin 	int rq, sba, fw, rate, arqsz, cal;
34959747216SDoug Rabson 
3509f9ccd20SJohn Baldwin 	tstatus = pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4);
3519f9ccd20SJohn Baldwin 	mstatus = pci_read_config(mdev, agp_find_caps(mdev) + AGP_STATUS, 4);
3529f9ccd20SJohn Baldwin 
3539f9ccd20SJohn Baldwin 	/* Set RQ to the min of mode, tstatus and mstatus */
3549f9ccd20SJohn Baldwin 	rq = AGP_MODE_GET_RQ(mode);
3559f9ccd20SJohn Baldwin 	if (AGP_MODE_GET_RQ(tstatus) < rq)
3569f9ccd20SJohn Baldwin 		rq = AGP_MODE_GET_RQ(tstatus);
3579f9ccd20SJohn Baldwin 	if (AGP_MODE_GET_RQ(mstatus) < rq)
3589f9ccd20SJohn Baldwin 		rq = AGP_MODE_GET_RQ(mstatus);
3599f9ccd20SJohn Baldwin 
3609f9ccd20SJohn Baldwin 	/*
3619f9ccd20SJohn Baldwin 	 * ARQSZ - Set the value to the maximum one.
3629f9ccd20SJohn Baldwin 	 * Don't allow the mode register to override values.
3639f9ccd20SJohn Baldwin 	 */
3649f9ccd20SJohn Baldwin 	arqsz = AGP_MODE_GET_ARQSZ(mode);
3659f9ccd20SJohn Baldwin 	if (AGP_MODE_GET_ARQSZ(tstatus) > rq)
3669f9ccd20SJohn Baldwin 		rq = AGP_MODE_GET_ARQSZ(tstatus);
3679f9ccd20SJohn Baldwin 	if (AGP_MODE_GET_ARQSZ(mstatus) > rq)
3689f9ccd20SJohn Baldwin 		rq = AGP_MODE_GET_ARQSZ(mstatus);
3699f9ccd20SJohn Baldwin 
3709f9ccd20SJohn Baldwin 	/* Calibration cycle - don't allow override by mode register */
3719f9ccd20SJohn Baldwin 	cal = AGP_MODE_GET_CAL(tstatus);
3729f9ccd20SJohn Baldwin 	if (AGP_MODE_GET_CAL(mstatus) < cal)
3739f9ccd20SJohn Baldwin 		cal = AGP_MODE_GET_CAL(mstatus);
3749f9ccd20SJohn Baldwin 
3759f9ccd20SJohn Baldwin 	/* SBA must be supported for AGP v3. */
3769f9ccd20SJohn Baldwin 	sba = 1;
3779f9ccd20SJohn Baldwin 
3789f9ccd20SJohn Baldwin 	/* Set FW if all three support it. */
3799f9ccd20SJohn Baldwin 	fw = (AGP_MODE_GET_FW(tstatus)
3809f9ccd20SJohn Baldwin 	       & AGP_MODE_GET_FW(mstatus)
3819f9ccd20SJohn Baldwin 	       & AGP_MODE_GET_FW(mode));
3829f9ccd20SJohn Baldwin 
3839f9ccd20SJohn Baldwin 	/* Figure out the max rate */
3849f9ccd20SJohn Baldwin 	rate = (AGP_MODE_GET_RATE(tstatus)
3859f9ccd20SJohn Baldwin 		& AGP_MODE_GET_RATE(mstatus)
3869f9ccd20SJohn Baldwin 		& AGP_MODE_GET_RATE(mode));
3879f9ccd20SJohn Baldwin 	if (rate & AGP_MODE_V3_RATE_8x)
3889f9ccd20SJohn Baldwin 		rate = AGP_MODE_V3_RATE_8x;
3899f9ccd20SJohn Baldwin 	else
3909f9ccd20SJohn Baldwin 		rate = AGP_MODE_V3_RATE_4x;
3919f9ccd20SJohn Baldwin 	if (bootverbose)
3929f9ccd20SJohn Baldwin 		device_printf(dev, "Setting AGP v3 mode %d\n", rate * 4);
3939f9ccd20SJohn Baldwin 
3949f9ccd20SJohn Baldwin 	pci_write_config(dev, agp_find_caps(dev) + AGP_COMMAND, 0, 4);
3959f9ccd20SJohn Baldwin 
3969f9ccd20SJohn Baldwin 	/* Construct the new mode word and tell the hardware */
39798935c58SJung-uk Kim 	command = 0;
3989f9ccd20SJohn Baldwin 	command = AGP_MODE_SET_RQ(0, rq);
3999f9ccd20SJohn Baldwin 	command = AGP_MODE_SET_ARQSZ(command, arqsz);
4009f9ccd20SJohn Baldwin 	command = AGP_MODE_SET_CAL(command, cal);
4019f9ccd20SJohn Baldwin 	command = AGP_MODE_SET_SBA(command, sba);
4029f9ccd20SJohn Baldwin 	command = AGP_MODE_SET_FW(command, fw);
4039f9ccd20SJohn Baldwin 	command = AGP_MODE_SET_RATE(command, rate);
40498935c58SJung-uk Kim 	command = AGP_MODE_SET_MODE_3(command, 1);
4059f9ccd20SJohn Baldwin 	command = AGP_MODE_SET_AGP(command, 1);
4069f9ccd20SJohn Baldwin 	pci_write_config(dev, agp_find_caps(dev) + AGP_COMMAND, command, 4);
4079f9ccd20SJohn Baldwin 	pci_write_config(mdev, agp_find_caps(mdev) + AGP_COMMAND, command, 4);
4089f9ccd20SJohn Baldwin 
4099f9ccd20SJohn Baldwin 	return 0;
41059747216SDoug Rabson }
41159747216SDoug Rabson 
4129f9ccd20SJohn Baldwin static int
4139f9ccd20SJohn Baldwin agp_v2_enable(device_t dev, device_t mdev, u_int32_t mode)
4149f9ccd20SJohn Baldwin {
4159f9ccd20SJohn Baldwin 	u_int32_t tstatus, mstatus;
4169f9ccd20SJohn Baldwin 	u_int32_t command;
4179f9ccd20SJohn Baldwin 	int rq, sba, fw, rate;
4189f9ccd20SJohn Baldwin 
41959747216SDoug Rabson 	tstatus = pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4);
42059747216SDoug Rabson 	mstatus = pci_read_config(mdev, agp_find_caps(mdev) + AGP_STATUS, 4);
42159747216SDoug Rabson 
42259747216SDoug Rabson 	/* Set RQ to the min of mode, tstatus and mstatus */
42359747216SDoug Rabson 	rq = AGP_MODE_GET_RQ(mode);
42459747216SDoug Rabson 	if (AGP_MODE_GET_RQ(tstatus) < rq)
42559747216SDoug Rabson 		rq = AGP_MODE_GET_RQ(tstatus);
42659747216SDoug Rabson 	if (AGP_MODE_GET_RQ(mstatus) < rq)
42759747216SDoug Rabson 		rq = AGP_MODE_GET_RQ(mstatus);
42859747216SDoug Rabson 
42959747216SDoug Rabson 	/* Set SBA if all three can deal with SBA */
43059747216SDoug Rabson 	sba = (AGP_MODE_GET_SBA(tstatus)
43159747216SDoug Rabson 	       & AGP_MODE_GET_SBA(mstatus)
43259747216SDoug Rabson 	       & AGP_MODE_GET_SBA(mode));
43359747216SDoug Rabson 
43459747216SDoug Rabson 	/* Similar for FW */
43559747216SDoug Rabson 	fw = (AGP_MODE_GET_FW(tstatus)
43659747216SDoug Rabson 	       & AGP_MODE_GET_FW(mstatus)
43759747216SDoug Rabson 	       & AGP_MODE_GET_FW(mode));
43859747216SDoug Rabson 
43959747216SDoug Rabson 	/* Figure out the max rate */
44059747216SDoug Rabson 	rate = (AGP_MODE_GET_RATE(tstatus)
44159747216SDoug Rabson 		& AGP_MODE_GET_RATE(mstatus)
44259747216SDoug Rabson 		& AGP_MODE_GET_RATE(mode));
4439f9ccd20SJohn Baldwin 	if (rate & AGP_MODE_V2_RATE_4x)
4449f9ccd20SJohn Baldwin 		rate = AGP_MODE_V2_RATE_4x;
4459f9ccd20SJohn Baldwin 	else if (rate & AGP_MODE_V2_RATE_2x)
4469f9ccd20SJohn Baldwin 		rate = AGP_MODE_V2_RATE_2x;
44759747216SDoug Rabson 	else
4489f9ccd20SJohn Baldwin 		rate = AGP_MODE_V2_RATE_1x;
4499f9ccd20SJohn Baldwin 	if (bootverbose)
4509f9ccd20SJohn Baldwin 		device_printf(dev, "Setting AGP v2 mode %d\n", rate);
45159747216SDoug Rabson 
45259747216SDoug Rabson 	/* Construct the new mode word and tell the hardware */
45398935c58SJung-uk Kim 	command = 0;
45459747216SDoug Rabson 	command = AGP_MODE_SET_RQ(0, rq);
45559747216SDoug Rabson 	command = AGP_MODE_SET_SBA(command, sba);
45659747216SDoug Rabson 	command = AGP_MODE_SET_FW(command, fw);
45759747216SDoug Rabson 	command = AGP_MODE_SET_RATE(command, rate);
45859747216SDoug Rabson 	command = AGP_MODE_SET_AGP(command, 1);
45959747216SDoug Rabson 	pci_write_config(dev, agp_find_caps(dev) + AGP_COMMAND, command, 4);
46059747216SDoug Rabson 	pci_write_config(mdev, agp_find_caps(mdev) + AGP_COMMAND, command, 4);
46159747216SDoug Rabson 
46259747216SDoug Rabson 	return 0;
46359747216SDoug Rabson }
46459747216SDoug Rabson 
4659f9ccd20SJohn Baldwin int
4669f9ccd20SJohn Baldwin agp_generic_enable(device_t dev, u_int32_t mode)
4679f9ccd20SJohn Baldwin {
4689f9ccd20SJohn Baldwin 	device_t mdev = agp_find_display();
4699f9ccd20SJohn Baldwin 	u_int32_t tstatus, mstatus;
4709f9ccd20SJohn Baldwin 
4719f9ccd20SJohn Baldwin 	if (!mdev) {
4729f9ccd20SJohn Baldwin 		AGP_DPF("can't find display\n");
4739f9ccd20SJohn Baldwin 		return ENXIO;
4749f9ccd20SJohn Baldwin 	}
4759f9ccd20SJohn Baldwin 
4769f9ccd20SJohn Baldwin 	tstatus = pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4);
4779f9ccd20SJohn Baldwin 	mstatus = pci_read_config(mdev, agp_find_caps(mdev) + AGP_STATUS, 4);
4789f9ccd20SJohn Baldwin 
4799f9ccd20SJohn Baldwin 	/*
4809f9ccd20SJohn Baldwin 	 * Check display and bridge for AGP v3 support.  AGP v3 allows
4819f9ccd20SJohn Baldwin 	 * more variety in topology than v2, e.g. multiple AGP devices
4829f9ccd20SJohn Baldwin 	 * attached to one bridge, or multiple AGP bridges in one
4839f9ccd20SJohn Baldwin 	 * system.  This doesn't attempt to address those situations,
4849f9ccd20SJohn Baldwin 	 * but should work fine for a classic single AGP slot system
4859f9ccd20SJohn Baldwin 	 * with AGP v3.
4869f9ccd20SJohn Baldwin 	 */
48798935c58SJung-uk Kim 	if (AGP_MODE_GET_MODE_3(mode) &&
48898935c58SJung-uk Kim 	    AGP_MODE_GET_MODE_3(tstatus) &&
48998935c58SJung-uk Kim 	    AGP_MODE_GET_MODE_3(mstatus))
4909f9ccd20SJohn Baldwin 		return (agp_v3_enable(dev, mdev, mode));
4919f9ccd20SJohn Baldwin 	else
4929f9ccd20SJohn Baldwin 		return (agp_v2_enable(dev, mdev, mode));
4939f9ccd20SJohn Baldwin }
4949f9ccd20SJohn Baldwin 
49559747216SDoug Rabson struct agp_memory *
49659747216SDoug Rabson agp_generic_alloc_memory(device_t dev, int type, vm_size_t size)
49759747216SDoug Rabson {
49859747216SDoug Rabson 	struct agp_softc *sc = device_get_softc(dev);
49959747216SDoug Rabson 	struct agp_memory *mem;
50059747216SDoug Rabson 
50159747216SDoug Rabson 	if ((size & (AGP_PAGE_SIZE - 1)) != 0)
50259747216SDoug Rabson 		return 0;
50359747216SDoug Rabson 
504cf99ea5dSTijl Coosemans 	if (size > sc->as_maxmem - sc->as_allocated)
50559747216SDoug Rabson 		return 0;
50659747216SDoug Rabson 
507e547d6fdSDoug Rabson 	if (type != 0) {
508e547d6fdSDoug Rabson 		printf("agp_generic_alloc_memory: unsupported type %d\n",
509e547d6fdSDoug Rabson 		       type);
510e547d6fdSDoug Rabson 		return 0;
511e547d6fdSDoug Rabson 	}
512e547d6fdSDoug Rabson 
513a163d034SWarner Losh 	mem = malloc(sizeof *mem, M_AGP, M_WAITOK);
51459747216SDoug Rabson 	mem->am_id = sc->as_nextid++;
51559747216SDoug Rabson 	mem->am_size = size;
516e547d6fdSDoug Rabson 	mem->am_type = 0;
517*6b389740SMark Johnston 	mem->am_obj = vm_object_allocate(OBJT_SWAP, atop(round_page(size)));
51859747216SDoug Rabson 	mem->am_physical = 0;
51959747216SDoug Rabson 	mem->am_offset = 0;
52059747216SDoug Rabson 	mem->am_is_bound = 0;
52159747216SDoug Rabson 	TAILQ_INSERT_TAIL(&sc->as_memory, mem, am_link);
52259747216SDoug Rabson 	sc->as_allocated += size;
52359747216SDoug Rabson 
52459747216SDoug Rabson 	return mem;
52559747216SDoug Rabson }
52659747216SDoug Rabson 
52759747216SDoug Rabson int
52859747216SDoug Rabson agp_generic_free_memory(device_t dev, struct agp_memory *mem)
52959747216SDoug Rabson {
53059747216SDoug Rabson 	struct agp_softc *sc = device_get_softc(dev);
53159747216SDoug Rabson 
53259747216SDoug Rabson 	if (mem->am_is_bound)
53359747216SDoug Rabson 		return EBUSY;
53459747216SDoug Rabson 
53559747216SDoug Rabson 	sc->as_allocated -= mem->am_size;
53659747216SDoug Rabson 	TAILQ_REMOVE(&sc->as_memory, mem, am_link);
53759747216SDoug Rabson 	vm_object_deallocate(mem->am_obj);
53859747216SDoug Rabson 	free(mem, M_AGP);
53959747216SDoug Rabson 	return 0;
54059747216SDoug Rabson }
54159747216SDoug Rabson 
54259747216SDoug Rabson int
54359747216SDoug Rabson agp_generic_bind_memory(device_t dev, struct agp_memory *mem,
54459747216SDoug Rabson 			vm_offset_t offset)
54559747216SDoug Rabson {
54659747216SDoug Rabson 	struct agp_softc *sc = device_get_softc(dev);
54759747216SDoug Rabson 	vm_offset_t i, j, k;
54859747216SDoug Rabson 	vm_page_t m;
54959747216SDoug Rabson 	int error;
55059747216SDoug Rabson 
551eb325974SMaxime Henrion 	/* Do some sanity checks first. */
55245d0290aSRobert Noland 	if ((offset & (AGP_PAGE_SIZE - 1)) != 0 ||
553eb325974SMaxime Henrion 	    offset + mem->am_size > AGP_GET_APERTURE(dev)) {
55459747216SDoug Rabson 		device_printf(dev, "binding memory at bad offset %#x\n",
55559747216SDoug Rabson 		    (int)offset);
55659747216SDoug Rabson 		return EINVAL;
55759747216SDoug Rabson 	}
55859747216SDoug Rabson 
55959747216SDoug Rabson 	/*
560eb325974SMaxime Henrion 	 * Allocate the pages early, before acquiring the lock,
56183d5d296SKonstantin Belousov 	 * because vm_page_grab() may sleep and we can't hold a mutex
56283d5d296SKonstantin Belousov 	 * while sleeping.
56359747216SDoug Rabson 	 */
56489f6b863SAttilio Rao 	VM_OBJECT_WLOCK(mem->am_obj);
56559747216SDoug Rabson 	for (i = 0; i < mem->am_size; i += PAGE_SIZE) {
56659747216SDoug Rabson 		/*
56759747216SDoug Rabson 		 * Find a page from the object and wire it
56859747216SDoug Rabson 		 * down. This page will be mapped using one or more
56959747216SDoug Rabson 		 * entries in the GATT (assuming that PAGE_SIZE >=
57059747216SDoug Rabson 		 * AGP_PAGE_SIZE. If this is the first call to bind,
57159747216SDoug Rabson 		 * the pages will be allocated and zeroed.
57259747216SDoug Rabson 		 */
57359747216SDoug Rabson 		m = vm_page_grab(mem->am_obj, OFF_TO_IDX(i),
5745944de8eSKonstantin Belousov 		    VM_ALLOC_WIRED | VM_ALLOC_ZERO);
575c353491aSWojciech A. Koszek 		AGP_DPF("found page pa=%#jx\n", (uintmax_t)VM_PAGE_TO_PHYS(m));
576eb325974SMaxime Henrion 	}
57789f6b863SAttilio Rao 	VM_OBJECT_WUNLOCK(mem->am_obj);
578eb325974SMaxime Henrion 
579eb325974SMaxime Henrion 	mtx_lock(&sc->as_lock);
580eb325974SMaxime Henrion 
581eb325974SMaxime Henrion 	if (mem->am_is_bound) {
582eb325974SMaxime Henrion 		device_printf(dev, "memory already bound\n");
583eb325974SMaxime Henrion 		error = EINVAL;
58489f6b863SAttilio Rao 		VM_OBJECT_WLOCK(mem->am_obj);
585fc72031cSKonstantin Belousov 		i = 0;
586eb325974SMaxime Henrion 		goto bad;
587eb325974SMaxime Henrion 	}
588eb325974SMaxime Henrion 
589eb325974SMaxime Henrion 	/*
590eb325974SMaxime Henrion 	 * Bind the individual pages and flush the chipset's
591eb325974SMaxime Henrion 	 * TLB.
592eb325974SMaxime Henrion 	 */
59389f6b863SAttilio Rao 	VM_OBJECT_WLOCK(mem->am_obj);
59491dd9693SAlan Cox 	for (i = 0; i < mem->am_size; i += PAGE_SIZE) {
595eb325974SMaxime Henrion 		m = vm_page_lookup(mem->am_obj, OFF_TO_IDX(i));
59659747216SDoug Rabson 
59759747216SDoug Rabson 		/*
59859747216SDoug Rabson 		 * Install entries in the GATT, making sure that if
59959747216SDoug Rabson 		 * AGP_PAGE_SIZE < PAGE_SIZE and mem->am_size is not
60059747216SDoug Rabson 		 * aligned to PAGE_SIZE, we don't modify too many GATT
60159747216SDoug Rabson 		 * entries.
60259747216SDoug Rabson 		 */
60359747216SDoug Rabson 		for (j = 0; j < PAGE_SIZE && i + j < mem->am_size;
60459747216SDoug Rabson 		     j += AGP_PAGE_SIZE) {
60559747216SDoug Rabson 			vm_offset_t pa = VM_PAGE_TO_PHYS(m) + j;
606c353491aSWojciech A. Koszek 			AGP_DPF("binding offset %#jx to pa %#jx\n",
607c353491aSWojciech A. Koszek 				(uintmax_t)offset + i + j, (uintmax_t)pa);
60859747216SDoug Rabson 			error = AGP_BIND_PAGE(dev, offset + i + j, pa);
60959747216SDoug Rabson 			if (error) {
61059747216SDoug Rabson 				/*
61159747216SDoug Rabson 				 * Bail out. Reverse all the mappings
61259747216SDoug Rabson 				 * and unwire the pages.
61359747216SDoug Rabson 				 */
61459747216SDoug Rabson 				for (k = 0; k < i + j; k += AGP_PAGE_SIZE)
61559747216SDoug Rabson 					AGP_UNBIND_PAGE(dev, offset + k);
616eb325974SMaxime Henrion 				goto bad;
61759747216SDoug Rabson 			}
61859747216SDoug Rabson 		}
619c7aebda8SAttilio Rao 		vm_page_xunbusy(m);
62059747216SDoug Rabson 	}
62189f6b863SAttilio Rao 	VM_OBJECT_WUNLOCK(mem->am_obj);
62259747216SDoug Rabson 
62359747216SDoug Rabson 	/*
62459747216SDoug Rabson 	 * Make sure the chipset gets the new mappings.
62559747216SDoug Rabson 	 */
62659747216SDoug Rabson 	AGP_FLUSH_TLB(dev);
62759747216SDoug Rabson 
62859747216SDoug Rabson 	mem->am_offset = offset;
62959747216SDoug Rabson 	mem->am_is_bound = 1;
63059747216SDoug Rabson 
631d8a821e8SMaxime Henrion 	mtx_unlock(&sc->as_lock);
63259747216SDoug Rabson 
63359747216SDoug Rabson 	return 0;
634eb325974SMaxime Henrion bad:
635eb325974SMaxime Henrion 	mtx_unlock(&sc->as_lock);
63689f6b863SAttilio Rao 	VM_OBJECT_ASSERT_WLOCKED(mem->am_obj);
637fc72031cSKonstantin Belousov 	for (k = 0; k < mem->am_size; k += PAGE_SIZE) {
638fc72031cSKonstantin Belousov 		m = vm_page_lookup(mem->am_obj, OFF_TO_IDX(k));
639fc72031cSKonstantin Belousov 		if (k >= i)
640c7aebda8SAttilio Rao 			vm_page_xunbusy(m);
6413ae10f74SAttilio Rao 		vm_page_unwire(m, PQ_INACTIVE);
642eb325974SMaxime Henrion 	}
64389f6b863SAttilio Rao 	VM_OBJECT_WUNLOCK(mem->am_obj);
644eb325974SMaxime Henrion 
645eb325974SMaxime Henrion 	return error;
64659747216SDoug Rabson }
64759747216SDoug Rabson 
64859747216SDoug Rabson int
64959747216SDoug Rabson agp_generic_unbind_memory(device_t dev, struct agp_memory *mem)
65059747216SDoug Rabson {
65159747216SDoug Rabson 	struct agp_softc *sc = device_get_softc(dev);
65259747216SDoug Rabson 	vm_page_t m;
65359747216SDoug Rabson 	int i;
65459747216SDoug Rabson 
655d8a821e8SMaxime Henrion 	mtx_lock(&sc->as_lock);
65659747216SDoug Rabson 
65759747216SDoug Rabson 	if (!mem->am_is_bound) {
65859747216SDoug Rabson 		device_printf(dev, "memory is not bound\n");
659d8a821e8SMaxime Henrion 		mtx_unlock(&sc->as_lock);
66059747216SDoug Rabson 		return EINVAL;
66159747216SDoug Rabson 	}
66259747216SDoug Rabson 
66359747216SDoug Rabson 	/*
66459747216SDoug Rabson 	 * Unbind the individual pages and flush the chipset's
66559747216SDoug Rabson 	 * TLB. Unwire the pages so they can be swapped.
66659747216SDoug Rabson 	 */
66759747216SDoug Rabson 	for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE)
66859747216SDoug Rabson 		AGP_UNBIND_PAGE(dev, mem->am_offset + i);
6690de6ac2eSTijl Coosemans 
6700de6ac2eSTijl Coosemans 	AGP_FLUSH_TLB(dev);
6710de6ac2eSTijl Coosemans 
67289f6b863SAttilio Rao 	VM_OBJECT_WLOCK(mem->am_obj);
67359747216SDoug Rabson 	for (i = 0; i < mem->am_size; i += PAGE_SIZE) {
67459747216SDoug Rabson 		m = vm_page_lookup(mem->am_obj, atop(i));
6753ae10f74SAttilio Rao 		vm_page_unwire(m, PQ_INACTIVE);
67659747216SDoug Rabson 	}
67789f6b863SAttilio Rao 	VM_OBJECT_WUNLOCK(mem->am_obj);
67859747216SDoug Rabson 
67959747216SDoug Rabson 	mem->am_offset = 0;
68059747216SDoug Rabson 	mem->am_is_bound = 0;
68159747216SDoug Rabson 
682d8a821e8SMaxime Henrion 	mtx_unlock(&sc->as_lock);
68359747216SDoug Rabson 
68459747216SDoug Rabson 	return 0;
68559747216SDoug Rabson }
68659747216SDoug Rabson 
68759747216SDoug Rabson /* Helper functions for implementing user/kernel api */
68859747216SDoug Rabson 
68959747216SDoug Rabson static int
69059747216SDoug Rabson agp_acquire_helper(device_t dev, enum agp_acquire_state state)
69159747216SDoug Rabson {
69259747216SDoug Rabson 	struct agp_softc *sc = device_get_softc(dev);
69359747216SDoug Rabson 
69459747216SDoug Rabson 	if (sc->as_state != AGP_ACQUIRE_FREE)
69559747216SDoug Rabson 		return EBUSY;
69659747216SDoug Rabson 	sc->as_state = state;
69759747216SDoug Rabson 
69859747216SDoug Rabson 	return 0;
69959747216SDoug Rabson }
70059747216SDoug Rabson 
70159747216SDoug Rabson static int
70259747216SDoug Rabson agp_release_helper(device_t dev, enum agp_acquire_state state)
70359747216SDoug Rabson {
70459747216SDoug Rabson 	struct agp_softc *sc = device_get_softc(dev);
70559747216SDoug Rabson 
70659747216SDoug Rabson 	if (sc->as_state == AGP_ACQUIRE_FREE)
70759747216SDoug Rabson 		return 0;
70859747216SDoug Rabson 
70959747216SDoug Rabson 	if (sc->as_state != state)
71059747216SDoug Rabson 		return EBUSY;
71159747216SDoug Rabson 
71259747216SDoug Rabson 	sc->as_state = AGP_ACQUIRE_FREE;
71359747216SDoug Rabson 	return 0;
71459747216SDoug Rabson }
71559747216SDoug Rabson 
71659747216SDoug Rabson static struct agp_memory *
71759747216SDoug Rabson agp_find_memory(device_t dev, int id)
71859747216SDoug Rabson {
71959747216SDoug Rabson 	struct agp_softc *sc = device_get_softc(dev);
72059747216SDoug Rabson 	struct agp_memory *mem;
72159747216SDoug Rabson 
72259747216SDoug Rabson 	AGP_DPF("searching for memory block %d\n", id);
72359747216SDoug Rabson 	TAILQ_FOREACH(mem, &sc->as_memory, am_link) {
72459747216SDoug Rabson 		AGP_DPF("considering memory block %d\n", mem->am_id);
72559747216SDoug Rabson 		if (mem->am_id == id)
72659747216SDoug Rabson 			return mem;
72759747216SDoug Rabson 	}
72859747216SDoug Rabson 	return 0;
72959747216SDoug Rabson }
73059747216SDoug Rabson 
73159747216SDoug Rabson /* Implementation of the userland ioctl api */
73259747216SDoug Rabson 
73359747216SDoug Rabson static int
73459747216SDoug Rabson agp_info_user(device_t dev, agp_info *info)
73559747216SDoug Rabson {
73659747216SDoug Rabson 	struct agp_softc *sc = device_get_softc(dev);
73759747216SDoug Rabson 
73859747216SDoug Rabson 	bzero(info, sizeof *info);
73959747216SDoug Rabson 	info->bridge_id = pci_get_devid(dev);
74059747216SDoug Rabson 	info->agp_mode =
74159747216SDoug Rabson 	    pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4);
74250fd2a5bSNathan Whitehorn 	if (sc->as_aperture)
74359747216SDoug Rabson 		info->aper_base = rman_get_start(sc->as_aperture);
74450fd2a5bSNathan Whitehorn 	else
74550fd2a5bSNathan Whitehorn 		info->aper_base = 0;
74659747216SDoug Rabson 	info->aper_size = AGP_GET_APERTURE(dev) >> 20;
74759747216SDoug Rabson 	info->pg_total = info->pg_system = sc->as_maxmem >> AGP_PAGE_SHIFT;
74859747216SDoug Rabson 	info->pg_used = sc->as_allocated >> AGP_PAGE_SHIFT;
74959747216SDoug Rabson 
75059747216SDoug Rabson 	return 0;
75159747216SDoug Rabson }
75259747216SDoug Rabson 
75359747216SDoug Rabson static int
75459747216SDoug Rabson agp_setup_user(device_t dev, agp_setup *setup)
75559747216SDoug Rabson {
75659747216SDoug Rabson 	return AGP_ENABLE(dev, setup->agp_mode);
75759747216SDoug Rabson }
75859747216SDoug Rabson 
75959747216SDoug Rabson static int
76059747216SDoug Rabson agp_allocate_user(device_t dev, agp_allocate *alloc)
76159747216SDoug Rabson {
76259747216SDoug Rabson 	struct agp_memory *mem;
76359747216SDoug Rabson 
76459747216SDoug Rabson 	mem = AGP_ALLOC_MEMORY(dev,
76559747216SDoug Rabson 			       alloc->type,
76659747216SDoug Rabson 			       alloc->pg_count << AGP_PAGE_SHIFT);
767e547d6fdSDoug Rabson 	if (mem) {
76859747216SDoug Rabson 		alloc->key = mem->am_id;
76959747216SDoug Rabson 		alloc->physical = mem->am_physical;
77059747216SDoug Rabson 		return 0;
771e547d6fdSDoug Rabson 	} else {
772e547d6fdSDoug Rabson 		return ENOMEM;
773e547d6fdSDoug Rabson 	}
77459747216SDoug Rabson }
77559747216SDoug Rabson 
77659747216SDoug Rabson static int
77759747216SDoug Rabson agp_deallocate_user(device_t dev, int id)
77859747216SDoug Rabson {
779c2ede4b3SMartin Blapp 	struct agp_memory *mem = agp_find_memory(dev, id);
78059747216SDoug Rabson 
78159747216SDoug Rabson 	if (mem) {
78259747216SDoug Rabson 		AGP_FREE_MEMORY(dev, mem);
78359747216SDoug Rabson 		return 0;
78459747216SDoug Rabson 	} else {
78559747216SDoug Rabson 		return ENOENT;
78659747216SDoug Rabson 	}
78759747216SDoug Rabson }
78859747216SDoug Rabson 
78959747216SDoug Rabson static int
79059747216SDoug Rabson agp_bind_user(device_t dev, agp_bind *bind)
79159747216SDoug Rabson {
79259747216SDoug Rabson 	struct agp_memory *mem = agp_find_memory(dev, bind->key);
79359747216SDoug Rabson 
79459747216SDoug Rabson 	if (!mem)
79559747216SDoug Rabson 		return ENOENT;
79659747216SDoug Rabson 
79759747216SDoug Rabson 	return AGP_BIND_MEMORY(dev, mem, bind->pg_start << AGP_PAGE_SHIFT);
79859747216SDoug Rabson }
79959747216SDoug Rabson 
80059747216SDoug Rabson static int
80159747216SDoug Rabson agp_unbind_user(device_t dev, agp_unbind *unbind)
80259747216SDoug Rabson {
80359747216SDoug Rabson 	struct agp_memory *mem = agp_find_memory(dev, unbind->key);
80459747216SDoug Rabson 
80559747216SDoug Rabson 	if (!mem)
80659747216SDoug Rabson 		return ENOENT;
80759747216SDoug Rabson 
80859747216SDoug Rabson 	return AGP_UNBIND_MEMORY(dev, mem);
80959747216SDoug Rabson }
81059747216SDoug Rabson 
81159747216SDoug Rabson static int
81228d86329SKonstantin Belousov agp_chipset_flush(device_t dev)
81328d86329SKonstantin Belousov {
81428d86329SKonstantin Belousov 
81528d86329SKonstantin Belousov 	return (AGP_CHIPSET_FLUSH(dev));
81628d86329SKonstantin Belousov }
81728d86329SKonstantin Belousov 
81828d86329SKonstantin Belousov static int
81989c9c53dSPoul-Henning Kamp agp_open(struct cdev *kdev, int oflags, int devtype, struct thread *td)
82059747216SDoug Rabson {
821b7d9e67cSEd Schouten 	device_t dev = kdev->si_drv1;
82259747216SDoug Rabson 	struct agp_softc *sc = device_get_softc(dev);
82359747216SDoug Rabson 
82459747216SDoug Rabson 	if (!sc->as_isopen) {
82559747216SDoug Rabson 		sc->as_isopen = 1;
82659747216SDoug Rabson 		device_busy(dev);
82759747216SDoug Rabson 	}
82859747216SDoug Rabson 
82959747216SDoug Rabson 	return 0;
83059747216SDoug Rabson }
83159747216SDoug Rabson 
83259747216SDoug Rabson static int
83389c9c53dSPoul-Henning Kamp agp_close(struct cdev *kdev, int fflag, int devtype, struct thread *td)
83459747216SDoug Rabson {
835b7d9e67cSEd Schouten 	device_t dev = kdev->si_drv1;
83659747216SDoug Rabson 	struct agp_softc *sc = device_get_softc(dev);
8373e0cee70SRuslan Ermilov 	struct agp_memory *mem;
83859747216SDoug Rabson 
83959747216SDoug Rabson 	/*
84059747216SDoug Rabson 	 * Clear the GATT and force release on last close
84159747216SDoug Rabson 	 */
8424d24901aSPedro F. Giffuni 	while ((mem = TAILQ_FIRST(&sc->as_memory)) != NULL) {
8433e0cee70SRuslan Ermilov 		if (mem->am_is_bound)
8443e0cee70SRuslan Ermilov 			AGP_UNBIND_MEMORY(dev, mem);
8453e0cee70SRuslan Ermilov 		AGP_FREE_MEMORY(dev, mem);
8463e0cee70SRuslan Ermilov 	}
84759747216SDoug Rabson 	if (sc->as_state == AGP_ACQUIRE_USER)
84859747216SDoug Rabson 		agp_release_helper(dev, AGP_ACQUIRE_USER);
84959747216SDoug Rabson 	sc->as_isopen = 0;
85059747216SDoug Rabson 	device_unbusy(dev);
85159747216SDoug Rabson 
85259747216SDoug Rabson 	return 0;
85359747216SDoug Rabson }
85459747216SDoug Rabson 
85559747216SDoug Rabson static int
85689c9c53dSPoul-Henning Kamp agp_ioctl(struct cdev *kdev, u_long cmd, caddr_t data, int fflag, struct thread *td)
85759747216SDoug Rabson {
858b7d9e67cSEd Schouten 	device_t dev = kdev->si_drv1;
85959747216SDoug Rabson 
86059747216SDoug Rabson 	switch (cmd) {
86159747216SDoug Rabson 	case AGPIOC_INFO:
86259747216SDoug Rabson 		return agp_info_user(dev, (agp_info *) data);
86359747216SDoug Rabson 
86459747216SDoug Rabson 	case AGPIOC_ACQUIRE:
86559747216SDoug Rabson 		return agp_acquire_helper(dev, AGP_ACQUIRE_USER);
86659747216SDoug Rabson 
86759747216SDoug Rabson 	case AGPIOC_RELEASE:
86859747216SDoug Rabson 		return agp_release_helper(dev, AGP_ACQUIRE_USER);
86959747216SDoug Rabson 
87059747216SDoug Rabson 	case AGPIOC_SETUP:
87159747216SDoug Rabson 		return agp_setup_user(dev, (agp_setup *)data);
87259747216SDoug Rabson 
87359747216SDoug Rabson 	case AGPIOC_ALLOCATE:
87459747216SDoug Rabson 		return agp_allocate_user(dev, (agp_allocate *)data);
87559747216SDoug Rabson 
87659747216SDoug Rabson 	case AGPIOC_DEALLOCATE:
87759747216SDoug Rabson 		return agp_deallocate_user(dev, *(int *) data);
87859747216SDoug Rabson 
87959747216SDoug Rabson 	case AGPIOC_BIND:
88059747216SDoug Rabson 		return agp_bind_user(dev, (agp_bind *)data);
88159747216SDoug Rabson 
88259747216SDoug Rabson 	case AGPIOC_UNBIND:
88359747216SDoug Rabson 		return agp_unbind_user(dev, (agp_unbind *)data);
88459747216SDoug Rabson 
88528d86329SKonstantin Belousov 	case AGPIOC_CHIPSET_FLUSH:
88628d86329SKonstantin Belousov 		return agp_chipset_flush(dev);
88759747216SDoug Rabson 	}
88859747216SDoug Rabson 
88959747216SDoug Rabson 	return EINVAL;
89059747216SDoug Rabson }
89159747216SDoug Rabson 
89259747216SDoug Rabson static int
893cfd7baceSRobert Noland agp_mmap(struct cdev *kdev, vm_ooffset_t offset, vm_paddr_t *paddr,
894cfd7baceSRobert Noland     int prot, vm_memattr_t *memattr)
89559747216SDoug Rabson {
896b7d9e67cSEd Schouten 	device_t dev = kdev->si_drv1;
89759747216SDoug Rabson 	struct agp_softc *sc = device_get_softc(dev);
89859747216SDoug Rabson 
89959747216SDoug Rabson 	if (offset > AGP_GET_APERTURE(dev))
90059747216SDoug Rabson 		return -1;
90150fd2a5bSNathan Whitehorn 	if (sc->as_aperture == NULL)
90250fd2a5bSNathan Whitehorn 		return -1;
90307159f9cSMaxime Henrion 	*paddr = rman_get_start(sc->as_aperture) + offset;
90407159f9cSMaxime Henrion 	return 0;
90559747216SDoug Rabson }
90659747216SDoug Rabson 
90759747216SDoug Rabson /* Implementation of the kernel api */
90859747216SDoug Rabson 
90959747216SDoug Rabson device_t
91059747216SDoug Rabson agp_find_device()
91159747216SDoug Rabson {
9122971d7abSJohn Baldwin 	device_t *children, child;
913c8d81a41SJohn Baldwin 	int i, count;
914c8d81a41SJohn Baldwin 
91559747216SDoug Rabson 	if (!agp_devclass)
916c8d81a41SJohn Baldwin 		return NULL;
917c8d81a41SJohn Baldwin 	if (devclass_get_devices(agp_devclass, &children, &count) != 0)
918c8d81a41SJohn Baldwin 		return NULL;
9192971d7abSJohn Baldwin 	child = NULL;
920c8d81a41SJohn Baldwin 	for (i = 0; i < count; i++) {
9212971d7abSJohn Baldwin 		if (device_is_attached(children[i])) {
9222971d7abSJohn Baldwin 			child = children[i];
9232971d7abSJohn Baldwin 			break;
924c8d81a41SJohn Baldwin 		}
9252971d7abSJohn Baldwin 	}
9262971d7abSJohn Baldwin 	free(children, M_TEMP);
9272971d7abSJohn Baldwin 	return child;
92859747216SDoug Rabson }
92959747216SDoug Rabson 
93059747216SDoug Rabson enum agp_acquire_state
93159747216SDoug Rabson agp_state(device_t dev)
93259747216SDoug Rabson {
93359747216SDoug Rabson 	struct agp_softc *sc = device_get_softc(dev);
93459747216SDoug Rabson 	return sc->as_state;
93559747216SDoug Rabson }
93659747216SDoug Rabson 
93759747216SDoug Rabson void
93859747216SDoug Rabson agp_get_info(device_t dev, struct agp_info *info)
93959747216SDoug Rabson {
94059747216SDoug Rabson 	struct agp_softc *sc = device_get_softc(dev);
94159747216SDoug Rabson 
94259747216SDoug Rabson 	info->ai_mode =
94359747216SDoug Rabson 		pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4);
94450fd2a5bSNathan Whitehorn 	if (sc->as_aperture != NULL)
94559747216SDoug Rabson 		info->ai_aperture_base = rman_get_start(sc->as_aperture);
94650fd2a5bSNathan Whitehorn 	else
94750fd2a5bSNathan Whitehorn 		info->ai_aperture_base = 0;
94850fd2a5bSNathan Whitehorn 	info->ai_aperture_size = AGP_GET_APERTURE(dev);
94959747216SDoug Rabson 	info->ai_memory_allowed = sc->as_maxmem;
95059747216SDoug Rabson 	info->ai_memory_used = sc->as_allocated;
95159747216SDoug Rabson }
95259747216SDoug Rabson 
95359747216SDoug Rabson int
95459747216SDoug Rabson agp_acquire(device_t dev)
95559747216SDoug Rabson {
95659747216SDoug Rabson 	return agp_acquire_helper(dev, AGP_ACQUIRE_KERNEL);
95759747216SDoug Rabson }
95859747216SDoug Rabson 
95959747216SDoug Rabson int
96059747216SDoug Rabson agp_release(device_t dev)
96159747216SDoug Rabson {
96259747216SDoug Rabson 	return agp_release_helper(dev, AGP_ACQUIRE_KERNEL);
96359747216SDoug Rabson }
96459747216SDoug Rabson 
96559747216SDoug Rabson int
96659747216SDoug Rabson agp_enable(device_t dev, u_int32_t mode)
96759747216SDoug Rabson {
96859747216SDoug Rabson 	return AGP_ENABLE(dev, mode);
96959747216SDoug Rabson }
97059747216SDoug Rabson 
97159747216SDoug Rabson void *agp_alloc_memory(device_t dev, int type, vm_size_t bytes)
97259747216SDoug Rabson {
97359747216SDoug Rabson 	return  (void *) AGP_ALLOC_MEMORY(dev, type, bytes);
97459747216SDoug Rabson }
97559747216SDoug Rabson 
97659747216SDoug Rabson void agp_free_memory(device_t dev, void *handle)
97759747216SDoug Rabson {
97859747216SDoug Rabson 	struct agp_memory *mem = (struct agp_memory *) handle;
97959747216SDoug Rabson 	AGP_FREE_MEMORY(dev, mem);
98059747216SDoug Rabson }
98159747216SDoug Rabson 
98259747216SDoug Rabson int agp_bind_memory(device_t dev, void *handle, vm_offset_t offset)
98359747216SDoug Rabson {
98459747216SDoug Rabson 	struct agp_memory *mem = (struct agp_memory *) handle;
98559747216SDoug Rabson 	return AGP_BIND_MEMORY(dev, mem, offset);
98659747216SDoug Rabson }
98759747216SDoug Rabson 
98859747216SDoug Rabson int agp_unbind_memory(device_t dev, void *handle)
98959747216SDoug Rabson {
99059747216SDoug Rabson 	struct agp_memory *mem = (struct agp_memory *) handle;
99159747216SDoug Rabson 	return AGP_UNBIND_MEMORY(dev, mem);
99259747216SDoug Rabson }
99359747216SDoug Rabson 
99459747216SDoug Rabson void agp_memory_info(device_t dev, void *handle, struct
99559747216SDoug Rabson 		     agp_memory_info *mi)
99659747216SDoug Rabson {
99759747216SDoug Rabson 	struct agp_memory *mem = (struct agp_memory *) handle;
99859747216SDoug Rabson 
99959747216SDoug Rabson 	mi->ami_size = mem->am_size;
100059747216SDoug Rabson 	mi->ami_physical = mem->am_physical;
100159747216SDoug Rabson 	mi->ami_offset = mem->am_offset;
100259747216SDoug Rabson 	mi->ami_is_bound = mem->am_is_bound;
100359747216SDoug Rabson }
1004903fb143STijl Coosemans 
1005903fb143STijl Coosemans int
1006903fb143STijl Coosemans agp_bind_pages(device_t dev, vm_page_t *pages, vm_size_t size,
1007903fb143STijl Coosemans     vm_offset_t offset)
1008903fb143STijl Coosemans {
1009903fb143STijl Coosemans 	struct agp_softc *sc;
1010903fb143STijl Coosemans 	vm_offset_t i, j, k, pa;
1011903fb143STijl Coosemans 	vm_page_t m;
1012903fb143STijl Coosemans 	int error;
1013903fb143STijl Coosemans 
1014903fb143STijl Coosemans 	if ((size & (AGP_PAGE_SIZE - 1)) != 0 ||
1015903fb143STijl Coosemans 	    (offset & (AGP_PAGE_SIZE - 1)) != 0)
1016903fb143STijl Coosemans 		return (EINVAL);
1017903fb143STijl Coosemans 
1018903fb143STijl Coosemans 	sc = device_get_softc(dev);
1019903fb143STijl Coosemans 
1020903fb143STijl Coosemans 	mtx_lock(&sc->as_lock);
1021903fb143STijl Coosemans 	for (i = 0; i < size; i += PAGE_SIZE) {
1022903fb143STijl Coosemans 		m = pages[OFF_TO_IDX(i)];
1023fee2a2faSMark Johnston 		KASSERT(vm_page_wired(m),
10240de6ac2eSTijl Coosemans 		    ("agp_bind_pages: page %p hasn't been wired", m));
1025903fb143STijl Coosemans 
1026903fb143STijl Coosemans 		/*
1027903fb143STijl Coosemans 		 * Install entries in the GATT, making sure that if
1028903fb143STijl Coosemans 		 * AGP_PAGE_SIZE < PAGE_SIZE and size is not
1029903fb143STijl Coosemans 		 * aligned to PAGE_SIZE, we don't modify too many GATT
1030903fb143STijl Coosemans 		 * entries.
1031903fb143STijl Coosemans 		 */
1032903fb143STijl Coosemans 		for (j = 0; j < PAGE_SIZE && i + j < size; j += AGP_PAGE_SIZE) {
1033903fb143STijl Coosemans 			pa = VM_PAGE_TO_PHYS(m) + j;
1034903fb143STijl Coosemans 			AGP_DPF("binding offset %#jx to pa %#jx\n",
1035903fb143STijl Coosemans 				(uintmax_t)offset + i + j, (uintmax_t)pa);
1036903fb143STijl Coosemans 			error = AGP_BIND_PAGE(dev, offset + i + j, pa);
1037903fb143STijl Coosemans 			if (error) {
1038903fb143STijl Coosemans 				/*
1039903fb143STijl Coosemans 				 * Bail out. Reverse all the mappings.
1040903fb143STijl Coosemans 				 */
1041903fb143STijl Coosemans 				for (k = 0; k < i + j; k += AGP_PAGE_SIZE)
1042903fb143STijl Coosemans 					AGP_UNBIND_PAGE(dev, offset + k);
1043903fb143STijl Coosemans 
1044903fb143STijl Coosemans 				mtx_unlock(&sc->as_lock);
1045903fb143STijl Coosemans 				return (error);
1046903fb143STijl Coosemans 			}
1047903fb143STijl Coosemans 		}
1048903fb143STijl Coosemans 	}
1049903fb143STijl Coosemans 
1050903fb143STijl Coosemans 	AGP_FLUSH_TLB(dev);
1051903fb143STijl Coosemans 
1052903fb143STijl Coosemans 	mtx_unlock(&sc->as_lock);
1053903fb143STijl Coosemans 	return (0);
1054903fb143STijl Coosemans }
1055903fb143STijl Coosemans 
1056903fb143STijl Coosemans int
1057903fb143STijl Coosemans agp_unbind_pages(device_t dev, vm_size_t size, vm_offset_t offset)
1058903fb143STijl Coosemans {
1059903fb143STijl Coosemans 	struct agp_softc *sc;
1060903fb143STijl Coosemans 	vm_offset_t i;
1061903fb143STijl Coosemans 
1062903fb143STijl Coosemans 	if ((size & (AGP_PAGE_SIZE - 1)) != 0 ||
1063903fb143STijl Coosemans 	    (offset & (AGP_PAGE_SIZE - 1)) != 0)
1064903fb143STijl Coosemans 		return (EINVAL);
1065903fb143STijl Coosemans 
1066903fb143STijl Coosemans 	sc = device_get_softc(dev);
1067903fb143STijl Coosemans 
1068903fb143STijl Coosemans 	mtx_lock(&sc->as_lock);
1069903fb143STijl Coosemans 	for (i = 0; i < size; i += AGP_PAGE_SIZE)
1070903fb143STijl Coosemans 		AGP_UNBIND_PAGE(dev, offset + i);
1071903fb143STijl Coosemans 
1072903fb143STijl Coosemans 	AGP_FLUSH_TLB(dev);
1073903fb143STijl Coosemans 
1074903fb143STijl Coosemans 	mtx_unlock(&sc->as_lock);
1075903fb143STijl Coosemans 	return (0);
1076903fb143STijl Coosemans }
1077