tdfx_pci.c revision 127135
1119418Sobrien/*-
274534Scokane * Copyright (c) 2000-2001 by Coleman Kane <cokane@FreeBSD.org>
361931Scokane * All rights reserved.
461931Scokane *
561931Scokane * Redistribution and use in source and binary forms, with or without
661931Scokane * modification, are permitted provided that the following conditions
761931Scokane * are met:
861931Scokane * 1. Redistributions of source code must retain the above copyright
961931Scokane *    notice, this list of conditions and the following disclaimer.
1061931Scokane * 2. Redistributions in binary form must reproduce the above copyright
1161931Scokane *    notice, this list of conditions and the following disclaimer in the
1261931Scokane *    documentation and/or other materials provided with the distribution.
1361931Scokane * 3. All advertising materials mentioning features or use of this software
1461931Scokane *    must display the following acknowledgement:
1561931Scokane *      This product includes software developed by Gardner Buchanan.
1661931Scokane * 4. The name of Gardner Buchanan may not be used to endorse or promote
1761931Scokane *    products derived from this software without specific prior written
1861931Scokane *    permission.
1961931Scokane *
2061931Scokane * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2161931Scokane * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2261931Scokane * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2361931Scokane * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2461931Scokane * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2561931Scokane * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2661931Scokane * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2761931Scokane * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2861931Scokane * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2961931Scokane * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3061931Scokane */
3161931Scokane
32119418Sobrien#include <sys/cdefs.h>
33119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/tdfx/tdfx_pci.c 127135 2004-03-17 17:50:55Z njl $");
34119418Sobrien
3561911Scokane/* 3dfx driver for FreeBSD 4.x - Finished 11 May 2000, 12:25AM ET
3661911Scokane *
3774534Scokane * Copyright (C) 2000-2001, by Coleman Kane <cokane@FreeBSD.org>,
3861911Scokane * based upon the 3dfx driver written for linux, by Daryll Straus, Jon Taylor,
3961911Scokane * and Jens Axboe, located at http://linux.3dfx.com.
4061911Scokane */
4161911Scokane
4261911Scokane#include <sys/param.h>
4361911Scokane
4461911Scokane#include <sys/bus.h>
4561911Scokane#include <sys/cdefs.h>
4661911Scokane#include <sys/conf.h>
4761911Scokane#include <sys/fcntl.h>
4861911Scokane#include <sys/file.h>
4961911Scokane#include <sys/filedesc.h>
5061911Scokane#include <sys/filio.h>
5161911Scokane#include <sys/ioccom.h>
5261911Scokane#include <sys/kernel.h>
5361911Scokane#include	<sys/malloc.h>
5461911Scokane#include <sys/mman.h>
5561911Scokane#include <sys/signalvar.h>
5661911Scokane#include <sys/systm.h>
5761911Scokane#include <sys/uio.h>
5861911Scokane
59119287Simp#include <dev/pci/pcivar.h>
60119287Simp#include <dev/pci/pcireg.h>
6161911Scokane
6261911Scokane#include <vm/vm.h>
6361911Scokane#include <vm/vm_kern.h>
6461911Scokane#include <vm/pmap.h>
6561911Scokane#include <vm/vm_extern.h>
6661911Scokane
6761911Scokane/* rman.h depends on machine/bus.h */
6861911Scokane#include <machine/resource.h>
6961911Scokane#include <machine/bus.h>
7061911Scokane#include <sys/rman.h>
7162028Scokane
7262028Scokane/* This must come first */
7362028Scokane#include "opt_tdfx.h"
7461931Scokane#ifdef TDFX_LINUX
7561931Scokane#include <dev/tdfx/tdfx_linux.h>
7661931Scokane#endif
7761911Scokane
7861911Scokane#include <dev/tdfx/tdfx_io.h>
7961911Scokane#include <dev/tdfx/tdfx_vars.h>
8061911Scokane#include <dev/tdfx/tdfx_pci.h>
8161911Scokane
8261911Scokane
8361911Scokanestatic devclass_t tdfx_devclass;
8461911Scokane
8561911Scokane
8661911Scokanestatic int tdfx_count = 0;
8761911Scokane
8861911Scokane
8961911Scokane/* Set up the boot probe/attach routines */
9061911Scokanestatic device_method_t tdfx_methods[] = {
9161911Scokane	DEVMETHOD(device_probe,		tdfx_probe),
9261911Scokane	DEVMETHOD(device_attach,	tdfx_attach),
9361911Scokane	DEVMETHOD(device_detach,	tdfx_detach),
9461911Scokane	DEVMETHOD(device_shutdown,	tdfx_shutdown),
9561911Scokane	{ 0, 0 }
9661911Scokane};
9761911Scokane
9861911ScokaneMALLOC_DEFINE(M_TDFX,"TDFX Driver","3DFX Graphics[/2D]/3D Accelerator(s)");
9961911Scokane
10061931Scokane#ifdef TDFX_LINUX
10161989ScokaneMODULE_DEPEND(tdfx, linux, 1, 1, 1);
10261931ScokaneLINUX_IOCTL_SET(tdfx, LINUX_IOCTL_TDFX_MIN, LINUX_IOCTL_TDFX_MAX);
10361931Scokane#endif
10461931Scokane
10561911Scokane/* Char. Dev. file operations structure */
10661911Scokanestatic struct cdevsw tdfx_cdev = {
107126080Sphk	.d_version =	D_VERSION,
108126080Sphk	.d_flags =	D_NEEDGIANT,
109111815Sphk	.d_open =	tdfx_open,
110111815Sphk	.d_close =	tdfx_close,
111111815Sphk	.d_ioctl =	tdfx_ioctl,
112111815Sphk	.d_mmap =	tdfx_mmap,
113111815Sphk	.d_name =	"tdfx",
11461911Scokane};
11561911Scokane
11661911Scokanestatic int
11761911Scokanetdfx_probe(device_t dev)
11861911Scokane{
11961911Scokane	/*
12061911Scokane	 * probe routine called on kernel boot to register supported devices. We get
12161911Scokane	 * a device structure to work with, and we can test the VENDOR/DEVICE IDs to
12261911Scokane	 * see if this PCI device is one that we support. Return 0 if yes, ENXIO if
12361911Scokane	 * not.
12461911Scokane	 */
12561911Scokane	switch(pci_get_devid(dev)) {
12661911Scokane	case PCI_DEVICE_ALLIANCE_AT3D:
12761911Scokane		device_set_desc(dev, "ProMotion At3D 3D Accelerator");
12861911Scokane		return 0;
12961911Scokane	case PCI_DEVICE_3DFX_VOODOO2:
13061911Scokane		device_set_desc(dev, "3DFX Voodoo II 3D Accelerator");
13161911Scokane		return 0;
13265146Scokane	/*case PCI_DEVICE_3DFX_BANSHEE:
13361911Scokane		device_set_desc(dev, "3DFX Voodoo Banshee 2D/3D Graphics Accelerator");
13461911Scokane		return 0;
13561911Scokane	case PCI_DEVICE_3DFX_VOODOO3:
13661911Scokane		device_set_desc(dev, "3DFX Voodoo3 2D/3D Graphics Accelerator");
13765146Scokane		return 0;*/
13861911Scokane	case PCI_DEVICE_3DFX_VOODOO1:
13961911Scokane		device_set_desc(dev, "3DFX Voodoo Graphics 3D Accelerator");
14061911Scokane		return 0;;
14161911Scokane	};
14261911Scokane
14361911Scokane	return ENXIO;
14461911Scokane}
14561911Scokane
14661911Scokanestatic int
14761911Scokanetdfx_attach(device_t dev) {
14861911Scokane	/*
14961911Scokane	 * The attach routine is called after the probe routine successfully says it
15061911Scokane	 * supports a given card. We now proceed to initialize this card for use with
15161911Scokane	 * the system. I want to map the device memory for userland allocation and
15261911Scokane	 * fill an information structure with information on this card. I'd also like
15361911Scokane	 * to set Write Combining with the MTRR code so that we can hopefully speed
15461911Scokane	 * up memory writes. The last thing is to register the character device
15561911Scokane	 * interface to the card, so we can open it from /dev/3dfxN, where N is a
15661911Scokane	 * small, whole number.
15761911Scokane	 */
15861911Scokane	struct tdfx_softc *tdfx_info;
15961911Scokane	u_long	val;
16061911Scokane	/* rid value tells bus_alloc_resource where to find the addresses of ports or
16161911Scokane	 * of memory ranges in the PCI config space*/
162119690Sjhb	int rid = PCIR_BAR(0);
16361911Scokane
16461911Scokane	/* Increment the card counter (for the ioctl code) */
16561911Scokane	tdfx_count++;
16661911Scokane
16761911Scokane 	/* Enable MemMap on Voodoo */
16861911Scokane	val = pci_read_config(dev, PCIR_COMMAND, 2);
16961911Scokane	val |= (PCIM_CMD_MEMEN);
17061911Scokane	pci_write_config(dev, PCIR_COMMAND, val, 2);
17161911Scokane	val = pci_read_config(dev, PCIR_COMMAND, 2);
17261911Scokane
17361911Scokane	/* Fill the soft config struct with info about this device*/
17461911Scokane	tdfx_info = device_get_softc(dev);
17561911Scokane	tdfx_info->dev = dev;
17661911Scokane	tdfx_info->vendor = pci_get_vendor(dev);
17761911Scokane	tdfx_info->type = pci_get_devid(dev) >> 16;
17861911Scokane	tdfx_info->bus = pci_get_bus(dev);
17961911Scokane	tdfx_info->dv = pci_get_slot(dev);
18061911Scokane	tdfx_info->curFile = NULL;
18161911Scokane
18261911Scokane	/*
18361911Scokane	 *	Get the Memory Location from the PCI Config, mask out lower word, since
18461911Scokane	 * the config space register is only one word long (this is nicer than a
18561911Scokane	 * bitshift).
18661911Scokane	 */
18761911Scokane	tdfx_info->addr0 = (pci_read_config(dev, 0x10, 4) & 0xffff0000);
18861931Scokane#ifdef DEBUG
18961911Scokane	device_printf(dev, "Base0 @ 0x%x\n", tdfx_info->addr0);
19061911Scokane#endif
19161911Scokane	/* Notify the VM that we will be mapping some memory later */
192127135Snjl	tdfx_info->memrange = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
193127135Snjl		&rid, RF_ACTIVE | RF_SHAREABLE);
19461911Scokane	if(tdfx_info->memrange == NULL) {
19561931Scokane#ifdef DEBUG
19661911Scokane		device_printf(dev, "Error mapping mem, won't be able to use mmap()\n");
19761911Scokane#endif
19861911Scokane		tdfx_info->memrid = 0;
19961911Scokane	}
20061911Scokane	else {
20161911Scokane		tdfx_info->memrid = rid;
20261931Scokane#ifdef DEBUG
20361911Scokane		device_printf(dev, "Mapped to: 0x%x\n",
20461911Scokane				(unsigned int)rman_get_start(tdfx_info->memrange));
20561911Scokane#endif
20661911Scokane	}
20761911Scokane
20863488Scokane	/* Setup for Voodoo3 and Banshee, PIO and an extram Memrange */
20963488Scokane	if(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3 ||
21063488Scokane		pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE) {
21164085Scokane		rid = 0x14;	/* 2nd mem map */
21263488Scokane		tdfx_info->addr1 = (pci_read_config(dev, 0x14, 4) & 0xffff0000);
21363488Scokane#ifdef DEBUG
21463488Scokane		device_printf(dev, "Base1 @ 0x%x\n", tdfx_info->addr1);
21563488Scokane#endif
216127135Snjl		tdfx_info->memrange2 = bus_alloc_resource_any(dev,
217127135Snjl			SYS_RES_MEMORY, &rid, RF_ACTIVE | RF_SHAREABLE);
21863488Scokane		if(tdfx_info->memrange2 == NULL) {
21963488Scokane#ifdef DEBUG
22063488Scokane			device_printf(dev, "Mem1 couldn't be allocated, glide may not work.");
22163488Scokane#endif
22263488Scokane			tdfx_info->memrid2 = 0;
22363488Scokane		}
22463488Scokane		else {
22563488Scokane			tdfx_info->memrid2 = rid;
22663488Scokane		}
22763488Scokane		/* Now to map the PIO stuff */
22865146Scokane		rid = PCIR_IOBASE0_2;
22965146Scokane		tdfx_info->pio0 = pci_read_config(dev, 0x2c, 2);
23065146Scokane		tdfx_info->pio0max = pci_read_config(dev, 0x30, 2) + tdfx_info->pio0;
231127135Snjl		tdfx_info->piorange = bus_alloc_resource_any(dev,
232127135Snjl			SYS_RES_IOPORT, &rid, RF_ACTIVE | RF_SHAREABLE);
23363488Scokane		if(tdfx_info->piorange == NULL) {
23463488Scokane#ifdef DEBUG
23563488Scokane			device_printf(dev, "Couldn't map PIO range.");
23663488Scokane#endif
23763488Scokane			tdfx_info->piorid = 0;
23863488Scokane		}
23963488Scokane		else {
24063488Scokane			tdfx_info->piorid = rid;
24165146Scokane		}
24263488Scokane	} else {
24363488Scokane	  tdfx_info->addr1 = 0;
24463488Scokane	  tdfx_info->memrange2 = NULL;
24563488Scokane	  tdfx_info->piorange = NULL;
24663488Scokane	}
24763488Scokane
24861911Scokane	/*
24961911Scokane	 *	Set Writecombining, or at least Uncacheable for the memory region, if we
25061911Scokane	 * are able to
25161911Scokane	 */
25261911Scokane
25361911Scokane	if(tdfx_setmtrr(dev) != 0) {
25461931Scokane#ifdef DEBUG
25561911Scokane		device_printf(dev, "Some weird error setting MTRRs");
25661911Scokane#endif
25761911Scokane		return -1;
25861911Scokane	}
25963488Scokane
26061911Scokane	/*
26161911Scokane	 * make_dev registers the cdev to access the 3dfx card from /dev
26261911Scokane	 *	use hex here for the dev num, simply to provide better support if > 10
26361911Scokane	 * voodoo cards, for the mad. The user must set the link, or use MAKEDEV.
26461911Scokane	 * Why would we want that many voodoo cards anyhow?
26561911Scokane	 */
266104111Sphk	tdfx_info->devt = make_dev(&tdfx_cdev, device_get_unit(dev),
267108323Srwatson		UID_ROOT, GID_WHEEL, 0600, "3dfx%x", device_get_unit(dev));
26861911Scokane
26961911Scokane	return 0;
27061911Scokane}
27161911Scokane
27261911Scokanestatic int
27361911Scokanetdfx_detach(device_t dev) {
27461911Scokane	struct tdfx_softc* tdfx_info;
27561911Scokane	int retval;
27661911Scokane	tdfx_info = device_get_softc(dev);
27761911Scokane
27861911Scokane	/* Delete allocated resource, of course */
27963488Scokane	bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid,
28061911Scokane			tdfx_info->memrange);
28163488Scokane
28263488Scokane	/* Release extended Voodoo3/Banshee resources */
28363488Scokane	if(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE ||
28463488Scokane			pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) {
28563488Scokane		if(tdfx_info->memrange2 != NULL)
28663488Scokane			bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid2,
28763488Scokane				tdfx_info->memrange);
28864085Scokane	/*	if(tdfx_info->piorange != NULL)
28963488Scokane			bus_release_resource(dev, SYS_RES_IOPORT, tdfx_info->piorid,
29064085Scokane				tdfx_info->piorange);*/
29163488Scokane	}
29263488Scokane
29361911Scokane	/* Though it is safe to leave the WRCOMB support since the
29461911Scokane		mem driver checks for it, we should remove it in order
29561911Scokane		to free an MTRR for another device */
29661911Scokane	retval = tdfx_clrmtrr(dev);
29761931Scokane#ifdef DEBUG
29861911Scokane	if(retval != 0)
29961911Scokane		printf("tdfx: For some reason, I couldn't clear the mtrr\n");
30061911Scokane#endif
30161989Scokane	/* Remove device entry when it can no longer be accessed */
30261989Scokane   destroy_dev(tdfx_info->devt);
30361911Scokane	return(0);
30461911Scokane}
30561911Scokane
30661911Scokanestatic int
30761911Scokanetdfx_shutdown(device_t dev) {
30861931Scokane#ifdef DEBUG
30961911Scokane	device_printf(dev, "tdfx: Device Shutdown\n");
31061911Scokane#endif
31161911Scokane	return 0;
31261911Scokane}
31361911Scokane
31461911Scokanestatic int
31561911Scokanetdfx_clrmtrr(device_t dev) {
31661911Scokane	/* This function removes the MTRR set by the attach call, so it can be used
31761911Scokane	 * in the future by other drivers.
31861911Scokane	 */
31961911Scokane	int retval, act;
32061911Scokane	struct tdfx_softc *tdfx_info = device_get_softc(dev);
32161911Scokane
32261911Scokane	act = MEMRANGE_SET_REMOVE;
32361911Scokane	retval = mem_range_attr_set(&tdfx_info->mrdesc, &act);
32461911Scokane	return retval;
32561911Scokane}
32661911Scokane
32761911Scokanestatic int
32861911Scokanetdfx_setmtrr(device_t dev) {
32961911Scokane	/*
33061911Scokane	 * This is the MTRR setting function for the 3dfx card. It is called from
33161911Scokane	 * tdfx_attach. If we can't set the MTRR properly, it's not the end of the
33261911Scokane	 * world. We can still continue, just with slightly (very slightly) degraded
33361911Scokane	 * performance.
33461911Scokane	 */
33561911Scokane	int retval = 0, act;
33661911Scokane	struct tdfx_softc *tdfx_info = device_get_softc(dev);
33761911Scokane
33861911Scokane	/* The older Voodoo cards have a shorter memrange than the newer ones */
33961911Scokane	if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1) || (pci_get_devid(dev) ==
34063488Scokane			PCI_DEVICE_3DFX_VOODOO2)) {
34161911Scokane		tdfx_info->mrdesc.mr_len = 0x400000;
34263488Scokane
34363488Scokane		/* The memory descriptor is described as the top 15 bits of the real
34463488Scokane			address */
34563488Scokane		tdfx_info->mrdesc.mr_base = tdfx_info->addr0 & 0xfffe0000;
34663488Scokane	}
34761911Scokane	else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) ||
34863488Scokane			(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE)) {
34961911Scokane		tdfx_info->mrdesc.mr_len = 0x1000000;
35063488Scokane		/* The Voodoo3 and Banshee LFB is the second memory address */
35163488Scokane		/* The memory descriptor is described as the top 15 bits of the real
35263488Scokane			address */
35363488Scokane		tdfx_info->mrdesc.mr_base = tdfx_info->addr1 & 0xfffe0000;
35463488Scokane	}
35563488Scokane	else
35663488Scokane		 return 0;
35761911Scokane	/*
35861911Scokane    *	The Alliance Pro Motion AT3D was not mentioned in the linux
35961911Scokane	 * driver as far as MTRR support goes, so I just won't put the
36061911Scokane	 * code in here for it. This is where it should go, though.
36161911Scokane	 */
36261911Scokane
36361911Scokane	/* Firstly, try to set write combining */
36461911Scokane	tdfx_info->mrdesc.mr_flags = MDF_WRITECOMBINE;
36561911Scokane	bcopy("tdfx", &tdfx_info->mrdesc.mr_owner, 4);
36661911Scokane	act = MEMRANGE_SET_UPDATE;
36761911Scokane	retval = mem_range_attr_set(&tdfx_info->mrdesc, &act);
36861911Scokane
36961911Scokane	if(retval == 0) {
37061931Scokane#ifdef DEBUG
37161911Scokane		device_printf(dev, "MTRR Set Correctly for tdfx\n");
37261911Scokane#endif
37361911Scokane	} else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO2) ||
37461911Scokane		(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1)) {
37561911Scokane		/* if, for some reason we can't set the WRCOMB range with the V1/V2, we
37661911Scokane		 * can still possibly use the UNCACHEABLE region for it instead, and help
37761911Scokane		 * out in a small way */
37861911Scokane		tdfx_info->mrdesc.mr_flags = MDF_UNCACHEABLE;
37961911Scokane		/* This length of 1000h was taken from the linux device driver... */
38061911Scokane		tdfx_info->mrdesc.mr_len = 0x1000;
38161911Scokane
38261911Scokane		/*
38361911Scokane		 * If, for some reason, we can't set the MTRR (N/A?) we may still continue
38461911Scokane		 */
38561931Scokane#ifdef DEBUG
38661911Scokane		if(retval == 0) {
38795092Smarcel			device_printf(dev, "MTRR Set Type Uncacheable %x\n",
38895092Smarcel			    (u_int32_t)tdfx_info->mrdesc.mr_base);
38961911Scokane		} else {
39061911Scokane			device_printf(dev, "Couldn't Set MTRR\n");
39161911Scokane		}
39261911Scokane#endif
39361911Scokane	}
39461931Scokane#ifdef DEBUG
39561911Scokane	else {
39661911Scokane		device_printf(dev, "Couldn't Set MTRR\n");
39761911Scokane		return 0;
39861911Scokane	}
39961911Scokane#endif
40061911Scokane	return 0;
40161911Scokane}
40261911Scokane
40361911Scokanestatic int
40483366Sjuliantdfx_open(dev_t dev, int flags, int fmt, struct thread *td)
40561911Scokane{
40661911Scokane	/*
40761911Scokane	 *	The open cdev method handles open(2) calls to /dev/3dfx[n]
40861911Scokane	 * We can pretty much allow any opening of the device.
40961911Scokane	 */
41061911Scokane	struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass,
41161911Scokane			UNIT(minor(dev)));
41261911Scokane	if(tdfx_info->busy != 0) return EBUSY;
41361931Scokane#ifdef	DEBUG
41483366Sjulian	printf("3dfx: Opened by #%d\n", td->td_proc->p_pid);
41561911Scokane#endif
41661911Scokane	/* Set the driver as busy */
41761911Scokane	tdfx_info->busy++;
41861911Scokane	return 0;
41961911Scokane}
42061911Scokane
42161911Scokanestatic int
42283366Sjuliantdfx_close(dev_t dev, int fflag, int devtype, struct thread *td)
42361911Scokane{
42461911Scokane	/*
42561911Scokane	 *	The close cdev method handles close(2) calls to /dev/3dfx[n]
42661911Scokane	 * We'll always want to close the device when it's called.
42761911Scokane	 */
42861911Scokane	struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass,
42961911Scokane		UNIT(minor(dev)));
43061911Scokane	if(tdfx_info->busy == 0) return EBADF;
43161911Scokane	tdfx_info->busy = 0;
43261931Scokane#ifdef	DEBUG
43383366Sjulian	printf("Closed by #%d\n", td->td_proc->p_pid);
43461911Scokane#endif
43561911Scokane	return 0;
43661911Scokane}
43761911Scokane
43861911Scokanestatic int
439112569Sjaketdfx_mmap(dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
44061911Scokane{
44161911Scokane	/*
44261911Scokane	 * mmap(2) is called by a user process to request that an area of memory
44361911Scokane	 * associated with this device be mapped for the process to work with. Nprot
44461911Scokane	 * holds the protections requested, PROT_READ, PROT_WRITE, or both.
44561911Scokane	 */
44666910Scokane
44766910Scokane	/**** OLD GET CONFIG ****/
44866910Scokane	/* struct tdfx_softc* tdfx_info; */
44961911Scokane
45061911Scokane	/* Get the configuration for our card XXX*/
45166910Scokane	/*tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
45266910Scokane			UNIT(minor(dev)));*/
45366910Scokane	/************************/
45466910Scokane
45566910Scokane	struct tdfx_softc* tdfx_info[2];
45661911Scokane
45766910Scokane	tdfx_info[0] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 0);
45866910Scokane
45961911Scokane	/* If, for some reason, its not configured, we bail out */
46066910Scokane	if(tdfx_info[0] == NULL) {
46161931Scokane#ifdef	DEBUG
46261911Scokane	   printf("tdfx: tdfx_info (softc) is NULL\n");
46361911Scokane#endif
46461911Scokane	   return -1;
46561911Scokane	}
46666910Scokane
46761911Scokane	/* We must stay within the bound of our address space */
46866910Scokane	if((offset & 0xff000000) == tdfx_info[0]->addr0) {
46961911Scokane		offset &= 0xffffff;
470111462Smux		*paddr = rman_get_start(tdfx_info[0]->memrange) + offset;
471111462Smux		return 0;
47266910Scokane	}
47366910Scokane
47466910Scokane	if(tdfx_count > 1) {
47566910Scokane		tdfx_info[1] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 1);
47666910Scokane		if((offset & 0xff000000) == tdfx_info[1]->addr0) {
47766910Scokane			offset &= 0xffffff;
478111462Smux			*paddr = rman_get_start(tdfx_info[1]->memrange) +
479111462Smux			    offset;
480111462Smux			return 0;
48166910Scokane		}
48266910Scokane	}
48363488Scokane
48463488Scokane	/* See if the Banshee/V3 LFB is being requested */
48566910Scokane	/*if(tdfx_info->memrange2 != NULL && (offset & 0xff000000) ==
48664085Scokane			tdfx_info->addr1) {
48763488Scokane	  	offset &= 0xffffff;
48866910Scokane		return atop(rman_get_start(tdfx_info[1]->memrange2) + offset);
48966910Scokane	}*/ /* VoodooNG code */
49063488Scokane
49166910Scokane	/* The ret call */
49261911Scokane	/* atop -> address to page
49361911Scokane	 * rman_get_start, get the (struct resource*)->r_start member,
49461911Scokane	 * the mapping base address.
49561911Scokane	 */
49666910Scokane	return -1;
49761911Scokane}
49861911Scokane
49961911Scokanestatic int
50061911Scokanetdfx_query_boards(void) {
50161911Scokane	/*
50261911Scokane    *	This returns the number of installed tdfx cards, we have been keeping
50361911Scokane	 * count, look at tdfx_attach
50461911Scokane	 */
50561911Scokane	return tdfx_count;
50661911Scokane}
50761911Scokane
50861911Scokanestatic int
50961911Scokanetdfx_query_fetch(u_int cmd, struct tdfx_pio_data *piod)
51061911Scokane{
51161911Scokane	/* XXX Comment this later, after careful inspection and spring cleaning :) */
51261911Scokane	/* Various return values 8bit-32bit */
51361911Scokane	u_int8_t  ret_byte;
51461911Scokane	u_int16_t ret_word;
51561911Scokane	u_int32_t ret_dword;
51661911Scokane	struct tdfx_softc* tdfx_info = NULL;
51761911Scokane
51861911Scokane	/* This one depend on the tdfx_* structs being properly initialized */
51961911Scokane
52061911Scokane	/*piod->device &= 0xf;*/
52161911Scokane	if((piod == NULL) ||(tdfx_count <= piod->device) ||
52261911Scokane			(piod->device < 0)) {
52361931Scokane#ifdef DEBUG
52461911Scokane		printf("tdfx: Bad device or internal struct in tdfx_query_fetch\n");
52561911Scokane#endif
52661911Scokane		return -EINVAL;
52761911Scokane	}
52861911Scokane
52961911Scokane	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
53061911Scokane			piod->device);
53161911Scokane
53261911Scokane	if(tdfx_info == NULL) return -ENXIO;
53361911Scokane
53461911Scokane	/* We must restrict the size reads from the port, since to high or low of a
53561911Scokane	 * size witll result in wrong data being passed, and that's bad */
53661911Scokane	/* A few of these were pulled during the attach phase */
53761911Scokane	switch(piod->port) {
53861911Scokane		case PCI_VENDOR_ID_FREEBSD:
53961911Scokane			if(piod->size != 2) return -EINVAL;
54061911Scokane			copyout(&tdfx_info->vendor, piod->value, piod->size);
54161911Scokane			return 0;
54261911Scokane		case PCI_DEVICE_ID_FREEBSD:
54361911Scokane			if(piod->size != 2) return -EINVAL;
54461911Scokane			copyout(&tdfx_info->type, piod->value, piod->size);
54561911Scokane			return 0;
54661911Scokane		case PCI_BASE_ADDRESS_0_FREEBSD:
54761911Scokane			if(piod->size != 4) return -EINVAL;
54861911Scokane			copyout(&tdfx_info->addr0, piod->value, piod->size);
54961911Scokane			return 0;
55065146Scokane		case PCI_BASE_ADDRESS_1_FREEBSD:
55165146Scokane			if(piod->size != 4) return -EINVAL;
55265146Scokane			copyout(&tdfx_info->addr1, piod->value, piod->size);
55365146Scokane			return 0;
55465146Scokane		case PCI_PRIBUS_FREEBSD:
55565146Scokane			if(piod->size != 1) return -EINVAL;
55665146Scokane			break;
55765146Scokane		case PCI_IOBASE_0_FREEBSD:
55865146Scokane			if(piod->size != 2) return -EINVAL;
55965146Scokane			break;
56065146Scokane		case PCI_IOLIMIT_0_FREEBSD:
56165146Scokane			if(piod->size != 2) return -EINVAL;
56265146Scokane			break;
56361911Scokane		case SST1_PCI_SPECIAL1_FREEBSD:
56461911Scokane			if(piod->size != 4) return -EINVAL;
56561911Scokane			break;
56661911Scokane		case PCI_REVISION_ID_FREEBSD:
56761911Scokane			if(piod->size != 1) return -EINVAL;
56861911Scokane			break;
56961911Scokane		case SST1_PCI_SPECIAL4_FREEBSD:
57061911Scokane			if(piod->size != 4) return -EINVAL;
57161911Scokane			break;
57261911Scokane		default:
57361911Scokane			return -EINVAL;
57461911Scokane	}
57561911Scokane
57661911Scokane
57761911Scokane	/* Read the value and return */
57861911Scokane	switch(piod->size) {
57961911Scokane		case 1:
58061911Scokane			ret_byte = pci_read_config(tdfx_info[piod->device].dev,
58161911Scokane					piod->port, 1);
58261911Scokane			copyout(&ret_byte, piod->value, 1);
58361911Scokane			break;
58461911Scokane		case 2:
58561911Scokane			ret_word = pci_read_config(tdfx_info[piod->device].dev,
58661911Scokane					piod->port, 2);
58761911Scokane			copyout(&ret_word, piod->value, 2);
58861911Scokane			break;
58961911Scokane		case 4:
59061911Scokane			ret_dword = pci_read_config(tdfx_info[piod->device].dev,
59161911Scokane					piod->port, 4);
59261911Scokane			copyout(&ret_dword, piod->value, 4);
59361911Scokane			break;
59461911Scokane		default:
59561911Scokane			return -EINVAL;
59661911Scokane	}
59761911Scokane	return 0;
59861911Scokane}
59961911Scokane
60061911Scokanestatic int
60161911Scokanetdfx_query_update(u_int cmd, struct tdfx_pio_data *piod)
60261911Scokane{
60361911Scokane	/* XXX Comment this later, after careful inspection and spring cleaning :) */
60461911Scokane	/* Return vals */
60561911Scokane	u_int8_t  ret_byte;
60661911Scokane	u_int16_t ret_word;
60761911Scokane	u_int32_t ret_dword;
60861911Scokane
60961911Scokane	/* Port vals, mask */
61061911Scokane	u_int32_t retval, preval, mask;
61161911Scokane	struct tdfx_softc* tdfx_info = NULL;
61261911Scokane
61361911Scokane
61461911Scokane	if((piod == NULL) || (piod->device >= (tdfx_count &
61561911Scokane					0xf))) {
61661931Scokane#ifdef DEBUG
61761911Scokane		printf("tdfx: Bad struct or device in tdfx_query_update\n");
61861911Scokane#endif
61961911Scokane		return -EINVAL;
62061911Scokane	}
62161911Scokane
62261911Scokane	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
62361911Scokane			piod->device);
62461911Scokane	if(tdfx_info == NULL) return -ENXIO;
62561911Scokane	/* Code below this line in the fuction was taken from the
62661911Scokane	 * Linux driver and converted for freebsd. */
62761911Scokane
62861911Scokane	/* Check the size for all the ports, to make sure stuff doesn't get messed up
62961911Scokane	 * by poorly written clients */
63061911Scokane
63161911Scokane	switch(piod->port) {
63261911Scokane		case PCI_COMMAND_FREEBSD:
63361911Scokane			if(piod->size != 2) return -EINVAL;
63461911Scokane			break;
63561911Scokane		case SST1_PCI_SPECIAL1_FREEBSD:
63661911Scokane			if(piod->size != 4) return -EINVAL;
63761911Scokane			break;
63861911Scokane		case SST1_PCI_SPECIAL2_FREEBSD:
63961911Scokane			if(piod->size != 4) return -EINVAL;
64061911Scokane			break;
64161911Scokane		case SST1_PCI_SPECIAL3_FREEBSD:
64261911Scokane			if(piod->size != 4) return -EINVAL;
64361911Scokane			break;
64461911Scokane		case SST1_PCI_SPECIAL4_FREEBSD:
64561911Scokane			if(piod->size != 4) return -EINVAL;
64661911Scokane			break;
64761911Scokane		default:
64861911Scokane			return -EINVAL;
64961911Scokane	}
65061911Scokane	/* Read the current value */
65161911Scokane	retval = pci_read_config(tdfx_info->dev, piod->port & ~3, 4);
65261911Scokane
65361911Scokane	/* These set up a mask to use, since apparently they wanted to write 4 bytes
65461911Scokane	 * at once to the ports */
65561911Scokane	switch (piod->size) {
65661911Scokane		case 1:
65761911Scokane			copyin(piod->value, &ret_byte, 1);
65861911Scokane			preval = ret_byte << (8 * (piod->port & 0x3));
65961911Scokane			mask = 0xff << (8 * (piod->port & 0x3));
66061911Scokane			break;
66161911Scokane		case 2:
66261911Scokane			copyin(piod->value, &ret_word, 2);
66361911Scokane			preval = ret_word << (8 * (piod->port & 0x3));
66461911Scokane			mask = 0xffff << (8 * (piod->port & 0x3));
66561911Scokane			break;
66661911Scokane		case 4:
66761911Scokane			copyin(piod->value, &ret_dword, 4);
66861911Scokane			preval = ret_dword;
66961911Scokane			mask = ~0;
67061911Scokane			break;
67161911Scokane		default:
67261911Scokane			return -EINVAL;
67361911Scokane	}
67461911Scokane	/* Finally, combine the values and write it to the port */
67561911Scokane	retval = (retval & ~mask) | preval;
67661911Scokane	pci_write_config(tdfx_info->dev, piod->port & ~3, retval, 4);
67761911Scokane
67861911Scokane	return 0;
67961911Scokane}
68061911Scokane
68163488Scokane/* For both of these, I added a variable named workport of type u_int so
68263488Scokane * that I could eliminate the warning about my data type size. The
68363488Scokane * applications expect the port to be of type short, so I needed to change
68463488Scokane * this within the function */
68561911Scokanestatic int
68661911Scokanetdfx_do_pio_rd(struct tdfx_pio_data *piod)
68761911Scokane{
68861911Scokane	/* Return val */
68961911Scokane	u_int8_t  ret_byte;
69063488Scokane	u_int 	 workport;
69165146Scokane	struct tdfx_softc *tdfx_info =
69265146Scokane		(struct tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device);
69365146Scokane
69461911Scokane	/* Restricts the access of ports other than those we use */
69565146Scokane	if(((piod->port != VGA_INPUT_STATUS_1C) || (piod->port != SC_INDEX) ||
69665146Scokane		(piod->port != SC_DATA) || (piod->port != VGA_MISC_OUTPUT_READ)) &&
69765146Scokane		(piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max))
69861911Scokane		return -EPERM;
69961911Scokane
70061911Scokane	/* All VGA STATUS REGS are byte registers, size should never be > 1 */
70161911Scokane	if(piod->size != 1) {
70261911Scokane		return -EINVAL;
70361911Scokane	}
70461911Scokane
70561911Scokane	/* Write the data to the intended port */
70663488Scokane	workport = piod->port;
70763488Scokane	ret_byte = inb(workport);
70861911Scokane	copyout(&ret_byte, piod->value, sizeof(u_int8_t));
70961911Scokane	return 0;
71061911Scokane}
71161911Scokane
71261911Scokanestatic int
71361911Scokanetdfx_do_pio_wt(struct tdfx_pio_data *piod)
71461911Scokane{
71561911Scokane	/* return val */
71661911Scokane	u_int8_t  ret_byte;
71763488Scokane	u_int		 workport;
71865146Scokane	struct tdfx_softc *tdfx_info = (struct
71965146Scokane			tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device);
72061911Scokane	/* Replace old switch w/ massive if(...) */
72161911Scokane	/* Restricts the access of ports other than those we use */
72265146Scokane	if(((piod->port != SC_INDEX) && (piod->port != SC_DATA) &&
72365146Scokane		(piod->port != VGA_MISC_OUTPUT_READ)) /* Can't write VGA_ST_1C */ &&
72465146Scokane		(piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max))
72561911Scokane		return -EPERM;
72661911Scokane
72761911Scokane	/* All VGA STATUS REGS are byte registers, size should never be > 1 */
72861911Scokane	if(piod->size != 1) {
72961911Scokane		return -EINVAL;
73061911Scokane	}
73161911Scokane
73261911Scokane	/* Write the data to the intended port */
73361911Scokane	copyin(piod->value, &ret_byte, sizeof(u_int8_t));
73463488Scokane	workport = piod->port;
73563488Scokane	outb(workport, ret_byte);
73661911Scokane	return 0;
73761911Scokane}
73861911Scokane
73961911Scokanestatic int
74061911Scokanetdfx_do_query(u_int cmd, struct tdfx_pio_data *piod)
74161911Scokane{
74261911Scokane	/* There are three sub-commands to the query 0x33 */
74361911Scokane	switch(_IOC_NR(cmd)) {
74461911Scokane		case 2:
74561911Scokane			return tdfx_query_boards();
74661911Scokane			break;
74761911Scokane		case 3:
74861911Scokane			return tdfx_query_fetch(cmd, piod);
74961911Scokane			break;
75061911Scokane		case 4:
75161911Scokane			return tdfx_query_update(cmd, piod);
75261911Scokane			break;
75361911Scokane		default:
75461911Scokane			/* In case we are thrown a bogus sub-command! */
75561931Scokane#ifdef DEBUG
75661911Scokane			printf("Bad Sub-cmd: 0x%x\n", _IOC_NR(cmd));
75761911Scokane#endif
75861911Scokane			return -EINVAL;
759115494Sphk	}
76061911Scokane}
76161911Scokane
76261911Scokanestatic int
76361911Scokanetdfx_do_pio(u_int cmd, struct tdfx_pio_data *piod)
76461911Scokane{
76561911Scokane	/* Two types of PIO, INPUT and OUTPUT, as the name suggests */
76661911Scokane	switch(_IOC_DIR(cmd)) {
76761911Scokane		case IOCV_OUT:
76861911Scokane			return tdfx_do_pio_rd(piod);
76961911Scokane			break;
77061911Scokane		case IOCV_IN:
77161911Scokane			return tdfx_do_pio_wt(piod);
77261911Scokane			break;
77361911Scokane		default:
77461911Scokane			return -EINVAL;
775115494Sphk	}
77661911Scokane}
77761911Scokane
77861911Scokane/* Calls to ioctl(2) eventually end up here. Unhandled ioctls return an ENXIO,
77961911Scokane * normally, you would read in the data pointed to by data, then write your
78061911Scokane * output to it. The ioctl *should* normally return zero if everything is
78161911Scokane * alright, but 3dfx didn't make it that way...
78261911Scokane *
78361911Scokane * For all of the ioctl code, in the event of a real error,
78461911Scokane * we return -Exxxx rather than simply Exxxx. The reason for this
78561911Scokane * is that the ioctls actually RET information back to the program
78661911Scokane * sometimes, rather than filling it in the passed structure. We
78761911Scokane * want to distinguish errors from useful data, and maintain compatibility.
78861911Scokane *
78961911Scokane * There is this portion of the proc struct called p_retval[], we can store a
79083366Sjulian * return value in td->td_retval[0] and place the return value if it is positive
79161911Scokane * in there, then we can return 0 (good). If the return value is negative, we
79261911Scokane * can return -retval and the error should be properly handled.
79361911Scokane */
79461911Scokanestatic int
79583366Sjuliantdfx_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
79661911Scokane{
79761911Scokane	int retval = 0;
79861911Scokane	struct tdfx_pio_data *piod = (struct tdfx_pio_data*)data;
79961931Scokane#ifdef	DEBUG
800106578Sjhb	printf("IOCTL'd by #%d, cmd: 0x%x, data: %p\n", td->td_proc->p_pid, (u_int32_t)cmd,
801106578Sjhb			piod);
80261911Scokane#endif
80361911Scokane	switch(_IOC_TYPE(cmd)) {
80461911Scokane		/* Return the real error if negative, or simply stick the valid return
80583366Sjulian		 * in td->td_retval */
80661911Scokane	case 0x33:
80761911Scokane			/* The '3'(0x33) type IOCTL is for querying the installed cards */
80883366Sjulian			if((retval = tdfx_do_query(cmd, piod)) > 0) td->td_retval[0] = retval;
80961911Scokane			else return -retval;
81061911Scokane			break;
81161911Scokane		case 0:
81261911Scokane			/* The 0 type IOCTL is for programmed I/O methods */
81383366Sjulian			if((tdfx_do_pio(cmd, piod)) > 0) td->td_retval[0] = retval;
81461911Scokane			else return -retval;
81561911Scokane			break;
81661911Scokane		default:
81761911Scokane			/* Technically, we won't reach this from linux emu, but when glide
81861911Scokane			 * finally gets ported, watch out! */
81961931Scokane#ifdef DEBUG
82083366Sjulian			printf("Bad IOCTL from #%d\n", td->td_proc->p_pid);
82161911Scokane#endif
82261911Scokane			return ENXIO;
82361911Scokane	}
82461911Scokane
82561911Scokane	return 0;
82661911Scokane}
82761911Scokane
82861931Scokane#ifdef TDFX_LINUX
82961931Scokane/*
83061931Scokane * Linux emulation IOCTL for /dev/tdfx
83161931Scokane */
83261931Scokanestatic int
83383366Sjulianlinux_ioctl_tdfx(struct thread *td, struct linux_ioctl_args* args)
83461931Scokane{
83561931Scokane   int error = 0;
83661931Scokane   u_long cmd = args->cmd & 0xffff;
83761911Scokane
83861931Scokane   /* The structure passed to ioctl has two shorts, one int
83961931Scokane      and one void*. */
84061931Scokane   char d_pio[2*sizeof(short) + sizeof(int) + sizeof(void*)];
84161931Scokane
84289306Salfred   struct file *fp;
84361931Scokane
84489319Salfred   if ((error = fget(td, args->fd, &fp)) != 0)
84589319Salfred	   return (error);
84661931Scokane   /* We simply copy the data and send it right to ioctl */
84761931Scokane   copyin((caddr_t)args->arg, &d_pio, sizeof(d_pio));
848102003Srwatson   error = fo_ioctl(fp, cmd, (caddr_t)&d_pio, td->td_ucred, td);
84989306Salfred   fdrop(fp, td);
85061931Scokane   return error;
85161931Scokane}
85261931Scokane#endif /* TDFX_LINUX */
85361931Scokane
85461931Scokane
85561911Scokane/* This is the device driver struct. This is sent to the driver subsystem to
85661911Scokane * register the method structure and the info strcut space for this particular
85761911Scokane * instance of the driver.
85861911Scokane */
85961911Scokanestatic driver_t tdfx_driver = {
86061911Scokane	"tdfx",
86161911Scokane	tdfx_methods,
86261911Scokane	sizeof(struct tdfx_softc),
86361911Scokane};
86461911Scokane
86561911Scokane/* Tell Mr. Kernel about us! */
86661911ScokaneDRIVER_MODULE(tdfx, pci, tdfx_driver, tdfx_devclass, 0, 0);
867