161452Sdfr/*-
261452Sdfr * Copyright (c) 2000 Doug Rabson
361452Sdfr * All rights reserved.
461452Sdfr *
561452Sdfr * Redistribution and use in source and binary forms, with or without
661452Sdfr * modification, are permitted provided that the following conditions
761452Sdfr * are met:
861452Sdfr * 1. Redistributions of source code must retain the above copyright
961452Sdfr *    notice, this list of conditions and the following disclaimer.
1061452Sdfr * 2. Redistributions in binary form must reproduce the above copyright
1161452Sdfr *    notice, this list of conditions and the following disclaimer in the
1261452Sdfr *    documentation and/or other materials provided with the distribution.
1361452Sdfr *
1461452Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1561452Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1661452Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1761452Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1861452Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1961452Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2061452Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2161452Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2261452Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2361452Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2461452Sdfr * SUCH DAMAGE.
2561452Sdfr */
2661452Sdfr
27116192Sobrien#include <sys/cdefs.h>
28116192Sobrien__FBSDID("$FreeBSD$");
29116192Sobrien
30188247Swkoszek#include "opt_agp.h"
3161452Sdfr
3261452Sdfr#include <sys/param.h>
3361452Sdfr#include <sys/systm.h>
3461452Sdfr#include <sys/malloc.h>
3561452Sdfr#include <sys/kernel.h>
36129878Sphk#include <sys/module.h>
3761452Sdfr#include <sys/bus.h>
3861452Sdfr#include <sys/conf.h>
3961452Sdfr#include <sys/ioccom.h>
4061452Sdfr#include <sys/agpio.h>
4161452Sdfr#include <sys/lock.h>
4276827Salfred#include <sys/mutex.h>
4369927Sjhb#include <sys/proc.h>
44248084Sattilio#include <sys/rwlock.h>
4561452Sdfr
46173573Sjhb#include <dev/agp/agppriv.h>
47173573Sjhb#include <dev/agp/agpvar.h>
48173573Sjhb#include <dev/agp/agpreg.h>
49119288Simp#include <dev/pci/pcivar.h>
50119288Simp#include <dev/pci/pcireg.h>
5161452Sdfr
5261452Sdfr#include <vm/vm.h>
53275406Stijl#include <vm/vm_extern.h>
54275406Stijl#include <vm/vm_kern.h>
55239065Skib#include <vm/vm_param.h>
5661452Sdfr#include <vm/vm_object.h>
5761452Sdfr#include <vm/vm_page.h>
5861452Sdfr#include <vm/vm_pageout.h>
5961452Sdfr#include <vm/pmap.h>
6061452Sdfr
6161452Sdfr#include <machine/bus.h>
6261452Sdfr#include <machine/resource.h>
6361452Sdfr#include <sys/rman.h>
6461452Sdfr
6561452SdfrMODULE_VERSION(agp, 1);
6661452Sdfr
6770185SassarMALLOC_DEFINE(M_AGP, "agp", "AGP data structures");
6861452Sdfr
6961452Sdfr				/* agp_drv.c */
7061452Sdfrstatic d_open_t agp_open;
7161452Sdfrstatic d_close_t agp_close;
7261452Sdfrstatic d_ioctl_t agp_ioctl;
7361452Sdfrstatic d_mmap_t agp_mmap;
7461452Sdfr
7561452Sdfrstatic struct cdevsw agp_cdevsw = {
76126080Sphk	.d_version =	D_VERSION,
77126080Sphk	.d_flags =	D_NEEDGIANT,
78111815Sphk	.d_open =	agp_open,
79111815Sphk	.d_close =	agp_close,
80111815Sphk	.d_ioctl =	agp_ioctl,
81111815Sphk	.d_mmap =	agp_mmap,
82111815Sphk	.d_name =	"agp",
8361452Sdfr};
8461452Sdfr
8561452Sdfrstatic devclass_t agp_devclass;
8661452Sdfr
8761452Sdfr/* Helper functions for implementing chipset mini drivers. */
8861452Sdfr
8961452Sdfru_int8_t
9061452Sdfragp_find_caps(device_t dev)
9161452Sdfr{
92153561Sjhb	int capreg;
9361452Sdfr
9461452Sdfr
95219902Sjhb	if (pci_find_cap(dev, PCIY_AGP, &capreg) != 0)
96153561Sjhb		capreg = 0;
97153561Sjhb	return (capreg);
9861452Sdfr}
9961452Sdfr
10061452Sdfr/*
10161452Sdfr * Find an AGP display device (if any).
10261452Sdfr */
10361452Sdfrstatic device_t
10461452Sdfragp_find_display(void)
10561452Sdfr{
10661452Sdfr	devclass_t pci = devclass_find("pci");
10761452Sdfr	device_t bus, dev = 0;
10861452Sdfr	device_t *kids;
10961452Sdfr	int busnum, numkids, i;
11061452Sdfr
11161452Sdfr	for (busnum = 0; busnum < devclass_get_maxunit(pci); busnum++) {
11261452Sdfr		bus = devclass_get_device(pci, busnum);
11361452Sdfr		if (!bus)
11461452Sdfr			continue;
115182068Simp		if (device_get_children(bus, &kids, &numkids) != 0)
116182068Simp			continue;
11761452Sdfr		for (i = 0; i < numkids; i++) {
11861452Sdfr			dev = kids[i];
11961452Sdfr			if (pci_get_class(dev) == PCIC_DISPLAY
12061452Sdfr			    && pci_get_subclass(dev) == PCIS_DISPLAY_VGA)
12161452Sdfr				if (agp_find_caps(dev)) {
12261452Sdfr					free(kids, M_TEMP);
12361452Sdfr					return dev;
12461452Sdfr				}
12561452Sdfr
12661452Sdfr		}
12761452Sdfr		free(kids, M_TEMP);
12861452Sdfr	}
12961452Sdfr
13061452Sdfr	return 0;
13161452Sdfr}
13261452Sdfr
13361452Sdfrstruct agp_gatt *
13461452Sdfragp_alloc_gatt(device_t dev)
13561452Sdfr{
13661452Sdfr	u_int32_t apsize = AGP_GET_APERTURE(dev);
13761452Sdfr	u_int32_t entries = apsize >> AGP_PAGE_SHIFT;
13861452Sdfr	struct agp_gatt *gatt;
13961452Sdfr
14061452Sdfr	if (bootverbose)
14161452Sdfr		device_printf(dev,
14261452Sdfr			      "allocating GATT for aperture of size %dM\n",
14361452Sdfr			      apsize / (1024*1024));
14461452Sdfr
145122513Sanholt	if (entries == 0) {
146122513Sanholt		device_printf(dev, "bad aperture size\n");
147122513Sanholt		return NULL;
148122513Sanholt	}
149122513Sanholt
15061452Sdfr	gatt = malloc(sizeof(struct agp_gatt), M_AGP, M_NOWAIT);
15161452Sdfr	if (!gatt)
15261452Sdfr		return 0;
15361452Sdfr
15461452Sdfr	gatt->ag_entries = entries;
155275406Stijl	gatt->ag_virtual = (void *)kmem_alloc_contig(kernel_arena,
156275406Stijl	    entries * sizeof(u_int32_t), M_NOWAIT | M_ZERO, 0, ~0, PAGE_SIZE,
157275406Stijl	    0, VM_MEMATTR_WRITE_COMBINING);
15861452Sdfr	if (!gatt->ag_virtual) {
15961452Sdfr		if (bootverbose)
16061452Sdfr			device_printf(dev, "contiguous allocation failed\n");
16161452Sdfr		free(gatt, M_AGP);
16261452Sdfr		return 0;
16361452Sdfr	}
16461452Sdfr	gatt->ag_physical = vtophys((vm_offset_t) gatt->ag_virtual);
16561452Sdfr
16661452Sdfr	return gatt;
16761452Sdfr}
16861452Sdfr
16961452Sdfrvoid
17061452Sdfragp_free_gatt(struct agp_gatt *gatt)
17161452Sdfr{
172275406Stijl	kmem_free(kernel_arena, (vm_offset_t)gatt->ag_virtual,
173275406Stijl	    gatt->ag_entries * sizeof(u_int32_t));
17461452Sdfr	free(gatt, M_AGP);
17561452Sdfr}
17661452Sdfr
177163362Stanimurastatic u_int agp_max[][2] = {
17861452Sdfr	{0,	0},
17961452Sdfr	{32,	4},
18061452Sdfr	{64,	28},
18161452Sdfr	{128,	96},
18261452Sdfr	{256,	204},
18361452Sdfr	{512,	440},
18461452Sdfr	{1024,	942},
18561452Sdfr	{2048,	1920},
18661452Sdfr	{4096,	3932}
18761452Sdfr};
18861452Sdfr#define agp_max_size	(sizeof(agp_max) / sizeof(agp_max[0]))
18961452Sdfr
190171433Sanholt/**
191171433Sanholt * Sets the PCI resource which represents the AGP aperture.
192171433Sanholt *
193171433Sanholt * If not called, the default AGP aperture resource of AGP_APBASE will
194171433Sanholt * be used.  Must be called before agp_generic_attach().
195171433Sanholt */
196171433Sanholtvoid
197171433Sanholtagp_set_aperture_resource(device_t dev, int rid)
198171433Sanholt{
199171433Sanholt	struct agp_softc *sc = device_get_softc(dev);
200171433Sanholt
201171433Sanholt	sc->as_aperture_rid = rid;
202171433Sanholt}
203171433Sanholt
20461452Sdfrint
20561452Sdfragp_generic_attach(device_t dev)
20661452Sdfr{
20761452Sdfr	struct agp_softc *sc = device_get_softc(dev);
208171433Sanholt	int i;
209163362Stanimura	u_int memsize;
21061452Sdfr
21161452Sdfr	/*
212171433Sanholt	 * Find and map the aperture, RF_SHAREABLE for DRM but not RF_ACTIVE
213171433Sanholt	 * because the kernel doesn't need to map it.
21461452Sdfr	 */
215171433Sanholt
216214603Snwhitehorn	if (sc->as_aperture_rid != -1) {
217214603Snwhitehorn		if (sc->as_aperture_rid == 0)
218214603Snwhitehorn			sc->as_aperture_rid = AGP_APBASE;
21961452Sdfr
220214603Snwhitehorn		sc->as_aperture = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
221214603Snwhitehorn		    &sc->as_aperture_rid, RF_SHAREABLE);
222214603Snwhitehorn		if (!sc->as_aperture)
223214603Snwhitehorn			return ENOMEM;
224214603Snwhitehorn	}
225214603Snwhitehorn
22661452Sdfr	/*
22761452Sdfr	 * Work out an upper bound for agp memory allocation. This
22861452Sdfr	 * uses a heurisitc table from the Linux driver.
22961452Sdfr	 */
230238172Smarcel	memsize = ptoa(realmem) >> 20;
23161452Sdfr	for (i = 0; i < agp_max_size; i++) {
23261452Sdfr		if (memsize <= agp_max[i][0])
23361452Sdfr			break;
23461452Sdfr	}
235235782Skib	if (i == agp_max_size)
236235782Skib		i = agp_max_size - 1;
23761452Sdfr	sc->as_maxmem = agp_max[i][1] << 20U;
23861452Sdfr
23961452Sdfr	/*
24061452Sdfr	 * The lock is used to prevent re-entry to
24161452Sdfr	 * agp_generic_bind_memory() since that function can sleep.
24261452Sdfr	 */
243129579Smux	mtx_init(&sc->as_lock, "agp lock", NULL, MTX_DEF);
24461452Sdfr
24561452Sdfr	/*
24661452Sdfr	 * Initialise stuff for the userland device.
24761452Sdfr	 */
24861452Sdfr	agp_devclass = devclass_find("agp");
24961452Sdfr	TAILQ_INIT(&sc->as_memory);
25061452Sdfr	sc->as_nextid = 1;
25161452Sdfr
25261452Sdfr	sc->as_devnode = make_dev(&agp_cdevsw,
253191057Sed	    0, UID_ROOT, GID_WHEEL, 0600, "agpgart");
254191057Sed	sc->as_devnode->si_drv1 = dev;
25561452Sdfr
25661452Sdfr	return 0;
25761452Sdfr}
25861452Sdfr
259173203Sjhbvoid
260173203Sjhbagp_free_cdev(device_t dev)
26161452Sdfr{
26261452Sdfr	struct agp_softc *sc = device_get_softc(dev);
263153562Sjhb
264153562Sjhb	destroy_dev(sc->as_devnode);
265173203Sjhb}
266173203Sjhb
267173203Sjhbvoid
268173203Sjhbagp_free_res(device_t dev)
269173203Sjhb{
270173203Sjhb	struct agp_softc *sc = device_get_softc(dev);
271173203Sjhb
272214603Snwhitehorn	if (sc->as_aperture != NULL)
273214603Snwhitehorn		bus_release_resource(dev, SYS_RES_MEMORY, sc->as_aperture_rid,
274214603Snwhitehorn		    sc->as_aperture);
275129579Smux	mtx_destroy(&sc->as_lock);
276173203Sjhb}
277173203Sjhb
278173203Sjhbint
279173203Sjhbagp_generic_detach(device_t dev)
280173203Sjhb{
281173203Sjhb
282173203Sjhb	agp_free_cdev(dev);
283173203Sjhb	agp_free_res(dev);
28461452Sdfr	return 0;
28561452Sdfr}
28661452Sdfr
287171433Sanholt/**
288171433Sanholt * Default AGP aperture size detection which simply returns the size of
289171433Sanholt * the aperture's PCI resource.
290171433Sanholt */
291189578Simpu_int32_t
292171433Sanholtagp_generic_get_aperture(device_t dev)
293171433Sanholt{
294171433Sanholt	struct agp_softc *sc = device_get_softc(dev);
295171433Sanholt
296171433Sanholt	return rman_get_size(sc->as_aperture);
297171433Sanholt}
298171433Sanholt
299171433Sanholt/**
300171433Sanholt * Default AGP aperture size setting function, which simply doesn't allow
301171433Sanholt * changes to resource size.
302171433Sanholt */
303171433Sanholtint
304171433Sanholtagp_generic_set_aperture(device_t dev, u_int32_t aperture)
305171433Sanholt{
306171433Sanholt	u_int32_t current_aperture;
307171433Sanholt
308171433Sanholt	current_aperture = AGP_GET_APERTURE(dev);
309171433Sanholt	if (current_aperture != aperture)
310171433Sanholt		return EINVAL;
311171433Sanholt	else
312171433Sanholt		return 0;
313171433Sanholt}
314171433Sanholt
315121440Sjhb/*
316121440Sjhb * This does the enable logic for v3, with the same topology
317121440Sjhb * restrictions as in place for v2 -- one bus, one device on the bus.
318121440Sjhb */
319121440Sjhbstatic int
320121440Sjhbagp_v3_enable(device_t dev, device_t mdev, u_int32_t mode)
32161452Sdfr{
32261452Sdfr	u_int32_t tstatus, mstatus;
32361452Sdfr	u_int32_t command;
324121440Sjhb	int rq, sba, fw, rate, arqsz, cal;
32561452Sdfr
326121440Sjhb	tstatus = pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4);
327121440Sjhb	mstatus = pci_read_config(mdev, agp_find_caps(mdev) + AGP_STATUS, 4);
32861452Sdfr
329121440Sjhb	/* Set RQ to the min of mode, tstatus and mstatus */
330121440Sjhb	rq = AGP_MODE_GET_RQ(mode);
331121440Sjhb	if (AGP_MODE_GET_RQ(tstatus) < rq)
332121440Sjhb		rq = AGP_MODE_GET_RQ(tstatus);
333121440Sjhb	if (AGP_MODE_GET_RQ(mstatus) < rq)
334121440Sjhb		rq = AGP_MODE_GET_RQ(mstatus);
335121440Sjhb
336121440Sjhb	/*
337121440Sjhb	 * ARQSZ - Set the value to the maximum one.
338121440Sjhb	 * Don't allow the mode register to override values.
339121440Sjhb	 */
340121440Sjhb	arqsz = AGP_MODE_GET_ARQSZ(mode);
341121440Sjhb	if (AGP_MODE_GET_ARQSZ(tstatus) > rq)
342121440Sjhb		rq = AGP_MODE_GET_ARQSZ(tstatus);
343121440Sjhb	if (AGP_MODE_GET_ARQSZ(mstatus) > rq)
344121440Sjhb		rq = AGP_MODE_GET_ARQSZ(mstatus);
345121440Sjhb
346121440Sjhb	/* Calibration cycle - don't allow override by mode register */
347121440Sjhb	cal = AGP_MODE_GET_CAL(tstatus);
348121440Sjhb	if (AGP_MODE_GET_CAL(mstatus) < cal)
349121440Sjhb		cal = AGP_MODE_GET_CAL(mstatus);
350121440Sjhb
351121440Sjhb	/* SBA must be supported for AGP v3. */
352121440Sjhb	sba = 1;
353121440Sjhb
354121440Sjhb	/* Set FW if all three support it. */
355121440Sjhb	fw = (AGP_MODE_GET_FW(tstatus)
356121440Sjhb	       & AGP_MODE_GET_FW(mstatus)
357121440Sjhb	       & AGP_MODE_GET_FW(mode));
358121440Sjhb
359121440Sjhb	/* Figure out the max rate */
360121440Sjhb	rate = (AGP_MODE_GET_RATE(tstatus)
361121440Sjhb		& AGP_MODE_GET_RATE(mstatus)
362121440Sjhb		& AGP_MODE_GET_RATE(mode));
363121440Sjhb	if (rate & AGP_MODE_V3_RATE_8x)
364121440Sjhb		rate = AGP_MODE_V3_RATE_8x;
365121440Sjhb	else
366121440Sjhb		rate = AGP_MODE_V3_RATE_4x;
367121440Sjhb	if (bootverbose)
368121440Sjhb		device_printf(dev, "Setting AGP v3 mode %d\n", rate * 4);
369121440Sjhb
370121440Sjhb	pci_write_config(dev, agp_find_caps(dev) + AGP_COMMAND, 0, 4);
371121440Sjhb
372121440Sjhb	/* Construct the new mode word and tell the hardware */
373161222Sjkim	command = 0;
374121440Sjhb	command = AGP_MODE_SET_RQ(0, rq);
375121440Sjhb	command = AGP_MODE_SET_ARQSZ(command, arqsz);
376121440Sjhb	command = AGP_MODE_SET_CAL(command, cal);
377121440Sjhb	command = AGP_MODE_SET_SBA(command, sba);
378121440Sjhb	command = AGP_MODE_SET_FW(command, fw);
379121440Sjhb	command = AGP_MODE_SET_RATE(command, rate);
380161222Sjkim	command = AGP_MODE_SET_MODE_3(command, 1);
381121440Sjhb	command = AGP_MODE_SET_AGP(command, 1);
382121440Sjhb	pci_write_config(dev, agp_find_caps(dev) + AGP_COMMAND, command, 4);
383121440Sjhb	pci_write_config(mdev, agp_find_caps(mdev) + AGP_COMMAND, command, 4);
384121440Sjhb
385121440Sjhb	return 0;
386121440Sjhb}
387121440Sjhb
388121440Sjhbstatic int
389121440Sjhbagp_v2_enable(device_t dev, device_t mdev, u_int32_t mode)
390121440Sjhb{
391121440Sjhb	u_int32_t tstatus, mstatus;
392121440Sjhb	u_int32_t command;
393121440Sjhb	int rq, sba, fw, rate;
394121440Sjhb
39561452Sdfr	tstatus = pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4);
39661452Sdfr	mstatus = pci_read_config(mdev, agp_find_caps(mdev) + AGP_STATUS, 4);
39761452Sdfr
39861452Sdfr	/* Set RQ to the min of mode, tstatus and mstatus */
39961452Sdfr	rq = AGP_MODE_GET_RQ(mode);
40061452Sdfr	if (AGP_MODE_GET_RQ(tstatus) < rq)
40161452Sdfr		rq = AGP_MODE_GET_RQ(tstatus);
40261452Sdfr	if (AGP_MODE_GET_RQ(mstatus) < rq)
40361452Sdfr		rq = AGP_MODE_GET_RQ(mstatus);
40461452Sdfr
40561452Sdfr	/* Set SBA if all three can deal with SBA */
40661452Sdfr	sba = (AGP_MODE_GET_SBA(tstatus)
40761452Sdfr	       & AGP_MODE_GET_SBA(mstatus)
40861452Sdfr	       & AGP_MODE_GET_SBA(mode));
40961452Sdfr
41061452Sdfr	/* Similar for FW */
41161452Sdfr	fw = (AGP_MODE_GET_FW(tstatus)
41261452Sdfr	       & AGP_MODE_GET_FW(mstatus)
41361452Sdfr	       & AGP_MODE_GET_FW(mode));
41461452Sdfr
41561452Sdfr	/* Figure out the max rate */
41661452Sdfr	rate = (AGP_MODE_GET_RATE(tstatus)
41761452Sdfr		& AGP_MODE_GET_RATE(mstatus)
41861452Sdfr		& AGP_MODE_GET_RATE(mode));
419121440Sjhb	if (rate & AGP_MODE_V2_RATE_4x)
420121440Sjhb		rate = AGP_MODE_V2_RATE_4x;
421121440Sjhb	else if (rate & AGP_MODE_V2_RATE_2x)
422121440Sjhb		rate = AGP_MODE_V2_RATE_2x;
42361452Sdfr	else
424121440Sjhb		rate = AGP_MODE_V2_RATE_1x;
425121440Sjhb	if (bootverbose)
426121440Sjhb		device_printf(dev, "Setting AGP v2 mode %d\n", rate);
42761452Sdfr
42861452Sdfr	/* Construct the new mode word and tell the hardware */
429161222Sjkim	command = 0;
43061452Sdfr	command = AGP_MODE_SET_RQ(0, rq);
43161452Sdfr	command = AGP_MODE_SET_SBA(command, sba);
43261452Sdfr	command = AGP_MODE_SET_FW(command, fw);
43361452Sdfr	command = AGP_MODE_SET_RATE(command, rate);
43461452Sdfr	command = AGP_MODE_SET_AGP(command, 1);
43561452Sdfr	pci_write_config(dev, agp_find_caps(dev) + AGP_COMMAND, command, 4);
43661452Sdfr	pci_write_config(mdev, agp_find_caps(mdev) + AGP_COMMAND, command, 4);
43761452Sdfr
43861452Sdfr	return 0;
43961452Sdfr}
44061452Sdfr
441121440Sjhbint
442121440Sjhbagp_generic_enable(device_t dev, u_int32_t mode)
443121440Sjhb{
444121440Sjhb	device_t mdev = agp_find_display();
445121440Sjhb	u_int32_t tstatus, mstatus;
446121440Sjhb
447121440Sjhb	if (!mdev) {
448121440Sjhb		AGP_DPF("can't find display\n");
449121440Sjhb		return ENXIO;
450121440Sjhb	}
451121440Sjhb
452121440Sjhb	tstatus = pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4);
453121440Sjhb	mstatus = pci_read_config(mdev, agp_find_caps(mdev) + AGP_STATUS, 4);
454121440Sjhb
455121440Sjhb	/*
456121440Sjhb	 * Check display and bridge for AGP v3 support.  AGP v3 allows
457121440Sjhb	 * more variety in topology than v2, e.g. multiple AGP devices
458121440Sjhb	 * attached to one bridge, or multiple AGP bridges in one
459121440Sjhb	 * system.  This doesn't attempt to address those situations,
460121440Sjhb	 * but should work fine for a classic single AGP slot system
461121440Sjhb	 * with AGP v3.
462121440Sjhb	 */
463161222Sjkim	if (AGP_MODE_GET_MODE_3(mode) &&
464161222Sjkim	    AGP_MODE_GET_MODE_3(tstatus) &&
465161222Sjkim	    AGP_MODE_GET_MODE_3(mstatus))
466121440Sjhb		return (agp_v3_enable(dev, mdev, mode));
467121440Sjhb	else
468121440Sjhb		return (agp_v2_enable(dev, mdev, mode));
469121440Sjhb}
470121440Sjhb
47161452Sdfrstruct agp_memory *
47261452Sdfragp_generic_alloc_memory(device_t dev, int type, vm_size_t size)
47361452Sdfr{
47461452Sdfr	struct agp_softc *sc = device_get_softc(dev);
47561452Sdfr	struct agp_memory *mem;
47661452Sdfr
47761452Sdfr	if ((size & (AGP_PAGE_SIZE - 1)) != 0)
47861452Sdfr		return 0;
47961452Sdfr
480275406Stijl	if (size > sc->as_maxmem - sc->as_allocated)
48161452Sdfr		return 0;
48261452Sdfr
48363010Sdfr	if (type != 0) {
48463010Sdfr		printf("agp_generic_alloc_memory: unsupported type %d\n",
48563010Sdfr		       type);
48663010Sdfr		return 0;
48763010Sdfr	}
48863010Sdfr
489111119Simp	mem = malloc(sizeof *mem, M_AGP, M_WAITOK);
49061452Sdfr	mem->am_id = sc->as_nextid++;
49161452Sdfr	mem->am_size = size;
49263010Sdfr	mem->am_type = 0;
49361452Sdfr	mem->am_obj = vm_object_allocate(OBJT_DEFAULT, atop(round_page(size)));
49461452Sdfr	mem->am_physical = 0;
49561452Sdfr	mem->am_offset = 0;
49661452Sdfr	mem->am_is_bound = 0;
49761452Sdfr	TAILQ_INSERT_TAIL(&sc->as_memory, mem, am_link);
49861452Sdfr	sc->as_allocated += size;
49961452Sdfr
50061452Sdfr	return mem;
50161452Sdfr}
50261452Sdfr
50361452Sdfrint
50461452Sdfragp_generic_free_memory(device_t dev, struct agp_memory *mem)
50561452Sdfr{
50661452Sdfr	struct agp_softc *sc = device_get_softc(dev);
50761452Sdfr
50861452Sdfr	if (mem->am_is_bound)
50961452Sdfr		return EBUSY;
51061452Sdfr
51161452Sdfr	sc->as_allocated -= mem->am_size;
51261452Sdfr	TAILQ_REMOVE(&sc->as_memory, mem, am_link);
51361452Sdfr	vm_object_deallocate(mem->am_obj);
51461452Sdfr	free(mem, M_AGP);
51561452Sdfr	return 0;
51661452Sdfr}
51761452Sdfr
51861452Sdfrint
51961452Sdfragp_generic_bind_memory(device_t dev, struct agp_memory *mem,
52061452Sdfr			vm_offset_t offset)
52161452Sdfr{
52261452Sdfr	struct agp_softc *sc = device_get_softc(dev);
52361452Sdfr	vm_offset_t i, j, k;
52461452Sdfr	vm_page_t m;
52561452Sdfr	int error;
52661452Sdfr
527129601Smux	/* Do some sanity checks first. */
528190169Srnoland	if ((offset & (AGP_PAGE_SIZE - 1)) != 0 ||
529129601Smux	    offset + mem->am_size > AGP_GET_APERTURE(dev)) {
53061452Sdfr		device_printf(dev, "binding memory at bad offset %#x\n",
531129601Smux		    (int)offset);
53261452Sdfr		return EINVAL;
53361452Sdfr	}
53461452Sdfr
53561452Sdfr	/*
536129601Smux	 * Allocate the pages early, before acquiring the lock,
537209793Skib	 * because vm_page_grab() may sleep and we can't hold a mutex
538209793Skib	 * while sleeping.
53961452Sdfr	 */
540248084Sattilio	VM_OBJECT_WLOCK(mem->am_obj);
54161452Sdfr	for (i = 0; i < mem->am_size; i += PAGE_SIZE) {
54261452Sdfr		/*
54361452Sdfr		 * Find a page from the object and wire it
54461452Sdfr		 * down. This page will be mapped using one or more
54561452Sdfr		 * entries in the GATT (assuming that PAGE_SIZE >=
54661452Sdfr		 * AGP_PAGE_SIZE. If this is the first call to bind,
54761452Sdfr		 * the pages will be allocated and zeroed.
54861452Sdfr		 */
54961452Sdfr		m = vm_page_grab(mem->am_obj, OFF_TO_IDX(i),
550254649Skib		    VM_ALLOC_WIRED | VM_ALLOC_ZERO);
551188247Swkoszek		AGP_DPF("found page pa=%#jx\n", (uintmax_t)VM_PAGE_TO_PHYS(m));
552129601Smux	}
553248084Sattilio	VM_OBJECT_WUNLOCK(mem->am_obj);
55461452Sdfr
555129601Smux	mtx_lock(&sc->as_lock);
556129601Smux
557129601Smux	if (mem->am_is_bound) {
558129601Smux		device_printf(dev, "memory already bound\n");
559129601Smux		error = EINVAL;
560248084Sattilio		VM_OBJECT_WLOCK(mem->am_obj);
561186433Skib		i = 0;
562129601Smux		goto bad;
563129601Smux	}
564129601Smux
565129601Smux	/*
566129601Smux	 * Bind the individual pages and flush the chipset's
567129601Smux	 * TLB.
568129601Smux	 */
569248084Sattilio	VM_OBJECT_WLOCK(mem->am_obj);
570129601Smux	for (i = 0; i < mem->am_size; i += PAGE_SIZE) {
571129601Smux		m = vm_page_lookup(mem->am_obj, OFF_TO_IDX(i));
572129601Smux
57361452Sdfr		/*
57461452Sdfr		 * Install entries in the GATT, making sure that if
57561452Sdfr		 * AGP_PAGE_SIZE < PAGE_SIZE and mem->am_size is not
57661452Sdfr		 * aligned to PAGE_SIZE, we don't modify too many GATT
57761452Sdfr		 * entries.
57861452Sdfr		 */
57961452Sdfr		for (j = 0; j < PAGE_SIZE && i + j < mem->am_size;
58061452Sdfr		     j += AGP_PAGE_SIZE) {
58161452Sdfr			vm_offset_t pa = VM_PAGE_TO_PHYS(m) + j;
582188247Swkoszek			AGP_DPF("binding offset %#jx to pa %#jx\n",
583188247Swkoszek				(uintmax_t)offset + i + j, (uintmax_t)pa);
58461452Sdfr			error = AGP_BIND_PAGE(dev, offset + i + j, pa);
58561452Sdfr			if (error) {
58661452Sdfr				/*
58761452Sdfr				 * Bail out. Reverse all the mappings
58861452Sdfr				 * and unwire the pages.
58961452Sdfr				 */
59061452Sdfr				for (k = 0; k < i + j; k += AGP_PAGE_SIZE)
59161452Sdfr					AGP_UNBIND_PAGE(dev, offset + k);
592129601Smux				goto bad;
59361452Sdfr			}
59461452Sdfr		}
595254138Sattilio		vm_page_xunbusy(m);
59661452Sdfr	}
597248084Sattilio	VM_OBJECT_WUNLOCK(mem->am_obj);
59861452Sdfr
59961452Sdfr	/*
60061452Sdfr	 * Make sure the chipset gets the new mappings.
60161452Sdfr	 */
60261452Sdfr	AGP_FLUSH_TLB(dev);
60361452Sdfr
60461452Sdfr	mem->am_offset = offset;
60561452Sdfr	mem->am_is_bound = 1;
60661452Sdfr
607129579Smux	mtx_unlock(&sc->as_lock);
60861452Sdfr
60961452Sdfr	return 0;
610129601Smuxbad:
611129601Smux	mtx_unlock(&sc->as_lock);
612248084Sattilio	VM_OBJECT_ASSERT_WLOCKED(mem->am_obj);
613186433Skib	for (k = 0; k < mem->am_size; k += PAGE_SIZE) {
614186433Skib		m = vm_page_lookup(mem->am_obj, OFF_TO_IDX(k));
615186433Skib		if (k >= i)
616254138Sattilio			vm_page_xunbusy(m);
617207574Salc		vm_page_lock(m);
618129601Smux		vm_page_unwire(m, 0);
619207574Salc		vm_page_unlock(m);
620129601Smux	}
621248084Sattilio	VM_OBJECT_WUNLOCK(mem->am_obj);
622129601Smux
623129601Smux	return error;
62461452Sdfr}
62561452Sdfr
62661452Sdfrint
62761452Sdfragp_generic_unbind_memory(device_t dev, struct agp_memory *mem)
62861452Sdfr{
62961452Sdfr	struct agp_softc *sc = device_get_softc(dev);
63061452Sdfr	vm_page_t m;
63161452Sdfr	int i;
63261452Sdfr
633129579Smux	mtx_lock(&sc->as_lock);
63461452Sdfr
63561452Sdfr	if (!mem->am_is_bound) {
63661452Sdfr		device_printf(dev, "memory is not bound\n");
637129579Smux		mtx_unlock(&sc->as_lock);
63861452Sdfr		return EINVAL;
63961452Sdfr	}
64061452Sdfr
64161452Sdfr
64261452Sdfr	/*
64361452Sdfr	 * Unbind the individual pages and flush the chipset's
64461452Sdfr	 * TLB. Unwire the pages so they can be swapped.
64561452Sdfr	 */
64661452Sdfr	for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE)
64761452Sdfr		AGP_UNBIND_PAGE(dev, mem->am_offset + i);
648275406Stijl
649275406Stijl	AGP_FLUSH_TLB(dev);
650275406Stijl
651248084Sattilio	VM_OBJECT_WLOCK(mem->am_obj);
65261452Sdfr	for (i = 0; i < mem->am_size; i += PAGE_SIZE) {
65361452Sdfr		m = vm_page_lookup(mem->am_obj, atop(i));
654207574Salc		vm_page_lock(m);
65561452Sdfr		vm_page_unwire(m, 0);
656207574Salc		vm_page_unlock(m);
65761452Sdfr	}
658248084Sattilio	VM_OBJECT_WUNLOCK(mem->am_obj);
65961452Sdfr
66061452Sdfr	mem->am_offset = 0;
66161452Sdfr	mem->am_is_bound = 0;
66261452Sdfr
663129579Smux	mtx_unlock(&sc->as_lock);
66461452Sdfr
66561452Sdfr	return 0;
66661452Sdfr}
66761452Sdfr
66861452Sdfr/* Helper functions for implementing user/kernel api */
66961452Sdfr
67061452Sdfrstatic int
67161452Sdfragp_acquire_helper(device_t dev, enum agp_acquire_state state)
67261452Sdfr{
67361452Sdfr	struct agp_softc *sc = device_get_softc(dev);
67461452Sdfr
67561452Sdfr	if (sc->as_state != AGP_ACQUIRE_FREE)
67661452Sdfr		return EBUSY;
67761452Sdfr	sc->as_state = state;
67861452Sdfr
67961452Sdfr	return 0;
68061452Sdfr}
68161452Sdfr
68261452Sdfrstatic int
68361452Sdfragp_release_helper(device_t dev, enum agp_acquire_state state)
68461452Sdfr{
68561452Sdfr	struct agp_softc *sc = device_get_softc(dev);
68661452Sdfr
68761452Sdfr	if (sc->as_state == AGP_ACQUIRE_FREE)
68861452Sdfr		return 0;
68961452Sdfr
69061452Sdfr	if (sc->as_state != state)
69161452Sdfr		return EBUSY;
69261452Sdfr
69361452Sdfr	sc->as_state = AGP_ACQUIRE_FREE;
69461452Sdfr	return 0;
69561452Sdfr}
69661452Sdfr
69761452Sdfrstatic struct agp_memory *
69861452Sdfragp_find_memory(device_t dev, int id)
69961452Sdfr{
70061452Sdfr	struct agp_softc *sc = device_get_softc(dev);
70161452Sdfr	struct agp_memory *mem;
70261452Sdfr
70361452Sdfr	AGP_DPF("searching for memory block %d\n", id);
70461452Sdfr	TAILQ_FOREACH(mem, &sc->as_memory, am_link) {
70561452Sdfr		AGP_DPF("considering memory block %d\n", mem->am_id);
70661452Sdfr		if (mem->am_id == id)
70761452Sdfr			return mem;
70861452Sdfr	}
70961452Sdfr	return 0;
71061452Sdfr}
71161452Sdfr
71261452Sdfr/* Implementation of the userland ioctl api */
71361452Sdfr
71461452Sdfrstatic int
71561452Sdfragp_info_user(device_t dev, agp_info *info)
71661452Sdfr{
71761452Sdfr	struct agp_softc *sc = device_get_softc(dev);
71861452Sdfr
71961452Sdfr	bzero(info, sizeof *info);
72061452Sdfr	info->bridge_id = pci_get_devid(dev);
72161452Sdfr	info->agp_mode =
72261452Sdfr	    pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4);
723214603Snwhitehorn	if (sc->as_aperture)
724214603Snwhitehorn		info->aper_base = rman_get_start(sc->as_aperture);
725214603Snwhitehorn	else
726214603Snwhitehorn		info->aper_base = 0;
72761452Sdfr	info->aper_size = AGP_GET_APERTURE(dev) >> 20;
72861452Sdfr	info->pg_total = info->pg_system = sc->as_maxmem >> AGP_PAGE_SHIFT;
72961452Sdfr	info->pg_used = sc->as_allocated >> AGP_PAGE_SHIFT;
73061452Sdfr
73161452Sdfr	return 0;
73261452Sdfr}
73361452Sdfr
73461452Sdfrstatic int
73561452Sdfragp_setup_user(device_t dev, agp_setup *setup)
73661452Sdfr{
73761452Sdfr	return AGP_ENABLE(dev, setup->agp_mode);
73861452Sdfr}
73961452Sdfr
74061452Sdfrstatic int
74161452Sdfragp_allocate_user(device_t dev, agp_allocate *alloc)
74261452Sdfr{
74361452Sdfr	struct agp_memory *mem;
74461452Sdfr
74561452Sdfr	mem = AGP_ALLOC_MEMORY(dev,
74661452Sdfr			       alloc->type,
74761452Sdfr			       alloc->pg_count << AGP_PAGE_SHIFT);
74863010Sdfr	if (mem) {
74963010Sdfr		alloc->key = mem->am_id;
75063010Sdfr		alloc->physical = mem->am_physical;
75163010Sdfr		return 0;
75263010Sdfr	} else {
75363010Sdfr		return ENOMEM;
75463010Sdfr	}
75561452Sdfr}
75661452Sdfr
75761452Sdfrstatic int
75861452Sdfragp_deallocate_user(device_t dev, int id)
75961452Sdfr{
760201758Smbr	struct agp_memory *mem = agp_find_memory(dev, id);
76161452Sdfr
76261452Sdfr	if (mem) {
76361452Sdfr		AGP_FREE_MEMORY(dev, mem);
76461452Sdfr		return 0;
76561452Sdfr	} else {
76661452Sdfr		return ENOENT;
76761452Sdfr	}
76861452Sdfr}
76961452Sdfr
77061452Sdfrstatic int
77161452Sdfragp_bind_user(device_t dev, agp_bind *bind)
77261452Sdfr{
77361452Sdfr	struct agp_memory *mem = agp_find_memory(dev, bind->key);
77461452Sdfr
77561452Sdfr	if (!mem)
77661452Sdfr		return ENOENT;
77761452Sdfr
77861452Sdfr	return AGP_BIND_MEMORY(dev, mem, bind->pg_start << AGP_PAGE_SHIFT);
77961452Sdfr}
78061452Sdfr
78161452Sdfrstatic int
78261452Sdfragp_unbind_user(device_t dev, agp_unbind *unbind)
78361452Sdfr{
78461452Sdfr	struct agp_memory *mem = agp_find_memory(dev, unbind->key);
78561452Sdfr
78661452Sdfr	if (!mem)
78761452Sdfr		return ENOENT;
78861452Sdfr
78961452Sdfr	return AGP_UNBIND_MEMORY(dev, mem);
79061452Sdfr}
79161452Sdfr
79261452Sdfrstatic int
793235782Skibagp_chipset_flush(device_t dev)
794235782Skib{
795235782Skib
796235782Skib	return (AGP_CHIPSET_FLUSH(dev));
797235782Skib}
798235782Skib
799235782Skibstatic int
800130585Sphkagp_open(struct cdev *kdev, int oflags, int devtype, struct thread *td)
80161452Sdfr{
802191057Sed	device_t dev = kdev->si_drv1;
80361452Sdfr	struct agp_softc *sc = device_get_softc(dev);
80461452Sdfr
80561452Sdfr	if (!sc->as_isopen) {
80661452Sdfr		sc->as_isopen = 1;
80761452Sdfr		device_busy(dev);
80861452Sdfr	}
80961452Sdfr
81061452Sdfr	return 0;
81161452Sdfr}
81261452Sdfr
81361452Sdfrstatic int
814130585Sphkagp_close(struct cdev *kdev, int fflag, int devtype, struct thread *td)
81561452Sdfr{
816191057Sed	device_t dev = kdev->si_drv1;
81761452Sdfr	struct agp_softc *sc = device_get_softc(dev);
81886976Sru	struct agp_memory *mem;
81961452Sdfr
82061452Sdfr	/*
82161452Sdfr	 * Clear the GATT and force release on last close
82261452Sdfr	 */
82386976Sru	while ((mem = TAILQ_FIRST(&sc->as_memory)) != 0) {
82486976Sru		if (mem->am_is_bound)
82586976Sru			AGP_UNBIND_MEMORY(dev, mem);
82686976Sru		AGP_FREE_MEMORY(dev, mem);
82786976Sru	}
82861452Sdfr	if (sc->as_state == AGP_ACQUIRE_USER)
82961452Sdfr		agp_release_helper(dev, AGP_ACQUIRE_USER);
83061452Sdfr	sc->as_isopen = 0;
83161452Sdfr	device_unbusy(dev);
83261452Sdfr
83361452Sdfr	return 0;
83461452Sdfr}
83561452Sdfr
83661452Sdfrstatic int
837130585Sphkagp_ioctl(struct cdev *kdev, u_long cmd, caddr_t data, int fflag, struct thread *td)
83861452Sdfr{
839191057Sed	device_t dev = kdev->si_drv1;
84061452Sdfr
84161452Sdfr	switch (cmd) {
84261452Sdfr	case AGPIOC_INFO:
84361452Sdfr		return agp_info_user(dev, (agp_info *) data);
84461452Sdfr
84561452Sdfr	case AGPIOC_ACQUIRE:
84661452Sdfr		return agp_acquire_helper(dev, AGP_ACQUIRE_USER);
84761452Sdfr
84861452Sdfr	case AGPIOC_RELEASE:
84961452Sdfr		return agp_release_helper(dev, AGP_ACQUIRE_USER);
85061452Sdfr
85161452Sdfr	case AGPIOC_SETUP:
85261452Sdfr		return agp_setup_user(dev, (agp_setup *)data);
85361452Sdfr
85461452Sdfr	case AGPIOC_ALLOCATE:
85561452Sdfr		return agp_allocate_user(dev, (agp_allocate *)data);
85661452Sdfr
85761452Sdfr	case AGPIOC_DEALLOCATE:
85861452Sdfr		return agp_deallocate_user(dev, *(int *) data);
85961452Sdfr
86061452Sdfr	case AGPIOC_BIND:
86161452Sdfr		return agp_bind_user(dev, (agp_bind *)data);
86261452Sdfr
86361452Sdfr	case AGPIOC_UNBIND:
86461452Sdfr		return agp_unbind_user(dev, (agp_unbind *)data);
86561452Sdfr
866235782Skib	case AGPIOC_CHIPSET_FLUSH:
867235782Skib		return agp_chipset_flush(dev);
86861452Sdfr	}
86961452Sdfr
87061452Sdfr	return EINVAL;
87161452Sdfr}
87261452Sdfr
87361452Sdfrstatic int
874201223Srnolandagp_mmap(struct cdev *kdev, vm_ooffset_t offset, vm_paddr_t *paddr,
875201223Srnoland    int prot, vm_memattr_t *memattr)
87661452Sdfr{
877191057Sed	device_t dev = kdev->si_drv1;
87861452Sdfr	struct agp_softc *sc = device_get_softc(dev);
87961452Sdfr
88061452Sdfr	if (offset > AGP_GET_APERTURE(dev))
88161452Sdfr		return -1;
882214603Snwhitehorn	if (sc->as_aperture == NULL)
883214603Snwhitehorn		return -1;
884111462Smux	*paddr = rman_get_start(sc->as_aperture) + offset;
885111462Smux	return 0;
88661452Sdfr}
88761452Sdfr
88861452Sdfr/* Implementation of the kernel api */
88961452Sdfr
89061452Sdfrdevice_t
89161452Sdfragp_find_device()
89261452Sdfr{
893154486Sjhb	device_t *children, child;
894153568Sjhb	int i, count;
895153568Sjhb
89661452Sdfr	if (!agp_devclass)
897153568Sjhb		return NULL;
898153568Sjhb	if (devclass_get_devices(agp_devclass, &children, &count) != 0)
899153568Sjhb		return NULL;
900154486Sjhb	child = NULL;
901153568Sjhb	for (i = 0; i < count; i++) {
902154486Sjhb		if (device_is_attached(children[i])) {
903154486Sjhb			child = children[i];
904154486Sjhb			break;
905154486Sjhb		}
906153568Sjhb	}
907154486Sjhb	free(children, M_TEMP);
908154486Sjhb	return child;
90961452Sdfr}
91061452Sdfr
91161452Sdfrenum agp_acquire_state
91261452Sdfragp_state(device_t dev)
91361452Sdfr{
91461452Sdfr	struct agp_softc *sc = device_get_softc(dev);
91561452Sdfr	return sc->as_state;
91661452Sdfr}
91761452Sdfr
91861452Sdfrvoid
91961452Sdfragp_get_info(device_t dev, struct agp_info *info)
92061452Sdfr{
92161452Sdfr	struct agp_softc *sc = device_get_softc(dev);
92261452Sdfr
92361452Sdfr	info->ai_mode =
92461452Sdfr		pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4);
925214603Snwhitehorn	if (sc->as_aperture != NULL)
926214603Snwhitehorn		info->ai_aperture_base = rman_get_start(sc->as_aperture);
927214603Snwhitehorn	else
928214603Snwhitehorn		info->ai_aperture_base = 0;
929214603Snwhitehorn	info->ai_aperture_size = AGP_GET_APERTURE(dev);
93061452Sdfr	info->ai_memory_allowed = sc->as_maxmem;
93161452Sdfr	info->ai_memory_used = sc->as_allocated;
93261452Sdfr}
93361452Sdfr
93461452Sdfrint
93561452Sdfragp_acquire(device_t dev)
93661452Sdfr{
93761452Sdfr	return agp_acquire_helper(dev, AGP_ACQUIRE_KERNEL);
93861452Sdfr}
93961452Sdfr
94061452Sdfrint
94161452Sdfragp_release(device_t dev)
94261452Sdfr{
94361452Sdfr	return agp_release_helper(dev, AGP_ACQUIRE_KERNEL);
94461452Sdfr}
94561452Sdfr
94661452Sdfrint
94761452Sdfragp_enable(device_t dev, u_int32_t mode)
94861452Sdfr{
94961452Sdfr	return AGP_ENABLE(dev, mode);
95061452Sdfr}
95161452Sdfr
95261452Sdfrvoid *agp_alloc_memory(device_t dev, int type, vm_size_t bytes)
95361452Sdfr{
95461452Sdfr	return  (void *) AGP_ALLOC_MEMORY(dev, type, bytes);
95561452Sdfr}
95661452Sdfr
95761452Sdfrvoid agp_free_memory(device_t dev, void *handle)
95861452Sdfr{
95961452Sdfr	struct agp_memory *mem = (struct agp_memory *) handle;
96061452Sdfr	AGP_FREE_MEMORY(dev, mem);
96161452Sdfr}
96261452Sdfr
96361452Sdfrint agp_bind_memory(device_t dev, void *handle, vm_offset_t offset)
96461452Sdfr{
96561452Sdfr	struct agp_memory *mem = (struct agp_memory *) handle;
96661452Sdfr	return AGP_BIND_MEMORY(dev, mem, offset);
96761452Sdfr}
96861452Sdfr
96961452Sdfrint agp_unbind_memory(device_t dev, void *handle)
97061452Sdfr{
97161452Sdfr	struct agp_memory *mem = (struct agp_memory *) handle;
97261452Sdfr	return AGP_UNBIND_MEMORY(dev, mem);
97361452Sdfr}
97461452Sdfr
97561452Sdfrvoid agp_memory_info(device_t dev, void *handle, struct
97661452Sdfr		     agp_memory_info *mi)
97761452Sdfr{
97861452Sdfr	struct agp_memory *mem = (struct agp_memory *) handle;
97961452Sdfr
98061452Sdfr	mi->ami_size = mem->am_size;
98161452Sdfr	mi->ami_physical = mem->am_physical;
98261452Sdfr	mi->ami_offset = mem->am_offset;
98361452Sdfr	mi->ami_is_bound = mem->am_is_bound;
98461452Sdfr}
985275406Stijl
986275406Stijlint
987275406Stijlagp_bind_pages(device_t dev, vm_page_t *pages, vm_size_t size,
988275406Stijl    vm_offset_t offset)
989275406Stijl{
990275406Stijl	struct agp_softc *sc;
991275406Stijl	vm_offset_t i, j, k, pa;
992275406Stijl	vm_page_t m;
993275406Stijl	int error;
994275406Stijl
995275406Stijl	if ((size & (AGP_PAGE_SIZE - 1)) != 0 ||
996275406Stijl	    (offset & (AGP_PAGE_SIZE - 1)) != 0)
997275406Stijl		return (EINVAL);
998275406Stijl
999275406Stijl	sc = device_get_softc(dev);
1000275406Stijl
1001275406Stijl	mtx_lock(&sc->as_lock);
1002275406Stijl	for (i = 0; i < size; i += PAGE_SIZE) {
1003275406Stijl		m = pages[OFF_TO_IDX(i)];
1004275406Stijl		KASSERT(m->wire_count > 0,
1005275406Stijl		    ("agp_bind_pages: page %p hasn't been wired", m));
1006275406Stijl
1007275406Stijl		/*
1008275406Stijl		 * Install entries in the GATT, making sure that if
1009275406Stijl		 * AGP_PAGE_SIZE < PAGE_SIZE and size is not
1010275406Stijl		 * aligned to PAGE_SIZE, we don't modify too many GATT
1011275406Stijl		 * entries.
1012275406Stijl		 */
1013275406Stijl		for (j = 0; j < PAGE_SIZE && i + j < size; j += AGP_PAGE_SIZE) {
1014275406Stijl			pa = VM_PAGE_TO_PHYS(m) + j;
1015275406Stijl			AGP_DPF("binding offset %#jx to pa %#jx\n",
1016275406Stijl				(uintmax_t)offset + i + j, (uintmax_t)pa);
1017275406Stijl			error = AGP_BIND_PAGE(dev, offset + i + j, pa);
1018275406Stijl			if (error) {
1019275406Stijl				/*
1020275406Stijl				 * Bail out. Reverse all the mappings.
1021275406Stijl				 */
1022275406Stijl				for (k = 0; k < i + j; k += AGP_PAGE_SIZE)
1023275406Stijl					AGP_UNBIND_PAGE(dev, offset + k);
1024275406Stijl
1025275406Stijl				mtx_unlock(&sc->as_lock);
1026275406Stijl				return (error);
1027275406Stijl			}
1028275406Stijl		}
1029275406Stijl	}
1030275406Stijl
1031275406Stijl	AGP_FLUSH_TLB(dev);
1032275406Stijl
1033275406Stijl	mtx_unlock(&sc->as_lock);
1034275406Stijl	return (0);
1035275406Stijl}
1036275406Stijl
1037275406Stijlint
1038275406Stijlagp_unbind_pages(device_t dev, vm_size_t size, vm_offset_t offset)
1039275406Stijl{
1040275406Stijl	struct agp_softc *sc;
1041275406Stijl	vm_offset_t i;
1042275406Stijl
1043275406Stijl	if ((size & (AGP_PAGE_SIZE - 1)) != 0 ||
1044275406Stijl	    (offset & (AGP_PAGE_SIZE - 1)) != 0)
1045275406Stijl		return (EINVAL);
1046275406Stijl
1047275406Stijl	sc = device_get_softc(dev);
1048275406Stijl
1049275406Stijl	mtx_lock(&sc->as_lock);
1050275406Stijl	for (i = 0; i < size; i += AGP_PAGE_SIZE)
1051275406Stijl		AGP_UNBIND_PAGE(dev, offset + i);
1052275406Stijl
1053275406Stijl	AGP_FLUSH_TLB(dev);
1054275406Stijl
1055275406Stijl	mtx_unlock(&sc->as_lock);
1056275406Stijl	return (0);
1057275406Stijl}
1058