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