tdfx_pci.c revision 111462
161931Scokane/*
274534Scokane * Copyright (c) 2000-2001 by Coleman Kane <cokane@FreeBSD.org>
361931Scokane * All rights reserved.
461931Scokane *
561931Scokane * Redistribution and use in source and binary forms, with or without
661931Scokane * modification, are permitted provided that the following conditions
761931Scokane * are met:
861931Scokane * 1. Redistributions of source code must retain the above copyright
961931Scokane *    notice, this list of conditions and the following disclaimer.
1061931Scokane * 2. Redistributions in binary form must reproduce the above copyright
1161931Scokane *    notice, this list of conditions and the following disclaimer in the
1261931Scokane *    documentation and/or other materials provided with the distribution.
1361931Scokane * 3. All advertising materials mentioning features or use of this software
1461931Scokane *    must display the following acknowledgement:
1561931Scokane *      This product includes software developed by Gardner Buchanan.
1661931Scokane * 4. The name of Gardner Buchanan may not be used to endorse or promote
1761931Scokane *    products derived from this software without specific prior written
1861931Scokane *    permission.
1961931Scokane *
2061931Scokane * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2161931Scokane * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2261931Scokane * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2361931Scokane * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2461931Scokane * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2561931Scokane * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2661931Scokane * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2761931Scokane * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2861931Scokane * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2961931Scokane * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3061931Scokane *
3161931Scokane *   $FreeBSD: head/sys/dev/tdfx/tdfx_pci.c 111462 2003-02-25 03:21:22Z mux $
3261931Scokane */
3361931Scokane
3461911Scokane/* 3dfx driver for FreeBSD 4.x - Finished 11 May 2000, 12:25AM ET
3561911Scokane *
3674534Scokane * Copyright (C) 2000-2001, by Coleman Kane <cokane@FreeBSD.org>,
3761911Scokane * based upon the 3dfx driver written for linux, by Daryll Straus, Jon Taylor,
3861911Scokane * and Jens Axboe, located at http://linux.3dfx.com.
3961911Scokane */
4061911Scokane
4161911Scokane#include <sys/param.h>
4261911Scokane
4361911Scokane#include <sys/bus.h>
4461911Scokane#include <sys/cdefs.h>
4561911Scokane#include <sys/conf.h>
4661911Scokane#include <sys/fcntl.h>
4761911Scokane#include <sys/file.h>
4861911Scokane#include <sys/filedesc.h>
4961911Scokane#include <sys/filio.h>
5061911Scokane#include <sys/ioccom.h>
5161911Scokane#include <sys/kernel.h>
5261911Scokane#include	<sys/malloc.h>
5361911Scokane#include <sys/mman.h>
5461911Scokane#include <sys/signalvar.h>
5561911Scokane#include <sys/systm.h>
5661911Scokane#include <sys/uio.h>
5761911Scokane
5861911Scokane#include <pci/pcivar.h>
5961911Scokane#include <pci/pcireg.h>
6061911Scokane
6161911Scokane#include <vm/vm.h>
6261911Scokane#include <vm/vm_kern.h>
6361911Scokane#include <vm/pmap.h>
6461911Scokane#include <vm/vm_extern.h>
6561911Scokane
6661911Scokane/* rman.h depends on machine/bus.h */
6761911Scokane#include <machine/resource.h>
6861911Scokane#include <machine/bus.h>
6961911Scokane#include <sys/rman.h>
7062028Scokane
7162028Scokane/* This must come first */
7262028Scokane#include "opt_tdfx.h"
7361931Scokane#ifdef TDFX_LINUX
7461931Scokane#include <dev/tdfx/tdfx_linux.h>
7561931Scokane#endif
7661911Scokane
7761911Scokane#include <dev/tdfx/tdfx_io.h>
7861911Scokane#include <dev/tdfx/tdfx_vars.h>
7961911Scokane#include <dev/tdfx/tdfx_pci.h>
8061911Scokane
8161911Scokane
8261911Scokanestatic devclass_t tdfx_devclass;
8361911Scokane
8461911Scokane
8561911Scokanestatic int tdfx_count = 0;
8661911Scokane
8761911Scokane
8861911Scokane/* Set up the boot probe/attach routines */
8961911Scokanestatic device_method_t tdfx_methods[] = {
9061911Scokane	DEVMETHOD(device_probe,		tdfx_probe),
9161911Scokane	DEVMETHOD(device_attach,	tdfx_attach),
9261911Scokane	DEVMETHOD(device_detach,	tdfx_detach),
9361911Scokane	DEVMETHOD(device_shutdown,	tdfx_shutdown),
9461911Scokane	{ 0, 0 }
9561911Scokane};
9661911Scokane
9761911ScokaneMALLOC_DEFINE(M_TDFX,"TDFX Driver","3DFX Graphics[/2D]/3D Accelerator(s)");
9861911Scokane
9961931Scokane#ifdef TDFX_LINUX
10061989ScokaneMODULE_DEPEND(tdfx, linux, 1, 1, 1);
10161931ScokaneLINUX_IOCTL_SET(tdfx, LINUX_IOCTL_TDFX_MIN, LINUX_IOCTL_TDFX_MAX);
10261931Scokane#endif
10361931Scokane
10461911Scokane/* Char. Dev. file operations structure */
10561911Scokanestatic struct cdevsw tdfx_cdev = {
10661911Scokane	tdfx_open,		/* open */
10761911Scokane	tdfx_close,		/* close */
10861911Scokane	noread,			/* read */
10961911Scokane	nowrite,			/* write */
11061911Scokane	tdfx_ioctl,		/* ioctl */
11161911Scokane	nopoll,			/* poll */
11261911Scokane	tdfx_mmap,		/* mmap */
11361911Scokane	nostrategy,		/* strategy */
11461911Scokane	"tdfx",			/* dev name */
11561911Scokane	CDEV_MAJOR, 	/* char major */
11661911Scokane	nodump,			/* dump */
11761911Scokane	nopsize,			/* size */
11861911Scokane	0,					/* flags (no set flags) */
11961911Scokane};
12061911Scokane
12161911Scokanestatic int
12261911Scokanetdfx_probe(device_t dev)
12361911Scokane{
12461911Scokane	/*
12561911Scokane	 * probe routine called on kernel boot to register supported devices. We get
12661911Scokane	 * a device structure to work with, and we can test the VENDOR/DEVICE IDs to
12761911Scokane	 * see if this PCI device is one that we support. Return 0 if yes, ENXIO if
12861911Scokane	 * not.
12961911Scokane	 */
13061911Scokane	switch(pci_get_devid(dev)) {
13161911Scokane	case PCI_DEVICE_ALLIANCE_AT3D:
13261911Scokane		device_set_desc(dev, "ProMotion At3D 3D Accelerator");
13361911Scokane		return 0;
13461911Scokane	case PCI_DEVICE_3DFX_VOODOO2:
13561911Scokane		device_set_desc(dev, "3DFX Voodoo II 3D Accelerator");
13661911Scokane		return 0;
13765146Scokane	/*case PCI_DEVICE_3DFX_BANSHEE:
13861911Scokane		device_set_desc(dev, "3DFX Voodoo Banshee 2D/3D Graphics Accelerator");
13961911Scokane		return 0;
14061911Scokane	case PCI_DEVICE_3DFX_VOODOO3:
14161911Scokane		device_set_desc(dev, "3DFX Voodoo3 2D/3D Graphics Accelerator");
14265146Scokane		return 0;*/
14361911Scokane	case PCI_DEVICE_3DFX_VOODOO1:
14461911Scokane		device_set_desc(dev, "3DFX Voodoo Graphics 3D Accelerator");
14561911Scokane		return 0;;
14661911Scokane	};
14761911Scokane
14861911Scokane	return ENXIO;
14961911Scokane}
15061911Scokane
15161911Scokanestatic int
15261911Scokanetdfx_attach(device_t dev) {
15361911Scokane	/*
15461911Scokane	 * The attach routine is called after the probe routine successfully says it
15561911Scokane	 * supports a given card. We now proceed to initialize this card for use with
15661911Scokane	 * the system. I want to map the device memory for userland allocation and
15761911Scokane	 * fill an information structure with information on this card. I'd also like
15861911Scokane	 * to set Write Combining with the MTRR code so that we can hopefully speed
15961911Scokane	 * up memory writes. The last thing is to register the character device
16061911Scokane	 * interface to the card, so we can open it from /dev/3dfxN, where N is a
16161911Scokane	 * small, whole number.
16261911Scokane	 */
16361911Scokane	struct tdfx_softc *tdfx_info;
16461911Scokane	u_long	val;
16561911Scokane	/* rid value tells bus_alloc_resource where to find the addresses of ports or
16661911Scokane	 * of memory ranges in the PCI config space*/
16761911Scokane	int rid = PCIR_MAPS;
16861911Scokane
16961911Scokane	/* Increment the card counter (for the ioctl code) */
17061911Scokane	tdfx_count++;
17161911Scokane
17261911Scokane 	/* Enable MemMap on Voodoo */
17361911Scokane	val = pci_read_config(dev, PCIR_COMMAND, 2);
17461911Scokane	val |= (PCIM_CMD_MEMEN);
17561911Scokane	pci_write_config(dev, PCIR_COMMAND, val, 2);
17661911Scokane	val = pci_read_config(dev, PCIR_COMMAND, 2);
17761911Scokane
17861911Scokane	/* Fill the soft config struct with info about this device*/
17961911Scokane	tdfx_info = device_get_softc(dev);
18061911Scokane	tdfx_info->dev = dev;
18161911Scokane	tdfx_info->vendor = pci_get_vendor(dev);
18261911Scokane	tdfx_info->type = pci_get_devid(dev) >> 16;
18361911Scokane	tdfx_info->bus = pci_get_bus(dev);
18461911Scokane	tdfx_info->dv = pci_get_slot(dev);
18561911Scokane	tdfx_info->curFile = NULL;
18661911Scokane
18761911Scokane	/*
18861911Scokane	 *	Get the Memory Location from the PCI Config, mask out lower word, since
18961911Scokane	 * the config space register is only one word long (this is nicer than a
19061911Scokane	 * bitshift).
19161911Scokane	 */
19261911Scokane	tdfx_info->addr0 = (pci_read_config(dev, 0x10, 4) & 0xffff0000);
19361931Scokane#ifdef DEBUG
19461911Scokane	device_printf(dev, "Base0 @ 0x%x\n", tdfx_info->addr0);
19561911Scokane#endif
19661911Scokane	/* Notify the VM that we will be mapping some memory later */
19761911Scokane	tdfx_info->memrange = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, ~0, 1,
19861967Scokane			RF_ACTIVE | RF_SHAREABLE);
19961911Scokane	if(tdfx_info->memrange == NULL) {
20061931Scokane#ifdef DEBUG
20161911Scokane		device_printf(dev, "Error mapping mem, won't be able to use mmap()\n");
20261911Scokane#endif
20361911Scokane		tdfx_info->memrid = 0;
20461911Scokane	}
20561911Scokane	else {
20661911Scokane		tdfx_info->memrid = rid;
20761931Scokane#ifdef DEBUG
20861911Scokane		device_printf(dev, "Mapped to: 0x%x\n",
20961911Scokane				(unsigned int)rman_get_start(tdfx_info->memrange));
21061911Scokane#endif
21161911Scokane	}
21261911Scokane
21363488Scokane	/* Setup for Voodoo3 and Banshee, PIO and an extram Memrange */
21463488Scokane	if(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3 ||
21563488Scokane		pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE) {
21664085Scokane		rid = 0x14;	/* 2nd mem map */
21763488Scokane		tdfx_info->addr1 = (pci_read_config(dev, 0x14, 4) & 0xffff0000);
21863488Scokane#ifdef DEBUG
21963488Scokane		device_printf(dev, "Base1 @ 0x%x\n", tdfx_info->addr1);
22063488Scokane#endif
22163488Scokane		tdfx_info->memrange2 = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
22263488Scokane			 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
22363488Scokane		if(tdfx_info->memrange2 == NULL) {
22463488Scokane#ifdef DEBUG
22563488Scokane			device_printf(dev, "Mem1 couldn't be allocated, glide may not work.");
22663488Scokane#endif
22763488Scokane			tdfx_info->memrid2 = 0;
22863488Scokane		}
22963488Scokane		else {
23063488Scokane			tdfx_info->memrid2 = rid;
23163488Scokane		}
23263488Scokane		/* Now to map the PIO stuff */
23365146Scokane		rid = PCIR_IOBASE0_2;
23465146Scokane		tdfx_info->pio0 = pci_read_config(dev, 0x2c, 2);
23565146Scokane		tdfx_info->pio0max = pci_read_config(dev, 0x30, 2) + tdfx_info->pio0;
23663488Scokane		tdfx_info->piorange = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
23763488Scokane			 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
23863488Scokane		if(tdfx_info->piorange == NULL) {
23963488Scokane#ifdef DEBUG
24063488Scokane			device_printf(dev, "Couldn't map PIO range.");
24163488Scokane#endif
24263488Scokane			tdfx_info->piorid = 0;
24363488Scokane		}
24463488Scokane		else {
24563488Scokane			tdfx_info->piorid = rid;
24665146Scokane		}
24763488Scokane	} else {
24863488Scokane	  tdfx_info->addr1 = 0;
24963488Scokane	  tdfx_info->memrange2 = NULL;
25063488Scokane	  tdfx_info->piorange = NULL;
25163488Scokane	}
25263488Scokane
25361911Scokane	/*
25461911Scokane	 *	Set Writecombining, or at least Uncacheable for the memory region, if we
25561911Scokane	 * are able to
25661911Scokane	 */
25761911Scokane
25861911Scokane	if(tdfx_setmtrr(dev) != 0) {
25961931Scokane#ifdef DEBUG
26061911Scokane		device_printf(dev, "Some weird error setting MTRRs");
26161911Scokane#endif
26261911Scokane		return -1;
26361911Scokane	}
26463488Scokane
26561911Scokane	/*
26661911Scokane	 * make_dev registers the cdev to access the 3dfx card from /dev
26761911Scokane	 *	use hex here for the dev num, simply to provide better support if > 10
26861911Scokane	 * voodoo cards, for the mad. The user must set the link, or use MAKEDEV.
26961911Scokane	 * Why would we want that many voodoo cards anyhow?
27061911Scokane	 */
271104111Sphk	tdfx_info->devt = make_dev(&tdfx_cdev, device_get_unit(dev),
272108323Srwatson		UID_ROOT, GID_WHEEL, 0600, "3dfx%x", device_get_unit(dev));
27361911Scokane
27461911Scokane	return 0;
27561911Scokane}
27661911Scokane
27761911Scokanestatic int
27861911Scokanetdfx_detach(device_t dev) {
27961911Scokane	struct tdfx_softc* tdfx_info;
28061911Scokane	int retval;
28161911Scokane	tdfx_info = device_get_softc(dev);
28261911Scokane
28361911Scokane	/* Delete allocated resource, of course */
28463488Scokane	bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid,
28561911Scokane			tdfx_info->memrange);
28663488Scokane
28763488Scokane	/* Release extended Voodoo3/Banshee resources */
28863488Scokane	if(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE ||
28963488Scokane			pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) {
29063488Scokane		if(tdfx_info->memrange2 != NULL)
29163488Scokane			bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid2,
29263488Scokane				tdfx_info->memrange);
29364085Scokane	/*	if(tdfx_info->piorange != NULL)
29463488Scokane			bus_release_resource(dev, SYS_RES_IOPORT, tdfx_info->piorid,
29564085Scokane				tdfx_info->piorange);*/
29663488Scokane	}
29763488Scokane
29861911Scokane	/* Though it is safe to leave the WRCOMB support since the
29961911Scokane		mem driver checks for it, we should remove it in order
30061911Scokane		to free an MTRR for another device */
30161911Scokane	retval = tdfx_clrmtrr(dev);
30261931Scokane#ifdef DEBUG
30361911Scokane	if(retval != 0)
30461911Scokane		printf("tdfx: For some reason, I couldn't clear the mtrr\n");
30561911Scokane#endif
30661989Scokane	/* Remove device entry when it can no longer be accessed */
30761989Scokane   destroy_dev(tdfx_info->devt);
30861911Scokane	return(0);
30961911Scokane}
31061911Scokane
31161911Scokanestatic int
31261911Scokanetdfx_shutdown(device_t dev) {
31361931Scokane#ifdef DEBUG
31461911Scokane	device_printf(dev, "tdfx: Device Shutdown\n");
31561911Scokane#endif
31661911Scokane	return 0;
31761911Scokane}
31861911Scokane
31961911Scokanestatic int
32061911Scokanetdfx_clrmtrr(device_t dev) {
32161911Scokane	/* This function removes the MTRR set by the attach call, so it can be used
32261911Scokane	 * in the future by other drivers.
32361911Scokane	 */
32461911Scokane	int retval, act;
32561911Scokane	struct tdfx_softc *tdfx_info = device_get_softc(dev);
32661911Scokane
32761911Scokane	act = MEMRANGE_SET_REMOVE;
32861911Scokane	retval = mem_range_attr_set(&tdfx_info->mrdesc, &act);
32961911Scokane	return retval;
33061911Scokane}
33161911Scokane
33261911Scokanestatic int
33361911Scokanetdfx_setmtrr(device_t dev) {
33461911Scokane	/*
33561911Scokane	 * This is the MTRR setting function for the 3dfx card. It is called from
33661911Scokane	 * tdfx_attach. If we can't set the MTRR properly, it's not the end of the
33761911Scokane	 * world. We can still continue, just with slightly (very slightly) degraded
33861911Scokane	 * performance.
33961911Scokane	 */
34061911Scokane	int retval = 0, act;
34161911Scokane	struct tdfx_softc *tdfx_info = device_get_softc(dev);
34261911Scokane
34361911Scokane	/* The older Voodoo cards have a shorter memrange than the newer ones */
34461911Scokane	if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1) || (pci_get_devid(dev) ==
34563488Scokane			PCI_DEVICE_3DFX_VOODOO2)) {
34661911Scokane		tdfx_info->mrdesc.mr_len = 0x400000;
34763488Scokane
34863488Scokane		/* The memory descriptor is described as the top 15 bits of the real
34963488Scokane			address */
35063488Scokane		tdfx_info->mrdesc.mr_base = tdfx_info->addr0 & 0xfffe0000;
35163488Scokane	}
35261911Scokane	else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) ||
35363488Scokane			(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE)) {
35461911Scokane		tdfx_info->mrdesc.mr_len = 0x1000000;
35563488Scokane		/* The Voodoo3 and Banshee LFB is the second memory address */
35663488Scokane		/* The memory descriptor is described as the top 15 bits of the real
35763488Scokane			address */
35863488Scokane		tdfx_info->mrdesc.mr_base = tdfx_info->addr1 & 0xfffe0000;
35963488Scokane	}
36063488Scokane	else
36163488Scokane		 return 0;
36261911Scokane	/*
36361911Scokane    *	The Alliance Pro Motion AT3D was not mentioned in the linux
36461911Scokane	 * driver as far as MTRR support goes, so I just won't put the
36561911Scokane	 * code in here for it. This is where it should go, though.
36661911Scokane	 */
36761911Scokane
36861911Scokane	/* Firstly, try to set write combining */
36961911Scokane	tdfx_info->mrdesc.mr_flags = MDF_WRITECOMBINE;
37061911Scokane	bcopy("tdfx", &tdfx_info->mrdesc.mr_owner, 4);
37161911Scokane	act = MEMRANGE_SET_UPDATE;
37261911Scokane	retval = mem_range_attr_set(&tdfx_info->mrdesc, &act);
37361911Scokane
37461911Scokane	if(retval == 0) {
37561931Scokane#ifdef DEBUG
37661911Scokane		device_printf(dev, "MTRR Set Correctly for tdfx\n");
37761911Scokane#endif
37861911Scokane	} else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO2) ||
37961911Scokane		(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1)) {
38061911Scokane		/* if, for some reason we can't set the WRCOMB range with the V1/V2, we
38161911Scokane		 * can still possibly use the UNCACHEABLE region for it instead, and help
38261911Scokane		 * out in a small way */
38361911Scokane		tdfx_info->mrdesc.mr_flags = MDF_UNCACHEABLE;
38461911Scokane		/* This length of 1000h was taken from the linux device driver... */
38561911Scokane		tdfx_info->mrdesc.mr_len = 0x1000;
38661911Scokane
38761911Scokane		/*
38861911Scokane		 * If, for some reason, we can't set the MTRR (N/A?) we may still continue
38961911Scokane		 */
39061931Scokane#ifdef DEBUG
39161911Scokane		if(retval == 0) {
39295092Smarcel			device_printf(dev, "MTRR Set Type Uncacheable %x\n",
39395092Smarcel			    (u_int32_t)tdfx_info->mrdesc.mr_base);
39461911Scokane		} else {
39561911Scokane			device_printf(dev, "Couldn't Set MTRR\n");
39661911Scokane		}
39761911Scokane#endif
39861911Scokane	}
39961931Scokane#ifdef DEBUG
40061911Scokane	else {
40161911Scokane		device_printf(dev, "Couldn't Set MTRR\n");
40261911Scokane		return 0;
40361911Scokane	}
40461911Scokane#endif
40561911Scokane	return 0;
40661911Scokane}
40761911Scokane
40861911Scokanestatic int
40983366Sjuliantdfx_open(dev_t dev, int flags, int fmt, struct thread *td)
41061911Scokane{
41161911Scokane	/*
41261911Scokane	 *	The open cdev method handles open(2) calls to /dev/3dfx[n]
41361911Scokane	 * We can pretty much allow any opening of the device.
41461911Scokane	 */
41561911Scokane	struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass,
41661911Scokane			UNIT(minor(dev)));
41761911Scokane	if(tdfx_info->busy != 0) return EBUSY;
41861931Scokane#ifdef	DEBUG
41983366Sjulian	printf("3dfx: Opened by #%d\n", td->td_proc->p_pid);
42061911Scokane#endif
42161911Scokane	/* Set the driver as busy */
42261911Scokane	tdfx_info->busy++;
42361911Scokane	return 0;
42461911Scokane}
42561911Scokane
42661911Scokanestatic int
42783366Sjuliantdfx_close(dev_t dev, int fflag, int devtype, struct thread *td)
42861911Scokane{
42961911Scokane	/*
43061911Scokane	 *	The close cdev method handles close(2) calls to /dev/3dfx[n]
43161911Scokane	 * We'll always want to close the device when it's called.
43261911Scokane	 */
43361911Scokane	struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass,
43461911Scokane		UNIT(minor(dev)));
43561911Scokane	if(tdfx_info->busy == 0) return EBADF;
43661911Scokane	tdfx_info->busy = 0;
43761931Scokane#ifdef	DEBUG
43883366Sjulian	printf("Closed by #%d\n", td->td_proc->p_pid);
43961911Scokane#endif
44061911Scokane	return 0;
44161911Scokane}
44261911Scokane
44361911Scokanestatic int
444111462Smuxtdfx_mmap(dev_t dev, vm_offset_t offset, vm_offset_t *paddr, int nprot)
44561911Scokane{
44661911Scokane	/*
44761911Scokane	 * mmap(2) is called by a user process to request that an area of memory
44861911Scokane	 * associated with this device be mapped for the process to work with. Nprot
44961911Scokane	 * holds the protections requested, PROT_READ, PROT_WRITE, or both.
45061911Scokane	 */
45166910Scokane
45266910Scokane	/**** OLD GET CONFIG ****/
45366910Scokane	/* struct tdfx_softc* tdfx_info; */
45461911Scokane
45561911Scokane	/* Get the configuration for our card XXX*/
45666910Scokane	/*tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
45766910Scokane			UNIT(minor(dev)));*/
45866910Scokane	/************************/
45966910Scokane
46066910Scokane	struct tdfx_softc* tdfx_info[2];
46161911Scokane
46266910Scokane	tdfx_info[0] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 0);
46366910Scokane
46461911Scokane	/* If, for some reason, its not configured, we bail out */
46566910Scokane	if(tdfx_info[0] == NULL) {
46661931Scokane#ifdef	DEBUG
46761911Scokane	   printf("tdfx: tdfx_info (softc) is NULL\n");
46861911Scokane#endif
46961911Scokane	   return -1;
47061911Scokane	}
47166910Scokane
47261911Scokane	/* We must stay within the bound of our address space */
47366910Scokane	if((offset & 0xff000000) == tdfx_info[0]->addr0) {
47461911Scokane		offset &= 0xffffff;
475111462Smux		*paddr = rman_get_start(tdfx_info[0]->memrange) + offset;
476111462Smux		return 0;
47766910Scokane	}
47866910Scokane
47966910Scokane	if(tdfx_count > 1) {
48066910Scokane		tdfx_info[1] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 1);
48166910Scokane		if((offset & 0xff000000) == tdfx_info[1]->addr0) {
48266910Scokane			offset &= 0xffffff;
483111462Smux			*paddr = rman_get_start(tdfx_info[1]->memrange) +
484111462Smux			    offset;
485111462Smux			return 0;
48666910Scokane		}
48766910Scokane	}
48863488Scokane
48963488Scokane	/* See if the Banshee/V3 LFB is being requested */
49066910Scokane	/*if(tdfx_info->memrange2 != NULL && (offset & 0xff000000) ==
49164085Scokane			tdfx_info->addr1) {
49263488Scokane	  	offset &= 0xffffff;
49366910Scokane		return atop(rman_get_start(tdfx_info[1]->memrange2) + offset);
49466910Scokane	}*/ /* VoodooNG code */
49563488Scokane
49666910Scokane	/* The ret call */
49761911Scokane	/* atop -> address to page
49861911Scokane	 * rman_get_start, get the (struct resource*)->r_start member,
49961911Scokane	 * the mapping base address.
50061911Scokane	 */
50166910Scokane	return -1;
50261911Scokane}
50361911Scokane
50461911Scokanestatic int
50561911Scokanetdfx_query_boards(void) {
50661911Scokane	/*
50761911Scokane    *	This returns the number of installed tdfx cards, we have been keeping
50861911Scokane	 * count, look at tdfx_attach
50961911Scokane	 */
51061911Scokane	return tdfx_count;
51161911Scokane}
51261911Scokane
51361911Scokanestatic int
51461911Scokanetdfx_query_fetch(u_int cmd, struct tdfx_pio_data *piod)
51561911Scokane{
51661911Scokane	/* XXX Comment this later, after careful inspection and spring cleaning :) */
51761911Scokane	/* Various return values 8bit-32bit */
51861911Scokane	u_int8_t  ret_byte;
51961911Scokane	u_int16_t ret_word;
52061911Scokane	u_int32_t ret_dword;
52161911Scokane	struct tdfx_softc* tdfx_info = NULL;
52261911Scokane
52361911Scokane	/* This one depend on the tdfx_* structs being properly initialized */
52461911Scokane
52561911Scokane	/*piod->device &= 0xf;*/
52661911Scokane	if((piod == NULL) ||(tdfx_count <= piod->device) ||
52761911Scokane			(piod->device < 0)) {
52861931Scokane#ifdef DEBUG
52961911Scokane		printf("tdfx: Bad device or internal struct in tdfx_query_fetch\n");
53061911Scokane#endif
53161911Scokane		return -EINVAL;
53261911Scokane	}
53361911Scokane
53461911Scokane	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
53561911Scokane			piod->device);
53661911Scokane
53761911Scokane	if(tdfx_info == NULL) return -ENXIO;
53861911Scokane
53961911Scokane	/* We must restrict the size reads from the port, since to high or low of a
54061911Scokane	 * size witll result in wrong data being passed, and that's bad */
54161911Scokane	/* A few of these were pulled during the attach phase */
54261911Scokane	switch(piod->port) {
54361911Scokane		case PCI_VENDOR_ID_FREEBSD:
54461911Scokane			if(piod->size != 2) return -EINVAL;
54561911Scokane			copyout(&tdfx_info->vendor, piod->value, piod->size);
54661911Scokane			return 0;
54761911Scokane		case PCI_DEVICE_ID_FREEBSD:
54861911Scokane			if(piod->size != 2) return -EINVAL;
54961911Scokane			copyout(&tdfx_info->type, piod->value, piod->size);
55061911Scokane			return 0;
55161911Scokane		case PCI_BASE_ADDRESS_0_FREEBSD:
55261911Scokane			if(piod->size != 4) return -EINVAL;
55361911Scokane			copyout(&tdfx_info->addr0, piod->value, piod->size);
55461911Scokane			return 0;
55565146Scokane		case PCI_BASE_ADDRESS_1_FREEBSD:
55665146Scokane			if(piod->size != 4) return -EINVAL;
55765146Scokane			copyout(&tdfx_info->addr1, piod->value, piod->size);
55865146Scokane			return 0;
55965146Scokane		case PCI_PRIBUS_FREEBSD:
56065146Scokane			if(piod->size != 1) return -EINVAL;
56165146Scokane			break;
56265146Scokane		case PCI_IOBASE_0_FREEBSD:
56365146Scokane			if(piod->size != 2) return -EINVAL;
56465146Scokane			break;
56565146Scokane		case PCI_IOLIMIT_0_FREEBSD:
56665146Scokane			if(piod->size != 2) return -EINVAL;
56765146Scokane			break;
56861911Scokane		case SST1_PCI_SPECIAL1_FREEBSD:
56961911Scokane			if(piod->size != 4) return -EINVAL;
57061911Scokane			break;
57161911Scokane		case PCI_REVISION_ID_FREEBSD:
57261911Scokane			if(piod->size != 1) return -EINVAL;
57361911Scokane			break;
57461911Scokane		case SST1_PCI_SPECIAL4_FREEBSD:
57561911Scokane			if(piod->size != 4) return -EINVAL;
57661911Scokane			break;
57761911Scokane		default:
57861911Scokane			return -EINVAL;
57961911Scokane	}
58061911Scokane
58161911Scokane
58261911Scokane	/* Read the value and return */
58361911Scokane	switch(piod->size) {
58461911Scokane		case 1:
58561911Scokane			ret_byte = pci_read_config(tdfx_info[piod->device].dev,
58661911Scokane					piod->port, 1);
58761911Scokane			copyout(&ret_byte, piod->value, 1);
58861911Scokane			break;
58961911Scokane		case 2:
59061911Scokane			ret_word = pci_read_config(tdfx_info[piod->device].dev,
59161911Scokane					piod->port, 2);
59261911Scokane			copyout(&ret_word, piod->value, 2);
59361911Scokane			break;
59461911Scokane		case 4:
59561911Scokane			ret_dword = pci_read_config(tdfx_info[piod->device].dev,
59661911Scokane					piod->port, 4);
59761911Scokane			copyout(&ret_dword, piod->value, 4);
59861911Scokane			break;
59961911Scokane		default:
60061911Scokane			return -EINVAL;
60161911Scokane	}
60261911Scokane	return 0;
60361911Scokane}
60461911Scokane
60561911Scokanestatic int
60661911Scokanetdfx_query_update(u_int cmd, struct tdfx_pio_data *piod)
60761911Scokane{
60861911Scokane	/* XXX Comment this later, after careful inspection and spring cleaning :) */
60961911Scokane	/* Return vals */
61061911Scokane	u_int8_t  ret_byte;
61161911Scokane	u_int16_t ret_word;
61261911Scokane	u_int32_t ret_dword;
61361911Scokane
61461911Scokane	/* Port vals, mask */
61561911Scokane	u_int32_t retval, preval, mask;
61661911Scokane	struct tdfx_softc* tdfx_info = NULL;
61761911Scokane
61861911Scokane
61961911Scokane	if((piod == NULL) || (piod->device >= (tdfx_count &
62061911Scokane					0xf))) {
62161931Scokane#ifdef DEBUG
62261911Scokane		printf("tdfx: Bad struct or device in tdfx_query_update\n");
62361911Scokane#endif
62461911Scokane		return -EINVAL;
62561911Scokane	}
62661911Scokane
62761911Scokane	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
62861911Scokane			piod->device);
62961911Scokane	if(tdfx_info == NULL) return -ENXIO;
63061911Scokane	/* Code below this line in the fuction was taken from the
63161911Scokane	 * Linux driver and converted for freebsd. */
63261911Scokane
63361911Scokane	/* Check the size for all the ports, to make sure stuff doesn't get messed up
63461911Scokane	 * by poorly written clients */
63561911Scokane
63661911Scokane	switch(piod->port) {
63761911Scokane		case PCI_COMMAND_FREEBSD:
63861911Scokane			if(piod->size != 2) return -EINVAL;
63961911Scokane			break;
64061911Scokane		case SST1_PCI_SPECIAL1_FREEBSD:
64161911Scokane			if(piod->size != 4) return -EINVAL;
64261911Scokane			break;
64361911Scokane		case SST1_PCI_SPECIAL2_FREEBSD:
64461911Scokane			if(piod->size != 4) return -EINVAL;
64561911Scokane			break;
64661911Scokane		case SST1_PCI_SPECIAL3_FREEBSD:
64761911Scokane			if(piod->size != 4) return -EINVAL;
64861911Scokane			break;
64961911Scokane		case SST1_PCI_SPECIAL4_FREEBSD:
65061911Scokane			if(piod->size != 4) return -EINVAL;
65161911Scokane			break;
65261911Scokane		default:
65361911Scokane			return -EINVAL;
65461911Scokane	}
65561911Scokane	/* Read the current value */
65661911Scokane	retval = pci_read_config(tdfx_info->dev, piod->port & ~3, 4);
65761911Scokane
65861911Scokane	/* These set up a mask to use, since apparently they wanted to write 4 bytes
65961911Scokane	 * at once to the ports */
66061911Scokane	switch (piod->size) {
66161911Scokane		case 1:
66261911Scokane			copyin(piod->value, &ret_byte, 1);
66361911Scokane			preval = ret_byte << (8 * (piod->port & 0x3));
66461911Scokane			mask = 0xff << (8 * (piod->port & 0x3));
66561911Scokane			break;
66661911Scokane		case 2:
66761911Scokane			copyin(piod->value, &ret_word, 2);
66861911Scokane			preval = ret_word << (8 * (piod->port & 0x3));
66961911Scokane			mask = 0xffff << (8 * (piod->port & 0x3));
67061911Scokane			break;
67161911Scokane		case 4:
67261911Scokane			copyin(piod->value, &ret_dword, 4);
67361911Scokane			preval = ret_dword;
67461911Scokane			mask = ~0;
67561911Scokane			break;
67661911Scokane		default:
67761911Scokane			return -EINVAL;
67861911Scokane	}
67961911Scokane	/* Finally, combine the values and write it to the port */
68061911Scokane	retval = (retval & ~mask) | preval;
68161911Scokane	pci_write_config(tdfx_info->dev, piod->port & ~3, retval, 4);
68261911Scokane
68361911Scokane	return 0;
68461911Scokane}
68561911Scokane
68663488Scokane/* For both of these, I added a variable named workport of type u_int so
68763488Scokane * that I could eliminate the warning about my data type size. The
68863488Scokane * applications expect the port to be of type short, so I needed to change
68963488Scokane * this within the function */
69061911Scokanestatic int
69161911Scokanetdfx_do_pio_rd(struct tdfx_pio_data *piod)
69261911Scokane{
69361911Scokane	/* Return val */
69461911Scokane	u_int8_t  ret_byte;
69563488Scokane	u_int 	 workport;
69665146Scokane	struct tdfx_softc *tdfx_info =
69765146Scokane		(struct tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device);
69865146Scokane
69961911Scokane	/* Restricts the access of ports other than those we use */
70065146Scokane	if(((piod->port != VGA_INPUT_STATUS_1C) || (piod->port != SC_INDEX) ||
70165146Scokane		(piod->port != SC_DATA) || (piod->port != VGA_MISC_OUTPUT_READ)) &&
70265146Scokane		(piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max))
70361911Scokane		return -EPERM;
70461911Scokane
70561911Scokane	/* All VGA STATUS REGS are byte registers, size should never be > 1 */
70661911Scokane	if(piod->size != 1) {
70761911Scokane		return -EINVAL;
70861911Scokane	}
70961911Scokane
71061911Scokane	/* Write the data to the intended port */
71163488Scokane	workport = piod->port;
71263488Scokane	ret_byte = inb(workport);
71361911Scokane	copyout(&ret_byte, piod->value, sizeof(u_int8_t));
71461911Scokane	return 0;
71561911Scokane}
71661911Scokane
71761911Scokanestatic int
71861911Scokanetdfx_do_pio_wt(struct tdfx_pio_data *piod)
71961911Scokane{
72061911Scokane	/* return val */
72161911Scokane	u_int8_t  ret_byte;
72263488Scokane	u_int		 workport;
72365146Scokane	struct tdfx_softc *tdfx_info = (struct
72465146Scokane			tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device);
72561911Scokane	/* Replace old switch w/ massive if(...) */
72661911Scokane	/* Restricts the access of ports other than those we use */
72765146Scokane	if(((piod->port != SC_INDEX) && (piod->port != SC_DATA) &&
72865146Scokane		(piod->port != VGA_MISC_OUTPUT_READ)) /* Can't write VGA_ST_1C */ &&
72965146Scokane		(piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max))
73061911Scokane		return -EPERM;
73161911Scokane
73261911Scokane	/* All VGA STATUS REGS are byte registers, size should never be > 1 */
73361911Scokane	if(piod->size != 1) {
73461911Scokane		return -EINVAL;
73561911Scokane	}
73661911Scokane
73761911Scokane	/* Write the data to the intended port */
73861911Scokane	copyin(piod->value, &ret_byte, sizeof(u_int8_t));
73963488Scokane	workport = piod->port;
74063488Scokane	outb(workport, ret_byte);
74161911Scokane	return 0;
74261911Scokane}
74361911Scokane
74461911Scokanestatic int
74561911Scokanetdfx_do_query(u_int cmd, struct tdfx_pio_data *piod)
74661911Scokane{
74761911Scokane	/* There are three sub-commands to the query 0x33 */
74861911Scokane	switch(_IOC_NR(cmd)) {
74961911Scokane		case 2:
75061911Scokane			return tdfx_query_boards();
75161911Scokane			break;
75261911Scokane		case 3:
75361911Scokane			return tdfx_query_fetch(cmd, piod);
75461911Scokane			break;
75561911Scokane		case 4:
75661911Scokane			return tdfx_query_update(cmd, piod);
75761911Scokane			break;
75861911Scokane		default:
75961911Scokane			/* In case we are thrown a bogus sub-command! */
76061931Scokane#ifdef DEBUG
76161911Scokane			printf("Bad Sub-cmd: 0x%x\n", _IOC_NR(cmd));
76261911Scokane#endif
76361911Scokane			return -EINVAL;
76461911Scokane	};
76561911Scokane}
76661911Scokane
76761911Scokanestatic int
76861911Scokanetdfx_do_pio(u_int cmd, struct tdfx_pio_data *piod)
76961911Scokane{
77061911Scokane	/* Two types of PIO, INPUT and OUTPUT, as the name suggests */
77161911Scokane	switch(_IOC_DIR(cmd)) {
77261911Scokane		case IOCV_OUT:
77361911Scokane			return tdfx_do_pio_rd(piod);
77461911Scokane			break;
77561911Scokane		case IOCV_IN:
77661911Scokane			return tdfx_do_pio_wt(piod);
77761911Scokane			break;
77861911Scokane		default:
77961911Scokane			return -EINVAL;
78061911Scokane	};
78161911Scokane}
78261911Scokane
78361911Scokane/* Calls to ioctl(2) eventually end up here. Unhandled ioctls return an ENXIO,
78461911Scokane * normally, you would read in the data pointed to by data, then write your
78561911Scokane * output to it. The ioctl *should* normally return zero if everything is
78661911Scokane * alright, but 3dfx didn't make it that way...
78761911Scokane *
78861911Scokane * For all of the ioctl code, in the event of a real error,
78961911Scokane * we return -Exxxx rather than simply Exxxx. The reason for this
79061911Scokane * is that the ioctls actually RET information back to the program
79161911Scokane * sometimes, rather than filling it in the passed structure. We
79261911Scokane * want to distinguish errors from useful data, and maintain compatibility.
79361911Scokane *
79461911Scokane * There is this portion of the proc struct called p_retval[], we can store a
79583366Sjulian * return value in td->td_retval[0] and place the return value if it is positive
79661911Scokane * in there, then we can return 0 (good). If the return value is negative, we
79761911Scokane * can return -retval and the error should be properly handled.
79861911Scokane */
79961911Scokanestatic int
80083366Sjuliantdfx_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
80161911Scokane{
80261911Scokane	int retval = 0;
80361911Scokane	struct tdfx_pio_data *piod = (struct tdfx_pio_data*)data;
80461931Scokane#ifdef	DEBUG
805106578Sjhb	printf("IOCTL'd by #%d, cmd: 0x%x, data: %p\n", td->td_proc->p_pid, (u_int32_t)cmd,
806106578Sjhb			piod);
80761911Scokane#endif
80861911Scokane	switch(_IOC_TYPE(cmd)) {
80961911Scokane		/* Return the real error if negative, or simply stick the valid return
81083366Sjulian		 * in td->td_retval */
81161911Scokane	case 0x33:
81261911Scokane			/* The '3'(0x33) type IOCTL is for querying the installed cards */
81383366Sjulian			if((retval = tdfx_do_query(cmd, piod)) > 0) td->td_retval[0] = retval;
81461911Scokane			else return -retval;
81561911Scokane			break;
81661911Scokane		case 0:
81761911Scokane			/* The 0 type IOCTL is for programmed I/O methods */
81883366Sjulian			if((tdfx_do_pio(cmd, piod)) > 0) td->td_retval[0] = retval;
81961911Scokane			else return -retval;
82061911Scokane			break;
82161911Scokane		default:
82261911Scokane			/* Technically, we won't reach this from linux emu, but when glide
82361911Scokane			 * finally gets ported, watch out! */
82461931Scokane#ifdef DEBUG
82583366Sjulian			printf("Bad IOCTL from #%d\n", td->td_proc->p_pid);
82661911Scokane#endif
82761911Scokane			return ENXIO;
82861911Scokane	}
82961911Scokane
83061911Scokane	return 0;
83161911Scokane}
83261911Scokane
83361931Scokane#ifdef TDFX_LINUX
83461931Scokane/*
83561931Scokane * Linux emulation IOCTL for /dev/tdfx
83661931Scokane */
83761931Scokanestatic int
83883366Sjulianlinux_ioctl_tdfx(struct thread *td, struct linux_ioctl_args* args)
83961931Scokane{
84061931Scokane   int error = 0;
84161931Scokane   u_long cmd = args->cmd & 0xffff;
84261911Scokane
84361931Scokane   /* The structure passed to ioctl has two shorts, one int
84461931Scokane      and one void*. */
84561931Scokane   char d_pio[2*sizeof(short) + sizeof(int) + sizeof(void*)];
84661931Scokane
84789306Salfred   struct file *fp;
84861931Scokane
84989319Salfred   if ((error = fget(td, args->fd, &fp)) != 0)
85089319Salfred	   return (error);
85161931Scokane   /* We simply copy the data and send it right to ioctl */
85261931Scokane   copyin((caddr_t)args->arg, &d_pio, sizeof(d_pio));
853102003Srwatson   error = fo_ioctl(fp, cmd, (caddr_t)&d_pio, td->td_ucred, td);
85489306Salfred   fdrop(fp, td);
85561931Scokane   return error;
85661931Scokane}
85761931Scokane#endif /* TDFX_LINUX */
85861931Scokane
85961931Scokane
86061911Scokane/* This is the device driver struct. This is sent to the driver subsystem to
86161911Scokane * register the method structure and the info strcut space for this particular
86261911Scokane * instance of the driver.
86361911Scokane */
86461911Scokanestatic driver_t tdfx_driver = {
86561911Scokane	"tdfx",
86661911Scokane	tdfx_methods,
86761911Scokane	sizeof(struct tdfx_softc),
86861911Scokane};
86961911Scokane
87061911Scokane/* Tell Mr. Kernel about us! */
87161911ScokaneDRIVER_MODULE(tdfx, pci, tdfx_driver, tdfx_devclass, 0, 0);
872