tdfx_pci.c revision 201223
134461Speter/*-
234461Speter * Copyright (c) 2000-2001 by Coleman Kane <cokane@FreeBSD.org>
334461Speter * All rights reserved.
434461Speter *
534461Speter * Redistribution and use in source and binary forms, with or without
634461Speter * modification, are permitted provided that the following conditions
734461Speter * are met:
834461Speter * 1. Redistributions of source code must retain the above copyright
934461Speter *    notice, this list of conditions and the following disclaimer.
1034461Speter * 2. Redistributions in binary form must reproduce the above copyright
1134461Speter *    notice, this list of conditions and the following disclaimer in the
1234461Speter *    documentation and/or other materials provided with the distribution.
1334461Speter * 3. All advertising materials mentioning features or use of this software
1434461Speter *    must display the following acknowledgement:
1534461Speter *      This product includes software developed by Gardner Buchanan.
1634461Speter * 4. The name of Gardner Buchanan may not be used to endorse or promote
1734461Speter *    products derived from this software without specific prior written
1834461Speter *    permission.
1934461Speter *
2034461Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2134461Speter * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2234461Speter * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2334461Speter * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2434461Speter * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2534461Speter * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2634461Speter * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2732785Speter * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2832785Speter * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2932785Speter * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3032785Speter */
3132785Speter
3232785Speter#include <sys/cdefs.h>
3332785Speter__FBSDID("$FreeBSD: head/sys/dev/tdfx/tdfx_pci.c 201223 2009-12-29 21:51:28Z rnoland $");
3432785Speter
3532785Speter/* 3dfx driver for FreeBSD 4.x - Finished 11 May 2000, 12:25AM ET
3632785Speter *
3732785Speter * Copyright (C) 2000-2001, by Coleman Kane <cokane@FreeBSD.org>,
3832785Speter * based upon the 3dfx driver written for linux, by Daryll Straus, Jon Taylor,
3932785Speter * and Jens Axboe, located at http://linux.3dfx.com.
4032785Speter */
4132785Speter
4232785Speter#include <sys/param.h>
4332785Speter
4432785Speter#include <sys/bus.h>
4532785Speter#include <sys/cdefs.h>
4632785Speter#include <sys/conf.h>
4732785Speter#include <sys/fcntl.h>
4832785Speter#include <sys/file.h>
4932785Speter#include <sys/filedesc.h>
5032785Speter#include <sys/filio.h>
5132785Speter#include <sys/ioccom.h>
5232785Speter#include <sys/kernel.h>
5332785Speter#include <sys/module.h>
5432785Speter#include <sys/malloc.h>
5532785Speter#include <sys/mman.h>
5632785Speter#include <sys/signalvar.h>
5732785Speter#include <sys/systm.h>
5832785Speter#include <sys/uio.h>
5932785Speter
6032785Speter#include <dev/pci/pcivar.h>
6132785Speter#include <dev/pci/pcireg.h>
6232785Speter
6332785Speter#include <vm/vm.h>
6432785Speter#include <vm/vm_kern.h>
6532785Speter#include <vm/pmap.h>
6632785Speter#include <vm/vm_extern.h>
6732785Speter
6832785Speter/* rman.h depends on machine/bus.h */
6932785Speter#include <machine/resource.h>
7032785Speter#include <machine/bus.h>
7132785Speter#include <sys/rman.h>
7232785Speter
7332785Speter#include <dev/tdfx/tdfx_io.h>
7432785Speter#include <dev/tdfx/tdfx_vars.h>
7532785Speter#include <dev/tdfx/tdfx_pci.h>
7632785Speter
7732785Speter
7832785Speterstatic devclass_t tdfx_devclass;
7932785Speter
8032785Speter
8132785Speterstatic int tdfx_count = 0;
8232785Speter
8332785Speter
8432785Speter/* Set up the boot probe/attach routines */
8532785Speterstatic device_method_t tdfx_methods[] = {
8632785Speter	DEVMETHOD(device_probe,		tdfx_probe),
8732785Speter	DEVMETHOD(device_attach,	tdfx_attach),
8832785Speter	DEVMETHOD(device_detach,	tdfx_detach),
8932785Speter	DEVMETHOD(device_shutdown,	tdfx_shutdown),
9032785Speter	{ 0, 0 }
9132785Speter};
9232785Speter
9332785SpeterMALLOC_DEFINE(M_TDFX,"tdfx_driver","3DFX Graphics[/2D]/3D Accelerator(s)");
9432785Speter
9532785Speter/* Char. Dev. file operations structure */
9632785Speterstatic struct cdevsw tdfx_cdev = {
9732785Speter	.d_version =	D_VERSION,
9832785Speter	.d_flags =	D_NEEDGIANT,
9932785Speter	.d_open =	tdfx_open,
10032785Speter	.d_close =	tdfx_close,
10132785Speter	.d_ioctl =	tdfx_ioctl,
10232785Speter	.d_mmap =	tdfx_mmap,
10332785Speter	.d_name =	"tdfx",
10432785Speter};
10532785Speter
10632785Speterstatic int
10732785Spetertdfx_probe(device_t dev)
10832785Speter{
10932785Speter	/*
11032785Speter	 * probe routine called on kernel boot to register supported devices. We get
11132785Speter	 * a device structure to work with, and we can test the VENDOR/DEVICE IDs to
11232785Speter	 * see if this PCI device is one that we support. Return BUS_PRROBE_DEFAULT
11332785Speter	 * if yes, ENXIO if not.
11432785Speter	 */
11532785Speter	switch(pci_get_devid(dev)) {
11632785Speter	case PCI_DEVICE_ALLIANCE_AT3D:
11732785Speter		device_set_desc(dev, "ProMotion At3D 3D Accelerator");
11832785Speter		return BUS_PROBE_DEFAULT;
11932785Speter	case PCI_DEVICE_3DFX_VOODOO2:
12032785Speter		device_set_desc(dev, "3DFX Voodoo II 3D Accelerator");
12132785Speter		return BUS_PROBE_DEFAULT;
12232785Speter	/*case PCI_DEVICE_3DFX_BANSHEE:
12332785Speter		device_set_desc(dev, "3DFX Voodoo Banshee 2D/3D Graphics Accelerator");
12432785Speter		return BUS_PROBE_DEFAULT;
12532785Speter	case PCI_DEVICE_3DFX_VOODOO3:
12632785Speter		device_set_desc(dev, "3DFX Voodoo3 2D/3D Graphics Accelerator");
12732785Speter		return BUS_PROBE_DEFAULT;*/
12832785Speter	case PCI_DEVICE_3DFX_VOODOO1:
12932785Speter		device_set_desc(dev, "3DFX Voodoo Graphics 3D Accelerator");
13032785Speter		return BUS_PROBE_DEFAULT;
13132785Speter	};
13232785Speter
13332785Speter	return ENXIO;
13432785Speter}
13532785Speter
13632785Speterstatic int
13732785Spetertdfx_attach(device_t dev) {
13832785Speter	/*
13932785Speter	 * The attach routine is called after the probe routine successfully says it
14032785Speter	 * supports a given card. We now proceed to initialize this card for use with
14132785Speter	 * the system. I want to map the device memory for userland allocation and
14232785Speter	 * fill an information structure with information on this card. I'd also like
14332785Speter	 * to set Write Combining with the MTRR code so that we can hopefully speed
14432785Speter	 * up memory writes. The last thing is to register the character device
14532785Speter	 * interface to the card, so we can open it from /dev/3dfxN, where N is a
14632785Speter	 * small, whole number.
14732785Speter	 */
14832785Speter	struct tdfx_softc *tdfx_info;
14932785Speter	u_long	val;
15032785Speter	/* rid value tells bus_alloc_resource where to find the addresses of ports or
15132785Speter	 * of memory ranges in the PCI config space*/
15232785Speter	int rid = PCIR_BAR(0);
15332785Speter
15432785Speter	/* Increment the card counter (for the ioctl code) */
15532785Speter	tdfx_count++;
15632785Speter
15732785Speter 	/* Enable MemMap on Voodoo */
15832785Speter	val = pci_read_config(dev, PCIR_COMMAND, 2);
15932785Speter	val |= (PCIM_CMD_MEMEN);
16032785Speter	pci_write_config(dev, PCIR_COMMAND, val, 2);
16132785Speter	val = pci_read_config(dev, PCIR_COMMAND, 2);
16232785Speter
16332785Speter	/* Fill the soft config struct with info about this device*/
16432785Speter	tdfx_info = device_get_softc(dev);
16532785Speter	tdfx_info->dev = dev;
16632785Speter	tdfx_info->vendor = pci_get_vendor(dev);
16732785Speter	tdfx_info->type = pci_get_devid(dev) >> 16;
16832785Speter	tdfx_info->bus = pci_get_bus(dev);
16932785Speter	tdfx_info->dv = pci_get_slot(dev);
17032785Speter	tdfx_info->curFile = NULL;
17132785Speter
17232785Speter	/*
17332785Speter	 *	Get the Memory Location from the PCI Config, mask out lower word, since
17432785Speter	 * the config space register is only one word long (this is nicer than a
17532785Speter	 * bitshift).
17632785Speter	 */
17732785Speter	tdfx_info->addr0 = (pci_read_config(dev, 0x10, 4) & 0xffff0000);
17832785Speter#ifdef DEBUG
17932785Speter	device_printf(dev, "Base0 @ 0x%x\n", tdfx_info->addr0);
18032785Speter#endif
18132785Speter	/* Notify the VM that we will be mapping some memory later */
18232785Speter	tdfx_info->memrange = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
18332785Speter		&rid, RF_ACTIVE | RF_SHAREABLE);
18432785Speter	if(tdfx_info->memrange == NULL) {
18532785Speter#ifdef DEBUG
18632785Speter		device_printf(dev, "Error mapping mem, won't be able to use mmap()\n");
18732785Speter#endif
18832785Speter		tdfx_info->memrid = 0;
18932785Speter	}
19032785Speter	else {
19132785Speter		tdfx_info->memrid = rid;
19232785Speter#ifdef DEBUG
19332785Speter		device_printf(dev, "Mapped to: 0x%x\n",
19432785Speter				(unsigned int)rman_get_start(tdfx_info->memrange));
19532785Speter#endif
19632785Speter	}
19732785Speter
19832785Speter	/* Setup for Voodoo3 and Banshee, PIO and an extram Memrange */
19932785Speter	if(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3 ||
20032785Speter		pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE) {
20132785Speter		rid = 0x14;	/* 2nd mem map */
20232785Speter		tdfx_info->addr1 = (pci_read_config(dev, 0x14, 4) & 0xffff0000);
20332785Speter#ifdef DEBUG
20432785Speter		device_printf(dev, "Base1 @ 0x%x\n", tdfx_info->addr1);
20532785Speter#endif
20632785Speter		tdfx_info->memrange2 = bus_alloc_resource_any(dev,
20732785Speter			SYS_RES_MEMORY, &rid, RF_ACTIVE | RF_SHAREABLE);
20832785Speter		if(tdfx_info->memrange2 == NULL) {
20932785Speter#ifdef DEBUG
21032785Speter			device_printf(dev, "Mem1 couldn't be allocated, glide may not work.");
21132785Speter#endif
21232785Speter			tdfx_info->memrid2 = 0;
21332785Speter		}
21432785Speter		else {
21532785Speter			tdfx_info->memrid2 = rid;
21632785Speter		}
21732785Speter		/* Now to map the PIO stuff */
21832785Speter		rid = PCIR_IOBASE0_2;
21932785Speter		tdfx_info->pio0 = pci_read_config(dev, 0x2c, 2);
22032785Speter		tdfx_info->pio0max = pci_read_config(dev, 0x30, 2) + tdfx_info->pio0;
22132785Speter		tdfx_info->piorange = bus_alloc_resource_any(dev,
22232785Speter			SYS_RES_IOPORT, &rid, RF_ACTIVE | RF_SHAREABLE);
22332785Speter		if(tdfx_info->piorange == NULL) {
22432785Speter#ifdef DEBUG
22532785Speter			device_printf(dev, "Couldn't map PIO range.");
22632785Speter#endif
22732785Speter			tdfx_info->piorid = 0;
22832785Speter		}
22932785Speter		else {
23032785Speter			tdfx_info->piorid = rid;
23132785Speter		}
23232785Speter	} else {
23332785Speter	  tdfx_info->addr1 = 0;
23432785Speter	  tdfx_info->memrange2 = NULL;
23532785Speter	  tdfx_info->piorange = NULL;
23632785Speter	}
23732785Speter
23832785Speter	/*
23932785Speter	 *	Set Writecombining, or at least Uncacheable for the memory region, if we
24032785Speter	 * are able to
24132785Speter	 */
24232785Speter
24332785Speter	if(tdfx_setmtrr(dev) != 0) {
24432785Speter#ifdef DEBUG
24532785Speter		device_printf(dev, "Some weird error setting MTRRs");
24632785Speter#endif
24732785Speter		return -1;
24832785Speter	}
24932785Speter
25032785Speter	/*
25132785Speter	 * make_dev registers the cdev to access the 3dfx card from /dev
25232785Speter	 *	use hex here for the dev num, simply to provide better support if > 10
25332785Speter	 * voodoo cards, for the mad. The user must set the link, or use MAKEDEV.
25432785Speter	 * Why would we want that many voodoo cards anyhow?
25532785Speter	 */
25632785Speter	tdfx_info->devt = make_dev(&tdfx_cdev, device_get_unit(dev),
25732785Speter		UID_ROOT, GID_WHEEL, 0600, "3dfx%x", device_get_unit(dev));
25832785Speter	tdfx_info->devt->si_drv1 = tdfx_info;
25932785Speter
26032785Speter	return 0;
26132785Speter}
26232785Speter
26332785Speterstatic int
26432785Spetertdfx_detach(device_t dev) {
26532785Speter	struct tdfx_softc* tdfx_info;
26632785Speter	int retval;
26732785Speter	tdfx_info = device_get_softc(dev);
26832785Speter
26932785Speter	/* Delete allocated resource, of course */
27032785Speter	bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid,
27132785Speter			tdfx_info->memrange);
27232785Speter
27332785Speter	/* Release extended Voodoo3/Banshee resources */
27432785Speter	if(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE ||
27532785Speter			pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) {
27632785Speter		if(tdfx_info->memrange2 != NULL)
27732785Speter			bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid2,
27832785Speter				tdfx_info->memrange);
27932785Speter	/*	if(tdfx_info->piorange != NULL)
28032785Speter			bus_release_resource(dev, SYS_RES_IOPORT, tdfx_info->piorid,
28132785Speter				tdfx_info->piorange);*/
28232785Speter	}
28332785Speter
28432785Speter	/* Though it is safe to leave the WRCOMB support since the
28532785Speter		mem driver checks for it, we should remove it in order
28632785Speter		to free an MTRR for another device */
28732785Speter	retval = tdfx_clrmtrr(dev);
28832785Speter#ifdef DEBUG
28932785Speter	if(retval != 0)
29032785Speter		printf("tdfx: For some reason, I couldn't clear the mtrr\n");
29132785Speter#endif
29232785Speter	/* Remove device entry when it can no longer be accessed */
29332785Speter   destroy_dev(tdfx_info->devt);
29432785Speter	return(0);
29532785Speter}
29632785Speter
29732785Speterstatic int
29832785Spetertdfx_shutdown(device_t dev) {
29932785Speter#ifdef DEBUG
30032785Speter	device_printf(dev, "tdfx: Device Shutdown\n");
30132785Speter#endif
30232785Speter	return 0;
30332785Speter}
30432785Speter
30532785Speterstatic int
30632785Spetertdfx_clrmtrr(device_t dev) {
30732785Speter	/* This function removes the MTRR set by the attach call, so it can be used
30832785Speter	 * in the future by other drivers.
30932785Speter	 */
31032785Speter	int retval, act;
31132785Speter	struct tdfx_softc *tdfx_info = device_get_softc(dev);
31232785Speter
31332785Speter	act = MEMRANGE_SET_REMOVE;
31432785Speter	retval = mem_range_attr_set(&tdfx_info->mrdesc, &act);
31532785Speter	return retval;
31632785Speter}
31732785Speter
31832785Speterstatic int
31932785Spetertdfx_setmtrr(device_t dev) {
32032785Speter	/*
32132785Speter	 * This is the MTRR setting function for the 3dfx card. It is called from
32232785Speter	 * tdfx_attach. If we can't set the MTRR properly, it's not the end of the
32332785Speter	 * world. We can still continue, just with slightly (very slightly) degraded
32432785Speter	 * performance.
32532785Speter	 */
32632785Speter	int retval = 0, act;
32732785Speter	struct tdfx_softc *tdfx_info = device_get_softc(dev);
32832785Speter
32932785Speter	/* The older Voodoo cards have a shorter memrange than the newer ones */
33032785Speter	if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1) || (pci_get_devid(dev) ==
33132785Speter			PCI_DEVICE_3DFX_VOODOO2)) {
33232785Speter		tdfx_info->mrdesc.mr_len = 0x400000;
33332785Speter
33432785Speter		/* The memory descriptor is described as the top 15 bits of the real
33532785Speter			address */
33632785Speter		tdfx_info->mrdesc.mr_base = tdfx_info->addr0 & 0xfffe0000;
33732785Speter	}
33832785Speter	else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) ||
33932785Speter			(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE)) {
34032785Speter		tdfx_info->mrdesc.mr_len = 0x1000000;
34132785Speter		/* The Voodoo3 and Banshee LFB is the second memory address */
34232785Speter		/* The memory descriptor is described as the top 15 bits of the real
34332785Speter			address */
34432785Speter		tdfx_info->mrdesc.mr_base = tdfx_info->addr1 & 0xfffe0000;
34532785Speter	}
34632785Speter	else
34732785Speter		 return 0;
34832785Speter	/*
34932785Speter    *	The Alliance Pro Motion AT3D was not mentioned in the linux
35032785Speter	 * driver as far as MTRR support goes, so I just won't put the
35132785Speter	 * code in here for it. This is where it should go, though.
35232785Speter	 */
35332785Speter
35432785Speter	/* Firstly, try to set write combining */
35532785Speter	tdfx_info->mrdesc.mr_flags = MDF_WRITECOMBINE;
35632785Speter	bcopy("tdfx", &tdfx_info->mrdesc.mr_owner, 4);
35732785Speter	act = MEMRANGE_SET_UPDATE;
35832785Speter	retval = mem_range_attr_set(&tdfx_info->mrdesc, &act);
35932785Speter
36032785Speter	if(retval == 0) {
36132785Speter#ifdef DEBUG
36232785Speter		device_printf(dev, "MTRR Set Correctly for tdfx\n");
36332785Speter#endif
36432785Speter	} else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO2) ||
36532785Speter		(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1)) {
36632785Speter		/* if, for some reason we can't set the WRCOMB range with the V1/V2, we
36732785Speter		 * can still possibly use the UNCACHEABLE region for it instead, and help
36832785Speter		 * out in a small way */
36932785Speter		tdfx_info->mrdesc.mr_flags = MDF_UNCACHEABLE;
37032785Speter		/* This length of 1000h was taken from the linux device driver... */
37132785Speter		tdfx_info->mrdesc.mr_len = 0x1000;
37232785Speter
37332785Speter		/*
37432785Speter		 * If, for some reason, we can't set the MTRR (N/A?) we may still continue
37532785Speter		 */
37632785Speter#ifdef DEBUG
37732785Speter		device_printf(dev, "MTRR Set Type Uncacheable %x\n",
37832785Speter		    (u_int32_t)tdfx_info->mrdesc.mr_base);
37932785Speter#endif
38032785Speter	}
38132785Speter#ifdef DEBUG
38232785Speter	else {
38332785Speter		device_printf(dev, "Couldn't Set MTRR\n");
38432785Speter		return 0;
38532785Speter	}
38632785Speter#endif
38732785Speter	return 0;
38832785Speter}
38932785Speter
39032785Speterstatic int
39132785Spetertdfx_open(struct cdev *dev, int flags, int fmt, struct thread *td)
39232785Speter{
39332785Speter	/*
39432785Speter	 *	The open cdev method handles open(2) calls to /dev/3dfx[n]
39532785Speter	 * We can pretty much allow any opening of the device.
39632785Speter	 */
39732785Speter	struct tdfx_softc *tdfx_info = dev->si_drv1;
39832785Speter	if(tdfx_info->busy != 0) return EBUSY;
39932785Speter#ifdef	DEBUG
40032785Speter	printf("3dfx: Opened by #%d\n", td->td_proc->p_pid);
40132785Speter#endif
40232785Speter	/* Set the driver as busy */
40332785Speter	tdfx_info->busy++;
40432785Speter	return 0;
40532785Speter}
40632785Speter
40732785Speterstatic int
40832785Spetertdfx_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
40932785Speter{
41032785Speter	/*
41132785Speter	 *	The close cdev method handles close(2) calls to /dev/3dfx[n]
41232785Speter	 * We'll always want to close the device when it's called.
41332785Speter	 */
41432785Speter	struct tdfx_softc *tdfx_info = dev->si_drv1;
41532785Speter	if(tdfx_info->busy == 0) return EBADF;
41632785Speter	tdfx_info->busy = 0;
41732785Speter#ifdef	DEBUG
41832785Speter	printf("Closed by #%d\n", td->td_proc->p_pid);
41932785Speter#endif
42032785Speter	return 0;
42132785Speter}
42232785Speter
42332785Speterstatic int
42432785Spetertdfx_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr,
42532785Speter    int nprot, vm_memattr_t *memattr)
42632785Speter{
42732785Speter	/*
42832785Speter	 * mmap(2) is called by a user process to request that an area of memory
42932785Speter	 * associated with this device be mapped for the process to work with. Nprot
43032785Speter	 * holds the protections requested, PROT_READ, PROT_WRITE, or both.
43132785Speter	 */
43232785Speter
43332785Speter	/**** OLD GET CONFIG ****/
43432785Speter	/* struct tdfx_softc* tdfx_info; */
43532785Speter
43632785Speter	/* Get the configuration for our card XXX*/
43732785Speter	/*tdfx_info = dev->si_drv1; */
43832785Speter	/************************/
43932785Speter
44032785Speter	struct tdfx_softc* tdfx_info[2];
44132785Speter
44232785Speter	tdfx_info[0] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 0);
44332785Speter
44432785Speter	/* If, for some reason, its not configured, we bail out */
44532785Speter	if(tdfx_info[0] == NULL) {
44632785Speter#ifdef	DEBUG
44732785Speter	   printf("tdfx: tdfx_info (softc) is NULL\n");
44832785Speter#endif
44932785Speter	   return -1;
45032785Speter	}
45132785Speter
45232785Speter	/* We must stay within the bound of our address space */
45332785Speter	if((offset & 0xff000000) == tdfx_info[0]->addr0) {
45432785Speter		offset &= 0xffffff;
45532785Speter		*paddr = rman_get_start(tdfx_info[0]->memrange) + offset;
45632785Speter		return 0;
45732785Speter	}
45832785Speter
45932785Speter	if(tdfx_count > 1) {
46032785Speter		tdfx_info[1] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 1);
46132785Speter		if((offset & 0xff000000) == tdfx_info[1]->addr0) {
46232785Speter			offset &= 0xffffff;
46332785Speter			*paddr = rman_get_start(tdfx_info[1]->memrange) +
46432785Speter			    offset;
46532785Speter			return 0;
46632785Speter		}
46732785Speter	}
46832785Speter
46932785Speter	/* See if the Banshee/V3 LFB is being requested */
47032785Speter	/*if(tdfx_info->memrange2 != NULL && (offset & 0xff000000) ==
47132785Speter			tdfx_info->addr1) {
47232785Speter	  	offset &= 0xffffff;
47332785Speter		return atop(rman_get_start(tdfx_info[1]->memrange2) + offset);
47432785Speter	}*/ /* VoodooNG code */
47532785Speter
47632785Speter	/* The ret call */
47732785Speter	/* atop -> address to page
47832785Speter	 * rman_get_start, get the (struct resource*)->r_start member,
47932785Speter	 * the mapping base address.
48032785Speter	 */
48132785Speter	return -1;
48232785Speter}
48332785Speter
48432785Speterstatic int
48532785Spetertdfx_query_boards(void) {
48632785Speter	/*
48732785Speter    *	This returns the number of installed tdfx cards, we have been keeping
48832785Speter	 * count, look at tdfx_attach
48932785Speter	 */
49032785Speter	return tdfx_count;
49132785Speter}
49232785Speter
49332785Speterstatic int
49432785Spetertdfx_query_fetch(u_int cmd, struct tdfx_pio_data *piod)
49532785Speter{
49632785Speter	/* XXX Comment this later, after careful inspection and spring cleaning :) */
49732785Speter	/* Various return values 8bit-32bit */
49832785Speter	u_int8_t  ret_byte;
49932785Speter	u_int16_t ret_word;
50032785Speter	u_int32_t ret_dword;
50132785Speter	struct tdfx_softc* tdfx_info = NULL;
50232785Speter
50332785Speter	/* This one depend on the tdfx_* structs being properly initialized */
50432785Speter
50532785Speter	/*piod->device &= 0xf;*/
50632785Speter	if((piod == NULL) ||(tdfx_count <= piod->device) ||
50732785Speter			(piod->device < 0)) {
50832785Speter#ifdef DEBUG
50932785Speter		printf("tdfx: Bad device or internal struct in tdfx_query_fetch\n");
51032785Speter#endif
51132785Speter		return -EINVAL;
51232785Speter	}
51332785Speter
51432785Speter	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
51532785Speter			piod->device);
51632785Speter
51732785Speter	if(tdfx_info == NULL) return -ENXIO;
51832785Speter
51932785Speter	/* We must restrict the size reads from the port, since to high or low of a
52032785Speter	 * size witll result in wrong data being passed, and that's bad */
52132785Speter	/* A few of these were pulled during the attach phase */
52232785Speter	switch(piod->port) {
52332785Speter		case PCI_VENDOR_ID_FREEBSD:
52426801Speter			if(piod->size != 2) return -EINVAL;
52526801Speter			copyout(&tdfx_info->vendor, piod->value, piod->size);
52626801Speter			return 0;
52726801Speter		case PCI_DEVICE_ID_FREEBSD:
52826801Speter			if(piod->size != 2) return -EINVAL;
52926801Speter			copyout(&tdfx_info->type, piod->value, piod->size);
53026801Speter			return 0;
53126801Speter		case PCI_BASE_ADDRESS_0_FREEBSD:
53226801Speter			if(piod->size != 4) return -EINVAL;
53326801Speter			copyout(&tdfx_info->addr0, piod->value, piod->size);
53426801Speter			return 0;
53526801Speter		case PCI_BASE_ADDRESS_1_FREEBSD:
53626801Speter			if(piod->size != 4) return -EINVAL;
53726801Speter			copyout(&tdfx_info->addr1, piod->value, piod->size);
53826801Speter			return 0;
53926801Speter		case PCI_PRIBUS_FREEBSD:
54026801Speter			if(piod->size != 1) return -EINVAL;
54126801Speter			break;
54226801Speter		case PCI_IOBASE_0_FREEBSD:
54326801Speter			if(piod->size != 2) return -EINVAL;
54426801Speter			break;
54526065Speter		case PCI_IOLIMIT_0_FREEBSD:
54626065Speter			if(piod->size != 2) return -EINVAL;
54726065Speter			break;
54826065Speter		case SST1_PCI_SPECIAL1_FREEBSD:
54926065Speter			if(piod->size != 4) return -EINVAL;
55026065Speter			break;
55126065Speter		case PCI_REVISION_ID_FREEBSD:
55226065Speter			if(piod->size != 1) return -EINVAL;
55325839Speter			break;
55425839Speter		case SST1_PCI_SPECIAL4_FREEBSD:
55525839Speter			if(piod->size != 4) return -EINVAL;
55625839Speter			break;
55725839Speter		default:
55825839Speter			return -EINVAL;
55925839Speter	}
56025839Speter
56125839Speter
56225839Speter	/* Read the value and return */
56325839Speter	switch(piod->size) {
56425839Speter		case 1:
56525839Speter			ret_byte = pci_read_config(tdfx_info[piod->device].dev,
56625839Speter					piod->port, 1);
56725839Speter			copyout(&ret_byte, piod->value, 1);
56825839Speter			break;
56925839Speter		case 2:
57025839Speter			ret_word = pci_read_config(tdfx_info[piod->device].dev,
57125839Speter					piod->port, 2);
57225839Speter			copyout(&ret_word, piod->value, 2);
57325839Speter			break;
57425839Speter		case 4:
57525839Speter			ret_dword = pci_read_config(tdfx_info[piod->device].dev,
57625839Speter					piod->port, 4);
57725839Speter			copyout(&ret_dword, piod->value, 4);
57825839Speter			break;
57925839Speter		default:
58025839Speter			return -EINVAL;
58125839Speter	}
58225839Speter	return 0;
58325839Speter}
58425839Speter
58525839Speterstatic int
58625839Spetertdfx_query_update(u_int cmd, struct tdfx_pio_data *piod)
58725839Speter{
58825839Speter	/* XXX Comment this later, after careful inspection and spring cleaning :) */
58925839Speter	/* Return vals */
59025839Speter	u_int8_t  ret_byte;
59125839Speter	u_int16_t ret_word;
59225839Speter	u_int32_t ret_dword;
59325839Speter
59425839Speter	/* Port vals, mask */
59525839Speter	u_int32_t retval, preval, mask;
59625839Speter	struct tdfx_softc* tdfx_info = NULL;
59725839Speter
59825839Speter
59925839Speter	if((piod == NULL) || (piod->device >= (tdfx_count &
60025839Speter					0xf))) {
60125839Speter#ifdef DEBUG
60225839Speter		printf("tdfx: Bad struct or device in tdfx_query_update\n");
60325839Speter#endif
60425839Speter		return -EINVAL;
60525839Speter	}
60625839Speter
60725839Speter	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
60825839Speter			piod->device);
60925839Speter	if(tdfx_info == NULL) return -ENXIO;
61025839Speter	/* Code below this line in the fuction was taken from the
61125839Speter	 * Linux driver and converted for freebsd. */
61225839Speter
61325839Speter	/* Check the size for all the ports, to make sure stuff doesn't get messed up
61425839Speter	 * by poorly written clients */
61525839Speter
61625839Speter	switch(piod->port) {
61725839Speter		case PCI_COMMAND_FREEBSD:
61825839Speter			if(piod->size != 2) return -EINVAL;
61925839Speter			break;
62025839Speter		case SST1_PCI_SPECIAL1_FREEBSD:
62125839Speter			if(piod->size != 4) return -EINVAL;
62225839Speter			break;
62325839Speter		case SST1_PCI_SPECIAL2_FREEBSD:
62425839Speter			if(piod->size != 4) return -EINVAL;
62525839Speter			break;
62625839Speter		case SST1_PCI_SPECIAL3_FREEBSD:
62725839Speter			if(piod->size != 4) return -EINVAL;
62825839Speter			break;
62925839Speter		case SST1_PCI_SPECIAL4_FREEBSD:
63025839Speter			if(piod->size != 4) return -EINVAL;
63125839Speter			break;
63225839Speter		default:
63325839Speter			return -EINVAL;
63425839Speter	}
63525839Speter	/* Read the current value */
63625839Speter	retval = pci_read_config(tdfx_info->dev, piod->port & ~3, 4);
63725839Speter
63825839Speter	/* These set up a mask to use, since apparently they wanted to write 4 bytes
63925839Speter	 * at once to the ports */
64025839Speter	switch (piod->size) {
64125839Speter		case 1:
64225839Speter			copyin(piod->value, &ret_byte, 1);
64325839Speter			preval = ret_byte << (8 * (piod->port & 0x3));
64425839Speter			mask = 0xff << (8 * (piod->port & 0x3));
64525839Speter			break;
64625839Speter		case 2:
64725839Speter			copyin(piod->value, &ret_word, 2);
64825839Speter			preval = ret_word << (8 * (piod->port & 0x3));
64925839Speter			mask = 0xffff << (8 * (piod->port & 0x3));
65025839Speter			break;
65125839Speter		case 4:
65225839Speter			copyin(piod->value, &ret_dword, 4);
65325839Speter			preval = ret_dword;
65425839Speter			mask = ~0;
65525839Speter			break;
65625839Speter		default:
65725839Speter			return -EINVAL;
65825839Speter	}
65925839Speter	/* Finally, combine the values and write it to the port */
66025839Speter	retval = (retval & ~mask) | preval;
66125839Speter	pci_write_config(tdfx_info->dev, piod->port & ~3, retval, 4);
66225839Speter
66325839Speter	return 0;
66425839Speter}
66525839Speter
66625839Speter/* For both of these, I added a variable named workport of type u_int so
66725839Speter * that I could eliminate the warning about my data type size. The
66825839Speter * applications expect the port to be of type short, so I needed to change
66925839Speter * this within the function */
67025839Speterstatic int
67125839Spetertdfx_do_pio_rd(struct tdfx_pio_data *piod)
67225839Speter{
67325839Speter	/* Return val */
67425839Speter	u_int8_t  ret_byte;
67525839Speter	u_int 	 workport;
67625839Speter	struct tdfx_softc *tdfx_info =
67725839Speter		(struct tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device);
67825839Speter
67925839Speter	/* Restricts the access of ports other than those we use */
68025839Speter	if(((piod->port != VGA_INPUT_STATUS_1C) || (piod->port != SC_INDEX) ||
68125839Speter		(piod->port != SC_DATA) || (piod->port != VGA_MISC_OUTPUT_READ)) &&
68225839Speter		(piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max))
68325839Speter		return -EPERM;
68425839Speter
68525839Speter	/* All VGA STATUS REGS are byte registers, size should never be > 1 */
68625839Speter	if(piod->size != 1) {
68725839Speter		return -EINVAL;
68825839Speter	}
68925839Speter
69025839Speter	/* Write the data to the intended port */
69125839Speter	workport = piod->port;
69225839Speter	ret_byte = inb(workport);
69325839Speter	copyout(&ret_byte, piod->value, sizeof(u_int8_t));
69425839Speter	return 0;
69525839Speter}
69625839Speter
69725839Speterstatic int
69825839Spetertdfx_do_pio_wt(struct tdfx_pio_data *piod)
69925839Speter{
70025839Speter	/* return val */
70125839Speter	u_int8_t  ret_byte;
70225839Speter	u_int		 workport;
70325839Speter	struct tdfx_softc *tdfx_info = (struct
70425839Speter			tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device);
70525839Speter	/* Replace old switch w/ massive if(...) */
70625839Speter	/* Restricts the access of ports other than those we use */
70725839Speter	if(((piod->port != SC_INDEX) && (piod->port != SC_DATA) &&
70825839Speter		(piod->port != VGA_MISC_OUTPUT_READ)) /* Can't write VGA_ST_1C */ &&
70925839Speter		(piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max))
71025839Speter		return -EPERM;
71125839Speter
71225839Speter	/* All VGA STATUS REGS are byte registers, size should never be > 1 */
71325839Speter	if(piod->size != 1) {
71425839Speter		return -EINVAL;
71525839Speter	}
71625839Speter
71725839Speter	/* Write the data to the intended port */
71825839Speter	copyin(piod->value, &ret_byte, sizeof(u_int8_t));
71925839Speter	workport = piod->port;
72025839Speter	outb(workport, ret_byte);
72125839Speter	return 0;
72225839Speter}
72325839Speter
72425839Speterstatic int
72525839Spetertdfx_do_query(u_int cmd, struct tdfx_pio_data *piod)
72625839Speter{
72725839Speter	/* There are three sub-commands to the query 0x33 */
72825839Speter	switch(_IOC_NR(cmd)) {
72925839Speter		case 2:
73025839Speter			return tdfx_query_boards();
73125839Speter			break;
73225839Speter		case 3:
73325839Speter			return tdfx_query_fetch(cmd, piod);
73425839Speter			break;
73525839Speter		case 4:
73625839Speter			return tdfx_query_update(cmd, piod);
73725839Speter			break;
73825839Speter		default:
73925839Speter			/* In case we are thrown a bogus sub-command! */
74025839Speter#ifdef DEBUG
74125839Speter			printf("Bad Sub-cmd: 0x%x\n", _IOC_NR(cmd));
74225839Speter#endif
74325839Speter			return -EINVAL;
74425839Speter	}
74525839Speter}
74625839Speter
74725839Speterstatic int
74825839Spetertdfx_do_pio(u_int cmd, struct tdfx_pio_data *piod)
74925839Speter{
75025839Speter	/* Two types of PIO, INPUT and OUTPUT, as the name suggests */
75125839Speter	switch(_IOC_DIR(cmd)) {
75225839Speter		case IOCV_OUT:
75325839Speter			return tdfx_do_pio_rd(piod);
75425839Speter			break;
75525839Speter		case IOCV_IN:
75625839Speter			return tdfx_do_pio_wt(piod);
75725839Speter			break;
75825839Speter		default:
75925839Speter			return -EINVAL;
76025839Speter	}
76125839Speter}
76225839Speter
76325839Speter/* Calls to ioctl(2) eventually end up here. Unhandled ioctls return an ENXIO,
76425839Speter * normally, you would read in the data pointed to by data, then write your
76525839Speter * output to it. The ioctl *should* normally return zero if everything is
76625839Speter * alright, but 3dfx didn't make it that way...
76725839Speter *
76825839Speter * For all of the ioctl code, in the event of a real error,
76925839Speter * we return -Exxxx rather than simply Exxxx. The reason for this
77025839Speter * is that the ioctls actually RET information back to the program
77125839Speter * sometimes, rather than filling it in the passed structure. We
77225839Speter * want to distinguish errors from useful data, and maintain compatibility.
77325839Speter *
77425839Speter * There is this portion of the proc struct called p_retval[], we can store a
77525839Speter * return value in td->td_retval[0] and place the return value if it is positive
77625839Speter * in there, then we can return 0 (good). If the return value is negative, we
77725839Speter * can return -retval and the error should be properly handled.
77825839Speter */
77925839Speterstatic int
78025839Spetertdfx_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
78125839Speter{
78225839Speter	int retval = 0;
78325839Speter	struct tdfx_pio_data *piod = (struct tdfx_pio_data*)data;
78425839Speter#ifdef	DEBUG
78525839Speter	printf("IOCTL'd by #%d, cmd: 0x%x, data: %p\n", td->td_proc->p_pid, (u_int32_t)cmd,
78625839Speter			piod);
78725839Speter#endif
78825839Speter	switch(_IOC_TYPE(cmd)) {
78925839Speter		/* Return the real error if negative, or simply stick the valid return
79025839Speter		 * in td->td_retval */
79125839Speter	case 0x33:
79225839Speter			/* The '3'(0x33) type IOCTL is for querying the installed cards */
79325839Speter			if((retval = tdfx_do_query(cmd, piod)) > 0) td->td_retval[0] = retval;
79425839Speter			else return -retval;
79525839Speter			break;
79625839Speter		case 0:
79725839Speter			/* The 0 type IOCTL is for programmed I/O methods */
79825839Speter			if((tdfx_do_pio(cmd, piod)) > 0) td->td_retval[0] = retval;
79925839Speter			else return -retval;
80025839Speter			break;
80125839Speter		default:
80225839Speter			/* Technically, we won't reach this from linux emu, but when glide
80325839Speter			 * finally gets ported, watch out! */
80425839Speter#ifdef DEBUG
80525839Speter			printf("Bad IOCTL from #%d\n", td->td_proc->p_pid);
80625839Speter#endif
80725839Speter			return ENXIO;
80825839Speter	}
80925839Speter
81025839Speter	return 0;
81125839Speter}
81225839Speter
81325839Speter/* This is the device driver struct. This is sent to the driver subsystem to
81425839Speter * register the method structure and the info strcut space for this particular
81525839Speter * instance of the driver.
81625839Speter */
81725839Speterstatic driver_t tdfx_driver = {
81825839Speter	"tdfx",
81925839Speter	tdfx_methods,
82025839Speter	sizeof(struct tdfx_softc),
82125839Speter};
82225839Speter
82325839Speter/* Tell Mr. Kernel about us! */
82425839SpeterDRIVER_MODULE(tdfx, pci, tdfx_driver, tdfx_devclass, 0, 0);
82525839SpeterMODULE_DEPEND(tdfx, mem, 1, 1, 1);
82625839SpeterMODULE_VERSION(tdfx, 1);
82725839Speter