tdfx_pci.c revision 66910
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 66910 2000-10-10 03:51:54Z 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;
14565146Scokane	/*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");
15065146Scokane		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	struct tdfx_softc *tdfx_info;
17261911Scokane	u_long	val;
17361911Scokane	/* rid value tells bus_alloc_resource where to find the addresses of ports or
17461911Scokane	 * of memory ranges in the PCI config space*/
17561911Scokane	int rid = PCIR_MAPS;
17661911Scokane
17761911Scokane	/* Increment the card counter (for the ioctl code) */
17861911Scokane	tdfx_count++;
17961911Scokane
18061911Scokane 	/* Enable MemMap on Voodoo */
18161911Scokane	val = pci_read_config(dev, PCIR_COMMAND, 2);
18261911Scokane	val |= (PCIM_CMD_MEMEN);
18361911Scokane	pci_write_config(dev, PCIR_COMMAND, val, 2);
18461911Scokane	val = pci_read_config(dev, PCIR_COMMAND, 2);
18561911Scokane
18661911Scokane	/* Fill the soft config struct with info about this device*/
18761911Scokane	tdfx_info = device_get_softc(dev);
18861911Scokane	tdfx_info->dev = dev;
18961911Scokane	tdfx_info->vendor = pci_get_vendor(dev);
19061911Scokane	tdfx_info->type = pci_get_devid(dev) >> 16;
19161911Scokane	tdfx_info->bus = pci_get_bus(dev);
19261911Scokane	tdfx_info->dv = pci_get_slot(dev);
19361911Scokane	tdfx_info->curFile = NULL;
19461911Scokane
19561911Scokane	/*
19661911Scokane	 *	Get the Memory Location from the PCI Config, mask out lower word, since
19761911Scokane	 * the config space register is only one word long (this is nicer than a
19861911Scokane	 * bitshift).
19961911Scokane	 */
20061911Scokane	tdfx_info->addr0 = (pci_read_config(dev, 0x10, 4) & 0xffff0000);
20161931Scokane#ifdef DEBUG
20261911Scokane	device_printf(dev, "Base0 @ 0x%x\n", tdfx_info->addr0);
20361911Scokane#endif
20461911Scokane	/* Notify the VM that we will be mapping some memory later */
20561911Scokane	tdfx_info->memrange = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, ~0, 1,
20661967Scokane			RF_ACTIVE | RF_SHAREABLE);
20761911Scokane	if(tdfx_info->memrange == NULL) {
20861931Scokane#ifdef DEBUG
20961911Scokane		device_printf(dev, "Error mapping mem, won't be able to use mmap()\n");
21061911Scokane#endif
21161911Scokane		tdfx_info->memrid = 0;
21261911Scokane	}
21361911Scokane	else {
21461911Scokane		tdfx_info->memrid = rid;
21561931Scokane#ifdef DEBUG
21661911Scokane		device_printf(dev, "Mapped to: 0x%x\n",
21761911Scokane				(unsigned int)rman_get_start(tdfx_info->memrange));
21861911Scokane#endif
21961911Scokane	}
22061911Scokane
22163488Scokane	/* Setup for Voodoo3 and Banshee, PIO and an extram Memrange */
22263488Scokane	if(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3 ||
22363488Scokane		pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE) {
22464085Scokane		rid = 0x14;	/* 2nd mem map */
22563488Scokane		tdfx_info->addr1 = (pci_read_config(dev, 0x14, 4) & 0xffff0000);
22663488Scokane#ifdef DEBUG
22763488Scokane		device_printf(dev, "Base1 @ 0x%x\n", tdfx_info->addr1);
22863488Scokane#endif
22963488Scokane		tdfx_info->memrange2 = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
23063488Scokane			 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
23163488Scokane		if(tdfx_info->memrange2 == NULL) {
23263488Scokane#ifdef DEBUG
23363488Scokane			device_printf(dev, "Mem1 couldn't be allocated, glide may not work.");
23463488Scokane#endif
23563488Scokane			tdfx_info->memrid2 = 0;
23663488Scokane		}
23763488Scokane		else {
23863488Scokane			tdfx_info->memrid2 = rid;
23963488Scokane		}
24063488Scokane		/* Now to map the PIO stuff */
24165146Scokane		rid = PCIR_IOBASE0_2;
24265146Scokane		tdfx_info->pio0 = pci_read_config(dev, 0x2c, 2);
24365146Scokane		tdfx_info->pio0max = pci_read_config(dev, 0x30, 2) + tdfx_info->pio0;
24463488Scokane		tdfx_info->piorange = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
24563488Scokane			 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
24663488Scokane		if(tdfx_info->piorange == NULL) {
24763488Scokane#ifdef DEBUG
24863488Scokane			device_printf(dev, "Couldn't map PIO range.");
24963488Scokane#endif
25063488Scokane			tdfx_info->piorid = 0;
25163488Scokane		}
25263488Scokane		else {
25363488Scokane			tdfx_info->piorid = rid;
25465146Scokane		}
25563488Scokane	} else {
25663488Scokane	  tdfx_info->addr1 = 0;
25763488Scokane	  tdfx_info->memrange2 = NULL;
25863488Scokane	  tdfx_info->piorange = NULL;
25963488Scokane	}
26063488Scokane
26161911Scokane	/*
26261911Scokane	 *	Set Writecombining, or at least Uncacheable for the memory region, if we
26361911Scokane	 * are able to
26461911Scokane	 */
26561911Scokane
26661911Scokane	if(tdfx_setmtrr(dev) != 0) {
26761931Scokane#ifdef DEBUG
26861911Scokane		device_printf(dev, "Some weird error setting MTRRs");
26961911Scokane#endif
27061911Scokane		return -1;
27161911Scokane	}
27263488Scokane
27361911Scokane	/*
27461911Scokane	 * make_dev registers the cdev to access the 3dfx card from /dev
27561911Scokane	 *	use hex here for the dev num, simply to provide better support if > 10
27661911Scokane	 * voodoo cards, for the mad. The user must set the link, or use MAKEDEV.
27761911Scokane	 * Why would we want that many voodoo cards anyhow?
27861911Scokane	 */
27961989Scokane	tdfx_info->devt = make_dev(&tdfx_cdev, dev->unit, 0, 0, 02660,
28061989Scokane		"3dfx%x", dev->unit);
28161911Scokane
28261911Scokane	return 0;
28361911Scokane}
28461911Scokane
28561911Scokanestatic int
28661911Scokanetdfx_detach(device_t dev) {
28761911Scokane	struct tdfx_softc* tdfx_info;
28861911Scokane	int retval;
28961911Scokane	tdfx_info = device_get_softc(dev);
29061911Scokane
29161911Scokane	/* Delete allocated resource, of course */
29263488Scokane	bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid,
29361911Scokane			tdfx_info->memrange);
29463488Scokane
29563488Scokane	/* Release extended Voodoo3/Banshee resources */
29663488Scokane	if(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE ||
29763488Scokane			pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) {
29863488Scokane		if(tdfx_info->memrange2 != NULL)
29963488Scokane			bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid2,
30063488Scokane				tdfx_info->memrange);
30164085Scokane	/*	if(tdfx_info->piorange != NULL)
30263488Scokane			bus_release_resource(dev, SYS_RES_IOPORT, tdfx_info->piorid,
30364085Scokane				tdfx_info->piorange);*/
30463488Scokane	}
30563488Scokane
30661911Scokane	/* Though it is safe to leave the WRCOMB support since the
30761911Scokane		mem driver checks for it, we should remove it in order
30861911Scokane		to free an MTRR for another device */
30961911Scokane	retval = tdfx_clrmtrr(dev);
31061931Scokane#ifdef DEBUG
31161911Scokane	if(retval != 0)
31261911Scokane		printf("tdfx: For some reason, I couldn't clear the mtrr\n");
31361911Scokane#endif
31461989Scokane	/* Remove device entry when it can no longer be accessed */
31561989Scokane   destroy_dev(tdfx_info->devt);
31661911Scokane	return(0);
31761911Scokane}
31861911Scokane
31961911Scokanestatic int
32061911Scokanetdfx_shutdown(device_t dev) {
32161931Scokane#ifdef DEBUG
32261911Scokane	device_printf(dev, "tdfx: Device Shutdown\n");
32361911Scokane#endif
32461911Scokane	return 0;
32561911Scokane}
32661911Scokane
32761911Scokanestatic int
32861911Scokanetdfx_clrmtrr(device_t dev) {
32961911Scokane	/* This function removes the MTRR set by the attach call, so it can be used
33061911Scokane	 * in the future by other drivers.
33161911Scokane	 */
33261911Scokane	int retval, act;
33361911Scokane	struct tdfx_softc *tdfx_info = device_get_softc(dev);
33461911Scokane
33561911Scokane	act = MEMRANGE_SET_REMOVE;
33661911Scokane	retval = mem_range_attr_set(&tdfx_info->mrdesc, &act);
33761911Scokane	return retval;
33861911Scokane}
33961911Scokane
34061911Scokanestatic int
34161911Scokanetdfx_setmtrr(device_t dev) {
34261911Scokane	/*
34361911Scokane	 * This is the MTRR setting function for the 3dfx card. It is called from
34461911Scokane	 * tdfx_attach. If we can't set the MTRR properly, it's not the end of the
34561911Scokane	 * world. We can still continue, just with slightly (very slightly) degraded
34661911Scokane	 * performance.
34761911Scokane	 */
34861911Scokane	int retval = 0, act;
34961911Scokane	struct tdfx_softc *tdfx_info = device_get_softc(dev);
35061911Scokane
35161911Scokane	/* The older Voodoo cards have a shorter memrange than the newer ones */
35261911Scokane	if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1) || (pci_get_devid(dev) ==
35363488Scokane			PCI_DEVICE_3DFX_VOODOO2)) {
35461911Scokane		tdfx_info->mrdesc.mr_len = 0x400000;
35563488Scokane
35663488Scokane		/* The memory descriptor is described as the top 15 bits of the real
35763488Scokane			address */
35863488Scokane		tdfx_info->mrdesc.mr_base = tdfx_info->addr0 & 0xfffe0000;
35963488Scokane	}
36061911Scokane	else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) ||
36163488Scokane			(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE)) {
36261911Scokane		tdfx_info->mrdesc.mr_len = 0x1000000;
36363488Scokane		/* The Voodoo3 and Banshee LFB is the second memory address */
36463488Scokane		/* The memory descriptor is described as the top 15 bits of the real
36563488Scokane			address */
36663488Scokane		tdfx_info->mrdesc.mr_base = tdfx_info->addr1 & 0xfffe0000;
36763488Scokane	}
36863488Scokane	else
36963488Scokane		 return 0;
37061911Scokane	/*
37161911Scokane    *	The Alliance Pro Motion AT3D was not mentioned in the linux
37261911Scokane	 * driver as far as MTRR support goes, so I just won't put the
37361911Scokane	 * code in here for it. This is where it should go, though.
37461911Scokane	 */
37561911Scokane
37661911Scokane	/* Firstly, try to set write combining */
37761911Scokane	tdfx_info->mrdesc.mr_flags = MDF_WRITECOMBINE;
37861911Scokane	bcopy("tdfx", &tdfx_info->mrdesc.mr_owner, 4);
37961911Scokane	act = MEMRANGE_SET_UPDATE;
38061911Scokane	retval = mem_range_attr_set(&tdfx_info->mrdesc, &act);
38161911Scokane
38261911Scokane	if(retval == 0) {
38361931Scokane#ifdef DEBUG
38461911Scokane		device_printf(dev, "MTRR Set Correctly for tdfx\n");
38561911Scokane#endif
38661911Scokane	} else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO2) ||
38761911Scokane		(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1)) {
38861911Scokane		/* if, for some reason we can't set the WRCOMB range with the V1/V2, we
38961911Scokane		 * can still possibly use the UNCACHEABLE region for it instead, and help
39061911Scokane		 * out in a small way */
39161911Scokane		tdfx_info->mrdesc.mr_flags = MDF_UNCACHEABLE;
39261911Scokane		/* This length of 1000h was taken from the linux device driver... */
39361911Scokane		tdfx_info->mrdesc.mr_len = 0x1000;
39461911Scokane
39561911Scokane		/*
39661911Scokane		 * If, for some reason, we can't set the MTRR (N/A?) we may still continue
39761911Scokane		 */
39861931Scokane#ifdef DEBUG
39961911Scokane		if(retval == 0) {
40061911Scokane			device_printf(dev, "MTRR Set Type Uncacheable
40161911Scokane					%x\n", (u_int32_t)tdfx_info->mrdesc.mr_base);
40261911Scokane		} else {
40361911Scokane			device_printf(dev, "Couldn't Set MTRR\n");
40461911Scokane		}
40561911Scokane#endif
40661911Scokane	}
40761931Scokane#ifdef DEBUG
40861911Scokane	else {
40961911Scokane		device_printf(dev, "Couldn't Set MTRR\n");
41061911Scokane		return 0;
41161911Scokane	}
41261911Scokane#endif
41361911Scokane	return 0;
41461911Scokane}
41561911Scokane
41661911Scokanestatic int
41761911Scokanetdfx_open(dev_t dev, int flags, int fmt, struct proc *p)
41861911Scokane{
41961911Scokane	/*
42061911Scokane	 *	The open cdev method handles open(2) calls to /dev/3dfx[n]
42161911Scokane	 * We can pretty much allow any opening of the device.
42261911Scokane	 */
42361911Scokane	struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass,
42461911Scokane			UNIT(minor(dev)));
42561911Scokane	if(tdfx_info->busy != 0) return EBUSY;
42661931Scokane#ifdef	DEBUG
42761911Scokane	printf("3dfx: Opened by #%d\n", p->p_pid);
42861911Scokane#endif
42961911Scokane	/* Set the driver as busy */
43061911Scokane	tdfx_info->busy++;
43161911Scokane	return 0;
43261911Scokane}
43361911Scokane
43461911Scokanestatic int
43561911Scokanetdfx_close(dev_t dev, int fflag, int devtype, struct proc* p)
43661911Scokane{
43761911Scokane	/*
43861911Scokane	 *	The close cdev method handles close(2) calls to /dev/3dfx[n]
43961911Scokane	 * We'll always want to close the device when it's called.
44061911Scokane	 */
44161911Scokane	struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass,
44261911Scokane		UNIT(minor(dev)));
44361911Scokane	if(tdfx_info->busy == 0) return EBADF;
44461911Scokane	tdfx_info->busy = 0;
44561931Scokane#ifdef	DEBUG
44661911Scokane	printf("Closed by #%d\n", p->p_pid);
44761911Scokane#endif
44861911Scokane	return 0;
44961911Scokane}
45061911Scokane
45161911Scokanestatic int
45261911Scokanetdfx_mmap(dev_t dev, vm_offset_t offset, int nprot)
45361911Scokane{
45461911Scokane	/*
45561911Scokane	 * mmap(2) is called by a user process to request that an area of memory
45661911Scokane	 * associated with this device be mapped for the process to work with. Nprot
45761911Scokane	 * holds the protections requested, PROT_READ, PROT_WRITE, or both.
45861911Scokane	 */
45966910Scokane
46066910Scokane	/**** OLD GET CONFIG ****/
46166910Scokane	/* struct tdfx_softc* tdfx_info; */
46261911Scokane
46361911Scokane	/* Get the configuration for our card XXX*/
46466910Scokane	/*tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
46566910Scokane			UNIT(minor(dev)));*/
46666910Scokane	/************************/
46766910Scokane
46866910Scokane	struct tdfx_softc* tdfx_info[2];
46961911Scokane
47066910Scokane	tdfx_info[0] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 0);
47166910Scokane
47261911Scokane	/* If, for some reason, its not configured, we bail out */
47366910Scokane	if(tdfx_info[0] == NULL) {
47461931Scokane#ifdef	DEBUG
47561911Scokane	   printf("tdfx: tdfx_info (softc) is NULL\n");
47661911Scokane#endif
47761911Scokane	   return -1;
47861911Scokane	}
47966910Scokane
48061911Scokane	/* We must stay within the bound of our address space */
48166910Scokane	if((offset & 0xff000000) == tdfx_info[0]->addr0) {
48261911Scokane		offset &= 0xffffff;
48366910Scokane		return atop(rman_get_start(tdfx_info[0]->memrange) + offset);
48466910Scokane	}
48566910Scokane
48666910Scokane	if(tdfx_count > 1) {
48766910Scokane		tdfx_info[1] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 1);
48866910Scokane		if((offset & 0xff000000) == tdfx_info[1]->addr0) {
48966910Scokane			offset &= 0xffffff;
49066910Scokane			return atop(rman_get_start(tdfx_info[1]->memrange) + offset);
49166910Scokane		}
49266910Scokane	}
49363488Scokane
49463488Scokane	/* See if the Banshee/V3 LFB is being requested */
49566910Scokane	/*if(tdfx_info->memrange2 != NULL && (offset & 0xff000000) ==
49664085Scokane			tdfx_info->addr1) {
49763488Scokane	  	offset &= 0xffffff;
49866910Scokane		return atop(rman_get_start(tdfx_info[1]->memrange2) + offset);
49966910Scokane	}*/ /* VoodooNG code */
50063488Scokane
50166910Scokane	/* The ret call */
50261911Scokane	/* atop -> address to page
50361911Scokane	 * rman_get_start, get the (struct resource*)->r_start member,
50461911Scokane	 * the mapping base address.
50561911Scokane	 */
50666910Scokane	return -1;
50761911Scokane}
50861911Scokane
50961911Scokanestatic int
51061911Scokanetdfx_query_boards(void) {
51161911Scokane	/*
51261911Scokane    *	This returns the number of installed tdfx cards, we have been keeping
51361911Scokane	 * count, look at tdfx_attach
51461911Scokane	 */
51561911Scokane	return tdfx_count;
51661911Scokane}
51761911Scokane
51861911Scokanestatic int
51961911Scokanetdfx_query_fetch(u_int cmd, struct tdfx_pio_data *piod)
52061911Scokane{
52161911Scokane	/* XXX Comment this later, after careful inspection and spring cleaning :) */
52261911Scokane	/* Various return values 8bit-32bit */
52361911Scokane	u_int8_t  ret_byte;
52461911Scokane	u_int16_t ret_word;
52561911Scokane	u_int32_t ret_dword;
52661911Scokane	struct tdfx_softc* tdfx_info = NULL;
52761911Scokane
52861911Scokane	/* This one depend on the tdfx_* structs being properly initialized */
52961911Scokane
53061911Scokane	/*piod->device &= 0xf;*/
53161911Scokane	if((piod == NULL) ||(tdfx_count <= piod->device) ||
53261911Scokane			(piod->device < 0)) {
53361931Scokane#ifdef DEBUG
53461911Scokane		printf("tdfx: Bad device or internal struct in tdfx_query_fetch\n");
53561911Scokane#endif
53661911Scokane		return -EINVAL;
53761911Scokane	}
53861911Scokane
53961911Scokane	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
54061911Scokane			piod->device);
54161911Scokane
54261911Scokane	if(tdfx_info == NULL) return -ENXIO;
54361911Scokane
54461911Scokane	/* We must restrict the size reads from the port, since to high or low of a
54561911Scokane	 * size witll result in wrong data being passed, and that's bad */
54661911Scokane	/* A few of these were pulled during the attach phase */
54761911Scokane	switch(piod->port) {
54861911Scokane		case PCI_VENDOR_ID_FREEBSD:
54961911Scokane			if(piod->size != 2) return -EINVAL;
55061911Scokane			copyout(&tdfx_info->vendor, piod->value, piod->size);
55161911Scokane			return 0;
55261911Scokane		case PCI_DEVICE_ID_FREEBSD:
55361911Scokane			if(piod->size != 2) return -EINVAL;
55461911Scokane			copyout(&tdfx_info->type, piod->value, piod->size);
55561911Scokane			return 0;
55661911Scokane		case PCI_BASE_ADDRESS_0_FREEBSD:
55761911Scokane			if(piod->size != 4) return -EINVAL;
55861911Scokane			copyout(&tdfx_info->addr0, piod->value, piod->size);
55961911Scokane			return 0;
56065146Scokane		case PCI_BASE_ADDRESS_1_FREEBSD:
56165146Scokane			if(piod->size != 4) return -EINVAL;
56265146Scokane			copyout(&tdfx_info->addr1, piod->value, piod->size);
56365146Scokane			return 0;
56465146Scokane		case PCI_PRIBUS_FREEBSD:
56565146Scokane			if(piod->size != 1) return -EINVAL;
56665146Scokane			break;
56765146Scokane		case PCI_IOBASE_0_FREEBSD:
56865146Scokane			if(piod->size != 2) return -EINVAL;
56965146Scokane			break;
57065146Scokane		case PCI_IOLIMIT_0_FREEBSD:
57165146Scokane			if(piod->size != 2) return -EINVAL;
57265146Scokane			break;
57361911Scokane		case SST1_PCI_SPECIAL1_FREEBSD:
57461911Scokane			if(piod->size != 4) return -EINVAL;
57561911Scokane			break;
57661911Scokane		case PCI_REVISION_ID_FREEBSD:
57761911Scokane			if(piod->size != 1) return -EINVAL;
57861911Scokane			break;
57961911Scokane		case SST1_PCI_SPECIAL4_FREEBSD:
58061911Scokane			if(piod->size != 4) return -EINVAL;
58161911Scokane			break;
58261911Scokane		default:
58361911Scokane			return -EINVAL;
58461911Scokane	}
58561911Scokane
58661911Scokane
58761911Scokane	/* Read the value and return */
58861911Scokane	switch(piod->size) {
58961911Scokane		case 1:
59061911Scokane			ret_byte = pci_read_config(tdfx_info[piod->device].dev,
59161911Scokane					piod->port, 1);
59261911Scokane			copyout(&ret_byte, piod->value, 1);
59361911Scokane			break;
59461911Scokane		case 2:
59561911Scokane			ret_word = pci_read_config(tdfx_info[piod->device].dev,
59661911Scokane					piod->port, 2);
59761911Scokane			copyout(&ret_word, piod->value, 2);
59861911Scokane			break;
59961911Scokane		case 4:
60061911Scokane			ret_dword = pci_read_config(tdfx_info[piod->device].dev,
60161911Scokane					piod->port, 4);
60261911Scokane			copyout(&ret_dword, piod->value, 4);
60361911Scokane			break;
60461911Scokane		default:
60561911Scokane			return -EINVAL;
60661911Scokane	}
60761911Scokane	return 0;
60861911Scokane}
60961911Scokane
61061911Scokanestatic int
61161911Scokanetdfx_query_update(u_int cmd, struct tdfx_pio_data *piod)
61261911Scokane{
61361911Scokane	/* XXX Comment this later, after careful inspection and spring cleaning :) */
61461911Scokane	/* Return vals */
61561911Scokane	u_int8_t  ret_byte;
61661911Scokane	u_int16_t ret_word;
61761911Scokane	u_int32_t ret_dword;
61861911Scokane
61961911Scokane	/* Port vals, mask */
62061911Scokane	u_int32_t retval, preval, mask;
62161911Scokane	struct tdfx_softc* tdfx_info = NULL;
62261911Scokane
62361911Scokane
62461911Scokane	if((piod == NULL) || (piod->device >= (tdfx_count &
62561911Scokane					0xf))) {
62661931Scokane#ifdef DEBUG
62761911Scokane		printf("tdfx: Bad struct or device in tdfx_query_update\n");
62861911Scokane#endif
62961911Scokane		return -EINVAL;
63061911Scokane	}
63161911Scokane
63261911Scokane	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
63361911Scokane			piod->device);
63461911Scokane	if(tdfx_info == NULL) return -ENXIO;
63561911Scokane	/* Code below this line in the fuction was taken from the
63661911Scokane	 * Linux driver and converted for freebsd. */
63761911Scokane
63861911Scokane	/* Check the size for all the ports, to make sure stuff doesn't get messed up
63961911Scokane	 * by poorly written clients */
64061911Scokane
64161911Scokane	switch(piod->port) {
64261911Scokane		case PCI_COMMAND_FREEBSD:
64361911Scokane			if(piod->size != 2) return -EINVAL;
64461911Scokane			break;
64561911Scokane		case SST1_PCI_SPECIAL1_FREEBSD:
64661911Scokane			if(piod->size != 4) return -EINVAL;
64761911Scokane			break;
64861911Scokane		case SST1_PCI_SPECIAL2_FREEBSD:
64961911Scokane			if(piod->size != 4) return -EINVAL;
65061911Scokane			break;
65161911Scokane		case SST1_PCI_SPECIAL3_FREEBSD:
65261911Scokane			if(piod->size != 4) return -EINVAL;
65361911Scokane			break;
65461911Scokane		case SST1_PCI_SPECIAL4_FREEBSD:
65561911Scokane			if(piod->size != 4) return -EINVAL;
65661911Scokane			break;
65761911Scokane		default:
65861911Scokane			return -EINVAL;
65961911Scokane	}
66061911Scokane	/* Read the current value */
66161911Scokane	retval = pci_read_config(tdfx_info->dev, piod->port & ~3, 4);
66261911Scokane
66361911Scokane	/* These set up a mask to use, since apparently they wanted to write 4 bytes
66461911Scokane	 * at once to the ports */
66561911Scokane	switch (piod->size) {
66661911Scokane		case 1:
66761911Scokane			copyin(piod->value, &ret_byte, 1);
66861911Scokane			preval = ret_byte << (8 * (piod->port & 0x3));
66961911Scokane			mask = 0xff << (8 * (piod->port & 0x3));
67061911Scokane			break;
67161911Scokane		case 2:
67261911Scokane			copyin(piod->value, &ret_word, 2);
67361911Scokane			preval = ret_word << (8 * (piod->port & 0x3));
67461911Scokane			mask = 0xffff << (8 * (piod->port & 0x3));
67561911Scokane			break;
67661911Scokane		case 4:
67761911Scokane			copyin(piod->value, &ret_dword, 4);
67861911Scokane			preval = ret_dword;
67961911Scokane			mask = ~0;
68061911Scokane			break;
68161911Scokane		default:
68261911Scokane			return -EINVAL;
68361911Scokane	}
68461911Scokane	/* Finally, combine the values and write it to the port */
68561911Scokane	retval = (retval & ~mask) | preval;
68661911Scokane	pci_write_config(tdfx_info->dev, piod->port & ~3, retval, 4);
68761911Scokane
68861911Scokane	return 0;
68961911Scokane}
69061911Scokane
69163488Scokane/* For both of these, I added a variable named workport of type u_int so
69263488Scokane * that I could eliminate the warning about my data type size. The
69363488Scokane * applications expect the port to be of type short, so I needed to change
69463488Scokane * this within the function */
69561911Scokanestatic int
69661911Scokanetdfx_do_pio_rd(struct tdfx_pio_data *piod)
69761911Scokane{
69861911Scokane	/* Return val */
69961911Scokane	u_int8_t  ret_byte;
70063488Scokane	u_int 	 workport;
70165146Scokane	struct tdfx_softc *tdfx_info =
70265146Scokane		(struct tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device);
70365146Scokane
70461911Scokane	/* Restricts the access of ports other than those we use */
70565146Scokane	if(((piod->port != VGA_INPUT_STATUS_1C) || (piod->port != SC_INDEX) ||
70665146Scokane		(piod->port != SC_DATA) || (piod->port != VGA_MISC_OUTPUT_READ)) &&
70765146Scokane		(piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max))
70861911Scokane		return -EPERM;
70961911Scokane
71061911Scokane	/* All VGA STATUS REGS are byte registers, size should never be > 1 */
71161911Scokane	if(piod->size != 1) {
71261911Scokane		return -EINVAL;
71361911Scokane	}
71461911Scokane
71561911Scokane	/* Write the data to the intended port */
71663488Scokane	workport = piod->port;
71763488Scokane	ret_byte = inb(workport);
71861911Scokane	copyout(&ret_byte, piod->value, sizeof(u_int8_t));
71961911Scokane	return 0;
72061911Scokane}
72161911Scokane
72261911Scokanestatic int
72361911Scokanetdfx_do_pio_wt(struct tdfx_pio_data *piod)
72461911Scokane{
72561911Scokane	/* return val */
72661911Scokane	u_int8_t  ret_byte;
72763488Scokane	u_int		 workport;
72865146Scokane	struct tdfx_softc *tdfx_info = (struct
72965146Scokane			tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device);
73061911Scokane	/* Replace old switch w/ massive if(...) */
73161911Scokane	/* Restricts the access of ports other than those we use */
73265146Scokane	if(((piod->port != SC_INDEX) && (piod->port != SC_DATA) &&
73365146Scokane		(piod->port != VGA_MISC_OUTPUT_READ)) /* Can't write VGA_ST_1C */ &&
73465146Scokane		(piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max))
73561911Scokane		return -EPERM;
73661911Scokane
73761911Scokane	/* All VGA STATUS REGS are byte registers, size should never be > 1 */
73861911Scokane	if(piod->size != 1) {
73961911Scokane		return -EINVAL;
74061911Scokane	}
74161911Scokane
74261911Scokane	/* Write the data to the intended port */
74361911Scokane	copyin(piod->value, &ret_byte, sizeof(u_int8_t));
74463488Scokane	workport = piod->port;
74563488Scokane	outb(workport, ret_byte);
74661911Scokane	return 0;
74761911Scokane}
74861911Scokane
74961911Scokanestatic int
75061911Scokanetdfx_do_query(u_int cmd, struct tdfx_pio_data *piod)
75161911Scokane{
75261911Scokane	/* There are three sub-commands to the query 0x33 */
75361911Scokane	switch(_IOC_NR(cmd)) {
75461911Scokane		case 2:
75561911Scokane			return tdfx_query_boards();
75661911Scokane			break;
75761911Scokane		case 3:
75861911Scokane			return tdfx_query_fetch(cmd, piod);
75961911Scokane			break;
76061911Scokane		case 4:
76161911Scokane			return tdfx_query_update(cmd, piod);
76261911Scokane			break;
76361911Scokane		default:
76461911Scokane			/* In case we are thrown a bogus sub-command! */
76561931Scokane#ifdef DEBUG
76661911Scokane			printf("Bad Sub-cmd: 0x%x\n", _IOC_NR(cmd));
76761911Scokane#endif
76861911Scokane			return -EINVAL;
76961911Scokane	};
77061911Scokane}
77161911Scokane
77261911Scokanestatic int
77361911Scokanetdfx_do_pio(u_int cmd, struct tdfx_pio_data *piod)
77461911Scokane{
77561911Scokane	/* Two types of PIO, INPUT and OUTPUT, as the name suggests */
77661911Scokane	switch(_IOC_DIR(cmd)) {
77761911Scokane		case IOCV_OUT:
77861911Scokane			return tdfx_do_pio_rd(piod);
77961911Scokane			break;
78061911Scokane		case IOCV_IN:
78161911Scokane			return tdfx_do_pio_wt(piod);
78261911Scokane			break;
78361911Scokane		default:
78461911Scokane			return -EINVAL;
78561911Scokane	};
78661911Scokane}
78761911Scokane
78861911Scokane/* Calls to ioctl(2) eventually end up here. Unhandled ioctls return an ENXIO,
78961911Scokane * normally, you would read in the data pointed to by data, then write your
79061911Scokane * output to it. The ioctl *should* normally return zero if everything is
79161911Scokane * alright, but 3dfx didn't make it that way...
79261911Scokane *
79361911Scokane * For all of the ioctl code, in the event of a real error,
79461911Scokane * we return -Exxxx rather than simply Exxxx. The reason for this
79561911Scokane * is that the ioctls actually RET information back to the program
79661911Scokane * sometimes, rather than filling it in the passed structure. We
79761911Scokane * want to distinguish errors from useful data, and maintain compatibility.
79861911Scokane *
79961911Scokane * There is this portion of the proc struct called p_retval[], we can store a
80061911Scokane * return value in p->p_retval[0] and place the return value if it is positive
80161911Scokane * in there, then we can return 0 (good). If the return value is negative, we
80261911Scokane * can return -retval and the error should be properly handled.
80361911Scokane */
80461911Scokanestatic int
80561911Scokanetdfx_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc * p)
80661911Scokane{
80761911Scokane	int retval = 0;
80861911Scokane	struct tdfx_pio_data *piod = (struct tdfx_pio_data*)data;
80961931Scokane#ifdef	DEBUG
81061911Scokane	printf("IOCTL'd by #%d, cmd: 0x%x, data: 0x%x\n", p->p_pid, (u_int32_t)cmd,
81161911Scokane			(unsigned int)piod);
81261911Scokane#endif
81361911Scokane	switch(_IOC_TYPE(cmd)) {
81461911Scokane		/* Return the real error if negative, or simply stick the valid return
81561911Scokane		 * in p->p_retval */
81661911Scokane	case 0x33:
81761911Scokane			/* The '3'(0x33) type IOCTL is for querying the installed cards */
81861911Scokane			if((retval = tdfx_do_query(cmd, piod)) > 0) p->p_retval[0] = retval;
81961911Scokane			else return -retval;
82061911Scokane			break;
82161911Scokane		case 0:
82261911Scokane			/* The 0 type IOCTL is for programmed I/O methods */
82361911Scokane			if((tdfx_do_pio(cmd, piod)) > 0) p->p_retval[0] = retval;
82461911Scokane			else return -retval;
82561911Scokane			break;
82661911Scokane		default:
82761911Scokane			/* Technically, we won't reach this from linux emu, but when glide
82861911Scokane			 * finally gets ported, watch out! */
82961931Scokane#ifdef DEBUG
83061911Scokane			printf("Bad IOCTL from #%d\n", p->p_pid);
83161911Scokane#endif
83261911Scokane			return ENXIO;
83361911Scokane	}
83461911Scokane
83561911Scokane	return 0;
83661911Scokane}
83761911Scokane
83861931Scokane#ifdef TDFX_LINUX
83961931Scokane/*
84061931Scokane * Linux emulation IOCTL for /dev/tdfx
84161931Scokane */
84261931Scokanestatic int
84361931Scokanelinux_ioctl_tdfx(struct proc* p, struct linux_ioctl_args* args)
84461931Scokane{
84561931Scokane   int error = 0;
84661931Scokane   u_long cmd = args->cmd & 0xffff;
84761911Scokane
84861931Scokane   /* The structure passed to ioctl has two shorts, one int
84961931Scokane      and one void*. */
85061931Scokane   char d_pio[2*sizeof(short) + sizeof(int) + sizeof(void*)];
85161931Scokane
85261931Scokane   struct file *fp = p->p_fd->fd_ofiles[args->fd];
85361931Scokane
85461931Scokane   /* We simply copy the data and send it right to ioctl */
85561931Scokane   copyin((caddr_t)args->arg, &d_pio, sizeof(d_pio));
85661931Scokane   error = fo_ioctl(fp, cmd, (caddr_t)&d_pio, p);
85761931Scokane   return error;
85861931Scokane}
85961931Scokane#endif /* TDFX_LINUX */
86061931Scokane
86161931Scokane
86261911Scokane/* This is the device driver struct. This is sent to the driver subsystem to
86361911Scokane * register the method structure and the info strcut space for this particular
86461911Scokane * instance of the driver.
86561911Scokane */
86661911Scokanestatic driver_t tdfx_driver = {
86761911Scokane	"tdfx",
86861911Scokane	tdfx_methods,
86961911Scokane	sizeof(struct tdfx_softc),
87061911Scokane};
87161911Scokane
87261911Scokane/* Tell Mr. Kernel about us! */
87361911ScokaneDRIVER_MODULE(tdfx, pci, tdfx_driver, tdfx_devclass, 0, 0);
87461911Scokane
87561911Scokane
87661911Scokane#endif	/* NPCI */
877