159747216SDoug Rabson /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
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>
30c353491aSWojciech A. Koszek #include "opt_agp.h"
3159747216SDoug Rabson
3259747216SDoug Rabson #include <sys/param.h>
3359747216SDoug Rabson #include <sys/systm.h>
3459747216SDoug Rabson #include <sys/malloc.h>
3559747216SDoug Rabson #include <sys/kernel.h>
36f11d01c3SPoul-Henning Kamp #include <sys/module.h>
3759747216SDoug Rabson #include <sys/bus.h>
3859747216SDoug Rabson #include <sys/conf.h>
3959747216SDoug Rabson #include <sys/ioccom.h>
4059747216SDoug Rabson #include <sys/agpio.h>
4159747216SDoug Rabson #include <sys/lock.h>
4223955314SAlfred Perlstein #include <sys/mutex.h>
431639f08bSJohn Baldwin #include <sys/proc.h>
4489f6b863SAttilio Rao #include <sys/rwlock.h>
4559747216SDoug Rabson
46dbac8ff4SJohn Baldwin #include <dev/agp/agppriv.h>
47dbac8ff4SJohn Baldwin #include <dev/agp/agpvar.h>
48dbac8ff4SJohn Baldwin #include <dev/agp/agpreg.h>
4919b7ffd1SWarner Losh #include <dev/pci/pcivar.h>
5019b7ffd1SWarner Losh #include <dev/pci/pcireg.h>
5159747216SDoug Rabson
5259747216SDoug Rabson #include <vm/vm.h>
534e612cddSTijl Coosemans #include <vm/vm_extern.h>
544e612cddSTijl Coosemans #include <vm/vm_kern.h>
551c771f92SKonstantin Belousov #include <vm/vm_param.h>
5659747216SDoug Rabson #include <vm/vm_object.h>
5759747216SDoug Rabson #include <vm/vm_page.h>
5859747216SDoug Rabson #include <vm/vm_pageout.h>
5959747216SDoug Rabson #include <vm/pmap.h>
6059747216SDoug Rabson
6159747216SDoug Rabson #include <machine/bus.h>
6259747216SDoug Rabson #include <machine/resource.h>
6359747216SDoug Rabson #include <sys/rman.h>
6459747216SDoug Rabson
6559747216SDoug Rabson MODULE_VERSION(agp, 1);
6659747216SDoug Rabson
67315a1645SAssar Westerlund MALLOC_DEFINE(M_AGP, "agp", "AGP data structures");
6859747216SDoug Rabson
6959747216SDoug Rabson /* agp_drv.c */
7059747216SDoug Rabson static d_open_t agp_open;
7159747216SDoug Rabson static d_close_t agp_close;
7259747216SDoug Rabson static d_ioctl_t agp_ioctl;
7359747216SDoug Rabson static d_mmap_t agp_mmap;
7459747216SDoug Rabson
7559747216SDoug Rabson static struct cdevsw agp_cdevsw = {
76dc08ffecSPoul-Henning Kamp .d_version = D_VERSION,
77dc08ffecSPoul-Henning Kamp .d_flags = D_NEEDGIANT,
787ac40f5fSPoul-Henning Kamp .d_open = agp_open,
797ac40f5fSPoul-Henning Kamp .d_close = agp_close,
807ac40f5fSPoul-Henning Kamp .d_ioctl = agp_ioctl,
817ac40f5fSPoul-Henning Kamp .d_mmap = agp_mmap,
827ac40f5fSPoul-Henning Kamp .d_name = "agp",
8359747216SDoug Rabson };
8459747216SDoug Rabson
8559747216SDoug Rabson static devclass_t agp_devclass;
8659747216SDoug Rabson
8759747216SDoug Rabson /* Helper functions for implementing chipset mini drivers. */
8859747216SDoug Rabson
8959747216SDoug Rabson u_int8_t
agp_find_caps(device_t dev)9059747216SDoug Rabson agp_find_caps(device_t dev)
9159747216SDoug Rabson {
9204d6a912SJohn Baldwin int capreg;
9359747216SDoug Rabson
943b0a4aefSJohn Baldwin if (pci_find_cap(dev, PCIY_AGP, &capreg) != 0)
9504d6a912SJohn Baldwin capreg = 0;
9604d6a912SJohn Baldwin return (capreg);
9759747216SDoug Rabson }
9859747216SDoug Rabson
9959747216SDoug Rabson /*
10059747216SDoug Rabson * Find an AGP display device (if any).
10159747216SDoug Rabson */
10259747216SDoug Rabson static device_t
agp_find_display(void)10359747216SDoug Rabson agp_find_display(void)
10459747216SDoug Rabson {
10559747216SDoug Rabson devclass_t pci = devclass_find("pci");
10659747216SDoug Rabson device_t bus, dev = 0;
10759747216SDoug Rabson device_t *kids;
10859747216SDoug Rabson int busnum, numkids, i;
10959747216SDoug Rabson
11059747216SDoug Rabson for (busnum = 0; busnum < devclass_get_maxunit(pci); busnum++) {
11159747216SDoug Rabson bus = devclass_get_device(pci, busnum);
11259747216SDoug Rabson if (!bus)
11359747216SDoug Rabson continue;
114d444cd5fSWarner Losh if (device_get_children(bus, &kids, &numkids) != 0)
115d444cd5fSWarner Losh continue;
11659747216SDoug Rabson for (i = 0; i < numkids; i++) {
11759747216SDoug Rabson dev = kids[i];
11859747216SDoug Rabson if (pci_get_class(dev) == PCIC_DISPLAY
11959747216SDoug Rabson && pci_get_subclass(dev) == PCIS_DISPLAY_VGA)
12059747216SDoug Rabson if (agp_find_caps(dev)) {
12159747216SDoug Rabson free(kids, M_TEMP);
12259747216SDoug Rabson return dev;
12359747216SDoug Rabson }
12459747216SDoug Rabson
12559747216SDoug Rabson }
12659747216SDoug Rabson free(kids, M_TEMP);
12759747216SDoug Rabson }
12859747216SDoug Rabson
12959747216SDoug Rabson return 0;
13059747216SDoug Rabson }
13159747216SDoug Rabson
13259747216SDoug Rabson struct agp_gatt *
agp_alloc_gatt(device_t dev)13359747216SDoug Rabson agp_alloc_gatt(device_t dev)
13459747216SDoug Rabson {
13559747216SDoug Rabson u_int32_t apsize = AGP_GET_APERTURE(dev);
13659747216SDoug Rabson u_int32_t entries = apsize >> AGP_PAGE_SHIFT;
13759747216SDoug Rabson struct agp_gatt *gatt;
13859747216SDoug Rabson
13959747216SDoug Rabson if (bootverbose)
14059747216SDoug Rabson device_printf(dev,
14159747216SDoug Rabson "allocating GATT for aperture of size %dM\n",
14259747216SDoug Rabson apsize / (1024*1024));
14359747216SDoug Rabson
144a6cb9d8eSEric Anholt if (entries == 0) {
145a6cb9d8eSEric Anholt device_printf(dev, "bad aperture size\n");
146a6cb9d8eSEric Anholt return NULL;
147a6cb9d8eSEric Anholt }
148a6cb9d8eSEric Anholt
14959747216SDoug Rabson gatt = malloc(sizeof(struct agp_gatt), M_AGP, M_NOWAIT);
15059747216SDoug Rabson if (!gatt)
15159747216SDoug Rabson return 0;
15259747216SDoug Rabson
15359747216SDoug Rabson gatt->ag_entries = entries;
154f49fd63aSJohn Baldwin gatt->ag_virtual = kmem_alloc_contig(entries * sizeof(uint32_t),
155f49fd63aSJohn Baldwin M_NOWAIT | M_ZERO, 0, ~0, PAGE_SIZE, 0, VM_MEMATTR_WRITE_COMBINING);
15659747216SDoug Rabson if (!gatt->ag_virtual) {
15759747216SDoug Rabson if (bootverbose)
15859747216SDoug Rabson device_printf(dev, "contiguous allocation failed\n");
15959747216SDoug Rabson free(gatt, M_AGP);
16059747216SDoug Rabson return 0;
16159747216SDoug Rabson }
16259747216SDoug Rabson gatt->ag_physical = vtophys((vm_offset_t) gatt->ag_virtual);
16359747216SDoug Rabson
16459747216SDoug Rabson return gatt;
16559747216SDoug Rabson }
16659747216SDoug Rabson
16759747216SDoug Rabson void
agp_free_gatt(struct agp_gatt * gatt)16859747216SDoug Rabson agp_free_gatt(struct agp_gatt *gatt)
16959747216SDoug Rabson {
170f49fd63aSJohn Baldwin kmem_free(gatt->ag_virtual, gatt->ag_entries * sizeof(uint32_t));
17159747216SDoug Rabson free(gatt, M_AGP);
17259747216SDoug Rabson }
17359747216SDoug Rabson
1747370bc77SSeigo Tanimura static u_int agp_max[][2] = {
17559747216SDoug Rabson {0, 0},
17659747216SDoug Rabson {32, 4},
17759747216SDoug Rabson {64, 28},
17859747216SDoug Rabson {128, 96},
17959747216SDoug Rabson {256, 204},
18059747216SDoug Rabson {512, 440},
18159747216SDoug Rabson {1024, 942},
18259747216SDoug Rabson {2048, 1920},
18359747216SDoug Rabson {4096, 3932}
18459747216SDoug Rabson };
1854ec642f1SPedro F. Giffuni #define AGP_MAX_SIZE nitems(agp_max)
18659747216SDoug Rabson
187d450e052SEric Anholt /**
188d450e052SEric Anholt * Sets the PCI resource which represents the AGP aperture.
189d450e052SEric Anholt *
190d450e052SEric Anholt * If not called, the default AGP aperture resource of AGP_APBASE will
191d450e052SEric Anholt * be used. Must be called before agp_generic_attach().
192d450e052SEric Anholt */
193d450e052SEric Anholt void
agp_set_aperture_resource(device_t dev,int rid)194d450e052SEric Anholt agp_set_aperture_resource(device_t dev, int rid)
195d450e052SEric Anholt {
196d450e052SEric Anholt struct agp_softc *sc = device_get_softc(dev);
197d450e052SEric Anholt
198d450e052SEric Anholt sc->as_aperture_rid = rid;
199d450e052SEric Anholt }
200d450e052SEric Anholt
20159747216SDoug Rabson int
agp_generic_attach(device_t dev)20259747216SDoug Rabson agp_generic_attach(device_t dev)
20359747216SDoug Rabson {
204437ea82cSMark Johnston struct make_dev_args mdargs;
20559747216SDoug Rabson struct agp_softc *sc = device_get_softc(dev);
206437ea82cSMark Johnston int error, i, unit;
2077370bc77SSeigo Tanimura u_int memsize;
20859747216SDoug Rabson
20959747216SDoug Rabson /*
210d450e052SEric Anholt * Find and map the aperture, RF_SHAREABLE for DRM but not RF_ACTIVE
211d450e052SEric Anholt * because the kernel doesn't need to map it.
21259747216SDoug Rabson */
21350fd2a5bSNathan Whitehorn
21450fd2a5bSNathan Whitehorn if (sc->as_aperture_rid != -1) {
215d450e052SEric Anholt if (sc->as_aperture_rid == 0)
216d450e052SEric Anholt sc->as_aperture_rid = AGP_APBASE;
217d450e052SEric Anholt
218d450e052SEric Anholt sc->as_aperture = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
219d450e052SEric Anholt &sc->as_aperture_rid, RF_SHAREABLE);
22059747216SDoug Rabson if (!sc->as_aperture)
22159747216SDoug Rabson return ENOMEM;
22250fd2a5bSNathan Whitehorn }
22359747216SDoug Rabson
22459747216SDoug Rabson /*
22559747216SDoug Rabson * Work out an upper bound for agp memory allocation. This
22659747216SDoug Rabson * uses a heurisitc table from the Linux driver.
22759747216SDoug Rabson */
228e11379e9SMarcel Moolenaar memsize = ptoa(realmem) >> 20;
2294ec642f1SPedro F. Giffuni for (i = 0; i < AGP_MAX_SIZE; i++) {
23059747216SDoug Rabson if (memsize <= agp_max[i][0])
23159747216SDoug Rabson break;
23259747216SDoug Rabson }
2334ec642f1SPedro F. Giffuni if (i == AGP_MAX_SIZE)
2344ec642f1SPedro F. Giffuni i = AGP_MAX_SIZE - 1;
23559747216SDoug Rabson sc->as_maxmem = agp_max[i][1] << 20U;
23659747216SDoug Rabson
23759747216SDoug Rabson /*
23859747216SDoug Rabson * The lock is used to prevent re-entry to
23959747216SDoug Rabson * agp_generic_bind_memory() since that function can sleep.
24059747216SDoug Rabson */
241d8a821e8SMaxime Henrion mtx_init(&sc->as_lock, "agp lock", NULL, MTX_DEF);
24259747216SDoug Rabson
24359747216SDoug Rabson /*
24459747216SDoug Rabson * Initialise stuff for the userland device.
24559747216SDoug Rabson */
24659747216SDoug Rabson agp_devclass = devclass_find("agp");
24759747216SDoug Rabson TAILQ_INIT(&sc->as_memory);
24859747216SDoug Rabson sc->as_nextid = 1;
24959747216SDoug Rabson
250437ea82cSMark Johnston sc->as_devalias = NULL;
25159747216SDoug Rabson
252437ea82cSMark Johnston make_dev_args_init(&mdargs);
253437ea82cSMark Johnston mdargs.mda_devsw = &agp_cdevsw;
254437ea82cSMark Johnston mdargs.mda_uid = UID_ROOT;
255437ea82cSMark Johnston mdargs.mda_gid = GID_WHEEL;
256437ea82cSMark Johnston mdargs.mda_mode = 0600;
257*12500c14SMark Johnston mdargs.mda_si_drv1 = dev;
258437ea82cSMark Johnston mdargs.mda_si_drv2 = NULL;
259437ea82cSMark Johnston
260437ea82cSMark Johnston unit = device_get_unit(dev);
261437ea82cSMark Johnston error = make_dev_s(&mdargs, &sc->as_devnode, "agpgart%d", unit);
262437ea82cSMark Johnston if (error == 0) {
263437ea82cSMark Johnston /*
264437ea82cSMark Johnston * Create an alias for the first device that shows up.
265437ea82cSMark Johnston */
266437ea82cSMark Johnston if (unit == 0) {
267437ea82cSMark Johnston (void)make_dev_alias_p(MAKEDEV_CHECKNAME,
268437ea82cSMark Johnston &sc->as_devalias, sc->as_devnode, "agpgart");
269437ea82cSMark Johnston }
270437ea82cSMark Johnston } else {
271437ea82cSMark Johnston agp_free_res(dev);
272437ea82cSMark Johnston }
273437ea82cSMark Johnston
274437ea82cSMark Johnston return error;
27559747216SDoug Rabson }
27659747216SDoug Rabson
277f82a1d49SJohn Baldwin void
agp_free_cdev(device_t dev)278f82a1d49SJohn Baldwin agp_free_cdev(device_t dev)
27959747216SDoug Rabson {
28059747216SDoug Rabson struct agp_softc *sc = device_get_softc(dev);
2814c2546c1SJohn Baldwin
2824c2546c1SJohn Baldwin destroy_dev(sc->as_devnode);
283437ea82cSMark Johnston if (sc->as_devalias != NULL)
284437ea82cSMark Johnston destroy_dev(sc->as_devalias);
285f82a1d49SJohn Baldwin }
286f82a1d49SJohn Baldwin
287f82a1d49SJohn Baldwin void
agp_free_res(device_t dev)288f82a1d49SJohn Baldwin agp_free_res(device_t dev)
289f82a1d49SJohn Baldwin {
290f82a1d49SJohn Baldwin struct agp_softc *sc = device_get_softc(dev);
291f82a1d49SJohn Baldwin
29250fd2a5bSNathan Whitehorn if (sc->as_aperture != NULL)
293d450e052SEric Anholt bus_release_resource(dev, SYS_RES_MEMORY, sc->as_aperture_rid,
294d450e052SEric Anholt sc->as_aperture);
295d8a821e8SMaxime Henrion mtx_destroy(&sc->as_lock);
296f82a1d49SJohn Baldwin }
297f82a1d49SJohn Baldwin
298f82a1d49SJohn Baldwin int
agp_generic_detach(device_t dev)299f82a1d49SJohn Baldwin agp_generic_detach(device_t dev)
300f82a1d49SJohn Baldwin {
301f82a1d49SJohn Baldwin
302f82a1d49SJohn Baldwin agp_free_cdev(dev);
303f82a1d49SJohn Baldwin agp_free_res(dev);
30459747216SDoug Rabson return 0;
30559747216SDoug Rabson }
30659747216SDoug Rabson
307d450e052SEric Anholt /**
308d450e052SEric Anholt * Default AGP aperture size detection which simply returns the size of
309d450e052SEric Anholt * the aperture's PCI resource.
310d450e052SEric Anholt */
311cd6d5177SWarner Losh u_int32_t
agp_generic_get_aperture(device_t dev)312d450e052SEric Anholt agp_generic_get_aperture(device_t dev)
313d450e052SEric Anholt {
314d450e052SEric Anholt struct agp_softc *sc = device_get_softc(dev);
315d450e052SEric Anholt
316d450e052SEric Anholt return rman_get_size(sc->as_aperture);
317d450e052SEric Anholt }
318d450e052SEric Anholt
319d450e052SEric Anholt /**
320d450e052SEric Anholt * Default AGP aperture size setting function, which simply doesn't allow
321d450e052SEric Anholt * changes to resource size.
322d450e052SEric Anholt */
323d450e052SEric Anholt int
agp_generic_set_aperture(device_t dev,u_int32_t aperture)324d450e052SEric Anholt agp_generic_set_aperture(device_t dev, u_int32_t aperture)
325d450e052SEric Anholt {
326d450e052SEric Anholt u_int32_t current_aperture;
327d450e052SEric Anholt
328d450e052SEric Anholt current_aperture = AGP_GET_APERTURE(dev);
329d450e052SEric Anholt if (current_aperture != aperture)
330d450e052SEric Anholt return EINVAL;
331d450e052SEric Anholt else
332d450e052SEric Anholt return 0;
333d450e052SEric Anholt }
334d450e052SEric Anholt
3359f9ccd20SJohn Baldwin /*
3369f9ccd20SJohn Baldwin * This does the enable logic for v3, with the same topology
3379f9ccd20SJohn Baldwin * restrictions as in place for v2 -- one bus, one device on the bus.
3389f9ccd20SJohn Baldwin */
3399f9ccd20SJohn Baldwin static int
agp_v3_enable(device_t dev,device_t mdev,u_int32_t mode)3409f9ccd20SJohn Baldwin agp_v3_enable(device_t dev, device_t mdev, u_int32_t mode)
34159747216SDoug Rabson {
34259747216SDoug Rabson u_int32_t tstatus, mstatus;
34359747216SDoug Rabson u_int32_t command;
3449f9ccd20SJohn Baldwin int rq, sba, fw, rate, arqsz, cal;
34559747216SDoug Rabson
3469f9ccd20SJohn Baldwin tstatus = pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4);
3479f9ccd20SJohn Baldwin mstatus = pci_read_config(mdev, agp_find_caps(mdev) + AGP_STATUS, 4);
3489f9ccd20SJohn Baldwin
3499f9ccd20SJohn Baldwin /* Set RQ to the min of mode, tstatus and mstatus */
3509f9ccd20SJohn Baldwin rq = AGP_MODE_GET_RQ(mode);
3519f9ccd20SJohn Baldwin if (AGP_MODE_GET_RQ(tstatus) < rq)
3529f9ccd20SJohn Baldwin rq = AGP_MODE_GET_RQ(tstatus);
3539f9ccd20SJohn Baldwin if (AGP_MODE_GET_RQ(mstatus) < rq)
3549f9ccd20SJohn Baldwin rq = AGP_MODE_GET_RQ(mstatus);
3559f9ccd20SJohn Baldwin
3569f9ccd20SJohn Baldwin /*
3579f9ccd20SJohn Baldwin * ARQSZ - Set the value to the maximum one.
3589f9ccd20SJohn Baldwin * Don't allow the mode register to override values.
3599f9ccd20SJohn Baldwin */
3609f9ccd20SJohn Baldwin arqsz = AGP_MODE_GET_ARQSZ(mode);
3619f9ccd20SJohn Baldwin if (AGP_MODE_GET_ARQSZ(tstatus) > rq)
3629f9ccd20SJohn Baldwin rq = AGP_MODE_GET_ARQSZ(tstatus);
3639f9ccd20SJohn Baldwin if (AGP_MODE_GET_ARQSZ(mstatus) > rq)
3649f9ccd20SJohn Baldwin rq = AGP_MODE_GET_ARQSZ(mstatus);
3659f9ccd20SJohn Baldwin
3669f9ccd20SJohn Baldwin /* Calibration cycle - don't allow override by mode register */
3679f9ccd20SJohn Baldwin cal = AGP_MODE_GET_CAL(tstatus);
3689f9ccd20SJohn Baldwin if (AGP_MODE_GET_CAL(mstatus) < cal)
3699f9ccd20SJohn Baldwin cal = AGP_MODE_GET_CAL(mstatus);
3709f9ccd20SJohn Baldwin
3719f9ccd20SJohn Baldwin /* SBA must be supported for AGP v3. */
3729f9ccd20SJohn Baldwin sba = 1;
3739f9ccd20SJohn Baldwin
3749f9ccd20SJohn Baldwin /* Set FW if all three support it. */
3759f9ccd20SJohn Baldwin fw = (AGP_MODE_GET_FW(tstatus)
3769f9ccd20SJohn Baldwin & AGP_MODE_GET_FW(mstatus)
3779f9ccd20SJohn Baldwin & AGP_MODE_GET_FW(mode));
3789f9ccd20SJohn Baldwin
3799f9ccd20SJohn Baldwin /* Figure out the max rate */
3809f9ccd20SJohn Baldwin rate = (AGP_MODE_GET_RATE(tstatus)
3819f9ccd20SJohn Baldwin & AGP_MODE_GET_RATE(mstatus)
3829f9ccd20SJohn Baldwin & AGP_MODE_GET_RATE(mode));
3839f9ccd20SJohn Baldwin if (rate & AGP_MODE_V3_RATE_8x)
3849f9ccd20SJohn Baldwin rate = AGP_MODE_V3_RATE_8x;
3859f9ccd20SJohn Baldwin else
3869f9ccd20SJohn Baldwin rate = AGP_MODE_V3_RATE_4x;
3879f9ccd20SJohn Baldwin if (bootverbose)
3889f9ccd20SJohn Baldwin device_printf(dev, "Setting AGP v3 mode %d\n", rate * 4);
3899f9ccd20SJohn Baldwin
3909f9ccd20SJohn Baldwin pci_write_config(dev, agp_find_caps(dev) + AGP_COMMAND, 0, 4);
3919f9ccd20SJohn Baldwin
3929f9ccd20SJohn Baldwin /* Construct the new mode word and tell the hardware */
39398935c58SJung-uk Kim command = 0;
3949f9ccd20SJohn Baldwin command = AGP_MODE_SET_RQ(0, rq);
3959f9ccd20SJohn Baldwin command = AGP_MODE_SET_ARQSZ(command, arqsz);
3969f9ccd20SJohn Baldwin command = AGP_MODE_SET_CAL(command, cal);
3979f9ccd20SJohn Baldwin command = AGP_MODE_SET_SBA(command, sba);
3989f9ccd20SJohn Baldwin command = AGP_MODE_SET_FW(command, fw);
3999f9ccd20SJohn Baldwin command = AGP_MODE_SET_RATE(command, rate);
40098935c58SJung-uk Kim command = AGP_MODE_SET_MODE_3(command, 1);
4019f9ccd20SJohn Baldwin command = AGP_MODE_SET_AGP(command, 1);
4029f9ccd20SJohn Baldwin pci_write_config(dev, agp_find_caps(dev) + AGP_COMMAND, command, 4);
4039f9ccd20SJohn Baldwin pci_write_config(mdev, agp_find_caps(mdev) + AGP_COMMAND, command, 4);
4049f9ccd20SJohn Baldwin
4059f9ccd20SJohn Baldwin return 0;
40659747216SDoug Rabson }
40759747216SDoug Rabson
4089f9ccd20SJohn Baldwin static int
agp_v2_enable(device_t dev,device_t mdev,u_int32_t mode)4099f9ccd20SJohn Baldwin agp_v2_enable(device_t dev, device_t mdev, u_int32_t mode)
4109f9ccd20SJohn Baldwin {
4119f9ccd20SJohn Baldwin u_int32_t tstatus, mstatus;
4129f9ccd20SJohn Baldwin u_int32_t command;
4139f9ccd20SJohn Baldwin int rq, sba, fw, rate;
4149f9ccd20SJohn Baldwin
41559747216SDoug Rabson tstatus = pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4);
41659747216SDoug Rabson mstatus = pci_read_config(mdev, agp_find_caps(mdev) + AGP_STATUS, 4);
41759747216SDoug Rabson
41859747216SDoug Rabson /* Set RQ to the min of mode, tstatus and mstatus */
41959747216SDoug Rabson rq = AGP_MODE_GET_RQ(mode);
42059747216SDoug Rabson if (AGP_MODE_GET_RQ(tstatus) < rq)
42159747216SDoug Rabson rq = AGP_MODE_GET_RQ(tstatus);
42259747216SDoug Rabson if (AGP_MODE_GET_RQ(mstatus) < rq)
42359747216SDoug Rabson rq = AGP_MODE_GET_RQ(mstatus);
42459747216SDoug Rabson
42559747216SDoug Rabson /* Set SBA if all three can deal with SBA */
42659747216SDoug Rabson sba = (AGP_MODE_GET_SBA(tstatus)
42759747216SDoug Rabson & AGP_MODE_GET_SBA(mstatus)
42859747216SDoug Rabson & AGP_MODE_GET_SBA(mode));
42959747216SDoug Rabson
43059747216SDoug Rabson /* Similar for FW */
43159747216SDoug Rabson fw = (AGP_MODE_GET_FW(tstatus)
43259747216SDoug Rabson & AGP_MODE_GET_FW(mstatus)
43359747216SDoug Rabson & AGP_MODE_GET_FW(mode));
43459747216SDoug Rabson
43559747216SDoug Rabson /* Figure out the max rate */
43659747216SDoug Rabson rate = (AGP_MODE_GET_RATE(tstatus)
43759747216SDoug Rabson & AGP_MODE_GET_RATE(mstatus)
43859747216SDoug Rabson & AGP_MODE_GET_RATE(mode));
4399f9ccd20SJohn Baldwin if (rate & AGP_MODE_V2_RATE_4x)
4409f9ccd20SJohn Baldwin rate = AGP_MODE_V2_RATE_4x;
4419f9ccd20SJohn Baldwin else if (rate & AGP_MODE_V2_RATE_2x)
4429f9ccd20SJohn Baldwin rate = AGP_MODE_V2_RATE_2x;
44359747216SDoug Rabson else
4449f9ccd20SJohn Baldwin rate = AGP_MODE_V2_RATE_1x;
4459f9ccd20SJohn Baldwin if (bootverbose)
4469f9ccd20SJohn Baldwin device_printf(dev, "Setting AGP v2 mode %d\n", rate);
44759747216SDoug Rabson
44859747216SDoug Rabson /* Construct the new mode word and tell the hardware */
44998935c58SJung-uk Kim command = 0;
45059747216SDoug Rabson command = AGP_MODE_SET_RQ(0, rq);
45159747216SDoug Rabson command = AGP_MODE_SET_SBA(command, sba);
45259747216SDoug Rabson command = AGP_MODE_SET_FW(command, fw);
45359747216SDoug Rabson command = AGP_MODE_SET_RATE(command, rate);
45459747216SDoug Rabson command = AGP_MODE_SET_AGP(command, 1);
45559747216SDoug Rabson pci_write_config(dev, agp_find_caps(dev) + AGP_COMMAND, command, 4);
45659747216SDoug Rabson pci_write_config(mdev, agp_find_caps(mdev) + AGP_COMMAND, command, 4);
45759747216SDoug Rabson
45859747216SDoug Rabson return 0;
45959747216SDoug Rabson }
46059747216SDoug Rabson
4619f9ccd20SJohn Baldwin int
agp_generic_enable(device_t dev,u_int32_t mode)4629f9ccd20SJohn Baldwin agp_generic_enable(device_t dev, u_int32_t mode)
4639f9ccd20SJohn Baldwin {
4649f9ccd20SJohn Baldwin device_t mdev = agp_find_display();
4659f9ccd20SJohn Baldwin u_int32_t tstatus, mstatus;
4669f9ccd20SJohn Baldwin
4679f9ccd20SJohn Baldwin if (!mdev) {
4689f9ccd20SJohn Baldwin AGP_DPF("can't find display\n");
4699f9ccd20SJohn Baldwin return ENXIO;
4709f9ccd20SJohn Baldwin }
4719f9ccd20SJohn Baldwin
4729f9ccd20SJohn Baldwin tstatus = pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4);
4739f9ccd20SJohn Baldwin mstatus = pci_read_config(mdev, agp_find_caps(mdev) + AGP_STATUS, 4);
4749f9ccd20SJohn Baldwin
4759f9ccd20SJohn Baldwin /*
4769f9ccd20SJohn Baldwin * Check display and bridge for AGP v3 support. AGP v3 allows
4779f9ccd20SJohn Baldwin * more variety in topology than v2, e.g. multiple AGP devices
4789f9ccd20SJohn Baldwin * attached to one bridge, or multiple AGP bridges in one
4799f9ccd20SJohn Baldwin * system. This doesn't attempt to address those situations,
4809f9ccd20SJohn Baldwin * but should work fine for a classic single AGP slot system
4819f9ccd20SJohn Baldwin * with AGP v3.
4829f9ccd20SJohn Baldwin */
48398935c58SJung-uk Kim if (AGP_MODE_GET_MODE_3(mode) &&
48498935c58SJung-uk Kim AGP_MODE_GET_MODE_3(tstatus) &&
48598935c58SJung-uk Kim AGP_MODE_GET_MODE_3(mstatus))
4869f9ccd20SJohn Baldwin return (agp_v3_enable(dev, mdev, mode));
4879f9ccd20SJohn Baldwin else
4889f9ccd20SJohn Baldwin return (agp_v2_enable(dev, mdev, mode));
4899f9ccd20SJohn Baldwin }
4909f9ccd20SJohn Baldwin
49159747216SDoug Rabson struct agp_memory *
agp_generic_alloc_memory(device_t dev,int type,vm_size_t size)49259747216SDoug Rabson agp_generic_alloc_memory(device_t dev, int type, vm_size_t size)
49359747216SDoug Rabson {
49459747216SDoug Rabson struct agp_softc *sc = device_get_softc(dev);
49559747216SDoug Rabson struct agp_memory *mem;
49659747216SDoug Rabson
49759747216SDoug Rabson if ((size & (AGP_PAGE_SIZE - 1)) != 0)
49859747216SDoug Rabson return 0;
49959747216SDoug Rabson
500cf99ea5dSTijl Coosemans if (size > sc->as_maxmem - sc->as_allocated)
50159747216SDoug Rabson return 0;
50259747216SDoug Rabson
503e547d6fdSDoug Rabson if (type != 0) {
504e547d6fdSDoug Rabson printf("agp_generic_alloc_memory: unsupported type %d\n",
505e547d6fdSDoug Rabson type);
506e547d6fdSDoug Rabson return 0;
507e547d6fdSDoug Rabson }
508e547d6fdSDoug Rabson
509a163d034SWarner Losh mem = malloc(sizeof *mem, M_AGP, M_WAITOK);
51059747216SDoug Rabson mem->am_id = sc->as_nextid++;
51159747216SDoug Rabson mem->am_size = size;
512e547d6fdSDoug Rabson mem->am_type = 0;
5136b389740SMark Johnston mem->am_obj = vm_object_allocate(OBJT_SWAP, atop(round_page(size)));
51459747216SDoug Rabson mem->am_physical = 0;
51559747216SDoug Rabson mem->am_offset = 0;
51659747216SDoug Rabson mem->am_is_bound = 0;
51759747216SDoug Rabson TAILQ_INSERT_TAIL(&sc->as_memory, mem, am_link);
51859747216SDoug Rabson sc->as_allocated += size;
51959747216SDoug Rabson
52059747216SDoug Rabson return mem;
52159747216SDoug Rabson }
52259747216SDoug Rabson
52359747216SDoug Rabson int
agp_generic_free_memory(device_t dev,struct agp_memory * mem)52459747216SDoug Rabson agp_generic_free_memory(device_t dev, struct agp_memory *mem)
52559747216SDoug Rabson {
52659747216SDoug Rabson struct agp_softc *sc = device_get_softc(dev);
52759747216SDoug Rabson
52859747216SDoug Rabson if (mem->am_is_bound)
52959747216SDoug Rabson return EBUSY;
53059747216SDoug Rabson
53159747216SDoug Rabson sc->as_allocated -= mem->am_size;
53259747216SDoug Rabson TAILQ_REMOVE(&sc->as_memory, mem, am_link);
53359747216SDoug Rabson vm_object_deallocate(mem->am_obj);
53459747216SDoug Rabson free(mem, M_AGP);
53559747216SDoug Rabson return 0;
53659747216SDoug Rabson }
53759747216SDoug Rabson
53859747216SDoug Rabson int
agp_generic_bind_memory(device_t dev,struct agp_memory * mem,vm_offset_t offset)53959747216SDoug Rabson agp_generic_bind_memory(device_t dev, struct agp_memory *mem,
54059747216SDoug Rabson vm_offset_t offset)
54159747216SDoug Rabson {
54259747216SDoug Rabson struct agp_softc *sc = device_get_softc(dev);
54359747216SDoug Rabson vm_offset_t i, j, k;
54459747216SDoug Rabson vm_page_t m;
54559747216SDoug Rabson int error;
54659747216SDoug Rabson
547eb325974SMaxime Henrion /* Do some sanity checks first. */
54845d0290aSRobert Noland if ((offset & (AGP_PAGE_SIZE - 1)) != 0 ||
549eb325974SMaxime Henrion offset + mem->am_size > AGP_GET_APERTURE(dev)) {
55059747216SDoug Rabson device_printf(dev, "binding memory at bad offset %#x\n",
55159747216SDoug Rabson (int)offset);
55259747216SDoug Rabson return EINVAL;
55359747216SDoug Rabson }
55459747216SDoug Rabson
55559747216SDoug Rabson /*
556eb325974SMaxime Henrion * Allocate the pages early, before acquiring the lock,
55783d5d296SKonstantin Belousov * because vm_page_grab() may sleep and we can't hold a mutex
55883d5d296SKonstantin Belousov * while sleeping.
55959747216SDoug Rabson */
56089f6b863SAttilio Rao VM_OBJECT_WLOCK(mem->am_obj);
56159747216SDoug Rabson for (i = 0; i < mem->am_size; i += PAGE_SIZE) {
56259747216SDoug Rabson /*
56359747216SDoug Rabson * Find a page from the object and wire it
56459747216SDoug Rabson * down. This page will be mapped using one or more
56559747216SDoug Rabson * entries in the GATT (assuming that PAGE_SIZE >=
56659747216SDoug Rabson * AGP_PAGE_SIZE. If this is the first call to bind,
56759747216SDoug Rabson * the pages will be allocated and zeroed.
56859747216SDoug Rabson */
56959747216SDoug Rabson m = vm_page_grab(mem->am_obj, OFF_TO_IDX(i),
5705944de8eSKonstantin Belousov VM_ALLOC_WIRED | VM_ALLOC_ZERO);
571c353491aSWojciech A. Koszek AGP_DPF("found page pa=%#jx\n", (uintmax_t)VM_PAGE_TO_PHYS(m));
572eb325974SMaxime Henrion }
57389f6b863SAttilio Rao VM_OBJECT_WUNLOCK(mem->am_obj);
574eb325974SMaxime Henrion
575eb325974SMaxime Henrion mtx_lock(&sc->as_lock);
576eb325974SMaxime Henrion
577eb325974SMaxime Henrion if (mem->am_is_bound) {
578eb325974SMaxime Henrion device_printf(dev, "memory already bound\n");
579eb325974SMaxime Henrion error = EINVAL;
58089f6b863SAttilio Rao VM_OBJECT_WLOCK(mem->am_obj);
581fc72031cSKonstantin Belousov i = 0;
582eb325974SMaxime Henrion goto bad;
583eb325974SMaxime Henrion }
584eb325974SMaxime Henrion
585eb325974SMaxime Henrion /*
586eb325974SMaxime Henrion * Bind the individual pages and flush the chipset's
587eb325974SMaxime Henrion * TLB.
588eb325974SMaxime Henrion */
58989f6b863SAttilio Rao VM_OBJECT_WLOCK(mem->am_obj);
59091dd9693SAlan Cox for (i = 0; i < mem->am_size; i += PAGE_SIZE) {
591eb325974SMaxime Henrion m = vm_page_lookup(mem->am_obj, OFF_TO_IDX(i));
59259747216SDoug Rabson
59359747216SDoug Rabson /*
59459747216SDoug Rabson * Install entries in the GATT, making sure that if
59559747216SDoug Rabson * AGP_PAGE_SIZE < PAGE_SIZE and mem->am_size is not
59659747216SDoug Rabson * aligned to PAGE_SIZE, we don't modify too many GATT
59759747216SDoug Rabson * entries.
59859747216SDoug Rabson */
59959747216SDoug Rabson for (j = 0; j < PAGE_SIZE && i + j < mem->am_size;
60059747216SDoug Rabson j += AGP_PAGE_SIZE) {
60159747216SDoug Rabson vm_offset_t pa = VM_PAGE_TO_PHYS(m) + j;
602c353491aSWojciech A. Koszek AGP_DPF("binding offset %#jx to pa %#jx\n",
603c353491aSWojciech A. Koszek (uintmax_t)offset + i + j, (uintmax_t)pa);
60459747216SDoug Rabson error = AGP_BIND_PAGE(dev, offset + i + j, pa);
60559747216SDoug Rabson if (error) {
60659747216SDoug Rabson /*
60759747216SDoug Rabson * Bail out. Reverse all the mappings
60859747216SDoug Rabson * and unwire the pages.
60959747216SDoug Rabson */
61059747216SDoug Rabson for (k = 0; k < i + j; k += AGP_PAGE_SIZE)
61159747216SDoug Rabson AGP_UNBIND_PAGE(dev, offset + k);
612eb325974SMaxime Henrion goto bad;
61359747216SDoug Rabson }
61459747216SDoug Rabson }
615c7aebda8SAttilio Rao vm_page_xunbusy(m);
61659747216SDoug Rabson }
61789f6b863SAttilio Rao VM_OBJECT_WUNLOCK(mem->am_obj);
61859747216SDoug Rabson
61959747216SDoug Rabson /*
62059747216SDoug Rabson * Make sure the chipset gets the new mappings.
62159747216SDoug Rabson */
62259747216SDoug Rabson AGP_FLUSH_TLB(dev);
62359747216SDoug Rabson
62459747216SDoug Rabson mem->am_offset = offset;
62559747216SDoug Rabson mem->am_is_bound = 1;
62659747216SDoug Rabson
627d8a821e8SMaxime Henrion mtx_unlock(&sc->as_lock);
62859747216SDoug Rabson
62959747216SDoug Rabson return 0;
630eb325974SMaxime Henrion bad:
631eb325974SMaxime Henrion mtx_unlock(&sc->as_lock);
63289f6b863SAttilio Rao VM_OBJECT_ASSERT_WLOCKED(mem->am_obj);
633fc72031cSKonstantin Belousov for (k = 0; k < mem->am_size; k += PAGE_SIZE) {
634fc72031cSKonstantin Belousov m = vm_page_lookup(mem->am_obj, OFF_TO_IDX(k));
635fc72031cSKonstantin Belousov if (k >= i)
636c7aebda8SAttilio Rao vm_page_xunbusy(m);
6373ae10f74SAttilio Rao vm_page_unwire(m, PQ_INACTIVE);
638eb325974SMaxime Henrion }
63989f6b863SAttilio Rao VM_OBJECT_WUNLOCK(mem->am_obj);
640eb325974SMaxime Henrion
641eb325974SMaxime Henrion return error;
64259747216SDoug Rabson }
64359747216SDoug Rabson
64459747216SDoug Rabson int
agp_generic_unbind_memory(device_t dev,struct agp_memory * mem)64559747216SDoug Rabson agp_generic_unbind_memory(device_t dev, struct agp_memory *mem)
64659747216SDoug Rabson {
64759747216SDoug Rabson struct agp_softc *sc = device_get_softc(dev);
64859747216SDoug Rabson vm_page_t m;
64959747216SDoug Rabson int i;
65059747216SDoug Rabson
651d8a821e8SMaxime Henrion mtx_lock(&sc->as_lock);
65259747216SDoug Rabson
65359747216SDoug Rabson if (!mem->am_is_bound) {
65459747216SDoug Rabson device_printf(dev, "memory is not bound\n");
655d8a821e8SMaxime Henrion mtx_unlock(&sc->as_lock);
65659747216SDoug Rabson return EINVAL;
65759747216SDoug Rabson }
65859747216SDoug Rabson
65959747216SDoug Rabson /*
66059747216SDoug Rabson * Unbind the individual pages and flush the chipset's
66159747216SDoug Rabson * TLB. Unwire the pages so they can be swapped.
66259747216SDoug Rabson */
66359747216SDoug Rabson for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE)
66459747216SDoug Rabson AGP_UNBIND_PAGE(dev, mem->am_offset + i);
6650de6ac2eSTijl Coosemans
6660de6ac2eSTijl Coosemans AGP_FLUSH_TLB(dev);
6670de6ac2eSTijl Coosemans
66889f6b863SAttilio Rao VM_OBJECT_WLOCK(mem->am_obj);
66959747216SDoug Rabson for (i = 0; i < mem->am_size; i += PAGE_SIZE) {
67059747216SDoug Rabson m = vm_page_lookup(mem->am_obj, atop(i));
6713ae10f74SAttilio Rao vm_page_unwire(m, PQ_INACTIVE);
67259747216SDoug Rabson }
67389f6b863SAttilio Rao VM_OBJECT_WUNLOCK(mem->am_obj);
67459747216SDoug Rabson
67559747216SDoug Rabson mem->am_offset = 0;
67659747216SDoug Rabson mem->am_is_bound = 0;
67759747216SDoug Rabson
678d8a821e8SMaxime Henrion mtx_unlock(&sc->as_lock);
67959747216SDoug Rabson
68059747216SDoug Rabson return 0;
68159747216SDoug Rabson }
68259747216SDoug Rabson
68359747216SDoug Rabson /* Helper functions for implementing user/kernel api */
68459747216SDoug Rabson
68559747216SDoug Rabson static int
agp_acquire_helper(device_t dev,enum agp_acquire_state state)68659747216SDoug Rabson agp_acquire_helper(device_t dev, enum agp_acquire_state state)
68759747216SDoug Rabson {
68859747216SDoug Rabson struct agp_softc *sc = device_get_softc(dev);
68959747216SDoug Rabson
69059747216SDoug Rabson if (sc->as_state != AGP_ACQUIRE_FREE)
69159747216SDoug Rabson return EBUSY;
69259747216SDoug Rabson sc->as_state = state;
69359747216SDoug Rabson
69459747216SDoug Rabson return 0;
69559747216SDoug Rabson }
69659747216SDoug Rabson
69759747216SDoug Rabson static int
agp_release_helper(device_t dev,enum agp_acquire_state state)69859747216SDoug Rabson agp_release_helper(device_t dev, enum agp_acquire_state state)
69959747216SDoug Rabson {
70059747216SDoug Rabson struct agp_softc *sc = device_get_softc(dev);
70159747216SDoug Rabson
70259747216SDoug Rabson if (sc->as_state == AGP_ACQUIRE_FREE)
70359747216SDoug Rabson return 0;
70459747216SDoug Rabson
70559747216SDoug Rabson if (sc->as_state != state)
70659747216SDoug Rabson return EBUSY;
70759747216SDoug Rabson
70859747216SDoug Rabson sc->as_state = AGP_ACQUIRE_FREE;
70959747216SDoug Rabson return 0;
71059747216SDoug Rabson }
71159747216SDoug Rabson
71259747216SDoug Rabson static struct agp_memory *
agp_find_memory(device_t dev,int id)71359747216SDoug Rabson agp_find_memory(device_t dev, int id)
71459747216SDoug Rabson {
71559747216SDoug Rabson struct agp_softc *sc = device_get_softc(dev);
71659747216SDoug Rabson struct agp_memory *mem;
71759747216SDoug Rabson
71859747216SDoug Rabson AGP_DPF("searching for memory block %d\n", id);
71959747216SDoug Rabson TAILQ_FOREACH(mem, &sc->as_memory, am_link) {
72059747216SDoug Rabson AGP_DPF("considering memory block %d\n", mem->am_id);
72159747216SDoug Rabson if (mem->am_id == id)
72259747216SDoug Rabson return mem;
72359747216SDoug Rabson }
72459747216SDoug Rabson return 0;
72559747216SDoug Rabson }
72659747216SDoug Rabson
72759747216SDoug Rabson /* Implementation of the userland ioctl api */
72859747216SDoug Rabson
72959747216SDoug Rabson static int
agp_info_user(device_t dev,agp_info * info)73059747216SDoug Rabson agp_info_user(device_t dev, agp_info *info)
73159747216SDoug Rabson {
73259747216SDoug Rabson struct agp_softc *sc = device_get_softc(dev);
73359747216SDoug Rabson
73459747216SDoug Rabson bzero(info, sizeof *info);
73559747216SDoug Rabson info->bridge_id = pci_get_devid(dev);
73659747216SDoug Rabson info->agp_mode =
73759747216SDoug Rabson pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4);
73850fd2a5bSNathan Whitehorn if (sc->as_aperture)
73959747216SDoug Rabson info->aper_base = rman_get_start(sc->as_aperture);
74050fd2a5bSNathan Whitehorn else
74150fd2a5bSNathan Whitehorn info->aper_base = 0;
74259747216SDoug Rabson info->aper_size = AGP_GET_APERTURE(dev) >> 20;
74359747216SDoug Rabson info->pg_total = info->pg_system = sc->as_maxmem >> AGP_PAGE_SHIFT;
74459747216SDoug Rabson info->pg_used = sc->as_allocated >> AGP_PAGE_SHIFT;
74559747216SDoug Rabson
74659747216SDoug Rabson return 0;
74759747216SDoug Rabson }
74859747216SDoug Rabson
74959747216SDoug Rabson static int
agp_setup_user(device_t dev,agp_setup * setup)75059747216SDoug Rabson agp_setup_user(device_t dev, agp_setup *setup)
75159747216SDoug Rabson {
75259747216SDoug Rabson return AGP_ENABLE(dev, setup->agp_mode);
75359747216SDoug Rabson }
75459747216SDoug Rabson
75559747216SDoug Rabson static int
agp_allocate_user(device_t dev,agp_allocate * alloc)75659747216SDoug Rabson agp_allocate_user(device_t dev, agp_allocate *alloc)
75759747216SDoug Rabson {
75859747216SDoug Rabson struct agp_memory *mem;
75959747216SDoug Rabson
76059747216SDoug Rabson mem = AGP_ALLOC_MEMORY(dev,
76159747216SDoug Rabson alloc->type,
76259747216SDoug Rabson alloc->pg_count << AGP_PAGE_SHIFT);
763e547d6fdSDoug Rabson if (mem) {
76459747216SDoug Rabson alloc->key = mem->am_id;
76559747216SDoug Rabson alloc->physical = mem->am_physical;
76659747216SDoug Rabson return 0;
767e547d6fdSDoug Rabson } else {
768e547d6fdSDoug Rabson return ENOMEM;
769e547d6fdSDoug Rabson }
77059747216SDoug Rabson }
77159747216SDoug Rabson
77259747216SDoug Rabson static int
agp_deallocate_user(device_t dev,int id)77359747216SDoug Rabson agp_deallocate_user(device_t dev, int id)
77459747216SDoug Rabson {
775c2ede4b3SMartin Blapp struct agp_memory *mem = agp_find_memory(dev, id);
77659747216SDoug Rabson
77759747216SDoug Rabson if (mem) {
77859747216SDoug Rabson AGP_FREE_MEMORY(dev, mem);
77959747216SDoug Rabson return 0;
78059747216SDoug Rabson } else {
78159747216SDoug Rabson return ENOENT;
78259747216SDoug Rabson }
78359747216SDoug Rabson }
78459747216SDoug Rabson
78559747216SDoug Rabson static int
agp_bind_user(device_t dev,agp_bind * bind)78659747216SDoug Rabson agp_bind_user(device_t dev, agp_bind *bind)
78759747216SDoug Rabson {
78859747216SDoug Rabson struct agp_memory *mem = agp_find_memory(dev, bind->key);
78959747216SDoug Rabson
79059747216SDoug Rabson if (!mem)
79159747216SDoug Rabson return ENOENT;
79259747216SDoug Rabson
79359747216SDoug Rabson return AGP_BIND_MEMORY(dev, mem, bind->pg_start << AGP_PAGE_SHIFT);
79459747216SDoug Rabson }
79559747216SDoug Rabson
79659747216SDoug Rabson static int
agp_unbind_user(device_t dev,agp_unbind * unbind)79759747216SDoug Rabson agp_unbind_user(device_t dev, agp_unbind *unbind)
79859747216SDoug Rabson {
79959747216SDoug Rabson struct agp_memory *mem = agp_find_memory(dev, unbind->key);
80059747216SDoug Rabson
80159747216SDoug Rabson if (!mem)
80259747216SDoug Rabson return ENOENT;
80359747216SDoug Rabson
80459747216SDoug Rabson return AGP_UNBIND_MEMORY(dev, mem);
80559747216SDoug Rabson }
80659747216SDoug Rabson
80759747216SDoug Rabson static int
agp_chipset_flush(device_t dev)80828d86329SKonstantin Belousov agp_chipset_flush(device_t dev)
80928d86329SKonstantin Belousov {
81028d86329SKonstantin Belousov
81128d86329SKonstantin Belousov return (AGP_CHIPSET_FLUSH(dev));
81228d86329SKonstantin Belousov }
81328d86329SKonstantin Belousov
81428d86329SKonstantin Belousov static int
agp_open(struct cdev * kdev,int oflags,int devtype,struct thread * td)81589c9c53dSPoul-Henning Kamp agp_open(struct cdev *kdev, int oflags, int devtype, struct thread *td)
81659747216SDoug Rabson {
817b7d9e67cSEd Schouten device_t dev = kdev->si_drv1;
81859747216SDoug Rabson struct agp_softc *sc = device_get_softc(dev);
81959747216SDoug Rabson
82059747216SDoug Rabson if (!sc->as_isopen) {
82159747216SDoug Rabson sc->as_isopen = 1;
82259747216SDoug Rabson device_busy(dev);
82359747216SDoug Rabson }
82459747216SDoug Rabson
82559747216SDoug Rabson return 0;
82659747216SDoug Rabson }
82759747216SDoug Rabson
82859747216SDoug Rabson static int
agp_close(struct cdev * kdev,int fflag,int devtype,struct thread * td)82989c9c53dSPoul-Henning Kamp agp_close(struct cdev *kdev, int fflag, int devtype, struct thread *td)
83059747216SDoug Rabson {
831b7d9e67cSEd Schouten device_t dev = kdev->si_drv1;
83259747216SDoug Rabson struct agp_softc *sc = device_get_softc(dev);
8333e0cee70SRuslan Ermilov struct agp_memory *mem;
83459747216SDoug Rabson
83559747216SDoug Rabson /*
83659747216SDoug Rabson * Clear the GATT and force release on last close
83759747216SDoug Rabson */
8384d24901aSPedro F. Giffuni while ((mem = TAILQ_FIRST(&sc->as_memory)) != NULL) {
8393e0cee70SRuslan Ermilov if (mem->am_is_bound)
8403e0cee70SRuslan Ermilov AGP_UNBIND_MEMORY(dev, mem);
8413e0cee70SRuslan Ermilov AGP_FREE_MEMORY(dev, mem);
8423e0cee70SRuslan Ermilov }
84359747216SDoug Rabson if (sc->as_state == AGP_ACQUIRE_USER)
84459747216SDoug Rabson agp_release_helper(dev, AGP_ACQUIRE_USER);
84559747216SDoug Rabson sc->as_isopen = 0;
84659747216SDoug Rabson device_unbusy(dev);
84759747216SDoug Rabson
84859747216SDoug Rabson return 0;
84959747216SDoug Rabson }
85059747216SDoug Rabson
85159747216SDoug Rabson static int
agp_ioctl(struct cdev * kdev,u_long cmd,caddr_t data,int fflag,struct thread * td)85289c9c53dSPoul-Henning Kamp agp_ioctl(struct cdev *kdev, u_long cmd, caddr_t data, int fflag, struct thread *td)
85359747216SDoug Rabson {
854b7d9e67cSEd Schouten device_t dev = kdev->si_drv1;
85559747216SDoug Rabson
85659747216SDoug Rabson switch (cmd) {
85759747216SDoug Rabson case AGPIOC_INFO:
85859747216SDoug Rabson return agp_info_user(dev, (agp_info *) data);
85959747216SDoug Rabson
86059747216SDoug Rabson case AGPIOC_ACQUIRE:
86159747216SDoug Rabson return agp_acquire_helper(dev, AGP_ACQUIRE_USER);
86259747216SDoug Rabson
86359747216SDoug Rabson case AGPIOC_RELEASE:
86459747216SDoug Rabson return agp_release_helper(dev, AGP_ACQUIRE_USER);
86559747216SDoug Rabson
86659747216SDoug Rabson case AGPIOC_SETUP:
86759747216SDoug Rabson return agp_setup_user(dev, (agp_setup *)data);
86859747216SDoug Rabson
86959747216SDoug Rabson case AGPIOC_ALLOCATE:
87059747216SDoug Rabson return agp_allocate_user(dev, (agp_allocate *)data);
87159747216SDoug Rabson
87259747216SDoug Rabson case AGPIOC_DEALLOCATE:
87359747216SDoug Rabson return agp_deallocate_user(dev, *(int *) data);
87459747216SDoug Rabson
87559747216SDoug Rabson case AGPIOC_BIND:
87659747216SDoug Rabson return agp_bind_user(dev, (agp_bind *)data);
87759747216SDoug Rabson
87859747216SDoug Rabson case AGPIOC_UNBIND:
87959747216SDoug Rabson return agp_unbind_user(dev, (agp_unbind *)data);
88059747216SDoug Rabson
88128d86329SKonstantin Belousov case AGPIOC_CHIPSET_FLUSH:
88228d86329SKonstantin Belousov return agp_chipset_flush(dev);
88359747216SDoug Rabson }
88459747216SDoug Rabson
88559747216SDoug Rabson return EINVAL;
88659747216SDoug Rabson }
88759747216SDoug Rabson
88859747216SDoug Rabson static int
agp_mmap(struct cdev * kdev,vm_ooffset_t offset,vm_paddr_t * paddr,int prot,vm_memattr_t * memattr)889cfd7baceSRobert Noland agp_mmap(struct cdev *kdev, vm_ooffset_t offset, vm_paddr_t *paddr,
890cfd7baceSRobert Noland int prot, vm_memattr_t *memattr)
89159747216SDoug Rabson {
892b7d9e67cSEd Schouten device_t dev = kdev->si_drv1;
89359747216SDoug Rabson struct agp_softc *sc = device_get_softc(dev);
89459747216SDoug Rabson
89559747216SDoug Rabson if (offset > AGP_GET_APERTURE(dev))
89659747216SDoug Rabson return -1;
89750fd2a5bSNathan Whitehorn if (sc->as_aperture == NULL)
89850fd2a5bSNathan Whitehorn return -1;
89907159f9cSMaxime Henrion *paddr = rman_get_start(sc->as_aperture) + offset;
90007159f9cSMaxime Henrion return 0;
90159747216SDoug Rabson }
90259747216SDoug Rabson
90359747216SDoug Rabson /* Implementation of the kernel api */
90459747216SDoug Rabson
90559747216SDoug Rabson device_t
agp_find_device(void)906c0817e2aSDimitry Andric agp_find_device(void)
90759747216SDoug Rabson {
9082971d7abSJohn Baldwin device_t *children, child;
909c8d81a41SJohn Baldwin int i, count;
910c8d81a41SJohn Baldwin
91159747216SDoug Rabson if (!agp_devclass)
912c8d81a41SJohn Baldwin return NULL;
913c8d81a41SJohn Baldwin if (devclass_get_devices(agp_devclass, &children, &count) != 0)
914c8d81a41SJohn Baldwin return NULL;
9152971d7abSJohn Baldwin child = NULL;
916c8d81a41SJohn Baldwin for (i = 0; i < count; i++) {
9172971d7abSJohn Baldwin if (device_is_attached(children[i])) {
9182971d7abSJohn Baldwin child = children[i];
9192971d7abSJohn Baldwin break;
920c8d81a41SJohn Baldwin }
9212971d7abSJohn Baldwin }
9222971d7abSJohn Baldwin free(children, M_TEMP);
9232971d7abSJohn Baldwin return child;
92459747216SDoug Rabson }
92559747216SDoug Rabson
92659747216SDoug Rabson enum agp_acquire_state
agp_state(device_t dev)92759747216SDoug Rabson agp_state(device_t dev)
92859747216SDoug Rabson {
92959747216SDoug Rabson struct agp_softc *sc = device_get_softc(dev);
93059747216SDoug Rabson return sc->as_state;
93159747216SDoug Rabson }
93259747216SDoug Rabson
93359747216SDoug Rabson void
agp_get_info(device_t dev,struct agp_info * info)93459747216SDoug Rabson agp_get_info(device_t dev, struct agp_info *info)
93559747216SDoug Rabson {
93659747216SDoug Rabson struct agp_softc *sc = device_get_softc(dev);
93759747216SDoug Rabson
93859747216SDoug Rabson info->ai_mode =
93959747216SDoug Rabson pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4);
94050fd2a5bSNathan Whitehorn if (sc->as_aperture != NULL)
94159747216SDoug Rabson info->ai_aperture_base = rman_get_start(sc->as_aperture);
94250fd2a5bSNathan Whitehorn else
94350fd2a5bSNathan Whitehorn info->ai_aperture_base = 0;
94450fd2a5bSNathan Whitehorn info->ai_aperture_size = AGP_GET_APERTURE(dev);
94559747216SDoug Rabson info->ai_memory_allowed = sc->as_maxmem;
94659747216SDoug Rabson info->ai_memory_used = sc->as_allocated;
94759747216SDoug Rabson }
94859747216SDoug Rabson
94959747216SDoug Rabson int
agp_acquire(device_t dev)95059747216SDoug Rabson agp_acquire(device_t dev)
95159747216SDoug Rabson {
95259747216SDoug Rabson return agp_acquire_helper(dev, AGP_ACQUIRE_KERNEL);
95359747216SDoug Rabson }
95459747216SDoug Rabson
95559747216SDoug Rabson int
agp_release(device_t dev)95659747216SDoug Rabson agp_release(device_t dev)
95759747216SDoug Rabson {
95859747216SDoug Rabson return agp_release_helper(dev, AGP_ACQUIRE_KERNEL);
95959747216SDoug Rabson }
96059747216SDoug Rabson
96159747216SDoug Rabson int
agp_enable(device_t dev,u_int32_t mode)96259747216SDoug Rabson agp_enable(device_t dev, u_int32_t mode)
96359747216SDoug Rabson {
96459747216SDoug Rabson return AGP_ENABLE(dev, mode);
96559747216SDoug Rabson }
96659747216SDoug Rabson
agp_alloc_memory(device_t dev,int type,vm_size_t bytes)96759747216SDoug Rabson void *agp_alloc_memory(device_t dev, int type, vm_size_t bytes)
96859747216SDoug Rabson {
96959747216SDoug Rabson return (void *) AGP_ALLOC_MEMORY(dev, type, bytes);
97059747216SDoug Rabson }
97159747216SDoug Rabson
agp_free_memory(device_t dev,void * handle)97259747216SDoug Rabson void agp_free_memory(device_t dev, void *handle)
97359747216SDoug Rabson {
97459747216SDoug Rabson struct agp_memory *mem = (struct agp_memory *) handle;
97559747216SDoug Rabson AGP_FREE_MEMORY(dev, mem);
97659747216SDoug Rabson }
97759747216SDoug Rabson
agp_bind_memory(device_t dev,void * handle,vm_offset_t offset)97859747216SDoug Rabson int agp_bind_memory(device_t dev, void *handle, vm_offset_t offset)
97959747216SDoug Rabson {
98059747216SDoug Rabson struct agp_memory *mem = (struct agp_memory *) handle;
98159747216SDoug Rabson return AGP_BIND_MEMORY(dev, mem, offset);
98259747216SDoug Rabson }
98359747216SDoug Rabson
agp_unbind_memory(device_t dev,void * handle)98459747216SDoug Rabson int agp_unbind_memory(device_t dev, void *handle)
98559747216SDoug Rabson {
98659747216SDoug Rabson struct agp_memory *mem = (struct agp_memory *) handle;
98759747216SDoug Rabson return AGP_UNBIND_MEMORY(dev, mem);
98859747216SDoug Rabson }
98959747216SDoug Rabson
agp_memory_info(device_t dev,void * handle,struct agp_memory_info * mi)99059747216SDoug Rabson void agp_memory_info(device_t dev, void *handle, struct
99159747216SDoug Rabson agp_memory_info *mi)
99259747216SDoug Rabson {
99359747216SDoug Rabson struct agp_memory *mem = (struct agp_memory *) handle;
99459747216SDoug Rabson
99559747216SDoug Rabson mi->ami_size = mem->am_size;
99659747216SDoug Rabson mi->ami_physical = mem->am_physical;
99759747216SDoug Rabson mi->ami_offset = mem->am_offset;
99859747216SDoug Rabson mi->ami_is_bound = mem->am_is_bound;
99959747216SDoug Rabson }
1000903fb143STijl Coosemans
1001903fb143STijl Coosemans int
agp_bind_pages(device_t dev,vm_page_t * pages,vm_size_t size,vm_offset_t offset)1002903fb143STijl Coosemans agp_bind_pages(device_t dev, vm_page_t *pages, vm_size_t size,
1003903fb143STijl Coosemans vm_offset_t offset)
1004903fb143STijl Coosemans {
1005903fb143STijl Coosemans struct agp_softc *sc;
1006903fb143STijl Coosemans vm_offset_t i, j, k, pa;
1007903fb143STijl Coosemans vm_page_t m;
1008903fb143STijl Coosemans int error;
1009903fb143STijl Coosemans
1010903fb143STijl Coosemans if ((size & (AGP_PAGE_SIZE - 1)) != 0 ||
1011903fb143STijl Coosemans (offset & (AGP_PAGE_SIZE - 1)) != 0)
1012903fb143STijl Coosemans return (EINVAL);
1013903fb143STijl Coosemans
1014903fb143STijl Coosemans sc = device_get_softc(dev);
1015903fb143STijl Coosemans
1016903fb143STijl Coosemans mtx_lock(&sc->as_lock);
1017903fb143STijl Coosemans for (i = 0; i < size; i += PAGE_SIZE) {
1018903fb143STijl Coosemans m = pages[OFF_TO_IDX(i)];
1019fee2a2faSMark Johnston KASSERT(vm_page_wired(m),
10200de6ac2eSTijl Coosemans ("agp_bind_pages: page %p hasn't been wired", m));
1021903fb143STijl Coosemans
1022903fb143STijl Coosemans /*
1023903fb143STijl Coosemans * Install entries in the GATT, making sure that if
1024903fb143STijl Coosemans * AGP_PAGE_SIZE < PAGE_SIZE and size is not
1025903fb143STijl Coosemans * aligned to PAGE_SIZE, we don't modify too many GATT
1026903fb143STijl Coosemans * entries.
1027903fb143STijl Coosemans */
1028903fb143STijl Coosemans for (j = 0; j < PAGE_SIZE && i + j < size; j += AGP_PAGE_SIZE) {
1029903fb143STijl Coosemans pa = VM_PAGE_TO_PHYS(m) + j;
1030903fb143STijl Coosemans AGP_DPF("binding offset %#jx to pa %#jx\n",
1031903fb143STijl Coosemans (uintmax_t)offset + i + j, (uintmax_t)pa);
1032903fb143STijl Coosemans error = AGP_BIND_PAGE(dev, offset + i + j, pa);
1033903fb143STijl Coosemans if (error) {
1034903fb143STijl Coosemans /*
1035903fb143STijl Coosemans * Bail out. Reverse all the mappings.
1036903fb143STijl Coosemans */
1037903fb143STijl Coosemans for (k = 0; k < i + j; k += AGP_PAGE_SIZE)
1038903fb143STijl Coosemans AGP_UNBIND_PAGE(dev, offset + k);
1039903fb143STijl Coosemans
1040903fb143STijl Coosemans mtx_unlock(&sc->as_lock);
1041903fb143STijl Coosemans return (error);
1042903fb143STijl Coosemans }
1043903fb143STijl Coosemans }
1044903fb143STijl Coosemans }
1045903fb143STijl Coosemans
1046903fb143STijl Coosemans AGP_FLUSH_TLB(dev);
1047903fb143STijl Coosemans
1048903fb143STijl Coosemans mtx_unlock(&sc->as_lock);
1049903fb143STijl Coosemans return (0);
1050903fb143STijl Coosemans }
1051903fb143STijl Coosemans
1052903fb143STijl Coosemans int
agp_unbind_pages(device_t dev,vm_size_t size,vm_offset_t offset)1053903fb143STijl Coosemans agp_unbind_pages(device_t dev, vm_size_t size, vm_offset_t offset)
1054903fb143STijl Coosemans {
1055903fb143STijl Coosemans struct agp_softc *sc;
1056903fb143STijl Coosemans vm_offset_t i;
1057903fb143STijl Coosemans
1058903fb143STijl Coosemans if ((size & (AGP_PAGE_SIZE - 1)) != 0 ||
1059903fb143STijl Coosemans (offset & (AGP_PAGE_SIZE - 1)) != 0)
1060903fb143STijl Coosemans return (EINVAL);
1061903fb143STijl Coosemans
1062903fb143STijl Coosemans sc = device_get_softc(dev);
1063903fb143STijl Coosemans
1064903fb143STijl Coosemans mtx_lock(&sc->as_lock);
1065903fb143STijl Coosemans for (i = 0; i < size; i += AGP_PAGE_SIZE)
1066903fb143STijl Coosemans AGP_UNBIND_PAGE(dev, offset + i);
1067903fb143STijl Coosemans
1068903fb143STijl Coosemans AGP_FLUSH_TLB(dev);
1069903fb143STijl Coosemans
1070903fb143STijl Coosemans mtx_unlock(&sc->as_lock);
1071903fb143STijl Coosemans return (0);
1072903fb143STijl Coosemans }
1073