tdfx_pci.c revision 115494
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 115494 2003-05-31 18:57:41Z phk $
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 = {
106111815Sphk	.d_open =	tdfx_open,
107111815Sphk	.d_close =	tdfx_close,
108111815Sphk	.d_ioctl =	tdfx_ioctl,
109111815Sphk	.d_mmap =	tdfx_mmap,
110111815Sphk	.d_name =	"tdfx",
111111815Sphk	.d_maj =	CDEV_MAJOR,
11261911Scokane};
11361911Scokane
11461911Scokanestatic int
11561911Scokanetdfx_probe(device_t dev)
11661911Scokane{
11761911Scokane	/*
11861911Scokane	 * probe routine called on kernel boot to register supported devices. We get
11961911Scokane	 * a device structure to work with, and we can test the VENDOR/DEVICE IDs to
12061911Scokane	 * see if this PCI device is one that we support. Return 0 if yes, ENXIO if
12161911Scokane	 * not.
12261911Scokane	 */
12361911Scokane	switch(pci_get_devid(dev)) {
12461911Scokane	case PCI_DEVICE_ALLIANCE_AT3D:
12561911Scokane		device_set_desc(dev, "ProMotion At3D 3D Accelerator");
12661911Scokane		return 0;
12761911Scokane	case PCI_DEVICE_3DFX_VOODOO2:
12861911Scokane		device_set_desc(dev, "3DFX Voodoo II 3D Accelerator");
12961911Scokane		return 0;
13065146Scokane	/*case PCI_DEVICE_3DFX_BANSHEE:
13161911Scokane		device_set_desc(dev, "3DFX Voodoo Banshee 2D/3D Graphics Accelerator");
13261911Scokane		return 0;
13361911Scokane	case PCI_DEVICE_3DFX_VOODOO3:
13461911Scokane		device_set_desc(dev, "3DFX Voodoo3 2D/3D Graphics Accelerator");
13565146Scokane		return 0;*/
13661911Scokane	case PCI_DEVICE_3DFX_VOODOO1:
13761911Scokane		device_set_desc(dev, "3DFX Voodoo Graphics 3D Accelerator");
13861911Scokane		return 0;;
13961911Scokane	};
14061911Scokane
14161911Scokane	return ENXIO;
14261911Scokane}
14361911Scokane
14461911Scokanestatic int
14561911Scokanetdfx_attach(device_t dev) {
14661911Scokane	/*
14761911Scokane	 * The attach routine is called after the probe routine successfully says it
14861911Scokane	 * supports a given card. We now proceed to initialize this card for use with
14961911Scokane	 * the system. I want to map the device memory for userland allocation and
15061911Scokane	 * fill an information structure with information on this card. I'd also like
15161911Scokane	 * to set Write Combining with the MTRR code so that we can hopefully speed
15261911Scokane	 * up memory writes. The last thing is to register the character device
15361911Scokane	 * interface to the card, so we can open it from /dev/3dfxN, where N is a
15461911Scokane	 * small, whole number.
15561911Scokane	 */
15661911Scokane	struct tdfx_softc *tdfx_info;
15761911Scokane	u_long	val;
15861911Scokane	/* rid value tells bus_alloc_resource where to find the addresses of ports or
15961911Scokane	 * of memory ranges in the PCI config space*/
16061911Scokane	int rid = PCIR_MAPS;
16161911Scokane
16261911Scokane	/* Increment the card counter (for the ioctl code) */
16361911Scokane	tdfx_count++;
16461911Scokane
16561911Scokane 	/* Enable MemMap on Voodoo */
16661911Scokane	val = pci_read_config(dev, PCIR_COMMAND, 2);
16761911Scokane	val |= (PCIM_CMD_MEMEN);
16861911Scokane	pci_write_config(dev, PCIR_COMMAND, val, 2);
16961911Scokane	val = pci_read_config(dev, PCIR_COMMAND, 2);
17061911Scokane
17161911Scokane	/* Fill the soft config struct with info about this device*/
17261911Scokane	tdfx_info = device_get_softc(dev);
17361911Scokane	tdfx_info->dev = dev;
17461911Scokane	tdfx_info->vendor = pci_get_vendor(dev);
17561911Scokane	tdfx_info->type = pci_get_devid(dev) >> 16;
17661911Scokane	tdfx_info->bus = pci_get_bus(dev);
17761911Scokane	tdfx_info->dv = pci_get_slot(dev);
17861911Scokane	tdfx_info->curFile = NULL;
17961911Scokane
18061911Scokane	/*
18161911Scokane	 *	Get the Memory Location from the PCI Config, mask out lower word, since
18261911Scokane	 * the config space register is only one word long (this is nicer than a
18361911Scokane	 * bitshift).
18461911Scokane	 */
18561911Scokane	tdfx_info->addr0 = (pci_read_config(dev, 0x10, 4) & 0xffff0000);
18661931Scokane#ifdef DEBUG
18761911Scokane	device_printf(dev, "Base0 @ 0x%x\n", tdfx_info->addr0);
18861911Scokane#endif
18961911Scokane	/* Notify the VM that we will be mapping some memory later */
19061911Scokane	tdfx_info->memrange = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, ~0, 1,
19161967Scokane			RF_ACTIVE | RF_SHAREABLE);
19261911Scokane	if(tdfx_info->memrange == NULL) {
19361931Scokane#ifdef DEBUG
19461911Scokane		device_printf(dev, "Error mapping mem, won't be able to use mmap()\n");
19561911Scokane#endif
19661911Scokane		tdfx_info->memrid = 0;
19761911Scokane	}
19861911Scokane	else {
19961911Scokane		tdfx_info->memrid = rid;
20061931Scokane#ifdef DEBUG
20161911Scokane		device_printf(dev, "Mapped to: 0x%x\n",
20261911Scokane				(unsigned int)rman_get_start(tdfx_info->memrange));
20361911Scokane#endif
20461911Scokane	}
20561911Scokane
20663488Scokane	/* Setup for Voodoo3 and Banshee, PIO and an extram Memrange */
20763488Scokane	if(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3 ||
20863488Scokane		pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE) {
20964085Scokane		rid = 0x14;	/* 2nd mem map */
21063488Scokane		tdfx_info->addr1 = (pci_read_config(dev, 0x14, 4) & 0xffff0000);
21163488Scokane#ifdef DEBUG
21263488Scokane		device_printf(dev, "Base1 @ 0x%x\n", tdfx_info->addr1);
21363488Scokane#endif
21463488Scokane		tdfx_info->memrange2 = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
21563488Scokane			 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
21663488Scokane		if(tdfx_info->memrange2 == NULL) {
21763488Scokane#ifdef DEBUG
21863488Scokane			device_printf(dev, "Mem1 couldn't be allocated, glide may not work.");
21963488Scokane#endif
22063488Scokane			tdfx_info->memrid2 = 0;
22163488Scokane		}
22263488Scokane		else {
22363488Scokane			tdfx_info->memrid2 = rid;
22463488Scokane		}
22563488Scokane		/* Now to map the PIO stuff */
22665146Scokane		rid = PCIR_IOBASE0_2;
22765146Scokane		tdfx_info->pio0 = pci_read_config(dev, 0x2c, 2);
22865146Scokane		tdfx_info->pio0max = pci_read_config(dev, 0x30, 2) + tdfx_info->pio0;
22963488Scokane		tdfx_info->piorange = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
23063488Scokane			 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
23163488Scokane		if(tdfx_info->piorange == NULL) {
23263488Scokane#ifdef DEBUG
23363488Scokane			device_printf(dev, "Couldn't map PIO range.");
23463488Scokane#endif
23563488Scokane			tdfx_info->piorid = 0;
23663488Scokane		}
23763488Scokane		else {
23863488Scokane			tdfx_info->piorid = rid;
23965146Scokane		}
24063488Scokane	} else {
24163488Scokane	  tdfx_info->addr1 = 0;
24263488Scokane	  tdfx_info->memrange2 = NULL;
24363488Scokane	  tdfx_info->piorange = NULL;
24463488Scokane	}
24563488Scokane
24661911Scokane	/*
24761911Scokane	 *	Set Writecombining, or at least Uncacheable for the memory region, if we
24861911Scokane	 * are able to
24961911Scokane	 */
25061911Scokane
25161911Scokane	if(tdfx_setmtrr(dev) != 0) {
25261931Scokane#ifdef DEBUG
25361911Scokane		device_printf(dev, "Some weird error setting MTRRs");
25461911Scokane#endif
25561911Scokane		return -1;
25661911Scokane	}
25763488Scokane
25861911Scokane	/*
25961911Scokane	 * make_dev registers the cdev to access the 3dfx card from /dev
26061911Scokane	 *	use hex here for the dev num, simply to provide better support if > 10
26161911Scokane	 * voodoo cards, for the mad. The user must set the link, or use MAKEDEV.
26261911Scokane	 * Why would we want that many voodoo cards anyhow?
26361911Scokane	 */
264104111Sphk	tdfx_info->devt = make_dev(&tdfx_cdev, device_get_unit(dev),
265108323Srwatson		UID_ROOT, GID_WHEEL, 0600, "3dfx%x", device_get_unit(dev));
26661911Scokane
26761911Scokane	return 0;
26861911Scokane}
26961911Scokane
27061911Scokanestatic int
27161911Scokanetdfx_detach(device_t dev) {
27261911Scokane	struct tdfx_softc* tdfx_info;
27361911Scokane	int retval;
27461911Scokane	tdfx_info = device_get_softc(dev);
27561911Scokane
27661911Scokane	/* Delete allocated resource, of course */
27763488Scokane	bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid,
27861911Scokane			tdfx_info->memrange);
27963488Scokane
28063488Scokane	/* Release extended Voodoo3/Banshee resources */
28163488Scokane	if(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE ||
28263488Scokane			pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) {
28363488Scokane		if(tdfx_info->memrange2 != NULL)
28463488Scokane			bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid2,
28563488Scokane				tdfx_info->memrange);
28664085Scokane	/*	if(tdfx_info->piorange != NULL)
28763488Scokane			bus_release_resource(dev, SYS_RES_IOPORT, tdfx_info->piorid,
28864085Scokane				tdfx_info->piorange);*/
28963488Scokane	}
29063488Scokane
29161911Scokane	/* Though it is safe to leave the WRCOMB support since the
29261911Scokane		mem driver checks for it, we should remove it in order
29361911Scokane		to free an MTRR for another device */
29461911Scokane	retval = tdfx_clrmtrr(dev);
29561931Scokane#ifdef DEBUG
29661911Scokane	if(retval != 0)
29761911Scokane		printf("tdfx: For some reason, I couldn't clear the mtrr\n");
29861911Scokane#endif
29961989Scokane	/* Remove device entry when it can no longer be accessed */
30061989Scokane   destroy_dev(tdfx_info->devt);
30161911Scokane	return(0);
30261911Scokane}
30361911Scokane
30461911Scokanestatic int
30561911Scokanetdfx_shutdown(device_t dev) {
30661931Scokane#ifdef DEBUG
30761911Scokane	device_printf(dev, "tdfx: Device Shutdown\n");
30861911Scokane#endif
30961911Scokane	return 0;
31061911Scokane}
31161911Scokane
31261911Scokanestatic int
31361911Scokanetdfx_clrmtrr(device_t dev) {
31461911Scokane	/* This function removes the MTRR set by the attach call, so it can be used
31561911Scokane	 * in the future by other drivers.
31661911Scokane	 */
31761911Scokane	int retval, act;
31861911Scokane	struct tdfx_softc *tdfx_info = device_get_softc(dev);
31961911Scokane
32061911Scokane	act = MEMRANGE_SET_REMOVE;
32161911Scokane	retval = mem_range_attr_set(&tdfx_info->mrdesc, &act);
32261911Scokane	return retval;
32361911Scokane}
32461911Scokane
32561911Scokanestatic int
32661911Scokanetdfx_setmtrr(device_t dev) {
32761911Scokane	/*
32861911Scokane	 * This is the MTRR setting function for the 3dfx card. It is called from
32961911Scokane	 * tdfx_attach. If we can't set the MTRR properly, it's not the end of the
33061911Scokane	 * world. We can still continue, just with slightly (very slightly) degraded
33161911Scokane	 * performance.
33261911Scokane	 */
33361911Scokane	int retval = 0, act;
33461911Scokane	struct tdfx_softc *tdfx_info = device_get_softc(dev);
33561911Scokane
33661911Scokane	/* The older Voodoo cards have a shorter memrange than the newer ones */
33761911Scokane	if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1) || (pci_get_devid(dev) ==
33863488Scokane			PCI_DEVICE_3DFX_VOODOO2)) {
33961911Scokane		tdfx_info->mrdesc.mr_len = 0x400000;
34063488Scokane
34163488Scokane		/* The memory descriptor is described as the top 15 bits of the real
34263488Scokane			address */
34363488Scokane		tdfx_info->mrdesc.mr_base = tdfx_info->addr0 & 0xfffe0000;
34463488Scokane	}
34561911Scokane	else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) ||
34663488Scokane			(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE)) {
34761911Scokane		tdfx_info->mrdesc.mr_len = 0x1000000;
34863488Scokane		/* The Voodoo3 and Banshee LFB is the second memory address */
34963488Scokane		/* The memory descriptor is described as the top 15 bits of the real
35063488Scokane			address */
35163488Scokane		tdfx_info->mrdesc.mr_base = tdfx_info->addr1 & 0xfffe0000;
35263488Scokane	}
35363488Scokane	else
35463488Scokane		 return 0;
35561911Scokane	/*
35661911Scokane    *	The Alliance Pro Motion AT3D was not mentioned in the linux
35761911Scokane	 * driver as far as MTRR support goes, so I just won't put the
35861911Scokane	 * code in here for it. This is where it should go, though.
35961911Scokane	 */
36061911Scokane
36161911Scokane	/* Firstly, try to set write combining */
36261911Scokane	tdfx_info->mrdesc.mr_flags = MDF_WRITECOMBINE;
36361911Scokane	bcopy("tdfx", &tdfx_info->mrdesc.mr_owner, 4);
36461911Scokane	act = MEMRANGE_SET_UPDATE;
36561911Scokane	retval = mem_range_attr_set(&tdfx_info->mrdesc, &act);
36661911Scokane
36761911Scokane	if(retval == 0) {
36861931Scokane#ifdef DEBUG
36961911Scokane		device_printf(dev, "MTRR Set Correctly for tdfx\n");
37061911Scokane#endif
37161911Scokane	} else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO2) ||
37261911Scokane		(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1)) {
37361911Scokane		/* if, for some reason we can't set the WRCOMB range with the V1/V2, we
37461911Scokane		 * can still possibly use the UNCACHEABLE region for it instead, and help
37561911Scokane		 * out in a small way */
37661911Scokane		tdfx_info->mrdesc.mr_flags = MDF_UNCACHEABLE;
37761911Scokane		/* This length of 1000h was taken from the linux device driver... */
37861911Scokane		tdfx_info->mrdesc.mr_len = 0x1000;
37961911Scokane
38061911Scokane		/*
38161911Scokane		 * If, for some reason, we can't set the MTRR (N/A?) we may still continue
38261911Scokane		 */
38361931Scokane#ifdef DEBUG
38461911Scokane		if(retval == 0) {
38595092Smarcel			device_printf(dev, "MTRR Set Type Uncacheable %x\n",
38695092Smarcel			    (u_int32_t)tdfx_info->mrdesc.mr_base);
38761911Scokane		} else {
38861911Scokane			device_printf(dev, "Couldn't Set MTRR\n");
38961911Scokane		}
39061911Scokane#endif
39161911Scokane	}
39261931Scokane#ifdef DEBUG
39361911Scokane	else {
39461911Scokane		device_printf(dev, "Couldn't Set MTRR\n");
39561911Scokane		return 0;
39661911Scokane	}
39761911Scokane#endif
39861911Scokane	return 0;
39961911Scokane}
40061911Scokane
40161911Scokanestatic int
40283366Sjuliantdfx_open(dev_t dev, int flags, int fmt, struct thread *td)
40361911Scokane{
40461911Scokane	/*
40561911Scokane	 *	The open cdev method handles open(2) calls to /dev/3dfx[n]
40661911Scokane	 * We can pretty much allow any opening of the device.
40761911Scokane	 */
40861911Scokane	struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass,
40961911Scokane			UNIT(minor(dev)));
41061911Scokane	if(tdfx_info->busy != 0) return EBUSY;
41161931Scokane#ifdef	DEBUG
41283366Sjulian	printf("3dfx: Opened by #%d\n", td->td_proc->p_pid);
41361911Scokane#endif
41461911Scokane	/* Set the driver as busy */
41561911Scokane	tdfx_info->busy++;
41661911Scokane	return 0;
41761911Scokane}
41861911Scokane
41961911Scokanestatic int
42083366Sjuliantdfx_close(dev_t dev, int fflag, int devtype, struct thread *td)
42161911Scokane{
42261911Scokane	/*
42361911Scokane	 *	The close cdev method handles close(2) calls to /dev/3dfx[n]
42461911Scokane	 * We'll always want to close the device when it's called.
42561911Scokane	 */
42661911Scokane	struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass,
42761911Scokane		UNIT(minor(dev)));
42861911Scokane	if(tdfx_info->busy == 0) return EBADF;
42961911Scokane	tdfx_info->busy = 0;
43061931Scokane#ifdef	DEBUG
43183366Sjulian	printf("Closed by #%d\n", td->td_proc->p_pid);
43261911Scokane#endif
43361911Scokane	return 0;
43461911Scokane}
43561911Scokane
43661911Scokanestatic int
437112569Sjaketdfx_mmap(dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
43861911Scokane{
43961911Scokane	/*
44061911Scokane	 * mmap(2) is called by a user process to request that an area of memory
44161911Scokane	 * associated with this device be mapped for the process to work with. Nprot
44261911Scokane	 * holds the protections requested, PROT_READ, PROT_WRITE, or both.
44361911Scokane	 */
44466910Scokane
44566910Scokane	/**** OLD GET CONFIG ****/
44666910Scokane	/* struct tdfx_softc* tdfx_info; */
44761911Scokane
44861911Scokane	/* Get the configuration for our card XXX*/
44966910Scokane	/*tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
45066910Scokane			UNIT(minor(dev)));*/
45166910Scokane	/************************/
45266910Scokane
45366910Scokane	struct tdfx_softc* tdfx_info[2];
45461911Scokane
45566910Scokane	tdfx_info[0] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 0);
45666910Scokane
45761911Scokane	/* If, for some reason, its not configured, we bail out */
45866910Scokane	if(tdfx_info[0] == NULL) {
45961931Scokane#ifdef	DEBUG
46061911Scokane	   printf("tdfx: tdfx_info (softc) is NULL\n");
46161911Scokane#endif
46261911Scokane	   return -1;
46361911Scokane	}
46466910Scokane
46561911Scokane	/* We must stay within the bound of our address space */
46666910Scokane	if((offset & 0xff000000) == tdfx_info[0]->addr0) {
46761911Scokane		offset &= 0xffffff;
468111462Smux		*paddr = rman_get_start(tdfx_info[0]->memrange) + offset;
469111462Smux		return 0;
47066910Scokane	}
47166910Scokane
47266910Scokane	if(tdfx_count > 1) {
47366910Scokane		tdfx_info[1] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 1);
47466910Scokane		if((offset & 0xff000000) == tdfx_info[1]->addr0) {
47566910Scokane			offset &= 0xffffff;
476111462Smux			*paddr = rman_get_start(tdfx_info[1]->memrange) +
477111462Smux			    offset;
478111462Smux			return 0;
47966910Scokane		}
48066910Scokane	}
48163488Scokane
48263488Scokane	/* See if the Banshee/V3 LFB is being requested */
48366910Scokane	/*if(tdfx_info->memrange2 != NULL && (offset & 0xff000000) ==
48464085Scokane			tdfx_info->addr1) {
48563488Scokane	  	offset &= 0xffffff;
48666910Scokane		return atop(rman_get_start(tdfx_info[1]->memrange2) + offset);
48766910Scokane	}*/ /* VoodooNG code */
48863488Scokane
48966910Scokane	/* The ret call */
49061911Scokane	/* atop -> address to page
49161911Scokane	 * rman_get_start, get the (struct resource*)->r_start member,
49261911Scokane	 * the mapping base address.
49361911Scokane	 */
49466910Scokane	return -1;
49561911Scokane}
49661911Scokane
49761911Scokanestatic int
49861911Scokanetdfx_query_boards(void) {
49961911Scokane	/*
50061911Scokane    *	This returns the number of installed tdfx cards, we have been keeping
50161911Scokane	 * count, look at tdfx_attach
50261911Scokane	 */
50361911Scokane	return tdfx_count;
50461911Scokane}
50561911Scokane
50661911Scokanestatic int
50761911Scokanetdfx_query_fetch(u_int cmd, struct tdfx_pio_data *piod)
50861911Scokane{
50961911Scokane	/* XXX Comment this later, after careful inspection and spring cleaning :) */
51061911Scokane	/* Various return values 8bit-32bit */
51161911Scokane	u_int8_t  ret_byte;
51261911Scokane	u_int16_t ret_word;
51361911Scokane	u_int32_t ret_dword;
51461911Scokane	struct tdfx_softc* tdfx_info = NULL;
51561911Scokane
51661911Scokane	/* This one depend on the tdfx_* structs being properly initialized */
51761911Scokane
51861911Scokane	/*piod->device &= 0xf;*/
51961911Scokane	if((piod == NULL) ||(tdfx_count <= piod->device) ||
52061911Scokane			(piod->device < 0)) {
52161931Scokane#ifdef DEBUG
52261911Scokane		printf("tdfx: Bad device or internal struct in tdfx_query_fetch\n");
52361911Scokane#endif
52461911Scokane		return -EINVAL;
52561911Scokane	}
52661911Scokane
52761911Scokane	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
52861911Scokane			piod->device);
52961911Scokane
53061911Scokane	if(tdfx_info == NULL) return -ENXIO;
53161911Scokane
53261911Scokane	/* We must restrict the size reads from the port, since to high or low of a
53361911Scokane	 * size witll result in wrong data being passed, and that's bad */
53461911Scokane	/* A few of these were pulled during the attach phase */
53561911Scokane	switch(piod->port) {
53661911Scokane		case PCI_VENDOR_ID_FREEBSD:
53761911Scokane			if(piod->size != 2) return -EINVAL;
53861911Scokane			copyout(&tdfx_info->vendor, piod->value, piod->size);
53961911Scokane			return 0;
54061911Scokane		case PCI_DEVICE_ID_FREEBSD:
54161911Scokane			if(piod->size != 2) return -EINVAL;
54261911Scokane			copyout(&tdfx_info->type, piod->value, piod->size);
54361911Scokane			return 0;
54461911Scokane		case PCI_BASE_ADDRESS_0_FREEBSD:
54561911Scokane			if(piod->size != 4) return -EINVAL;
54661911Scokane			copyout(&tdfx_info->addr0, piod->value, piod->size);
54761911Scokane			return 0;
54865146Scokane		case PCI_BASE_ADDRESS_1_FREEBSD:
54965146Scokane			if(piod->size != 4) return -EINVAL;
55065146Scokane			copyout(&tdfx_info->addr1, piod->value, piod->size);
55165146Scokane			return 0;
55265146Scokane		case PCI_PRIBUS_FREEBSD:
55365146Scokane			if(piod->size != 1) return -EINVAL;
55465146Scokane			break;
55565146Scokane		case PCI_IOBASE_0_FREEBSD:
55665146Scokane			if(piod->size != 2) return -EINVAL;
55765146Scokane			break;
55865146Scokane		case PCI_IOLIMIT_0_FREEBSD:
55965146Scokane			if(piod->size != 2) return -EINVAL;
56065146Scokane			break;
56161911Scokane		case SST1_PCI_SPECIAL1_FREEBSD:
56261911Scokane			if(piod->size != 4) return -EINVAL;
56361911Scokane			break;
56461911Scokane		case PCI_REVISION_ID_FREEBSD:
56561911Scokane			if(piod->size != 1) return -EINVAL;
56661911Scokane			break;
56761911Scokane		case SST1_PCI_SPECIAL4_FREEBSD:
56861911Scokane			if(piod->size != 4) return -EINVAL;
56961911Scokane			break;
57061911Scokane		default:
57161911Scokane			return -EINVAL;
57261911Scokane	}
57361911Scokane
57461911Scokane
57561911Scokane	/* Read the value and return */
57661911Scokane	switch(piod->size) {
57761911Scokane		case 1:
57861911Scokane			ret_byte = pci_read_config(tdfx_info[piod->device].dev,
57961911Scokane					piod->port, 1);
58061911Scokane			copyout(&ret_byte, piod->value, 1);
58161911Scokane			break;
58261911Scokane		case 2:
58361911Scokane			ret_word = pci_read_config(tdfx_info[piod->device].dev,
58461911Scokane					piod->port, 2);
58561911Scokane			copyout(&ret_word, piod->value, 2);
58661911Scokane			break;
58761911Scokane		case 4:
58861911Scokane			ret_dword = pci_read_config(tdfx_info[piod->device].dev,
58961911Scokane					piod->port, 4);
59061911Scokane			copyout(&ret_dword, piod->value, 4);
59161911Scokane			break;
59261911Scokane		default:
59361911Scokane			return -EINVAL;
59461911Scokane	}
59561911Scokane	return 0;
59661911Scokane}
59761911Scokane
59861911Scokanestatic int
59961911Scokanetdfx_query_update(u_int cmd, struct tdfx_pio_data *piod)
60061911Scokane{
60161911Scokane	/* XXX Comment this later, after careful inspection and spring cleaning :) */
60261911Scokane	/* Return vals */
60361911Scokane	u_int8_t  ret_byte;
60461911Scokane	u_int16_t ret_word;
60561911Scokane	u_int32_t ret_dword;
60661911Scokane
60761911Scokane	/* Port vals, mask */
60861911Scokane	u_int32_t retval, preval, mask;
60961911Scokane	struct tdfx_softc* tdfx_info = NULL;
61061911Scokane
61161911Scokane
61261911Scokane	if((piod == NULL) || (piod->device >= (tdfx_count &
61361911Scokane					0xf))) {
61461931Scokane#ifdef DEBUG
61561911Scokane		printf("tdfx: Bad struct or device in tdfx_query_update\n");
61661911Scokane#endif
61761911Scokane		return -EINVAL;
61861911Scokane	}
61961911Scokane
62061911Scokane	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
62161911Scokane			piod->device);
62261911Scokane	if(tdfx_info == NULL) return -ENXIO;
62361911Scokane	/* Code below this line in the fuction was taken from the
62461911Scokane	 * Linux driver and converted for freebsd. */
62561911Scokane
62661911Scokane	/* Check the size for all the ports, to make sure stuff doesn't get messed up
62761911Scokane	 * by poorly written clients */
62861911Scokane
62961911Scokane	switch(piod->port) {
63061911Scokane		case PCI_COMMAND_FREEBSD:
63161911Scokane			if(piod->size != 2) return -EINVAL;
63261911Scokane			break;
63361911Scokane		case SST1_PCI_SPECIAL1_FREEBSD:
63461911Scokane			if(piod->size != 4) return -EINVAL;
63561911Scokane			break;
63661911Scokane		case SST1_PCI_SPECIAL2_FREEBSD:
63761911Scokane			if(piod->size != 4) return -EINVAL;
63861911Scokane			break;
63961911Scokane		case SST1_PCI_SPECIAL3_FREEBSD:
64061911Scokane			if(piod->size != 4) return -EINVAL;
64161911Scokane			break;
64261911Scokane		case SST1_PCI_SPECIAL4_FREEBSD:
64361911Scokane			if(piod->size != 4) return -EINVAL;
64461911Scokane			break;
64561911Scokane		default:
64661911Scokane			return -EINVAL;
64761911Scokane	}
64861911Scokane	/* Read the current value */
64961911Scokane	retval = pci_read_config(tdfx_info->dev, piod->port & ~3, 4);
65061911Scokane
65161911Scokane	/* These set up a mask to use, since apparently they wanted to write 4 bytes
65261911Scokane	 * at once to the ports */
65361911Scokane	switch (piod->size) {
65461911Scokane		case 1:
65561911Scokane			copyin(piod->value, &ret_byte, 1);
65661911Scokane			preval = ret_byte << (8 * (piod->port & 0x3));
65761911Scokane			mask = 0xff << (8 * (piod->port & 0x3));
65861911Scokane			break;
65961911Scokane		case 2:
66061911Scokane			copyin(piod->value, &ret_word, 2);
66161911Scokane			preval = ret_word << (8 * (piod->port & 0x3));
66261911Scokane			mask = 0xffff << (8 * (piod->port & 0x3));
66361911Scokane			break;
66461911Scokane		case 4:
66561911Scokane			copyin(piod->value, &ret_dword, 4);
66661911Scokane			preval = ret_dword;
66761911Scokane			mask = ~0;
66861911Scokane			break;
66961911Scokane		default:
67061911Scokane			return -EINVAL;
67161911Scokane	}
67261911Scokane	/* Finally, combine the values and write it to the port */
67361911Scokane	retval = (retval & ~mask) | preval;
67461911Scokane	pci_write_config(tdfx_info->dev, piod->port & ~3, retval, 4);
67561911Scokane
67661911Scokane	return 0;
67761911Scokane}
67861911Scokane
67963488Scokane/* For both of these, I added a variable named workport of type u_int so
68063488Scokane * that I could eliminate the warning about my data type size. The
68163488Scokane * applications expect the port to be of type short, so I needed to change
68263488Scokane * this within the function */
68361911Scokanestatic int
68461911Scokanetdfx_do_pio_rd(struct tdfx_pio_data *piod)
68561911Scokane{
68661911Scokane	/* Return val */
68761911Scokane	u_int8_t  ret_byte;
68863488Scokane	u_int 	 workport;
68965146Scokane	struct tdfx_softc *tdfx_info =
69065146Scokane		(struct tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device);
69165146Scokane
69261911Scokane	/* Restricts the access of ports other than those we use */
69365146Scokane	if(((piod->port != VGA_INPUT_STATUS_1C) || (piod->port != SC_INDEX) ||
69465146Scokane		(piod->port != SC_DATA) || (piod->port != VGA_MISC_OUTPUT_READ)) &&
69565146Scokane		(piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max))
69661911Scokane		return -EPERM;
69761911Scokane
69861911Scokane	/* All VGA STATUS REGS are byte registers, size should never be > 1 */
69961911Scokane	if(piod->size != 1) {
70061911Scokane		return -EINVAL;
70161911Scokane	}
70261911Scokane
70361911Scokane	/* Write the data to the intended port */
70463488Scokane	workport = piod->port;
70563488Scokane	ret_byte = inb(workport);
70661911Scokane	copyout(&ret_byte, piod->value, sizeof(u_int8_t));
70761911Scokane	return 0;
70861911Scokane}
70961911Scokane
71061911Scokanestatic int
71161911Scokanetdfx_do_pio_wt(struct tdfx_pio_data *piod)
71261911Scokane{
71361911Scokane	/* return val */
71461911Scokane	u_int8_t  ret_byte;
71563488Scokane	u_int		 workport;
71665146Scokane	struct tdfx_softc *tdfx_info = (struct
71765146Scokane			tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device);
71861911Scokane	/* Replace old switch w/ massive if(...) */
71961911Scokane	/* Restricts the access of ports other than those we use */
72065146Scokane	if(((piod->port != SC_INDEX) && (piod->port != SC_DATA) &&
72165146Scokane		(piod->port != VGA_MISC_OUTPUT_READ)) /* Can't write VGA_ST_1C */ &&
72265146Scokane		(piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max))
72361911Scokane		return -EPERM;
72461911Scokane
72561911Scokane	/* All VGA STATUS REGS are byte registers, size should never be > 1 */
72661911Scokane	if(piod->size != 1) {
72761911Scokane		return -EINVAL;
72861911Scokane	}
72961911Scokane
73061911Scokane	/* Write the data to the intended port */
73161911Scokane	copyin(piod->value, &ret_byte, sizeof(u_int8_t));
73263488Scokane	workport = piod->port;
73363488Scokane	outb(workport, ret_byte);
73461911Scokane	return 0;
73561911Scokane}
73661911Scokane
73761911Scokanestatic int
73861911Scokanetdfx_do_query(u_int cmd, struct tdfx_pio_data *piod)
73961911Scokane{
74061911Scokane	/* There are three sub-commands to the query 0x33 */
74161911Scokane	switch(_IOC_NR(cmd)) {
74261911Scokane		case 2:
74361911Scokane			return tdfx_query_boards();
74461911Scokane			break;
74561911Scokane		case 3:
74661911Scokane			return tdfx_query_fetch(cmd, piod);
74761911Scokane			break;
74861911Scokane		case 4:
74961911Scokane			return tdfx_query_update(cmd, piod);
75061911Scokane			break;
75161911Scokane		default:
75261911Scokane			/* In case we are thrown a bogus sub-command! */
75361931Scokane#ifdef DEBUG
75461911Scokane			printf("Bad Sub-cmd: 0x%x\n", _IOC_NR(cmd));
75561911Scokane#endif
75661911Scokane			return -EINVAL;
757115494Sphk	}
75861911Scokane}
75961911Scokane
76061911Scokanestatic int
76161911Scokanetdfx_do_pio(u_int cmd, struct tdfx_pio_data *piod)
76261911Scokane{
76361911Scokane	/* Two types of PIO, INPUT and OUTPUT, as the name suggests */
76461911Scokane	switch(_IOC_DIR(cmd)) {
76561911Scokane		case IOCV_OUT:
76661911Scokane			return tdfx_do_pio_rd(piod);
76761911Scokane			break;
76861911Scokane		case IOCV_IN:
76961911Scokane			return tdfx_do_pio_wt(piod);
77061911Scokane			break;
77161911Scokane		default:
77261911Scokane			return -EINVAL;
773115494Sphk	}
77461911Scokane}
77561911Scokane
77661911Scokane/* Calls to ioctl(2) eventually end up here. Unhandled ioctls return an ENXIO,
77761911Scokane * normally, you would read in the data pointed to by data, then write your
77861911Scokane * output to it. The ioctl *should* normally return zero if everything is
77961911Scokane * alright, but 3dfx didn't make it that way...
78061911Scokane *
78161911Scokane * For all of the ioctl code, in the event of a real error,
78261911Scokane * we return -Exxxx rather than simply Exxxx. The reason for this
78361911Scokane * is that the ioctls actually RET information back to the program
78461911Scokane * sometimes, rather than filling it in the passed structure. We
78561911Scokane * want to distinguish errors from useful data, and maintain compatibility.
78661911Scokane *
78761911Scokane * There is this portion of the proc struct called p_retval[], we can store a
78883366Sjulian * return value in td->td_retval[0] and place the return value if it is positive
78961911Scokane * in there, then we can return 0 (good). If the return value is negative, we
79061911Scokane * can return -retval and the error should be properly handled.
79161911Scokane */
79261911Scokanestatic int
79383366Sjuliantdfx_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
79461911Scokane{
79561911Scokane	int retval = 0;
79661911Scokane	struct tdfx_pio_data *piod = (struct tdfx_pio_data*)data;
79761931Scokane#ifdef	DEBUG
798106578Sjhb	printf("IOCTL'd by #%d, cmd: 0x%x, data: %p\n", td->td_proc->p_pid, (u_int32_t)cmd,
799106578Sjhb			piod);
80061911Scokane#endif
80161911Scokane	switch(_IOC_TYPE(cmd)) {
80261911Scokane		/* Return the real error if negative, or simply stick the valid return
80383366Sjulian		 * in td->td_retval */
80461911Scokane	case 0x33:
80561911Scokane			/* The '3'(0x33) type IOCTL is for querying the installed cards */
80683366Sjulian			if((retval = tdfx_do_query(cmd, piod)) > 0) td->td_retval[0] = retval;
80761911Scokane			else return -retval;
80861911Scokane			break;
80961911Scokane		case 0:
81061911Scokane			/* The 0 type IOCTL is for programmed I/O methods */
81183366Sjulian			if((tdfx_do_pio(cmd, piod)) > 0) td->td_retval[0] = retval;
81261911Scokane			else return -retval;
81361911Scokane			break;
81461911Scokane		default:
81561911Scokane			/* Technically, we won't reach this from linux emu, but when glide
81661911Scokane			 * finally gets ported, watch out! */
81761931Scokane#ifdef DEBUG
81883366Sjulian			printf("Bad IOCTL from #%d\n", td->td_proc->p_pid);
81961911Scokane#endif
82061911Scokane			return ENXIO;
82161911Scokane	}
82261911Scokane
82361911Scokane	return 0;
82461911Scokane}
82561911Scokane
82661931Scokane#ifdef TDFX_LINUX
82761931Scokane/*
82861931Scokane * Linux emulation IOCTL for /dev/tdfx
82961931Scokane */
83061931Scokanestatic int
83183366Sjulianlinux_ioctl_tdfx(struct thread *td, struct linux_ioctl_args* args)
83261931Scokane{
83361931Scokane   int error = 0;
83461931Scokane   u_long cmd = args->cmd & 0xffff;
83561911Scokane
83661931Scokane   /* The structure passed to ioctl has two shorts, one int
83761931Scokane      and one void*. */
83861931Scokane   char d_pio[2*sizeof(short) + sizeof(int) + sizeof(void*)];
83961931Scokane
84089306Salfred   struct file *fp;
84161931Scokane
84289319Salfred   if ((error = fget(td, args->fd, &fp)) != 0)
84389319Salfred	   return (error);
84461931Scokane   /* We simply copy the data and send it right to ioctl */
84561931Scokane   copyin((caddr_t)args->arg, &d_pio, sizeof(d_pio));
846102003Srwatson   error = fo_ioctl(fp, cmd, (caddr_t)&d_pio, td->td_ucred, td);
84789306Salfred   fdrop(fp, td);
84861931Scokane   return error;
84961931Scokane}
85061931Scokane#endif /* TDFX_LINUX */
85161931Scokane
85261931Scokane
85361911Scokane/* This is the device driver struct. This is sent to the driver subsystem to
85461911Scokane * register the method structure and the info strcut space for this particular
85561911Scokane * instance of the driver.
85661911Scokane */
85761911Scokanestatic driver_t tdfx_driver = {
85861911Scokane	"tdfx",
85961911Scokane	tdfx_methods,
86061911Scokane	sizeof(struct tdfx_softc),
86161911Scokane};
86261911Scokane
86361911Scokane/* Tell Mr. Kernel about us! */
86461911ScokaneDRIVER_MODULE(tdfx, pci, tdfx_driver, tdfx_devclass, 0, 0);
865