tdfx_pci.c revision 62028
161931Scokane/*
261931Scokane * Copyright (c) 2000 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 62028 2000-06-24 06:20:55Z cokane $
3261931Scokane */
3361931Scokane
3461911Scokane/* 3dfx driver for FreeBSD 4.x - Finished 11 May 2000, 12:25AM ET
3561911Scokane *
3661931Scokane * Copyright (C) 2000, 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/*
4261911Scokane * put this here, so as to bail out immediately if we have no PCI BUS installed
4361911Scokane */
4461911Scokane#include	"pci.h"
4561911Scokane#if NPCI > 0
4661911Scokane
4761911Scokane#include <sys/param.h>
4861911Scokane
4961911Scokane#include <sys/bus_private.h>
5061911Scokane#include <sys/bus.h>
5161911Scokane#include <sys/cdefs.h>
5261911Scokane#include <sys/conf.h>
5361911Scokane#include <sys/fcntl.h>
5461911Scokane#include <sys/file.h>
5561911Scokane#include <sys/filedesc.h>
5661911Scokane#include <sys/filio.h>
5761911Scokane#include <sys/ioccom.h>
5861911Scokane#include <sys/kernel.h>
5961911Scokane#include	<sys/malloc.h>
6061911Scokane#include <sys/mman.h>
6161911Scokane#include <sys/signalvar.h>
6261911Scokane#include <sys/systm.h>
6361911Scokane#include <sys/uio.h>
6461911Scokane
6561911Scokane#include <pci/pcivar.h>
6661911Scokane#include <pci/pcireg.h>
6761911Scokane
6861911Scokane#include <vm/vm.h>
6961911Scokane#include <vm/vm_kern.h>
7061911Scokane#include <vm/pmap.h>
7161911Scokane#include <vm/vm_extern.h>
7261911Scokane
7361911Scokane/* rman.h depends on machine/bus.h */
7461911Scokane#include <machine/resource.h>
7561911Scokane#include <machine/bus.h>
7661911Scokane#include <sys/rman.h>
7762028Scokane
7862028Scokane/* This must come first */
7962028Scokane#include "opt_tdfx.h"
8061931Scokane#ifdef TDFX_LINUX
8161931Scokane#include <dev/tdfx/tdfx_linux.h>
8261931Scokane#endif
8361911Scokane
8461911Scokane#include <dev/tdfx/tdfx_io.h>
8561911Scokane#include <dev/tdfx/tdfx_vars.h>
8661911Scokane#include <dev/tdfx/tdfx_pci.h>
8761911Scokane
8861911Scokane
8961911Scokanestatic devclass_t tdfx_devclass;
9061911Scokane
9161911Scokane
9261911Scokanestatic int tdfx_count = 0;
9361911Scokane
9461911Scokane
9561911Scokane/* Set up the boot probe/attach routines */
9661911Scokanestatic device_method_t tdfx_methods[] = {
9761911Scokane	DEVMETHOD(device_probe,		tdfx_probe),
9861911Scokane	DEVMETHOD(device_attach,	tdfx_attach),
9961911Scokane	DEVMETHOD(device_detach,	tdfx_detach),
10061911Scokane	DEVMETHOD(device_shutdown,	tdfx_shutdown),
10161911Scokane	{ 0, 0 }
10261911Scokane};
10361911Scokane
10461911ScokaneMALLOC_DEFINE(M_TDFX,"TDFX Driver","3DFX Graphics[/2D]/3D Accelerator(s)");
10561911Scokane
10661931Scokane#ifdef TDFX_LINUX
10761989ScokaneMODULE_DEPEND(tdfx, linux, 1, 1, 1);
10861931ScokaneLINUX_IOCTL_SET(tdfx, LINUX_IOCTL_TDFX_MIN, LINUX_IOCTL_TDFX_MAX);
10961931Scokane#endif
11061931Scokane
11161911Scokane/* Char. Dev. file operations structure */
11261911Scokanestatic struct cdevsw tdfx_cdev = {
11361911Scokane	tdfx_open,		/* open */
11461911Scokane	tdfx_close,		/* close */
11561911Scokane	noread,			/* read */
11661911Scokane	nowrite,			/* write */
11761911Scokane	tdfx_ioctl,		/* ioctl */
11861911Scokane	nopoll,			/* poll */
11961911Scokane	tdfx_mmap,		/* mmap */
12061911Scokane	nostrategy,		/* strategy */
12161911Scokane	"tdfx",			/* dev name */
12261911Scokane	CDEV_MAJOR, 	/* char major */
12361911Scokane	nodump,			/* dump */
12461911Scokane	nopsize,			/* size */
12561911Scokane	0,					/* flags (no set flags) */
12661911Scokane	-1 				/* bmaj (no block dev) */
12761911Scokane};
12861911Scokane
12961911Scokanestatic int
13061911Scokanetdfx_probe(device_t dev)
13161911Scokane{
13261911Scokane	/*
13361911Scokane	 * probe routine called on kernel boot to register supported devices. We get
13461911Scokane	 * a device structure to work with, and we can test the VENDOR/DEVICE IDs to
13561911Scokane	 * see if this PCI device is one that we support. Return 0 if yes, ENXIO if
13661911Scokane	 * not.
13761911Scokane	 */
13861911Scokane	switch(pci_get_devid(dev)) {
13961911Scokane	case PCI_DEVICE_ALLIANCE_AT3D:
14061911Scokane		device_set_desc(dev, "ProMotion At3D 3D Accelerator");
14161911Scokane		return 0;
14261911Scokane	case PCI_DEVICE_3DFX_VOODOO2:
14361911Scokane		device_set_desc(dev, "3DFX Voodoo II 3D Accelerator");
14461911Scokane		return 0;
14561911Scokane	case PCI_DEVICE_3DFX_BANSHEE:
14661911Scokane		device_set_desc(dev, "3DFX Voodoo Banshee 2D/3D Graphics Accelerator");
14761911Scokane		return 0;
14861911Scokane	case PCI_DEVICE_3DFX_VOODOO3:
14961911Scokane		device_set_desc(dev, "3DFX Voodoo3 2D/3D Graphics Accelerator");
15061911Scokane		return 0;
15161911Scokane	case PCI_DEVICE_3DFX_VOODOO1:
15261911Scokane		device_set_desc(dev, "3DFX Voodoo Graphics 3D Accelerator");
15361911Scokane		return 0;;
15461911Scokane	};
15561911Scokane
15661911Scokane	return ENXIO;
15761911Scokane}
15861911Scokane
15961911Scokanestatic int
16061911Scokanetdfx_attach(device_t dev) {
16161911Scokane	/*
16261911Scokane	 * The attach routine is called after the probe routine successfully says it
16361911Scokane	 * supports a given card. We now proceed to initialize this card for use with
16461911Scokane	 * the system. I want to map the device memory for userland allocation and
16561911Scokane	 * fill an information structure with information on this card. I'd also like
16661911Scokane	 * to set Write Combining with the MTRR code so that we can hopefully speed
16761911Scokane	 * up memory writes. The last thing is to register the character device
16861911Scokane	 * interface to the card, so we can open it from /dev/3dfxN, where N is a
16961911Scokane	 * small, whole number.
17061911Scokane	 */
17161911Scokane
17261911Scokane	struct tdfx_softc *tdfx_info;
17361911Scokane	u_long	val;
17461911Scokane	/* rid value tells bus_alloc_resource where to find the addresses of ports or
17561911Scokane	 * of memory ranges in the PCI config space*/
17661911Scokane	int rid = PCIR_MAPS;
17761911Scokane
17861911Scokane	/* Increment the card counter (for the ioctl code) */
17961911Scokane	tdfx_count++;
18061911Scokane
18161911Scokane 	/* Enable MemMap on Voodoo */
18261911Scokane	val = pci_read_config(dev, PCIR_COMMAND, 2);
18361911Scokane	val |= (PCIM_CMD_MEMEN);
18461911Scokane	pci_write_config(dev, PCIR_COMMAND, val, 2);
18561911Scokane	val = pci_read_config(dev, PCIR_COMMAND, 2);
18661911Scokane
18761911Scokane	/* Fill the soft config struct with info about this device*/
18861911Scokane	tdfx_info = device_get_softc(dev);
18961911Scokane	tdfx_info->dev = dev;
19061911Scokane	tdfx_info->vendor = pci_get_vendor(dev);
19161911Scokane	tdfx_info->type = pci_get_devid(dev) >> 16;
19261911Scokane	tdfx_info->bus = pci_get_bus(dev);
19361911Scokane	tdfx_info->dv = pci_get_slot(dev);
19461911Scokane	tdfx_info->curFile = NULL;
19561911Scokane
19661911Scokane	/*
19761911Scokane	 *	Get the Memory Location from the PCI Config, mask out lower word, since
19861911Scokane	 * the config space register is only one word long (this is nicer than a
19961911Scokane	 * bitshift).
20061911Scokane	 */
20161911Scokane	tdfx_info->addr0 = (pci_read_config(dev, 0x10, 4) & 0xffff0000);
20261931Scokane#ifdef DEBUG
20361911Scokane	device_printf(dev, "Base0 @ 0x%x\n", tdfx_info->addr0);
20461911Scokane#endif
20561911Scokane
20661911Scokane	/* Notify the VM that we will be mapping some memory later */
20761911Scokane	tdfx_info->memrange = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, ~0, 1,
20861967Scokane			RF_ACTIVE | RF_SHAREABLE);
20961911Scokane	if(tdfx_info->memrange == NULL) {
21061931Scokane#ifdef DEBUG
21161911Scokane		device_printf(dev, "Error mapping mem, won't be able to use mmap()\n");
21261911Scokane#endif
21361911Scokane		tdfx_info->memrid = 0;
21461911Scokane	}
21561911Scokane	else {
21661911Scokane		tdfx_info->memrid = rid;
21761931Scokane#ifdef DEBUG
21861911Scokane		device_printf(dev, "Mapped to: 0x%x\n",
21961911Scokane				(unsigned int)rman_get_start(tdfx_info->memrange));
22061911Scokane#endif
22161911Scokane	}
22261911Scokane
22361911Scokane	/*
22461911Scokane	 *	Set Writecombining, or at least Uncacheable for the memory region, if we
22561911Scokane	 * are able to
22661911Scokane	 */
22761911Scokane
22861911Scokane	if(tdfx_setmtrr(dev) != 0) {
22961931Scokane#ifdef DEBUG
23061911Scokane		device_printf(dev, "Some weird error setting MTRRs");
23161911Scokane#endif
23261911Scokane		return -1;
23361911Scokane	}
23461911Scokane
23561911Scokane	/*
23661911Scokane	 * make_dev registers the cdev to access the 3dfx card from /dev
23761911Scokane	 *	use hex here for the dev num, simply to provide better support if > 10
23861911Scokane	 * voodoo cards, for the mad. The user must set the link, or use MAKEDEV.
23961911Scokane	 * Why would we want that many voodoo cards anyhow?
24061911Scokane	 */
24161989Scokane	tdfx_info->devt = make_dev(&tdfx_cdev, dev->unit, 0, 0, 02660,
24261989Scokane		"3dfx%x", dev->unit);
24361911Scokane
24461911Scokane	return 0;
24561911Scokane}
24661911Scokane
24761911Scokanestatic int
24861911Scokanetdfx_detach(device_t dev) {
24961911Scokane	struct tdfx_softc* tdfx_info;
25061911Scokane	int retval;
25161911Scokane	tdfx_info = device_get_softc(dev);
25261911Scokane
25361911Scokane	/* Delete allocated resource, of course */
25461911Scokane	bus_release_resource(dev, SYS_RES_MEMORY, PCI_MAP_REG_START,
25561911Scokane			tdfx_info->memrange);
25661911Scokane
25761911Scokane	/* Though it is safe to leave the WRCOMB support since the
25861911Scokane		mem driver checks for it, we should remove it in order
25961911Scokane		to free an MTRR for another device */
26061911Scokane	retval = tdfx_clrmtrr(dev);
26161931Scokane#ifdef DEBUG
26261911Scokane	if(retval != 0)
26361911Scokane		printf("tdfx: For some reason, I couldn't clear the mtrr\n");
26461911Scokane#endif
26561989Scokane	/* Remove device entry when it can no longer be accessed */
26661989Scokane   destroy_dev(tdfx_info->devt);
26761911Scokane	return(0);
26861911Scokane}
26961911Scokane
27061911Scokanestatic int
27161911Scokanetdfx_shutdown(device_t dev) {
27261931Scokane#ifdef DEBUG
27361911Scokane	device_printf(dev, "tdfx: Device Shutdown\n");
27461911Scokane#endif
27561911Scokane	return 0;
27661911Scokane}
27761911Scokane
27861911Scokanestatic int
27961911Scokanetdfx_clrmtrr(device_t dev) {
28061911Scokane	/* This function removes the MTRR set by the attach call, so it can be used
28161911Scokane	 * in the future by other drivers.
28261911Scokane	 */
28361911Scokane	int retval, act;
28461911Scokane	struct tdfx_softc *tdfx_info = device_get_softc(dev);
28561911Scokane
28661911Scokane	act = MEMRANGE_SET_REMOVE;
28761911Scokane	retval = mem_range_attr_set(&tdfx_info->mrdesc, &act);
28861911Scokane	return retval;
28961911Scokane}
29061911Scokane
29161911Scokanestatic int
29261911Scokanetdfx_setmtrr(device_t dev) {
29361911Scokane	/*
29461911Scokane	 * This is the MTRR setting function for the 3dfx card. It is called from
29561911Scokane	 * tdfx_attach. If we can't set the MTRR properly, it's not the end of the
29661911Scokane	 * world. We can still continue, just with slightly (very slightly) degraded
29761911Scokane	 * performance.
29861911Scokane	 */
29961911Scokane	int retval = 0, act;
30061911Scokane	struct tdfx_softc *tdfx_info = device_get_softc(dev);
30161911Scokane	/* The memory descriptor is described as the top 15 bits of the real
30261911Scokane		address */
30361911Scokane	tdfx_info->mrdesc.mr_base = pci_read_config(dev, 0x10, 4) & 0xfffe0000;
30461911Scokane
30561911Scokane	/* The older Voodoo cards have a shorter memrange than the newer ones */
30661911Scokane	if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1) || (pci_get_devid(dev) ==
30761911Scokane			PCI_DEVICE_3DFX_VOODOO2))
30861911Scokane		tdfx_info->mrdesc.mr_len = 0x400000;
30961911Scokane	else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) ||
31061911Scokane			(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE))
31161911Scokane		tdfx_info->mrdesc.mr_len = 0x1000000;
31261911Scokane
31361911Scokane	else return 0;
31461911Scokane	/*
31561911Scokane    *	The Alliance Pro Motion AT3D was not mentioned in the linux
31661911Scokane	 * driver as far as MTRR support goes, so I just won't put the
31761911Scokane	 * code in here for it. This is where it should go, though.
31861911Scokane	 */
31961911Scokane
32061911Scokane	/* Firstly, try to set write combining */
32161911Scokane	tdfx_info->mrdesc.mr_flags = MDF_WRITECOMBINE;
32261911Scokane	bcopy("tdfx", &tdfx_info->mrdesc.mr_owner, 4);
32361911Scokane	act = MEMRANGE_SET_UPDATE;
32461911Scokane	retval = mem_range_attr_set(&tdfx_info->mrdesc, &act);
32561911Scokane
32661911Scokane	if(retval == 0) {
32761931Scokane#ifdef DEBUG
32861911Scokane		device_printf(dev, "MTRR Set Correctly for tdfx\n");
32961911Scokane#endif
33061911Scokane	} else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO2) ||
33161911Scokane		(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1)) {
33261911Scokane		/* if, for some reason we can't set the WRCOMB range with the V1/V2, we
33361911Scokane		 * can still possibly use the UNCACHEABLE region for it instead, and help
33461911Scokane		 * out in a small way */
33561911Scokane		tdfx_info->mrdesc.mr_flags = MDF_UNCACHEABLE;
33661911Scokane		/* This length of 1000h was taken from the linux device driver... */
33761911Scokane		tdfx_info->mrdesc.mr_len = 0x1000;
33861911Scokane
33961911Scokane		/*
34061911Scokane		 * If, for some reason, we can't set the MTRR (N/A?) we may still continue
34161911Scokane		 */
34261931Scokane#ifdef DEBUG
34361911Scokane		if(retval == 0) {
34461911Scokane			device_printf(dev, "MTRR Set Type Uncacheable
34561911Scokane					%x\n", (u_int32_t)tdfx_info->mrdesc.mr_base);
34661911Scokane		} else {
34761911Scokane			device_printf(dev, "Couldn't Set MTRR\n");
34861911Scokane		}
34961911Scokane#endif
35061911Scokane	}
35161931Scokane#ifdef DEBUG
35261911Scokane	else {
35361911Scokane		device_printf(dev, "Couldn't Set MTRR\n");
35461911Scokane		return 0;
35561911Scokane	}
35661911Scokane#endif
35761911Scokane	return 0;
35861911Scokane}
35961911Scokane
36061911Scokanestatic int
36161911Scokanetdfx_open(dev_t dev, int flags, int fmt, struct proc *p)
36261911Scokane{
36361911Scokane	/*
36461911Scokane	 *	The open cdev method handles open(2) calls to /dev/3dfx[n]
36561911Scokane	 * We can pretty much allow any opening of the device.
36661911Scokane	 */
36761911Scokane	struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass,
36861911Scokane			UNIT(minor(dev)));
36961911Scokane	if(tdfx_info->busy != 0) return EBUSY;
37061931Scokane#ifdef	DEBUG
37161911Scokane	printf("3dfx: Opened by #%d\n", p->p_pid);
37261911Scokane#endif
37361911Scokane	/* Set the driver as busy */
37461911Scokane	tdfx_info->busy++;
37561911Scokane	return 0;
37661911Scokane}
37761911Scokane
37861911Scokanestatic int
37961911Scokanetdfx_close(dev_t dev, int fflag, int devtype, struct proc* p)
38061911Scokane{
38161911Scokane	/*
38261911Scokane	 *	The close cdev method handles close(2) calls to /dev/3dfx[n]
38361911Scokane	 * We'll always want to close the device when it's called.
38461911Scokane	 */
38561911Scokane	struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass,
38661911Scokane		UNIT(minor(dev)));
38761911Scokane	if(tdfx_info->busy == 0) return EBADF;
38861911Scokane	tdfx_info->busy = 0;
38961931Scokane#ifdef	DEBUG
39061911Scokane	printf("Closed by #%d\n", p->p_pid);
39161911Scokane#endif
39261911Scokane	return 0;
39361911Scokane}
39461911Scokane
39561911Scokanestatic int
39661911Scokanetdfx_mmap(dev_t dev, vm_offset_t offset, int nprot)
39761911Scokane{
39861911Scokane	/*
39961911Scokane	 * mmap(2) is called by a user process to request that an area of memory
40061911Scokane	 * associated with this device be mapped for the process to work with. Nprot
40161911Scokane	 * holds the protections requested, PROT_READ, PROT_WRITE, or both.
40261911Scokane	 */
40361911Scokane	struct tdfx_softc* tdfx_info;
40461911Scokane
40561911Scokane	/* Get the configuration for our card XXX*/
40661911Scokane	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
40761911Scokane			UNIT(minor(dev)));
40861911Scokane
40961911Scokane	/* If, for some reason, its not configured, we bail out */
41061911Scokane	if(tdfx_info == NULL) {
41161931Scokane#ifdef	DEBUG
41261911Scokane	   printf("tdfx: tdfx_info (softc) is NULL\n");
41361911Scokane#endif
41461911Scokane	   return -1;
41561911Scokane	}
41661911Scokane
41761911Scokane	/* We must stay within the bound of our address space */
41861911Scokane	if((offset & 0xff000000) == tdfx_info->addr0)
41961911Scokane		offset &= 0xffffff;
42061911Scokane	if((offset >= 0x1000000) || (offset < 0)) {
42161931Scokane#ifdef  DEBUG
42261911Scokane	   printf("tdfx: offset %x out of range\n", offset);
42361911Scokane#endif
42461911Scokane	   return -1;
42561911Scokane	}
42661911Scokane
42761911Scokane	/* atop -> address to page
42861911Scokane	 * rman_get_start, get the (struct resource*)->r_start member,
42961911Scokane	 * the mapping base address.
43061911Scokane	 */
43161911Scokane	return atop(rman_get_start(tdfx_info->memrange) + offset);
43261911Scokane}
43361911Scokane
43461911Scokanestatic int
43561911Scokanetdfx_query_boards(void) {
43661911Scokane	/*
43761911Scokane    *	This returns the number of installed tdfx cards, we have been keeping
43861911Scokane	 * count, look at tdfx_attach
43961911Scokane	 */
44061911Scokane	return tdfx_count;
44161911Scokane}
44261911Scokane
44361911Scokanestatic int
44461911Scokanetdfx_query_fetch(u_int cmd, struct tdfx_pio_data *piod)
44561911Scokane{
44661911Scokane	/* XXX Comment this later, after careful inspection and spring cleaning :) */
44761911Scokane	/* Various return values 8bit-32bit */
44861911Scokane	u_int8_t  ret_byte;
44961911Scokane	u_int16_t ret_word;
45061911Scokane	u_int32_t ret_dword;
45161911Scokane	struct tdfx_softc* tdfx_info = NULL;
45261911Scokane
45361911Scokane	/* This one depend on the tdfx_* structs being properly initialized */
45461911Scokane
45561911Scokane	/*piod->device &= 0xf;*/
45661911Scokane	if((piod == NULL) ||(tdfx_count <= piod->device) ||
45761911Scokane			(piod->device < 0)) {
45861931Scokane#ifdef DEBUG
45961911Scokane		printf("tdfx: Bad device or internal struct in tdfx_query_fetch\n");
46061911Scokane#endif
46161911Scokane		return -EINVAL;
46261911Scokane	}
46361911Scokane
46461911Scokane	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
46561911Scokane			piod->device);
46661911Scokane
46761911Scokane	if(tdfx_info == NULL) return -ENXIO;
46861911Scokane
46961911Scokane	/* We must restrict the size reads from the port, since to high or low of a
47061911Scokane	 * size witll result in wrong data being passed, and that's bad */
47161911Scokane	/* A few of these were pulled during the attach phase */
47261911Scokane	switch(piod->port) {
47361911Scokane		case PCI_VENDOR_ID_FREEBSD:
47461911Scokane			if(piod->size != 2) return -EINVAL;
47561911Scokane			copyout(&tdfx_info->vendor, piod->value, piod->size);
47661911Scokane			return 0;
47761911Scokane		case PCI_DEVICE_ID_FREEBSD:
47861911Scokane			if(piod->size != 2) return -EINVAL;
47961911Scokane			copyout(&tdfx_info->type, piod->value, piod->size);
48061911Scokane			return 0;
48161911Scokane		case PCI_BASE_ADDRESS_0_FREEBSD:
48261911Scokane			if(piod->size != 4) return -EINVAL;
48361911Scokane			copyout(&tdfx_info->addr0, piod->value, piod->size);
48461911Scokane			return 0;
48561911Scokane		case SST1_PCI_SPECIAL1_FREEBSD:
48661911Scokane			if(piod->size != 4) return -EINVAL;
48761911Scokane			break;
48861911Scokane		case PCI_REVISION_ID_FREEBSD:
48961911Scokane			if(piod->size != 1) return -EINVAL;
49061911Scokane			break;
49161911Scokane		case SST1_PCI_SPECIAL4_FREEBSD:
49261911Scokane			if(piod->size != 4) return -EINVAL;
49361911Scokane			break;
49461911Scokane		default:
49561911Scokane			return -EINVAL;
49661911Scokane	}
49761911Scokane
49861911Scokane
49961911Scokane	/* Read the value and return */
50061911Scokane	switch(piod->size) {
50161911Scokane		case 1:
50261911Scokane			ret_byte = pci_read_config(tdfx_info[piod->device].dev,
50361911Scokane					piod->port, 1);
50461911Scokane			copyout(&ret_byte, piod->value, 1);
50561911Scokane			break;
50661911Scokane		case 2:
50761911Scokane			ret_word = pci_read_config(tdfx_info[piod->device].dev,
50861911Scokane					piod->port, 2);
50961911Scokane			copyout(&ret_word, piod->value, 2);
51061911Scokane			break;
51161911Scokane		case 4:
51261911Scokane			ret_dword = pci_read_config(tdfx_info[piod->device].dev,
51361911Scokane					piod->port, 4);
51461911Scokane			copyout(&ret_dword, piod->value, 4);
51561911Scokane			break;
51661911Scokane		default:
51761911Scokane			return -EINVAL;
51861911Scokane	}
51961911Scokane	return 0;
52061911Scokane}
52161911Scokane
52261911Scokanestatic int
52361911Scokanetdfx_query_update(u_int cmd, struct tdfx_pio_data *piod)
52461911Scokane{
52561911Scokane	/* XXX Comment this later, after careful inspection and spring cleaning :) */
52661911Scokane	/* Return vals */
52761911Scokane	u_int8_t  ret_byte;
52861911Scokane	u_int16_t ret_word;
52961911Scokane	u_int32_t ret_dword;
53061911Scokane
53161911Scokane	/* Port vals, mask */
53261911Scokane	u_int32_t retval, preval, mask;
53361911Scokane	struct tdfx_softc* tdfx_info = NULL;
53461911Scokane
53561911Scokane
53661911Scokane	if((piod == NULL) || (piod->device >= (tdfx_count &
53761911Scokane					0xf))) {
53861931Scokane#ifdef DEBUG
53961911Scokane		printf("tdfx: Bad struct or device in tdfx_query_update\n");
54061911Scokane#endif
54161911Scokane		return -EINVAL;
54261911Scokane	}
54361911Scokane
54461911Scokane	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
54561911Scokane			piod->device);
54661911Scokane	if(tdfx_info == NULL) return -ENXIO;
54761911Scokane	/* Code below this line in the fuction was taken from the
54861911Scokane	 * Linux driver and converted for freebsd. */
54961911Scokane
55061911Scokane	/* Check the size for all the ports, to make sure stuff doesn't get messed up
55161911Scokane	 * by poorly written clients */
55261911Scokane
55361911Scokane	switch(piod->port) {
55461911Scokane		case PCI_COMMAND_FREEBSD:
55561911Scokane			if(piod->size != 2) return -EINVAL;
55661911Scokane			break;
55761911Scokane		case SST1_PCI_SPECIAL1_FREEBSD:
55861911Scokane			if(piod->size != 4) return -EINVAL;
55961911Scokane			break;
56061911Scokane		case SST1_PCI_SPECIAL2_FREEBSD:
56161911Scokane			if(piod->size != 4) return -EINVAL;
56261911Scokane			break;
56361911Scokane		case SST1_PCI_SPECIAL3_FREEBSD:
56461911Scokane			if(piod->size != 4) return -EINVAL;
56561911Scokane			break;
56661911Scokane		case SST1_PCI_SPECIAL4_FREEBSD:
56761911Scokane			if(piod->size != 4) return -EINVAL;
56861911Scokane			break;
56961911Scokane		default:
57061911Scokane			return -EINVAL;
57161911Scokane	}
57261911Scokane	/* Read the current value */
57361911Scokane	retval = pci_read_config(tdfx_info->dev, piod->port & ~3, 4);
57461911Scokane
57561911Scokane	/* These set up a mask to use, since apparently they wanted to write 4 bytes
57661911Scokane	 * at once to the ports */
57761911Scokane	switch (piod->size) {
57861911Scokane		case 1:
57961911Scokane			copyin(piod->value, &ret_byte, 1);
58061911Scokane			preval = ret_byte << (8 * (piod->port & 0x3));
58161911Scokane			mask = 0xff << (8 * (piod->port & 0x3));
58261911Scokane			break;
58361911Scokane		case 2:
58461911Scokane			copyin(piod->value, &ret_word, 2);
58561911Scokane			preval = ret_word << (8 * (piod->port & 0x3));
58661911Scokane			mask = 0xffff << (8 * (piod->port & 0x3));
58761911Scokane			break;
58861911Scokane		case 4:
58961911Scokane			copyin(piod->value, &ret_dword, 4);
59061911Scokane			preval = ret_dword;
59161911Scokane			mask = ~0;
59261911Scokane			break;
59361911Scokane		default:
59461911Scokane			return -EINVAL;
59561911Scokane	}
59661911Scokane	/* Finally, combine the values and write it to the port */
59761911Scokane	retval = (retval & ~mask) | preval;
59861911Scokane	pci_write_config(tdfx_info->dev, piod->port & ~3, retval, 4);
59961911Scokane
60061911Scokane	return 0;
60161911Scokane}
60261911Scokane
60361911Scokanestatic int
60461911Scokanetdfx_do_pio_rd(struct tdfx_pio_data *piod)
60561911Scokane{
60661911Scokane	/* Return val */
60761911Scokane	u_int8_t  ret_byte;
60861911Scokane
60961911Scokane	/* Restricts the access of ports other than those we use */
61061911Scokane	if((piod->port != VGA_INPUT_STATUS_1C) || (piod->port != SC_INDEX) ||
61161911Scokane		(piod->port != SC_DATA) || (piod->port != VGA_MISC_OUTPUT_READ))
61261911Scokane		return -EPERM;
61361911Scokane
61461911Scokane	/* All VGA STATUS REGS are byte registers, size should never be > 1 */
61561911Scokane	if(piod->size != 1) {
61661911Scokane		return -EINVAL;
61761911Scokane	}
61861911Scokane
61961911Scokane	/* Write the data to the intended port */
62061911Scokane	ret_byte = inb(piod->port);
62161911Scokane	copyout(&ret_byte, piod->value, sizeof(u_int8_t));
62261911Scokane	return 0;
62361911Scokane}
62461911Scokane
62561911Scokanestatic int
62661911Scokanetdfx_do_pio_wt(struct tdfx_pio_data *piod)
62761911Scokane{
62861911Scokane	/* return val */
62961911Scokane	u_int8_t  ret_byte;
63061911Scokane
63161911Scokane	/* Replace old switch w/ massive if(...) */
63261911Scokane	/* Restricts the access of ports other than those we use */
63361911Scokane	if((piod->port != SC_INDEX) && (piod->port != SC_DATA) &&
63461911Scokane		(piod->port != VGA_MISC_OUTPUT_READ)) /* Can't write VGA_ST_1C */
63561911Scokane		return -EPERM;
63661911Scokane
63761911Scokane	/* All VGA STATUS REGS are byte registers, size should never be > 1 */
63861911Scokane	if(piod->size != 1) {
63961911Scokane		return -EINVAL;
64061911Scokane	}
64161911Scokane
64261911Scokane	/* Write the data to the intended port */
64361911Scokane	copyin(piod->value, &ret_byte, sizeof(u_int8_t));
64461911Scokane	outb(piod->port, ret_byte);
64561911Scokane	return 0;
64661911Scokane}
64761911Scokane
64861911Scokanestatic int
64961911Scokanetdfx_do_query(u_int cmd, struct tdfx_pio_data *piod)
65061911Scokane{
65161911Scokane	/* There are three sub-commands to the query 0x33 */
65261911Scokane	switch(_IOC_NR(cmd)) {
65361911Scokane		case 2:
65461911Scokane			return tdfx_query_boards();
65561911Scokane			break;
65661911Scokane		case 3:
65761911Scokane			return tdfx_query_fetch(cmd, piod);
65861911Scokane			break;
65961911Scokane		case 4:
66061911Scokane			return tdfx_query_update(cmd, piod);
66161911Scokane			break;
66261911Scokane		default:
66361911Scokane			/* In case we are thrown a bogus sub-command! */
66461931Scokane#ifdef DEBUG
66561911Scokane			printf("Bad Sub-cmd: 0x%x\n", _IOC_NR(cmd));
66661911Scokane#endif
66761911Scokane			return -EINVAL;
66861911Scokane	};
66961911Scokane}
67061911Scokane
67161911Scokanestatic int
67261911Scokanetdfx_do_pio(u_int cmd, struct tdfx_pio_data *piod)
67361911Scokane{
67461911Scokane	/* Two types of PIO, INPUT and OUTPUT, as the name suggests */
67561911Scokane	switch(_IOC_DIR(cmd)) {
67661911Scokane		case IOCV_OUT:
67761911Scokane			return tdfx_do_pio_rd(piod);
67861911Scokane			break;
67961911Scokane		case IOCV_IN:
68061911Scokane			return tdfx_do_pio_wt(piod);
68161911Scokane			break;
68261911Scokane		default:
68361911Scokane			return -EINVAL;
68461911Scokane	};
68561911Scokane}
68661911Scokane
68761911Scokane/* Calls to ioctl(2) eventually end up here. Unhandled ioctls return an ENXIO,
68861911Scokane * normally, you would read in the data pointed to by data, then write your
68961911Scokane * output to it. The ioctl *should* normally return zero if everything is
69061911Scokane * alright, but 3dfx didn't make it that way...
69161911Scokane *
69261911Scokane * For all of the ioctl code, in the event of a real error,
69361911Scokane * we return -Exxxx rather than simply Exxxx. The reason for this
69461911Scokane * is that the ioctls actually RET information back to the program
69561911Scokane * sometimes, rather than filling it in the passed structure. We
69661911Scokane * want to distinguish errors from useful data, and maintain compatibility.
69761911Scokane *
69861911Scokane * There is this portion of the proc struct called p_retval[], we can store a
69961911Scokane * return value in p->p_retval[0] and place the return value if it is positive
70061911Scokane * in there, then we can return 0 (good). If the return value is negative, we
70161911Scokane * can return -retval and the error should be properly handled.
70261911Scokane */
70361911Scokanestatic int
70461911Scokanetdfx_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc * p)
70561911Scokane{
70661911Scokane	int retval = 0;
70761911Scokane	struct tdfx_pio_data *piod = (struct tdfx_pio_data*)data;
70861931Scokane#ifdef	DEBUG
70961911Scokane	printf("IOCTL'd by #%d, cmd: 0x%x, data: 0x%x\n", p->p_pid, (u_int32_t)cmd,
71061911Scokane			(unsigned int)piod);
71161911Scokane#endif
71261911Scokane	switch(_IOC_TYPE(cmd)) {
71361911Scokane		/* Return the real error if negative, or simply stick the valid return
71461911Scokane		 * in p->p_retval */
71561911Scokane	case 0x33:
71661911Scokane			/* The '3'(0x33) type IOCTL is for querying the installed cards */
71761911Scokane			if((retval = tdfx_do_query(cmd, piod)) > 0) p->p_retval[0] = retval;
71861911Scokane			else return -retval;
71961911Scokane			break;
72061911Scokane		case 0:
72161911Scokane			/* The 0 type IOCTL is for programmed I/O methods */
72261911Scokane			if((tdfx_do_pio(cmd, piod)) > 0) p->p_retval[0] = retval;
72361911Scokane			else return -retval;
72461911Scokane			break;
72561911Scokane		default:
72661911Scokane			/* Technically, we won't reach this from linux emu, but when glide
72761911Scokane			 * finally gets ported, watch out! */
72861931Scokane#ifdef DEBUG
72961911Scokane			printf("Bad IOCTL from #%d\n", p->p_pid);
73061911Scokane#endif
73161911Scokane			return ENXIO;
73261911Scokane	}
73361911Scokane
73461911Scokane	return 0;
73561911Scokane}
73661911Scokane
73761931Scokane#ifdef TDFX_LINUX
73861931Scokane/*
73961931Scokane * Linux emulation IOCTL for /dev/tdfx
74061931Scokane */
74161931Scokanestatic int
74261931Scokanelinux_ioctl_tdfx(struct proc* p, struct linux_ioctl_args* args)
74361931Scokane{
74461931Scokane   int error = 0;
74561931Scokane   u_long cmd = args->cmd & 0xffff;
74661911Scokane
74761931Scokane   /* The structure passed to ioctl has two shorts, one int
74861931Scokane      and one void*. */
74961931Scokane   char d_pio[2*sizeof(short) + sizeof(int) + sizeof(void*)];
75061931Scokane
75161931Scokane   struct file *fp = p->p_fd->fd_ofiles[args->fd];
75261931Scokane
75361931Scokane   /* We simply copy the data and send it right to ioctl */
75461931Scokane   copyin((caddr_t)args->arg, &d_pio, sizeof(d_pio));
75561931Scokane   error = fo_ioctl(fp, cmd, (caddr_t)&d_pio, p);
75661931Scokane   return error;
75761931Scokane}
75861931Scokane#endif /* TDFX_LINUX */
75961931Scokane
76061931Scokane
76161911Scokane/* This is the device driver struct. This is sent to the driver subsystem to
76261911Scokane * register the method structure and the info strcut space for this particular
76361911Scokane * instance of the driver.
76461911Scokane */
76561911Scokanestatic driver_t tdfx_driver = {
76661911Scokane	"tdfx",
76761911Scokane	tdfx_methods,
76861911Scokane	sizeof(struct tdfx_softc),
76961911Scokane};
77061911Scokane
77161911Scokane/* Tell Mr. Kernel about us! */
77261911ScokaneDRIVER_MODULE(tdfx, pci, tdfx_driver, tdfx_devclass, 0, 0);
77361911Scokane
77461911Scokane
77561911Scokane#endif	/* NPCI */
776