tdfx_pci.c revision 129879
1119418Sobrien/*-
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
32119418Sobrien#include <sys/cdefs.h>
33119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/tdfx/tdfx_pci.c 129879 2004-05-30 20:08:47Z phk $");
34119418Sobrien
3561911Scokane/* 3dfx driver for FreeBSD 4.x - Finished 11 May 2000, 12:25AM ET
3661911Scokane *
3774534Scokane * Copyright (C) 2000-2001, by Coleman Kane <cokane@FreeBSD.org>,
3861911Scokane * based upon the 3dfx driver written for linux, by Daryll Straus, Jon Taylor,
3961911Scokane * and Jens Axboe, located at http://linux.3dfx.com.
4061911Scokane */
4161911Scokane
4261911Scokane#include <sys/param.h>
4361911Scokane
4461911Scokane#include <sys/bus.h>
4561911Scokane#include <sys/cdefs.h>
4661911Scokane#include <sys/conf.h>
4761911Scokane#include <sys/fcntl.h>
4861911Scokane#include <sys/file.h>
4961911Scokane#include <sys/filedesc.h>
5061911Scokane#include <sys/filio.h>
5161911Scokane#include <sys/ioccom.h>
5261911Scokane#include <sys/kernel.h>
53129879Sphk#include <sys/module.h>
54129879Sphk#include <sys/malloc.h>
5561911Scokane#include <sys/mman.h>
5661911Scokane#include <sys/signalvar.h>
5761911Scokane#include <sys/systm.h>
5861911Scokane#include <sys/uio.h>
5961911Scokane
60119287Simp#include <dev/pci/pcivar.h>
61119287Simp#include <dev/pci/pcireg.h>
6261911Scokane
6361911Scokane#include <vm/vm.h>
6461911Scokane#include <vm/vm_kern.h>
6561911Scokane#include <vm/pmap.h>
6661911Scokane#include <vm/vm_extern.h>
6761911Scokane
6861911Scokane/* rman.h depends on machine/bus.h */
6961911Scokane#include <machine/resource.h>
7061911Scokane#include <machine/bus.h>
7161911Scokane#include <sys/rman.h>
7262028Scokane
7362028Scokane/* This must come first */
7462028Scokane#include "opt_tdfx.h"
7561931Scokane#ifdef TDFX_LINUX
7661931Scokane#include <dev/tdfx/tdfx_linux.h>
7761931Scokane#endif
7861911Scokane
7961911Scokane#include <dev/tdfx/tdfx_io.h>
8061911Scokane#include <dev/tdfx/tdfx_vars.h>
8161911Scokane#include <dev/tdfx/tdfx_pci.h>
8261911Scokane
8361911Scokane
8461911Scokanestatic devclass_t tdfx_devclass;
8561911Scokane
8661911Scokane
8761911Scokanestatic int tdfx_count = 0;
8861911Scokane
8961911Scokane
9061911Scokane/* Set up the boot probe/attach routines */
9161911Scokanestatic device_method_t tdfx_methods[] = {
9261911Scokane	DEVMETHOD(device_probe,		tdfx_probe),
9361911Scokane	DEVMETHOD(device_attach,	tdfx_attach),
9461911Scokane	DEVMETHOD(device_detach,	tdfx_detach),
9561911Scokane	DEVMETHOD(device_shutdown,	tdfx_shutdown),
9661911Scokane	{ 0, 0 }
9761911Scokane};
9861911Scokane
9961911ScokaneMALLOC_DEFINE(M_TDFX,"TDFX Driver","3DFX Graphics[/2D]/3D Accelerator(s)");
10061911Scokane
10161931Scokane#ifdef TDFX_LINUX
10261989ScokaneMODULE_DEPEND(tdfx, linux, 1, 1, 1);
10361931ScokaneLINUX_IOCTL_SET(tdfx, LINUX_IOCTL_TDFX_MIN, LINUX_IOCTL_TDFX_MAX);
10461931Scokane#endif
10561931Scokane
10661911Scokane/* Char. Dev. file operations structure */
10761911Scokanestatic struct cdevsw tdfx_cdev = {
108126080Sphk	.d_version =	D_VERSION,
109126080Sphk	.d_flags =	D_NEEDGIANT,
110111815Sphk	.d_open =	tdfx_open,
111111815Sphk	.d_close =	tdfx_close,
112111815Sphk	.d_ioctl =	tdfx_ioctl,
113111815Sphk	.d_mmap =	tdfx_mmap,
114111815Sphk	.d_name =	"tdfx",
11561911Scokane};
11661911Scokane
11761911Scokanestatic int
11861911Scokanetdfx_probe(device_t dev)
11961911Scokane{
12061911Scokane	/*
12161911Scokane	 * probe routine called on kernel boot to register supported devices. We get
12261911Scokane	 * a device structure to work with, and we can test the VENDOR/DEVICE IDs to
12361911Scokane	 * see if this PCI device is one that we support. Return 0 if yes, ENXIO if
12461911Scokane	 * not.
12561911Scokane	 */
12661911Scokane	switch(pci_get_devid(dev)) {
12761911Scokane	case PCI_DEVICE_ALLIANCE_AT3D:
12861911Scokane		device_set_desc(dev, "ProMotion At3D 3D Accelerator");
12961911Scokane		return 0;
13061911Scokane	case PCI_DEVICE_3DFX_VOODOO2:
13161911Scokane		device_set_desc(dev, "3DFX Voodoo II 3D Accelerator");
13261911Scokane		return 0;
13365146Scokane	/*case PCI_DEVICE_3DFX_BANSHEE:
13461911Scokane		device_set_desc(dev, "3DFX Voodoo Banshee 2D/3D Graphics Accelerator");
13561911Scokane		return 0;
13661911Scokane	case PCI_DEVICE_3DFX_VOODOO3:
13761911Scokane		device_set_desc(dev, "3DFX Voodoo3 2D/3D Graphics Accelerator");
13865146Scokane		return 0;*/
13961911Scokane	case PCI_DEVICE_3DFX_VOODOO1:
14061911Scokane		device_set_desc(dev, "3DFX Voodoo Graphics 3D Accelerator");
14161911Scokane		return 0;;
14261911Scokane	};
14361911Scokane
14461911Scokane	return ENXIO;
14561911Scokane}
14661911Scokane
14761911Scokanestatic int
14861911Scokanetdfx_attach(device_t dev) {
14961911Scokane	/*
15061911Scokane	 * The attach routine is called after the probe routine successfully says it
15161911Scokane	 * supports a given card. We now proceed to initialize this card for use with
15261911Scokane	 * the system. I want to map the device memory for userland allocation and
15361911Scokane	 * fill an information structure with information on this card. I'd also like
15461911Scokane	 * to set Write Combining with the MTRR code so that we can hopefully speed
15561911Scokane	 * up memory writes. The last thing is to register the character device
15661911Scokane	 * interface to the card, so we can open it from /dev/3dfxN, where N is a
15761911Scokane	 * small, whole number.
15861911Scokane	 */
15961911Scokane	struct tdfx_softc *tdfx_info;
16061911Scokane	u_long	val;
16161911Scokane	/* rid value tells bus_alloc_resource where to find the addresses of ports or
16261911Scokane	 * of memory ranges in the PCI config space*/
163119690Sjhb	int rid = PCIR_BAR(0);
16461911Scokane
16561911Scokane	/* Increment the card counter (for the ioctl code) */
16661911Scokane	tdfx_count++;
16761911Scokane
16861911Scokane 	/* Enable MemMap on Voodoo */
16961911Scokane	val = pci_read_config(dev, PCIR_COMMAND, 2);
17061911Scokane	val |= (PCIM_CMD_MEMEN);
17161911Scokane	pci_write_config(dev, PCIR_COMMAND, val, 2);
17261911Scokane	val = pci_read_config(dev, PCIR_COMMAND, 2);
17361911Scokane
17461911Scokane	/* Fill the soft config struct with info about this device*/
17561911Scokane	tdfx_info = device_get_softc(dev);
17661911Scokane	tdfx_info->dev = dev;
17761911Scokane	tdfx_info->vendor = pci_get_vendor(dev);
17861911Scokane	tdfx_info->type = pci_get_devid(dev) >> 16;
17961911Scokane	tdfx_info->bus = pci_get_bus(dev);
18061911Scokane	tdfx_info->dv = pci_get_slot(dev);
18161911Scokane	tdfx_info->curFile = NULL;
18261911Scokane
18361911Scokane	/*
18461911Scokane	 *	Get the Memory Location from the PCI Config, mask out lower word, since
18561911Scokane	 * the config space register is only one word long (this is nicer than a
18661911Scokane	 * bitshift).
18761911Scokane	 */
18861911Scokane	tdfx_info->addr0 = (pci_read_config(dev, 0x10, 4) & 0xffff0000);
18961931Scokane#ifdef DEBUG
19061911Scokane	device_printf(dev, "Base0 @ 0x%x\n", tdfx_info->addr0);
19161911Scokane#endif
19261911Scokane	/* Notify the VM that we will be mapping some memory later */
193127135Snjl	tdfx_info->memrange = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
194127135Snjl		&rid, RF_ACTIVE | RF_SHAREABLE);
19561911Scokane	if(tdfx_info->memrange == NULL) {
19661931Scokane#ifdef DEBUG
19761911Scokane		device_printf(dev, "Error mapping mem, won't be able to use mmap()\n");
19861911Scokane#endif
19961911Scokane		tdfx_info->memrid = 0;
20061911Scokane	}
20161911Scokane	else {
20261911Scokane		tdfx_info->memrid = rid;
20361931Scokane#ifdef DEBUG
20461911Scokane		device_printf(dev, "Mapped to: 0x%x\n",
20561911Scokane				(unsigned int)rman_get_start(tdfx_info->memrange));
20661911Scokane#endif
20761911Scokane	}
20861911Scokane
20963488Scokane	/* Setup for Voodoo3 and Banshee, PIO and an extram Memrange */
21063488Scokane	if(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3 ||
21163488Scokane		pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE) {
21264085Scokane		rid = 0x14;	/* 2nd mem map */
21363488Scokane		tdfx_info->addr1 = (pci_read_config(dev, 0x14, 4) & 0xffff0000);
21463488Scokane#ifdef DEBUG
21563488Scokane		device_printf(dev, "Base1 @ 0x%x\n", tdfx_info->addr1);
21663488Scokane#endif
217127135Snjl		tdfx_info->memrange2 = bus_alloc_resource_any(dev,
218127135Snjl			SYS_RES_MEMORY, &rid, RF_ACTIVE | RF_SHAREABLE);
21963488Scokane		if(tdfx_info->memrange2 == NULL) {
22063488Scokane#ifdef DEBUG
22163488Scokane			device_printf(dev, "Mem1 couldn't be allocated, glide may not work.");
22263488Scokane#endif
22363488Scokane			tdfx_info->memrid2 = 0;
22463488Scokane		}
22563488Scokane		else {
22663488Scokane			tdfx_info->memrid2 = rid;
22763488Scokane		}
22863488Scokane		/* Now to map the PIO stuff */
22965146Scokane		rid = PCIR_IOBASE0_2;
23065146Scokane		tdfx_info->pio0 = pci_read_config(dev, 0x2c, 2);
23165146Scokane		tdfx_info->pio0max = pci_read_config(dev, 0x30, 2) + tdfx_info->pio0;
232127135Snjl		tdfx_info->piorange = bus_alloc_resource_any(dev,
233127135Snjl			SYS_RES_IOPORT, &rid, RF_ACTIVE | RF_SHAREABLE);
23463488Scokane		if(tdfx_info->piorange == NULL) {
23563488Scokane#ifdef DEBUG
23663488Scokane			device_printf(dev, "Couldn't map PIO range.");
23763488Scokane#endif
23863488Scokane			tdfx_info->piorid = 0;
23963488Scokane		}
24063488Scokane		else {
24163488Scokane			tdfx_info->piorid = rid;
24265146Scokane		}
24363488Scokane	} else {
24463488Scokane	  tdfx_info->addr1 = 0;
24563488Scokane	  tdfx_info->memrange2 = NULL;
24663488Scokane	  tdfx_info->piorange = NULL;
24763488Scokane	}
24863488Scokane
24961911Scokane	/*
25061911Scokane	 *	Set Writecombining, or at least Uncacheable for the memory region, if we
25161911Scokane	 * are able to
25261911Scokane	 */
25361911Scokane
25461911Scokane	if(tdfx_setmtrr(dev) != 0) {
25561931Scokane#ifdef DEBUG
25661911Scokane		device_printf(dev, "Some weird error setting MTRRs");
25761911Scokane#endif
25861911Scokane		return -1;
25961911Scokane	}
26063488Scokane
26161911Scokane	/*
26261911Scokane	 * make_dev registers the cdev to access the 3dfx card from /dev
26361911Scokane	 *	use hex here for the dev num, simply to provide better support if > 10
26461911Scokane	 * voodoo cards, for the mad. The user must set the link, or use MAKEDEV.
26561911Scokane	 * Why would we want that many voodoo cards anyhow?
26661911Scokane	 */
267104111Sphk	tdfx_info->devt = make_dev(&tdfx_cdev, device_get_unit(dev),
268108323Srwatson		UID_ROOT, GID_WHEEL, 0600, "3dfx%x", device_get_unit(dev));
26961911Scokane
27061911Scokane	return 0;
27161911Scokane}
27261911Scokane
27361911Scokanestatic int
27461911Scokanetdfx_detach(device_t dev) {
27561911Scokane	struct tdfx_softc* tdfx_info;
27661911Scokane	int retval;
27761911Scokane	tdfx_info = device_get_softc(dev);
27861911Scokane
27961911Scokane	/* Delete allocated resource, of course */
28063488Scokane	bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid,
28161911Scokane			tdfx_info->memrange);
28263488Scokane
28363488Scokane	/* Release extended Voodoo3/Banshee resources */
28463488Scokane	if(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE ||
28563488Scokane			pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) {
28663488Scokane		if(tdfx_info->memrange2 != NULL)
28763488Scokane			bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid2,
28863488Scokane				tdfx_info->memrange);
28964085Scokane	/*	if(tdfx_info->piorange != NULL)
29063488Scokane			bus_release_resource(dev, SYS_RES_IOPORT, tdfx_info->piorid,
29164085Scokane				tdfx_info->piorange);*/
29263488Scokane	}
29363488Scokane
29461911Scokane	/* Though it is safe to leave the WRCOMB support since the
29561911Scokane		mem driver checks for it, we should remove it in order
29661911Scokane		to free an MTRR for another device */
29761911Scokane	retval = tdfx_clrmtrr(dev);
29861931Scokane#ifdef DEBUG
29961911Scokane	if(retval != 0)
30061911Scokane		printf("tdfx: For some reason, I couldn't clear the mtrr\n");
30161911Scokane#endif
30261989Scokane	/* Remove device entry when it can no longer be accessed */
30361989Scokane   destroy_dev(tdfx_info->devt);
30461911Scokane	return(0);
30561911Scokane}
30661911Scokane
30761911Scokanestatic int
30861911Scokanetdfx_shutdown(device_t dev) {
30961931Scokane#ifdef DEBUG
31061911Scokane	device_printf(dev, "tdfx: Device Shutdown\n");
31161911Scokane#endif
31261911Scokane	return 0;
31361911Scokane}
31461911Scokane
31561911Scokanestatic int
31661911Scokanetdfx_clrmtrr(device_t dev) {
31761911Scokane	/* This function removes the MTRR set by the attach call, so it can be used
31861911Scokane	 * in the future by other drivers.
31961911Scokane	 */
32061911Scokane	int retval, act;
32161911Scokane	struct tdfx_softc *tdfx_info = device_get_softc(dev);
32261911Scokane
32361911Scokane	act = MEMRANGE_SET_REMOVE;
32461911Scokane	retval = mem_range_attr_set(&tdfx_info->mrdesc, &act);
32561911Scokane	return retval;
32661911Scokane}
32761911Scokane
32861911Scokanestatic int
32961911Scokanetdfx_setmtrr(device_t dev) {
33061911Scokane	/*
33161911Scokane	 * This is the MTRR setting function for the 3dfx card. It is called from
33261911Scokane	 * tdfx_attach. If we can't set the MTRR properly, it's not the end of the
33361911Scokane	 * world. We can still continue, just with slightly (very slightly) degraded
33461911Scokane	 * performance.
33561911Scokane	 */
33661911Scokane	int retval = 0, act;
33761911Scokane	struct tdfx_softc *tdfx_info = device_get_softc(dev);
33861911Scokane
33961911Scokane	/* The older Voodoo cards have a shorter memrange than the newer ones */
34061911Scokane	if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1) || (pci_get_devid(dev) ==
34163488Scokane			PCI_DEVICE_3DFX_VOODOO2)) {
34261911Scokane		tdfx_info->mrdesc.mr_len = 0x400000;
34363488Scokane
34463488Scokane		/* The memory descriptor is described as the top 15 bits of the real
34563488Scokane			address */
34663488Scokane		tdfx_info->mrdesc.mr_base = tdfx_info->addr0 & 0xfffe0000;
34763488Scokane	}
34861911Scokane	else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) ||
34963488Scokane			(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE)) {
35061911Scokane		tdfx_info->mrdesc.mr_len = 0x1000000;
35163488Scokane		/* The Voodoo3 and Banshee LFB is the second memory address */
35263488Scokane		/* The memory descriptor is described as the top 15 bits of the real
35363488Scokane			address */
35463488Scokane		tdfx_info->mrdesc.mr_base = tdfx_info->addr1 & 0xfffe0000;
35563488Scokane	}
35663488Scokane	else
35763488Scokane		 return 0;
35861911Scokane	/*
35961911Scokane    *	The Alliance Pro Motion AT3D was not mentioned in the linux
36061911Scokane	 * driver as far as MTRR support goes, so I just won't put the
36161911Scokane	 * code in here for it. This is where it should go, though.
36261911Scokane	 */
36361911Scokane
36461911Scokane	/* Firstly, try to set write combining */
36561911Scokane	tdfx_info->mrdesc.mr_flags = MDF_WRITECOMBINE;
36661911Scokane	bcopy("tdfx", &tdfx_info->mrdesc.mr_owner, 4);
36761911Scokane	act = MEMRANGE_SET_UPDATE;
36861911Scokane	retval = mem_range_attr_set(&tdfx_info->mrdesc, &act);
36961911Scokane
37061911Scokane	if(retval == 0) {
37161931Scokane#ifdef DEBUG
37261911Scokane		device_printf(dev, "MTRR Set Correctly for tdfx\n");
37361911Scokane#endif
37461911Scokane	} else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO2) ||
37561911Scokane		(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1)) {
37661911Scokane		/* if, for some reason we can't set the WRCOMB range with the V1/V2, we
37761911Scokane		 * can still possibly use the UNCACHEABLE region for it instead, and help
37861911Scokane		 * out in a small way */
37961911Scokane		tdfx_info->mrdesc.mr_flags = MDF_UNCACHEABLE;
38061911Scokane		/* This length of 1000h was taken from the linux device driver... */
38161911Scokane		tdfx_info->mrdesc.mr_len = 0x1000;
38261911Scokane
38361911Scokane		/*
38461911Scokane		 * If, for some reason, we can't set the MTRR (N/A?) we may still continue
38561911Scokane		 */
38661931Scokane#ifdef DEBUG
38761911Scokane		if(retval == 0) {
38895092Smarcel			device_printf(dev, "MTRR Set Type Uncacheable %x\n",
38995092Smarcel			    (u_int32_t)tdfx_info->mrdesc.mr_base);
39061911Scokane		} else {
39161911Scokane			device_printf(dev, "Couldn't Set MTRR\n");
39261911Scokane		}
39361911Scokane#endif
39461911Scokane	}
39561931Scokane#ifdef DEBUG
39661911Scokane	else {
39761911Scokane		device_printf(dev, "Couldn't Set MTRR\n");
39861911Scokane		return 0;
39961911Scokane	}
40061911Scokane#endif
40161911Scokane	return 0;
40261911Scokane}
40361911Scokane
40461911Scokanestatic int
40583366Sjuliantdfx_open(dev_t dev, int flags, int fmt, struct thread *td)
40661911Scokane{
40761911Scokane	/*
40861911Scokane	 *	The open cdev method handles open(2) calls to /dev/3dfx[n]
40961911Scokane	 * We can pretty much allow any opening of the device.
41061911Scokane	 */
41161911Scokane	struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass,
41261911Scokane			UNIT(minor(dev)));
41361911Scokane	if(tdfx_info->busy != 0) return EBUSY;
41461931Scokane#ifdef	DEBUG
41583366Sjulian	printf("3dfx: Opened by #%d\n", td->td_proc->p_pid);
41661911Scokane#endif
41761911Scokane	/* Set the driver as busy */
41861911Scokane	tdfx_info->busy++;
41961911Scokane	return 0;
42061911Scokane}
42161911Scokane
42261911Scokanestatic int
42383366Sjuliantdfx_close(dev_t dev, int fflag, int devtype, struct thread *td)
42461911Scokane{
42561911Scokane	/*
42661911Scokane	 *	The close cdev method handles close(2) calls to /dev/3dfx[n]
42761911Scokane	 * We'll always want to close the device when it's called.
42861911Scokane	 */
42961911Scokane	struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass,
43061911Scokane		UNIT(minor(dev)));
43161911Scokane	if(tdfx_info->busy == 0) return EBADF;
43261911Scokane	tdfx_info->busy = 0;
43361931Scokane#ifdef	DEBUG
43483366Sjulian	printf("Closed by #%d\n", td->td_proc->p_pid);
43561911Scokane#endif
43661911Scokane	return 0;
43761911Scokane}
43861911Scokane
43961911Scokanestatic int
440112569Sjaketdfx_mmap(dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
44161911Scokane{
44261911Scokane	/*
44361911Scokane	 * mmap(2) is called by a user process to request that an area of memory
44461911Scokane	 * associated with this device be mapped for the process to work with. Nprot
44561911Scokane	 * holds the protections requested, PROT_READ, PROT_WRITE, or both.
44661911Scokane	 */
44766910Scokane
44866910Scokane	/**** OLD GET CONFIG ****/
44966910Scokane	/* struct tdfx_softc* tdfx_info; */
45061911Scokane
45161911Scokane	/* Get the configuration for our card XXX*/
45266910Scokane	/*tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
45366910Scokane			UNIT(minor(dev)));*/
45466910Scokane	/************************/
45566910Scokane
45666910Scokane	struct tdfx_softc* tdfx_info[2];
45761911Scokane
45866910Scokane	tdfx_info[0] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 0);
45966910Scokane
46061911Scokane	/* If, for some reason, its not configured, we bail out */
46166910Scokane	if(tdfx_info[0] == NULL) {
46261931Scokane#ifdef	DEBUG
46361911Scokane	   printf("tdfx: tdfx_info (softc) is NULL\n");
46461911Scokane#endif
46561911Scokane	   return -1;
46661911Scokane	}
46766910Scokane
46861911Scokane	/* We must stay within the bound of our address space */
46966910Scokane	if((offset & 0xff000000) == tdfx_info[0]->addr0) {
47061911Scokane		offset &= 0xffffff;
471111462Smux		*paddr = rman_get_start(tdfx_info[0]->memrange) + offset;
472111462Smux		return 0;
47366910Scokane	}
47466910Scokane
47566910Scokane	if(tdfx_count > 1) {
47666910Scokane		tdfx_info[1] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 1);
47766910Scokane		if((offset & 0xff000000) == tdfx_info[1]->addr0) {
47866910Scokane			offset &= 0xffffff;
479111462Smux			*paddr = rman_get_start(tdfx_info[1]->memrange) +
480111462Smux			    offset;
481111462Smux			return 0;
48266910Scokane		}
48366910Scokane	}
48463488Scokane
48563488Scokane	/* See if the Banshee/V3 LFB is being requested */
48666910Scokane	/*if(tdfx_info->memrange2 != NULL && (offset & 0xff000000) ==
48764085Scokane			tdfx_info->addr1) {
48863488Scokane	  	offset &= 0xffffff;
48966910Scokane		return atop(rman_get_start(tdfx_info[1]->memrange2) + offset);
49066910Scokane	}*/ /* VoodooNG code */
49163488Scokane
49266910Scokane	/* The ret call */
49361911Scokane	/* atop -> address to page
49461911Scokane	 * rman_get_start, get the (struct resource*)->r_start member,
49561911Scokane	 * the mapping base address.
49661911Scokane	 */
49766910Scokane	return -1;
49861911Scokane}
49961911Scokane
50061911Scokanestatic int
50161911Scokanetdfx_query_boards(void) {
50261911Scokane	/*
50361911Scokane    *	This returns the number of installed tdfx cards, we have been keeping
50461911Scokane	 * count, look at tdfx_attach
50561911Scokane	 */
50661911Scokane	return tdfx_count;
50761911Scokane}
50861911Scokane
50961911Scokanestatic int
51061911Scokanetdfx_query_fetch(u_int cmd, struct tdfx_pio_data *piod)
51161911Scokane{
51261911Scokane	/* XXX Comment this later, after careful inspection and spring cleaning :) */
51361911Scokane	/* Various return values 8bit-32bit */
51461911Scokane	u_int8_t  ret_byte;
51561911Scokane	u_int16_t ret_word;
51661911Scokane	u_int32_t ret_dword;
51761911Scokane	struct tdfx_softc* tdfx_info = NULL;
51861911Scokane
51961911Scokane	/* This one depend on the tdfx_* structs being properly initialized */
52061911Scokane
52161911Scokane	/*piod->device &= 0xf;*/
52261911Scokane	if((piod == NULL) ||(tdfx_count <= piod->device) ||
52361911Scokane			(piod->device < 0)) {
52461931Scokane#ifdef DEBUG
52561911Scokane		printf("tdfx: Bad device or internal struct in tdfx_query_fetch\n");
52661911Scokane#endif
52761911Scokane		return -EINVAL;
52861911Scokane	}
52961911Scokane
53061911Scokane	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
53161911Scokane			piod->device);
53261911Scokane
53361911Scokane	if(tdfx_info == NULL) return -ENXIO;
53461911Scokane
53561911Scokane	/* We must restrict the size reads from the port, since to high or low of a
53661911Scokane	 * size witll result in wrong data being passed, and that's bad */
53761911Scokane	/* A few of these were pulled during the attach phase */
53861911Scokane	switch(piod->port) {
53961911Scokane		case PCI_VENDOR_ID_FREEBSD:
54061911Scokane			if(piod->size != 2) return -EINVAL;
54161911Scokane			copyout(&tdfx_info->vendor, piod->value, piod->size);
54261911Scokane			return 0;
54361911Scokane		case PCI_DEVICE_ID_FREEBSD:
54461911Scokane			if(piod->size != 2) return -EINVAL;
54561911Scokane			copyout(&tdfx_info->type, piod->value, piod->size);
54661911Scokane			return 0;
54761911Scokane		case PCI_BASE_ADDRESS_0_FREEBSD:
54861911Scokane			if(piod->size != 4) return -EINVAL;
54961911Scokane			copyout(&tdfx_info->addr0, piod->value, piod->size);
55061911Scokane			return 0;
55165146Scokane		case PCI_BASE_ADDRESS_1_FREEBSD:
55265146Scokane			if(piod->size != 4) return -EINVAL;
55365146Scokane			copyout(&tdfx_info->addr1, piod->value, piod->size);
55465146Scokane			return 0;
55565146Scokane		case PCI_PRIBUS_FREEBSD:
55665146Scokane			if(piod->size != 1) return -EINVAL;
55765146Scokane			break;
55865146Scokane		case PCI_IOBASE_0_FREEBSD:
55965146Scokane			if(piod->size != 2) return -EINVAL;
56065146Scokane			break;
56165146Scokane		case PCI_IOLIMIT_0_FREEBSD:
56265146Scokane			if(piod->size != 2) return -EINVAL;
56365146Scokane			break;
56461911Scokane		case SST1_PCI_SPECIAL1_FREEBSD:
56561911Scokane			if(piod->size != 4) return -EINVAL;
56661911Scokane			break;
56761911Scokane		case PCI_REVISION_ID_FREEBSD:
56861911Scokane			if(piod->size != 1) return -EINVAL;
56961911Scokane			break;
57061911Scokane		case SST1_PCI_SPECIAL4_FREEBSD:
57161911Scokane			if(piod->size != 4) return -EINVAL;
57261911Scokane			break;
57361911Scokane		default:
57461911Scokane			return -EINVAL;
57561911Scokane	}
57661911Scokane
57761911Scokane
57861911Scokane	/* Read the value and return */
57961911Scokane	switch(piod->size) {
58061911Scokane		case 1:
58161911Scokane			ret_byte = pci_read_config(tdfx_info[piod->device].dev,
58261911Scokane					piod->port, 1);
58361911Scokane			copyout(&ret_byte, piod->value, 1);
58461911Scokane			break;
58561911Scokane		case 2:
58661911Scokane			ret_word = pci_read_config(tdfx_info[piod->device].dev,
58761911Scokane					piod->port, 2);
58861911Scokane			copyout(&ret_word, piod->value, 2);
58961911Scokane			break;
59061911Scokane		case 4:
59161911Scokane			ret_dword = pci_read_config(tdfx_info[piod->device].dev,
59261911Scokane					piod->port, 4);
59361911Scokane			copyout(&ret_dword, piod->value, 4);
59461911Scokane			break;
59561911Scokane		default:
59661911Scokane			return -EINVAL;
59761911Scokane	}
59861911Scokane	return 0;
59961911Scokane}
60061911Scokane
60161911Scokanestatic int
60261911Scokanetdfx_query_update(u_int cmd, struct tdfx_pio_data *piod)
60361911Scokane{
60461911Scokane	/* XXX Comment this later, after careful inspection and spring cleaning :) */
60561911Scokane	/* Return vals */
60661911Scokane	u_int8_t  ret_byte;
60761911Scokane	u_int16_t ret_word;
60861911Scokane	u_int32_t ret_dword;
60961911Scokane
61061911Scokane	/* Port vals, mask */
61161911Scokane	u_int32_t retval, preval, mask;
61261911Scokane	struct tdfx_softc* tdfx_info = NULL;
61361911Scokane
61461911Scokane
61561911Scokane	if((piod == NULL) || (piod->device >= (tdfx_count &
61661911Scokane					0xf))) {
61761931Scokane#ifdef DEBUG
61861911Scokane		printf("tdfx: Bad struct or device in tdfx_query_update\n");
61961911Scokane#endif
62061911Scokane		return -EINVAL;
62161911Scokane	}
62261911Scokane
62361911Scokane	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
62461911Scokane			piod->device);
62561911Scokane	if(tdfx_info == NULL) return -ENXIO;
62661911Scokane	/* Code below this line in the fuction was taken from the
62761911Scokane	 * Linux driver and converted for freebsd. */
62861911Scokane
62961911Scokane	/* Check the size for all the ports, to make sure stuff doesn't get messed up
63061911Scokane	 * by poorly written clients */
63161911Scokane
63261911Scokane	switch(piod->port) {
63361911Scokane		case PCI_COMMAND_FREEBSD:
63461911Scokane			if(piod->size != 2) return -EINVAL;
63561911Scokane			break;
63661911Scokane		case SST1_PCI_SPECIAL1_FREEBSD:
63761911Scokane			if(piod->size != 4) return -EINVAL;
63861911Scokane			break;
63961911Scokane		case SST1_PCI_SPECIAL2_FREEBSD:
64061911Scokane			if(piod->size != 4) return -EINVAL;
64161911Scokane			break;
64261911Scokane		case SST1_PCI_SPECIAL3_FREEBSD:
64361911Scokane			if(piod->size != 4) return -EINVAL;
64461911Scokane			break;
64561911Scokane		case SST1_PCI_SPECIAL4_FREEBSD:
64661911Scokane			if(piod->size != 4) return -EINVAL;
64761911Scokane			break;
64861911Scokane		default:
64961911Scokane			return -EINVAL;
65061911Scokane	}
65161911Scokane	/* Read the current value */
65261911Scokane	retval = pci_read_config(tdfx_info->dev, piod->port & ~3, 4);
65361911Scokane
65461911Scokane	/* These set up a mask to use, since apparently they wanted to write 4 bytes
65561911Scokane	 * at once to the ports */
65661911Scokane	switch (piod->size) {
65761911Scokane		case 1:
65861911Scokane			copyin(piod->value, &ret_byte, 1);
65961911Scokane			preval = ret_byte << (8 * (piod->port & 0x3));
66061911Scokane			mask = 0xff << (8 * (piod->port & 0x3));
66161911Scokane			break;
66261911Scokane		case 2:
66361911Scokane			copyin(piod->value, &ret_word, 2);
66461911Scokane			preval = ret_word << (8 * (piod->port & 0x3));
66561911Scokane			mask = 0xffff << (8 * (piod->port & 0x3));
66661911Scokane			break;
66761911Scokane		case 4:
66861911Scokane			copyin(piod->value, &ret_dword, 4);
66961911Scokane			preval = ret_dword;
67061911Scokane			mask = ~0;
67161911Scokane			break;
67261911Scokane		default:
67361911Scokane			return -EINVAL;
67461911Scokane	}
67561911Scokane	/* Finally, combine the values and write it to the port */
67661911Scokane	retval = (retval & ~mask) | preval;
67761911Scokane	pci_write_config(tdfx_info->dev, piod->port & ~3, retval, 4);
67861911Scokane
67961911Scokane	return 0;
68061911Scokane}
68161911Scokane
68263488Scokane/* For both of these, I added a variable named workport of type u_int so
68363488Scokane * that I could eliminate the warning about my data type size. The
68463488Scokane * applications expect the port to be of type short, so I needed to change
68563488Scokane * this within the function */
68661911Scokanestatic int
68761911Scokanetdfx_do_pio_rd(struct tdfx_pio_data *piod)
68861911Scokane{
68961911Scokane	/* Return val */
69061911Scokane	u_int8_t  ret_byte;
69163488Scokane	u_int 	 workport;
69265146Scokane	struct tdfx_softc *tdfx_info =
69365146Scokane		(struct tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device);
69465146Scokane
69561911Scokane	/* Restricts the access of ports other than those we use */
69665146Scokane	if(((piod->port != VGA_INPUT_STATUS_1C) || (piod->port != SC_INDEX) ||
69765146Scokane		(piod->port != SC_DATA) || (piod->port != VGA_MISC_OUTPUT_READ)) &&
69865146Scokane		(piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max))
69961911Scokane		return -EPERM;
70061911Scokane
70161911Scokane	/* All VGA STATUS REGS are byte registers, size should never be > 1 */
70261911Scokane	if(piod->size != 1) {
70361911Scokane		return -EINVAL;
70461911Scokane	}
70561911Scokane
70661911Scokane	/* Write the data to the intended port */
70763488Scokane	workport = piod->port;
70863488Scokane	ret_byte = inb(workport);
70961911Scokane	copyout(&ret_byte, piod->value, sizeof(u_int8_t));
71061911Scokane	return 0;
71161911Scokane}
71261911Scokane
71361911Scokanestatic int
71461911Scokanetdfx_do_pio_wt(struct tdfx_pio_data *piod)
71561911Scokane{
71661911Scokane	/* return val */
71761911Scokane	u_int8_t  ret_byte;
71863488Scokane	u_int		 workport;
71965146Scokane	struct tdfx_softc *tdfx_info = (struct
72065146Scokane			tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device);
72161911Scokane	/* Replace old switch w/ massive if(...) */
72261911Scokane	/* Restricts the access of ports other than those we use */
72365146Scokane	if(((piod->port != SC_INDEX) && (piod->port != SC_DATA) &&
72465146Scokane		(piod->port != VGA_MISC_OUTPUT_READ)) /* Can't write VGA_ST_1C */ &&
72565146Scokane		(piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max))
72661911Scokane		return -EPERM;
72761911Scokane
72861911Scokane	/* All VGA STATUS REGS are byte registers, size should never be > 1 */
72961911Scokane	if(piod->size != 1) {
73061911Scokane		return -EINVAL;
73161911Scokane	}
73261911Scokane
73361911Scokane	/* Write the data to the intended port */
73461911Scokane	copyin(piod->value, &ret_byte, sizeof(u_int8_t));
73563488Scokane	workport = piod->port;
73663488Scokane	outb(workport, ret_byte);
73761911Scokane	return 0;
73861911Scokane}
73961911Scokane
74061911Scokanestatic int
74161911Scokanetdfx_do_query(u_int cmd, struct tdfx_pio_data *piod)
74261911Scokane{
74361911Scokane	/* There are three sub-commands to the query 0x33 */
74461911Scokane	switch(_IOC_NR(cmd)) {
74561911Scokane		case 2:
74661911Scokane			return tdfx_query_boards();
74761911Scokane			break;
74861911Scokane		case 3:
74961911Scokane			return tdfx_query_fetch(cmd, piod);
75061911Scokane			break;
75161911Scokane		case 4:
75261911Scokane			return tdfx_query_update(cmd, piod);
75361911Scokane			break;
75461911Scokane		default:
75561911Scokane			/* In case we are thrown a bogus sub-command! */
75661931Scokane#ifdef DEBUG
75761911Scokane			printf("Bad Sub-cmd: 0x%x\n", _IOC_NR(cmd));
75861911Scokane#endif
75961911Scokane			return -EINVAL;
760115494Sphk	}
76161911Scokane}
76261911Scokane
76361911Scokanestatic int
76461911Scokanetdfx_do_pio(u_int cmd, struct tdfx_pio_data *piod)
76561911Scokane{
76661911Scokane	/* Two types of PIO, INPUT and OUTPUT, as the name suggests */
76761911Scokane	switch(_IOC_DIR(cmd)) {
76861911Scokane		case IOCV_OUT:
76961911Scokane			return tdfx_do_pio_rd(piod);
77061911Scokane			break;
77161911Scokane		case IOCV_IN:
77261911Scokane			return tdfx_do_pio_wt(piod);
77361911Scokane			break;
77461911Scokane		default:
77561911Scokane			return -EINVAL;
776115494Sphk	}
77761911Scokane}
77861911Scokane
77961911Scokane/* Calls to ioctl(2) eventually end up here. Unhandled ioctls return an ENXIO,
78061911Scokane * normally, you would read in the data pointed to by data, then write your
78161911Scokane * output to it. The ioctl *should* normally return zero if everything is
78261911Scokane * alright, but 3dfx didn't make it that way...
78361911Scokane *
78461911Scokane * For all of the ioctl code, in the event of a real error,
78561911Scokane * we return -Exxxx rather than simply Exxxx. The reason for this
78661911Scokane * is that the ioctls actually RET information back to the program
78761911Scokane * sometimes, rather than filling it in the passed structure. We
78861911Scokane * want to distinguish errors from useful data, and maintain compatibility.
78961911Scokane *
79061911Scokane * There is this portion of the proc struct called p_retval[], we can store a
79183366Sjulian * return value in td->td_retval[0] and place the return value if it is positive
79261911Scokane * in there, then we can return 0 (good). If the return value is negative, we
79361911Scokane * can return -retval and the error should be properly handled.
79461911Scokane */
79561911Scokanestatic int
79683366Sjuliantdfx_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
79761911Scokane{
79861911Scokane	int retval = 0;
79961911Scokane	struct tdfx_pio_data *piod = (struct tdfx_pio_data*)data;
80061931Scokane#ifdef	DEBUG
801106578Sjhb	printf("IOCTL'd by #%d, cmd: 0x%x, data: %p\n", td->td_proc->p_pid, (u_int32_t)cmd,
802106578Sjhb			piod);
80361911Scokane#endif
80461911Scokane	switch(_IOC_TYPE(cmd)) {
80561911Scokane		/* Return the real error if negative, or simply stick the valid return
80683366Sjulian		 * in td->td_retval */
80761911Scokane	case 0x33:
80861911Scokane			/* The '3'(0x33) type IOCTL is for querying the installed cards */
80983366Sjulian			if((retval = tdfx_do_query(cmd, piod)) > 0) td->td_retval[0] = retval;
81061911Scokane			else return -retval;
81161911Scokane			break;
81261911Scokane		case 0:
81361911Scokane			/* The 0 type IOCTL is for programmed I/O methods */
81483366Sjulian			if((tdfx_do_pio(cmd, piod)) > 0) td->td_retval[0] = retval;
81561911Scokane			else return -retval;
81661911Scokane			break;
81761911Scokane		default:
81861911Scokane			/* Technically, we won't reach this from linux emu, but when glide
81961911Scokane			 * finally gets ported, watch out! */
82061931Scokane#ifdef DEBUG
82183366Sjulian			printf("Bad IOCTL from #%d\n", td->td_proc->p_pid);
82261911Scokane#endif
82361911Scokane			return ENXIO;
82461911Scokane	}
82561911Scokane
82661911Scokane	return 0;
82761911Scokane}
82861911Scokane
82961931Scokane#ifdef TDFX_LINUX
83061931Scokane/*
83161931Scokane * Linux emulation IOCTL for /dev/tdfx
83261931Scokane */
83361931Scokanestatic int
83483366Sjulianlinux_ioctl_tdfx(struct thread *td, struct linux_ioctl_args* args)
83561931Scokane{
83661931Scokane   int error = 0;
83761931Scokane   u_long cmd = args->cmd & 0xffff;
83861911Scokane
83961931Scokane   /* The structure passed to ioctl has two shorts, one int
84061931Scokane      and one void*. */
84161931Scokane   char d_pio[2*sizeof(short) + sizeof(int) + sizeof(void*)];
84261931Scokane
84389306Salfred   struct file *fp;
84461931Scokane
84589319Salfred   if ((error = fget(td, args->fd, &fp)) != 0)
84689319Salfred	   return (error);
84761931Scokane   /* We simply copy the data and send it right to ioctl */
84861931Scokane   copyin((caddr_t)args->arg, &d_pio, sizeof(d_pio));
849102003Srwatson   error = fo_ioctl(fp, cmd, (caddr_t)&d_pio, td->td_ucred, td);
85089306Salfred   fdrop(fp, td);
85161931Scokane   return error;
85261931Scokane}
85361931Scokane#endif /* TDFX_LINUX */
85461931Scokane
85561931Scokane
85661911Scokane/* This is the device driver struct. This is sent to the driver subsystem to
85761911Scokane * register the method structure and the info strcut space for this particular
85861911Scokane * instance of the driver.
85961911Scokane */
86061911Scokanestatic driver_t tdfx_driver = {
86161911Scokane	"tdfx",
86261911Scokane	tdfx_methods,
86361911Scokane	sizeof(struct tdfx_softc),
86461911Scokane};
86561911Scokane
86661911Scokane/* Tell Mr. Kernel about us! */
86761911ScokaneDRIVER_MODULE(tdfx, pci, tdfx_driver, tdfx_devclass, 0, 0);
868