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$");
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/conf.h>
4661911Scokane#include <sys/fcntl.h>
4761911Scokane#include <sys/file.h>
4861911Scokane#include <sys/filedesc.h>
4961911Scokane#include <sys/filio.h>
5061911Scokane#include <sys/ioccom.h>
5161911Scokane#include <sys/kernel.h>
52129879Sphk#include <sys/module.h>
53129879Sphk#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
7261911Scokane#include <dev/tdfx/tdfx_io.h>
7361911Scokane#include <dev/tdfx/tdfx_vars.h>
7461911Scokane#include <dev/tdfx/tdfx_pci.h>
7561911Scokane
7661911Scokane
7761911Scokanestatic devclass_t tdfx_devclass;
7861911Scokane
7961911Scokane
8061911Scokanestatic int tdfx_count = 0;
8161911Scokane
8261911Scokane
8361911Scokane/* Set up the boot probe/attach routines */
8461911Scokanestatic device_method_t tdfx_methods[] = {
8561911Scokane	DEVMETHOD(device_probe,		tdfx_probe),
8661911Scokane	DEVMETHOD(device_attach,	tdfx_attach),
8761911Scokane	DEVMETHOD(device_detach,	tdfx_detach),
8861911Scokane	DEVMETHOD(device_shutdown,	tdfx_shutdown),
8961911Scokane	{ 0, 0 }
9061911Scokane};
9161911Scokane
92249132Smavstatic MALLOC_DEFINE(M_TDFX,"tdfx_driver","3DFX Graphics[/2D]/3D Accelerators");
9361911Scokane
9461911Scokane/* Char. Dev. file operations structure */
9561911Scokanestatic struct cdevsw tdfx_cdev = {
96126080Sphk	.d_version =	D_VERSION,
97126080Sphk	.d_flags =	D_NEEDGIANT,
98111815Sphk	.d_open =	tdfx_open,
99111815Sphk	.d_close =	tdfx_close,
100111815Sphk	.d_ioctl =	tdfx_ioctl,
101111815Sphk	.d_mmap =	tdfx_mmap,
102111815Sphk	.d_name =	"tdfx",
10361911Scokane};
10461911Scokane
10561911Scokanestatic int
10661911Scokanetdfx_probe(device_t dev)
10761911Scokane{
10861911Scokane	/*
10961911Scokane	 * probe routine called on kernel boot to register supported devices. We get
11061911Scokane	 * a device structure to work with, and we can test the VENDOR/DEVICE IDs to
111142880Simp	 * see if this PCI device is one that we support. Return BUS_PRROBE_DEFAULT
112142880Simp	 * if yes, ENXIO if not.
11361911Scokane	 */
11461911Scokane	switch(pci_get_devid(dev)) {
11561911Scokane	case PCI_DEVICE_ALLIANCE_AT3D:
11661911Scokane		device_set_desc(dev, "ProMotion At3D 3D Accelerator");
117142880Simp		return BUS_PROBE_DEFAULT;
11861911Scokane	case PCI_DEVICE_3DFX_VOODOO2:
11961911Scokane		device_set_desc(dev, "3DFX Voodoo II 3D Accelerator");
120142880Simp		return BUS_PROBE_DEFAULT;
12165146Scokane	/*case PCI_DEVICE_3DFX_BANSHEE:
12261911Scokane		device_set_desc(dev, "3DFX Voodoo Banshee 2D/3D Graphics Accelerator");
123142880Simp		return BUS_PROBE_DEFAULT;
12461911Scokane	case PCI_DEVICE_3DFX_VOODOO3:
12561911Scokane		device_set_desc(dev, "3DFX Voodoo3 2D/3D Graphics Accelerator");
126142880Simp		return BUS_PROBE_DEFAULT;*/
12761911Scokane	case PCI_DEVICE_3DFX_VOODOO1:
12861911Scokane		device_set_desc(dev, "3DFX Voodoo Graphics 3D Accelerator");
129142880Simp		return BUS_PROBE_DEFAULT;
13061911Scokane	};
13161911Scokane
13261911Scokane	return ENXIO;
13361911Scokane}
13461911Scokane
13561911Scokanestatic int
13661911Scokanetdfx_attach(device_t dev) {
13761911Scokane	/*
13861911Scokane	 * The attach routine is called after the probe routine successfully says it
13961911Scokane	 * supports a given card. We now proceed to initialize this card for use with
14061911Scokane	 * the system. I want to map the device memory for userland allocation and
14161911Scokane	 * fill an information structure with information on this card. I'd also like
14261911Scokane	 * to set Write Combining with the MTRR code so that we can hopefully speed
14361911Scokane	 * up memory writes. The last thing is to register the character device
14461911Scokane	 * interface to the card, so we can open it from /dev/3dfxN, where N is a
14561911Scokane	 * small, whole number.
14661911Scokane	 */
14761911Scokane	struct tdfx_softc *tdfx_info;
14861911Scokane	/* rid value tells bus_alloc_resource where to find the addresses of ports or
14961911Scokane	 * of memory ranges in the PCI config space*/
150119690Sjhb	int rid = PCIR_BAR(0);
15161911Scokane
15261911Scokane	/* Increment the card counter (for the ioctl code) */
15361911Scokane	tdfx_count++;
15461911Scokane
15561911Scokane	/* Fill the soft config struct with info about this device*/
15661911Scokane	tdfx_info = device_get_softc(dev);
15761911Scokane	tdfx_info->dev = dev;
15861911Scokane	tdfx_info->vendor = pci_get_vendor(dev);
15961911Scokane	tdfx_info->type = pci_get_devid(dev) >> 16;
16061911Scokane	tdfx_info->bus = pci_get_bus(dev);
16161911Scokane	tdfx_info->dv = pci_get_slot(dev);
16261911Scokane	tdfx_info->curFile = NULL;
16361911Scokane
16461911Scokane	/*
16561911Scokane	 *	Get the Memory Location from the PCI Config, mask out lower word, since
16661911Scokane	 * the config space register is only one word long (this is nicer than a
16761911Scokane	 * bitshift).
16861911Scokane	 */
16961911Scokane	tdfx_info->addr0 = (pci_read_config(dev, 0x10, 4) & 0xffff0000);
17061931Scokane#ifdef DEBUG
17161911Scokane	device_printf(dev, "Base0 @ 0x%x\n", tdfx_info->addr0);
17261911Scokane#endif
17361911Scokane	/* Notify the VM that we will be mapping some memory later */
174127135Snjl	tdfx_info->memrange = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
175127135Snjl		&rid, RF_ACTIVE | RF_SHAREABLE);
17661911Scokane	if(tdfx_info->memrange == NULL) {
17761931Scokane#ifdef DEBUG
17861911Scokane		device_printf(dev, "Error mapping mem, won't be able to use mmap()\n");
17961911Scokane#endif
18061911Scokane		tdfx_info->memrid = 0;
18161911Scokane	}
18261911Scokane	else {
18361911Scokane		tdfx_info->memrid = rid;
18461931Scokane#ifdef DEBUG
18561911Scokane		device_printf(dev, "Mapped to: 0x%x\n",
18661911Scokane				(unsigned int)rman_get_start(tdfx_info->memrange));
18761911Scokane#endif
18861911Scokane	}
18961911Scokane
19063488Scokane	/* Setup for Voodoo3 and Banshee, PIO and an extram Memrange */
19163488Scokane	if(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3 ||
19263488Scokane		pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE) {
19364085Scokane		rid = 0x14;	/* 2nd mem map */
19463488Scokane		tdfx_info->addr1 = (pci_read_config(dev, 0x14, 4) & 0xffff0000);
19563488Scokane#ifdef DEBUG
19663488Scokane		device_printf(dev, "Base1 @ 0x%x\n", tdfx_info->addr1);
19763488Scokane#endif
198127135Snjl		tdfx_info->memrange2 = bus_alloc_resource_any(dev,
199127135Snjl			SYS_RES_MEMORY, &rid, RF_ACTIVE | RF_SHAREABLE);
20063488Scokane		if(tdfx_info->memrange2 == NULL) {
20163488Scokane#ifdef DEBUG
20263488Scokane			device_printf(dev, "Mem1 couldn't be allocated, glide may not work.");
20363488Scokane#endif
20463488Scokane			tdfx_info->memrid2 = 0;
20563488Scokane		}
20663488Scokane		else {
20763488Scokane			tdfx_info->memrid2 = rid;
20863488Scokane		}
20963488Scokane		/* Now to map the PIO stuff */
21065146Scokane		rid = PCIR_IOBASE0_2;
21165146Scokane		tdfx_info->pio0 = pci_read_config(dev, 0x2c, 2);
21265146Scokane		tdfx_info->pio0max = pci_read_config(dev, 0x30, 2) + tdfx_info->pio0;
213127135Snjl		tdfx_info->piorange = bus_alloc_resource_any(dev,
214127135Snjl			SYS_RES_IOPORT, &rid, RF_ACTIVE | RF_SHAREABLE);
21563488Scokane		if(tdfx_info->piorange == NULL) {
21663488Scokane#ifdef DEBUG
21763488Scokane			device_printf(dev, "Couldn't map PIO range.");
21863488Scokane#endif
21963488Scokane			tdfx_info->piorid = 0;
22063488Scokane		}
22163488Scokane		else {
22263488Scokane			tdfx_info->piorid = rid;
22365146Scokane		}
22463488Scokane	} else {
22563488Scokane	  tdfx_info->addr1 = 0;
22663488Scokane	  tdfx_info->memrange2 = NULL;
22763488Scokane	  tdfx_info->piorange = NULL;
22863488Scokane	}
22963488Scokane
23061911Scokane	/*
23161911Scokane	 *	Set Writecombining, or at least Uncacheable for the memory region, if we
23261911Scokane	 * are able to
23361911Scokane	 */
23461911Scokane
23561911Scokane	if(tdfx_setmtrr(dev) != 0) {
23661931Scokane#ifdef DEBUG
23761911Scokane		device_printf(dev, "Some weird error setting MTRRs");
23861911Scokane#endif
23961911Scokane		return -1;
24061911Scokane	}
24163488Scokane
24261911Scokane	/*
24361911Scokane	 * make_dev registers the cdev to access the 3dfx card from /dev
24461911Scokane	 *	use hex here for the dev num, simply to provide better support if > 10
245215334Sdougb	 * voodoo cards, for the mad. The user must set the link.
24661911Scokane	 * Why would we want that many voodoo cards anyhow?
24761911Scokane	 */
248104111Sphk	tdfx_info->devt = make_dev(&tdfx_cdev, device_get_unit(dev),
249108323Srwatson		UID_ROOT, GID_WHEEL, 0600, "3dfx%x", device_get_unit(dev));
250191056Sed	tdfx_info->devt->si_drv1 = tdfx_info;
25161911Scokane
25261911Scokane	return 0;
25361911Scokane}
25461911Scokane
25561911Scokanestatic int
25661911Scokanetdfx_detach(device_t dev) {
25761911Scokane	struct tdfx_softc* tdfx_info;
25861911Scokane	int retval;
25961911Scokane	tdfx_info = device_get_softc(dev);
26061911Scokane
26161911Scokane	/* Delete allocated resource, of course */
26263488Scokane	bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid,
26361911Scokane			tdfx_info->memrange);
26463488Scokane
26563488Scokane	/* Release extended Voodoo3/Banshee resources */
26663488Scokane	if(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE ||
26763488Scokane			pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) {
26863488Scokane		if(tdfx_info->memrange2 != NULL)
26963488Scokane			bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid2,
27063488Scokane				tdfx_info->memrange);
27164085Scokane	/*	if(tdfx_info->piorange != NULL)
27263488Scokane			bus_release_resource(dev, SYS_RES_IOPORT, tdfx_info->piorid,
27364085Scokane				tdfx_info->piorange);*/
27463488Scokane	}
27563488Scokane
27661911Scokane	/* Though it is safe to leave the WRCOMB support since the
27761911Scokane		mem driver checks for it, we should remove it in order
27861911Scokane		to free an MTRR for another device */
27961911Scokane	retval = tdfx_clrmtrr(dev);
28061931Scokane#ifdef DEBUG
28161911Scokane	if(retval != 0)
28261911Scokane		printf("tdfx: For some reason, I couldn't clear the mtrr\n");
28361911Scokane#endif
28461989Scokane	/* Remove device entry when it can no longer be accessed */
28561989Scokane   destroy_dev(tdfx_info->devt);
28661911Scokane	return(0);
28761911Scokane}
28861911Scokane
28961911Scokanestatic int
29061911Scokanetdfx_shutdown(device_t dev) {
29161931Scokane#ifdef DEBUG
29261911Scokane	device_printf(dev, "tdfx: Device Shutdown\n");
29361911Scokane#endif
29461911Scokane	return 0;
29561911Scokane}
29661911Scokane
29761911Scokanestatic int
29861911Scokanetdfx_clrmtrr(device_t dev) {
29961911Scokane	/* This function removes the MTRR set by the attach call, so it can be used
30061911Scokane	 * in the future by other drivers.
30161911Scokane	 */
30261911Scokane	int retval, act;
30361911Scokane	struct tdfx_softc *tdfx_info = device_get_softc(dev);
30461911Scokane
30561911Scokane	act = MEMRANGE_SET_REMOVE;
30661911Scokane	retval = mem_range_attr_set(&tdfx_info->mrdesc, &act);
30761911Scokane	return retval;
30861911Scokane}
30961911Scokane
31061911Scokanestatic int
31161911Scokanetdfx_setmtrr(device_t dev) {
31261911Scokane	/*
31361911Scokane	 * This is the MTRR setting function for the 3dfx card. It is called from
31461911Scokane	 * tdfx_attach. If we can't set the MTRR properly, it's not the end of the
31561911Scokane	 * world. We can still continue, just with slightly (very slightly) degraded
31661911Scokane	 * performance.
31761911Scokane	 */
31861911Scokane	int retval = 0, act;
31961911Scokane	struct tdfx_softc *tdfx_info = device_get_softc(dev);
32061911Scokane
32161911Scokane	/* The older Voodoo cards have a shorter memrange than the newer ones */
32261911Scokane	if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1) || (pci_get_devid(dev) ==
32363488Scokane			PCI_DEVICE_3DFX_VOODOO2)) {
32461911Scokane		tdfx_info->mrdesc.mr_len = 0x400000;
32563488Scokane
32663488Scokane		/* The memory descriptor is described as the top 15 bits of the real
32763488Scokane			address */
32863488Scokane		tdfx_info->mrdesc.mr_base = tdfx_info->addr0 & 0xfffe0000;
32963488Scokane	}
33061911Scokane	else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) ||
33163488Scokane			(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE)) {
33261911Scokane		tdfx_info->mrdesc.mr_len = 0x1000000;
33363488Scokane		/* The Voodoo3 and Banshee LFB is the second memory address */
33463488Scokane		/* The memory descriptor is described as the top 15 bits of the real
33563488Scokane			address */
33663488Scokane		tdfx_info->mrdesc.mr_base = tdfx_info->addr1 & 0xfffe0000;
33763488Scokane	}
33863488Scokane	else
33963488Scokane		 return 0;
34061911Scokane	/*
34161911Scokane    *	The Alliance Pro Motion AT3D was not mentioned in the linux
34261911Scokane	 * driver as far as MTRR support goes, so I just won't put the
34361911Scokane	 * code in here for it. This is where it should go, though.
34461911Scokane	 */
34561911Scokane
34661911Scokane	/* Firstly, try to set write combining */
34761911Scokane	tdfx_info->mrdesc.mr_flags = MDF_WRITECOMBINE;
34861911Scokane	bcopy("tdfx", &tdfx_info->mrdesc.mr_owner, 4);
34961911Scokane	act = MEMRANGE_SET_UPDATE;
35061911Scokane	retval = mem_range_attr_set(&tdfx_info->mrdesc, &act);
35161911Scokane
35261911Scokane	if(retval == 0) {
35361931Scokane#ifdef DEBUG
35461911Scokane		device_printf(dev, "MTRR Set Correctly for tdfx\n");
35561911Scokane#endif
35661911Scokane	} else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO2) ||
35761911Scokane		(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1)) {
35861911Scokane		/* if, for some reason we can't set the WRCOMB range with the V1/V2, we
35961911Scokane		 * can still possibly use the UNCACHEABLE region for it instead, and help
36061911Scokane		 * out in a small way */
36161911Scokane		tdfx_info->mrdesc.mr_flags = MDF_UNCACHEABLE;
36261911Scokane		/* This length of 1000h was taken from the linux device driver... */
36361911Scokane		tdfx_info->mrdesc.mr_len = 0x1000;
36461911Scokane
36561911Scokane		/*
36661911Scokane		 * If, for some reason, we can't set the MTRR (N/A?) we may still continue
36761911Scokane		 */
36861931Scokane#ifdef DEBUG
369142253Ssam		device_printf(dev, "MTRR Set Type Uncacheable %x\n",
370142253Ssam		    (u_int32_t)tdfx_info->mrdesc.mr_base);
37161911Scokane#endif
37261911Scokane	}
37361931Scokane#ifdef DEBUG
37461911Scokane	else {
37561911Scokane		device_printf(dev, "Couldn't Set MTRR\n");
37661911Scokane		return 0;
37761911Scokane	}
37861911Scokane#endif
37961911Scokane	return 0;
38061911Scokane}
38161911Scokane
38261911Scokanestatic int
383130585Sphktdfx_open(struct cdev *dev, int flags, int fmt, struct thread *td)
38461911Scokane{
38561911Scokane	/*
38661911Scokane	 *	The open cdev method handles open(2) calls to /dev/3dfx[n]
38761911Scokane	 * We can pretty much allow any opening of the device.
38861911Scokane	 */
389191056Sed	struct tdfx_softc *tdfx_info = dev->si_drv1;
39061911Scokane	if(tdfx_info->busy != 0) return EBUSY;
39161931Scokane#ifdef	DEBUG
39283366Sjulian	printf("3dfx: Opened by #%d\n", td->td_proc->p_pid);
39361911Scokane#endif
39461911Scokane	/* Set the driver as busy */
39561911Scokane	tdfx_info->busy++;
39661911Scokane	return 0;
39761911Scokane}
39861911Scokane
39961911Scokanestatic int
400130585Sphktdfx_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
40161911Scokane{
40261911Scokane	/*
40361911Scokane	 *	The close cdev method handles close(2) calls to /dev/3dfx[n]
40461911Scokane	 * We'll always want to close the device when it's called.
40561911Scokane	 */
406191056Sed	struct tdfx_softc *tdfx_info = dev->si_drv1;
40761911Scokane	if(tdfx_info->busy == 0) return EBADF;
40861911Scokane	tdfx_info->busy = 0;
40961931Scokane#ifdef	DEBUG
41083366Sjulian	printf("Closed by #%d\n", td->td_proc->p_pid);
41161911Scokane#endif
41261911Scokane	return 0;
41361911Scokane}
41461911Scokane
41561911Scokanestatic int
416201223Srnolandtdfx_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr,
417201223Srnoland    int nprot, vm_memattr_t *memattr)
41861911Scokane{
41961911Scokane	/*
42061911Scokane	 * mmap(2) is called by a user process to request that an area of memory
42161911Scokane	 * associated with this device be mapped for the process to work with. Nprot
42261911Scokane	 * holds the protections requested, PROT_READ, PROT_WRITE, or both.
42361911Scokane	 */
42466910Scokane
42566910Scokane	/**** OLD GET CONFIG ****/
42666910Scokane	/* struct tdfx_softc* tdfx_info; */
42761911Scokane
42861911Scokane	/* Get the configuration for our card XXX*/
429191056Sed	/*tdfx_info = dev->si_drv1; */
43066910Scokane	/************************/
43166910Scokane
43266910Scokane	struct tdfx_softc* tdfx_info[2];
43361911Scokane
43466910Scokane	tdfx_info[0] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 0);
43566910Scokane
43661911Scokane	/* If, for some reason, its not configured, we bail out */
43766910Scokane	if(tdfx_info[0] == NULL) {
43861931Scokane#ifdef	DEBUG
43961911Scokane	   printf("tdfx: tdfx_info (softc) is NULL\n");
44061911Scokane#endif
44161911Scokane	   return -1;
44261911Scokane	}
44366910Scokane
44461911Scokane	/* We must stay within the bound of our address space */
44566910Scokane	if((offset & 0xff000000) == tdfx_info[0]->addr0) {
44661911Scokane		offset &= 0xffffff;
447111462Smux		*paddr = rman_get_start(tdfx_info[0]->memrange) + offset;
448111462Smux		return 0;
44966910Scokane	}
45066910Scokane
45166910Scokane	if(tdfx_count > 1) {
45266910Scokane		tdfx_info[1] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 1);
45366910Scokane		if((offset & 0xff000000) == tdfx_info[1]->addr0) {
45466910Scokane			offset &= 0xffffff;
455111462Smux			*paddr = rman_get_start(tdfx_info[1]->memrange) +
456111462Smux			    offset;
457111462Smux			return 0;
45866910Scokane		}
45966910Scokane	}
46063488Scokane
46163488Scokane	/* See if the Banshee/V3 LFB is being requested */
46266910Scokane	/*if(tdfx_info->memrange2 != NULL && (offset & 0xff000000) ==
46364085Scokane			tdfx_info->addr1) {
46463488Scokane	  	offset &= 0xffffff;
46566910Scokane		return atop(rman_get_start(tdfx_info[1]->memrange2) + offset);
46666910Scokane	}*/ /* VoodooNG code */
46763488Scokane
46866910Scokane	/* The ret call */
46961911Scokane	/* atop -> address to page
47061911Scokane	 * rman_get_start, get the (struct resource*)->r_start member,
47161911Scokane	 * the mapping base address.
47261911Scokane	 */
47366910Scokane	return -1;
47461911Scokane}
47561911Scokane
47661911Scokanestatic int
47761911Scokanetdfx_query_boards(void) {
47861911Scokane	/*
47961911Scokane    *	This returns the number of installed tdfx cards, we have been keeping
48061911Scokane	 * count, look at tdfx_attach
48161911Scokane	 */
48261911Scokane	return tdfx_count;
48361911Scokane}
48461911Scokane
48561911Scokanestatic int
48661911Scokanetdfx_query_fetch(u_int cmd, struct tdfx_pio_data *piod)
48761911Scokane{
48861911Scokane	/* XXX Comment this later, after careful inspection and spring cleaning :) */
48961911Scokane	/* Various return values 8bit-32bit */
49061911Scokane	u_int8_t  ret_byte;
49161911Scokane	u_int16_t ret_word;
49261911Scokane	u_int32_t ret_dword;
49361911Scokane	struct tdfx_softc* tdfx_info = NULL;
49461911Scokane
49561911Scokane	/* This one depend on the tdfx_* structs being properly initialized */
49661911Scokane
49761911Scokane	/*piod->device &= 0xf;*/
49861911Scokane	if((piod == NULL) ||(tdfx_count <= piod->device) ||
49961911Scokane			(piod->device < 0)) {
50061931Scokane#ifdef DEBUG
50161911Scokane		printf("tdfx: Bad device or internal struct in tdfx_query_fetch\n");
50261911Scokane#endif
50361911Scokane		return -EINVAL;
50461911Scokane	}
50561911Scokane
50661911Scokane	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
50761911Scokane			piod->device);
50861911Scokane
50961911Scokane	if(tdfx_info == NULL) return -ENXIO;
51061911Scokane
51161911Scokane	/* We must restrict the size reads from the port, since to high or low of a
51261911Scokane	 * size witll result in wrong data being passed, and that's bad */
51361911Scokane	/* A few of these were pulled during the attach phase */
51461911Scokane	switch(piod->port) {
51561911Scokane		case PCI_VENDOR_ID_FREEBSD:
51661911Scokane			if(piod->size != 2) return -EINVAL;
51761911Scokane			copyout(&tdfx_info->vendor, piod->value, piod->size);
51861911Scokane			return 0;
51961911Scokane		case PCI_DEVICE_ID_FREEBSD:
52061911Scokane			if(piod->size != 2) return -EINVAL;
52161911Scokane			copyout(&tdfx_info->type, piod->value, piod->size);
52261911Scokane			return 0;
52361911Scokane		case PCI_BASE_ADDRESS_0_FREEBSD:
52461911Scokane			if(piod->size != 4) return -EINVAL;
52561911Scokane			copyout(&tdfx_info->addr0, piod->value, piod->size);
52661911Scokane			return 0;
52765146Scokane		case PCI_BASE_ADDRESS_1_FREEBSD:
52865146Scokane			if(piod->size != 4) return -EINVAL;
52965146Scokane			copyout(&tdfx_info->addr1, piod->value, piod->size);
53065146Scokane			return 0;
53165146Scokane		case PCI_PRIBUS_FREEBSD:
53265146Scokane			if(piod->size != 1) return -EINVAL;
53365146Scokane			break;
53465146Scokane		case PCI_IOBASE_0_FREEBSD:
53565146Scokane			if(piod->size != 2) return -EINVAL;
53665146Scokane			break;
53765146Scokane		case PCI_IOLIMIT_0_FREEBSD:
53865146Scokane			if(piod->size != 2) return -EINVAL;
53965146Scokane			break;
54061911Scokane		case SST1_PCI_SPECIAL1_FREEBSD:
54161911Scokane			if(piod->size != 4) return -EINVAL;
54261911Scokane			break;
54361911Scokane		case PCI_REVISION_ID_FREEBSD:
54461911Scokane			if(piod->size != 1) return -EINVAL;
54561911Scokane			break;
54661911Scokane		case SST1_PCI_SPECIAL4_FREEBSD:
54761911Scokane			if(piod->size != 4) return -EINVAL;
54861911Scokane			break;
54961911Scokane		default:
55061911Scokane			return -EINVAL;
55161911Scokane	}
55261911Scokane
55361911Scokane
55461911Scokane	/* Read the value and return */
55561911Scokane	switch(piod->size) {
55661911Scokane		case 1:
55761911Scokane			ret_byte = pci_read_config(tdfx_info[piod->device].dev,
55861911Scokane					piod->port, 1);
55961911Scokane			copyout(&ret_byte, piod->value, 1);
56061911Scokane			break;
56161911Scokane		case 2:
56261911Scokane			ret_word = pci_read_config(tdfx_info[piod->device].dev,
56361911Scokane					piod->port, 2);
56461911Scokane			copyout(&ret_word, piod->value, 2);
56561911Scokane			break;
56661911Scokane		case 4:
56761911Scokane			ret_dword = pci_read_config(tdfx_info[piod->device].dev,
56861911Scokane					piod->port, 4);
56961911Scokane			copyout(&ret_dword, piod->value, 4);
57061911Scokane			break;
57161911Scokane		default:
57261911Scokane			return -EINVAL;
57361911Scokane	}
57461911Scokane	return 0;
57561911Scokane}
57661911Scokane
57761911Scokanestatic int
57861911Scokanetdfx_query_update(u_int cmd, struct tdfx_pio_data *piod)
57961911Scokane{
58061911Scokane	/* XXX Comment this later, after careful inspection and spring cleaning :) */
58161911Scokane	/* Return vals */
58261911Scokane	u_int8_t  ret_byte;
58361911Scokane	u_int16_t ret_word;
58461911Scokane	u_int32_t ret_dword;
58561911Scokane
58661911Scokane	/* Port vals, mask */
58761911Scokane	u_int32_t retval, preval, mask;
58861911Scokane	struct tdfx_softc* tdfx_info = NULL;
58961911Scokane
59061911Scokane
59161911Scokane	if((piod == NULL) || (piod->device >= (tdfx_count &
59261911Scokane					0xf))) {
59361931Scokane#ifdef DEBUG
59461911Scokane		printf("tdfx: Bad struct or device in tdfx_query_update\n");
59561911Scokane#endif
59661911Scokane		return -EINVAL;
59761911Scokane	}
59861911Scokane
59961911Scokane	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
60061911Scokane			piod->device);
60161911Scokane	if(tdfx_info == NULL) return -ENXIO;
60261911Scokane	/* Code below this line in the fuction was taken from the
60361911Scokane	 * Linux driver and converted for freebsd. */
60461911Scokane
60561911Scokane	/* Check the size for all the ports, to make sure stuff doesn't get messed up
60661911Scokane	 * by poorly written clients */
60761911Scokane
60861911Scokane	switch(piod->port) {
60961911Scokane		case PCI_COMMAND_FREEBSD:
61061911Scokane			if(piod->size != 2) return -EINVAL;
61161911Scokane			break;
61261911Scokane		case SST1_PCI_SPECIAL1_FREEBSD:
61361911Scokane			if(piod->size != 4) return -EINVAL;
61461911Scokane			break;
61561911Scokane		case SST1_PCI_SPECIAL2_FREEBSD:
61661911Scokane			if(piod->size != 4) return -EINVAL;
61761911Scokane			break;
61861911Scokane		case SST1_PCI_SPECIAL3_FREEBSD:
61961911Scokane			if(piod->size != 4) return -EINVAL;
62061911Scokane			break;
62161911Scokane		case SST1_PCI_SPECIAL4_FREEBSD:
62261911Scokane			if(piod->size != 4) return -EINVAL;
62361911Scokane			break;
62461911Scokane		default:
62561911Scokane			return -EINVAL;
62661911Scokane	}
62761911Scokane	/* Read the current value */
62861911Scokane	retval = pci_read_config(tdfx_info->dev, piod->port & ~3, 4);
62961911Scokane
63061911Scokane	/* These set up a mask to use, since apparently they wanted to write 4 bytes
63161911Scokane	 * at once to the ports */
63261911Scokane	switch (piod->size) {
63361911Scokane		case 1:
63461911Scokane			copyin(piod->value, &ret_byte, 1);
63561911Scokane			preval = ret_byte << (8 * (piod->port & 0x3));
63661911Scokane			mask = 0xff << (8 * (piod->port & 0x3));
63761911Scokane			break;
63861911Scokane		case 2:
63961911Scokane			copyin(piod->value, &ret_word, 2);
64061911Scokane			preval = ret_word << (8 * (piod->port & 0x3));
64161911Scokane			mask = 0xffff << (8 * (piod->port & 0x3));
64261911Scokane			break;
64361911Scokane		case 4:
64461911Scokane			copyin(piod->value, &ret_dword, 4);
64561911Scokane			preval = ret_dword;
64661911Scokane			mask = ~0;
64761911Scokane			break;
64861911Scokane		default:
64961911Scokane			return -EINVAL;
65061911Scokane	}
65161911Scokane	/* Finally, combine the values and write it to the port */
65261911Scokane	retval = (retval & ~mask) | preval;
65361911Scokane	pci_write_config(tdfx_info->dev, piod->port & ~3, retval, 4);
65461911Scokane
65561911Scokane	return 0;
65661911Scokane}
65761911Scokane
65863488Scokane/* For both of these, I added a variable named workport of type u_int so
65963488Scokane * that I could eliminate the warning about my data type size. The
66063488Scokane * applications expect the port to be of type short, so I needed to change
66163488Scokane * this within the function */
66261911Scokanestatic int
66361911Scokanetdfx_do_pio_rd(struct tdfx_pio_data *piod)
66461911Scokane{
66561911Scokane	/* Return val */
66661911Scokane	u_int8_t  ret_byte;
66763488Scokane	u_int 	 workport;
66865146Scokane	struct tdfx_softc *tdfx_info =
66965146Scokane		(struct tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device);
67065146Scokane
67161911Scokane	/* Restricts the access of ports other than those we use */
67265146Scokane	if(((piod->port != VGA_INPUT_STATUS_1C) || (piod->port != SC_INDEX) ||
67365146Scokane		(piod->port != SC_DATA) || (piod->port != VGA_MISC_OUTPUT_READ)) &&
67465146Scokane		(piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max))
67561911Scokane		return -EPERM;
67661911Scokane
67761911Scokane	/* All VGA STATUS REGS are byte registers, size should never be > 1 */
67861911Scokane	if(piod->size != 1) {
67961911Scokane		return -EINVAL;
68061911Scokane	}
68161911Scokane
68261911Scokane	/* Write the data to the intended port */
68363488Scokane	workport = piod->port;
68463488Scokane	ret_byte = inb(workport);
68561911Scokane	copyout(&ret_byte, piod->value, sizeof(u_int8_t));
68661911Scokane	return 0;
68761911Scokane}
68861911Scokane
68961911Scokanestatic int
69061911Scokanetdfx_do_pio_wt(struct tdfx_pio_data *piod)
69161911Scokane{
69261911Scokane	/* return val */
69361911Scokane	u_int8_t  ret_byte;
69463488Scokane	u_int		 workport;
69565146Scokane	struct tdfx_softc *tdfx_info = (struct
69665146Scokane			tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device);
69761911Scokane	/* Replace old switch w/ massive if(...) */
69861911Scokane	/* Restricts the access of ports other than those we use */
69965146Scokane	if(((piod->port != SC_INDEX) && (piod->port != SC_DATA) &&
70065146Scokane		(piod->port != VGA_MISC_OUTPUT_READ)) /* Can't write VGA_ST_1C */ &&
70165146Scokane		(piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max))
70261911Scokane		return -EPERM;
70361911Scokane
70461911Scokane	/* All VGA STATUS REGS are byte registers, size should never be > 1 */
70561911Scokane	if(piod->size != 1) {
70661911Scokane		return -EINVAL;
70761911Scokane	}
70861911Scokane
70961911Scokane	/* Write the data to the intended port */
71061911Scokane	copyin(piod->value, &ret_byte, sizeof(u_int8_t));
71163488Scokane	workport = piod->port;
71263488Scokane	outb(workport, ret_byte);
71361911Scokane	return 0;
71461911Scokane}
71561911Scokane
71661911Scokanestatic int
71761911Scokanetdfx_do_query(u_int cmd, struct tdfx_pio_data *piod)
71861911Scokane{
71961911Scokane	/* There are three sub-commands to the query 0x33 */
72061911Scokane	switch(_IOC_NR(cmd)) {
72161911Scokane		case 2:
72261911Scokane			return tdfx_query_boards();
72361911Scokane			break;
72461911Scokane		case 3:
72561911Scokane			return tdfx_query_fetch(cmd, piod);
72661911Scokane			break;
72761911Scokane		case 4:
72861911Scokane			return tdfx_query_update(cmd, piod);
72961911Scokane			break;
73061911Scokane		default:
73161911Scokane			/* In case we are thrown a bogus sub-command! */
73261931Scokane#ifdef DEBUG
73361911Scokane			printf("Bad Sub-cmd: 0x%x\n", _IOC_NR(cmd));
73461911Scokane#endif
73561911Scokane			return -EINVAL;
736115494Sphk	}
73761911Scokane}
73861911Scokane
73961911Scokanestatic int
74061911Scokanetdfx_do_pio(u_int cmd, struct tdfx_pio_data *piod)
74161911Scokane{
74261911Scokane	/* Two types of PIO, INPUT and OUTPUT, as the name suggests */
74361911Scokane	switch(_IOC_DIR(cmd)) {
74461911Scokane		case IOCV_OUT:
74561911Scokane			return tdfx_do_pio_rd(piod);
74661911Scokane			break;
74761911Scokane		case IOCV_IN:
74861911Scokane			return tdfx_do_pio_wt(piod);
74961911Scokane			break;
75061911Scokane		default:
75161911Scokane			return -EINVAL;
752115494Sphk	}
75361911Scokane}
75461911Scokane
75561911Scokane/* Calls to ioctl(2) eventually end up here. Unhandled ioctls return an ENXIO,
75661911Scokane * normally, you would read in the data pointed to by data, then write your
75761911Scokane * output to it. The ioctl *should* normally return zero if everything is
75861911Scokane * alright, but 3dfx didn't make it that way...
75961911Scokane *
76061911Scokane * For all of the ioctl code, in the event of a real error,
76161911Scokane * we return -Exxxx rather than simply Exxxx. The reason for this
76261911Scokane * is that the ioctls actually RET information back to the program
76361911Scokane * sometimes, rather than filling it in the passed structure. We
76461911Scokane * want to distinguish errors from useful data, and maintain compatibility.
76561911Scokane *
76661911Scokane * There is this portion of the proc struct called p_retval[], we can store a
76783366Sjulian * return value in td->td_retval[0] and place the return value if it is positive
76861911Scokane * in there, then we can return 0 (good). If the return value is negative, we
76961911Scokane * can return -retval and the error should be properly handled.
77061911Scokane */
77161911Scokanestatic int
772130585Sphktdfx_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
77361911Scokane{
77461911Scokane	int retval = 0;
77561911Scokane	struct tdfx_pio_data *piod = (struct tdfx_pio_data*)data;
77661931Scokane#ifdef	DEBUG
777106578Sjhb	printf("IOCTL'd by #%d, cmd: 0x%x, data: %p\n", td->td_proc->p_pid, (u_int32_t)cmd,
778106578Sjhb			piod);
77961911Scokane#endif
78061911Scokane	switch(_IOC_TYPE(cmd)) {
78161911Scokane		/* Return the real error if negative, or simply stick the valid return
78283366Sjulian		 * in td->td_retval */
78361911Scokane	case 0x33:
78461911Scokane			/* The '3'(0x33) type IOCTL is for querying the installed cards */
78583366Sjulian			if((retval = tdfx_do_query(cmd, piod)) > 0) td->td_retval[0] = retval;
78661911Scokane			else return -retval;
78761911Scokane			break;
78861911Scokane		case 0:
78961911Scokane			/* The 0 type IOCTL is for programmed I/O methods */
79083366Sjulian			if((tdfx_do_pio(cmd, piod)) > 0) td->td_retval[0] = retval;
79161911Scokane			else return -retval;
79261911Scokane			break;
79361911Scokane		default:
79461911Scokane			/* Technically, we won't reach this from linux emu, but when glide
79561911Scokane			 * finally gets ported, watch out! */
79661931Scokane#ifdef DEBUG
79783366Sjulian			printf("Bad IOCTL from #%d\n", td->td_proc->p_pid);
79861911Scokane#endif
79961911Scokane			return ENXIO;
80061911Scokane	}
80161911Scokane
80261911Scokane	return 0;
80361911Scokane}
80461911Scokane
80561911Scokane/* This is the device driver struct. This is sent to the driver subsystem to
80661911Scokane * register the method structure and the info strcut space for this particular
80761911Scokane * instance of the driver.
80861911Scokane */
80961911Scokanestatic driver_t tdfx_driver = {
81061911Scokane	"tdfx",
81161911Scokane	tdfx_methods,
81261911Scokane	sizeof(struct tdfx_softc),
81361911Scokane};
81461911Scokane
81561911Scokane/* Tell Mr. Kernel about us! */
81661911ScokaneDRIVER_MODULE(tdfx, pci, tdfx_driver, tdfx_devclass, 0, 0);
817177147ScokaneMODULE_DEPEND(tdfx, mem, 1, 1, 1);
818156260SyarMODULE_VERSION(tdfx, 1);
819