tdfx_pci.c revision 227293
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 227293 2011-11-07 06:44:47Z ed $");
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
92227293Sedstatic 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	u_long	val;
14961911Scokane	/* rid value tells bus_alloc_resource where to find the addresses of ports or
15061911Scokane	 * of memory ranges in the PCI config space*/
151119690Sjhb	int rid = PCIR_BAR(0);
15261911Scokane
15361911Scokane	/* Increment the card counter (for the ioctl code) */
15461911Scokane	tdfx_count++;
15561911Scokane
15661911Scokane 	/* Enable MemMap on Voodoo */
15761911Scokane	val = pci_read_config(dev, PCIR_COMMAND, 2);
15861911Scokane	val |= (PCIM_CMD_MEMEN);
15961911Scokane	pci_write_config(dev, PCIR_COMMAND, val, 2);
16061911Scokane	val = pci_read_config(dev, PCIR_COMMAND, 2);
16161911Scokane
16261911Scokane	/* Fill the soft config struct with info about this device*/
16361911Scokane	tdfx_info = device_get_softc(dev);
16461911Scokane	tdfx_info->dev = dev;
16561911Scokane	tdfx_info->vendor = pci_get_vendor(dev);
16661911Scokane	tdfx_info->type = pci_get_devid(dev) >> 16;
16761911Scokane	tdfx_info->bus = pci_get_bus(dev);
16861911Scokane	tdfx_info->dv = pci_get_slot(dev);
16961911Scokane	tdfx_info->curFile = NULL;
17061911Scokane
17161911Scokane	/*
17261911Scokane	 *	Get the Memory Location from the PCI Config, mask out lower word, since
17361911Scokane	 * the config space register is only one word long (this is nicer than a
17461911Scokane	 * bitshift).
17561911Scokane	 */
17661911Scokane	tdfx_info->addr0 = (pci_read_config(dev, 0x10, 4) & 0xffff0000);
17761931Scokane#ifdef DEBUG
17861911Scokane	device_printf(dev, "Base0 @ 0x%x\n", tdfx_info->addr0);
17961911Scokane#endif
18061911Scokane	/* Notify the VM that we will be mapping some memory later */
181127135Snjl	tdfx_info->memrange = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
182127135Snjl		&rid, RF_ACTIVE | RF_SHAREABLE);
18361911Scokane	if(tdfx_info->memrange == NULL) {
18461931Scokane#ifdef DEBUG
18561911Scokane		device_printf(dev, "Error mapping mem, won't be able to use mmap()\n");
18661911Scokane#endif
18761911Scokane		tdfx_info->memrid = 0;
18861911Scokane	}
18961911Scokane	else {
19061911Scokane		tdfx_info->memrid = rid;
19161931Scokane#ifdef DEBUG
19261911Scokane		device_printf(dev, "Mapped to: 0x%x\n",
19361911Scokane				(unsigned int)rman_get_start(tdfx_info->memrange));
19461911Scokane#endif
19561911Scokane	}
19661911Scokane
19763488Scokane	/* Setup for Voodoo3 and Banshee, PIO and an extram Memrange */
19863488Scokane	if(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3 ||
19963488Scokane		pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE) {
20064085Scokane		rid = 0x14;	/* 2nd mem map */
20163488Scokane		tdfx_info->addr1 = (pci_read_config(dev, 0x14, 4) & 0xffff0000);
20263488Scokane#ifdef DEBUG
20363488Scokane		device_printf(dev, "Base1 @ 0x%x\n", tdfx_info->addr1);
20463488Scokane#endif
205127135Snjl		tdfx_info->memrange2 = bus_alloc_resource_any(dev,
206127135Snjl			SYS_RES_MEMORY, &rid, RF_ACTIVE | RF_SHAREABLE);
20763488Scokane		if(tdfx_info->memrange2 == NULL) {
20863488Scokane#ifdef DEBUG
20963488Scokane			device_printf(dev, "Mem1 couldn't be allocated, glide may not work.");
21063488Scokane#endif
21163488Scokane			tdfx_info->memrid2 = 0;
21263488Scokane		}
21363488Scokane		else {
21463488Scokane			tdfx_info->memrid2 = rid;
21563488Scokane		}
21663488Scokane		/* Now to map the PIO stuff */
21765146Scokane		rid = PCIR_IOBASE0_2;
21865146Scokane		tdfx_info->pio0 = pci_read_config(dev, 0x2c, 2);
21965146Scokane		tdfx_info->pio0max = pci_read_config(dev, 0x30, 2) + tdfx_info->pio0;
220127135Snjl		tdfx_info->piorange = bus_alloc_resource_any(dev,
221127135Snjl			SYS_RES_IOPORT, &rid, RF_ACTIVE | RF_SHAREABLE);
22263488Scokane		if(tdfx_info->piorange == NULL) {
22363488Scokane#ifdef DEBUG
22463488Scokane			device_printf(dev, "Couldn't map PIO range.");
22563488Scokane#endif
22663488Scokane			tdfx_info->piorid = 0;
22763488Scokane		}
22863488Scokane		else {
22963488Scokane			tdfx_info->piorid = rid;
23065146Scokane		}
23163488Scokane	} else {
23263488Scokane	  tdfx_info->addr1 = 0;
23363488Scokane	  tdfx_info->memrange2 = NULL;
23463488Scokane	  tdfx_info->piorange = NULL;
23563488Scokane	}
23663488Scokane
23761911Scokane	/*
23861911Scokane	 *	Set Writecombining, or at least Uncacheable for the memory region, if we
23961911Scokane	 * are able to
24061911Scokane	 */
24161911Scokane
24261911Scokane	if(tdfx_setmtrr(dev) != 0) {
24361931Scokane#ifdef DEBUG
24461911Scokane		device_printf(dev, "Some weird error setting MTRRs");
24561911Scokane#endif
24661911Scokane		return -1;
24761911Scokane	}
24863488Scokane
24961911Scokane	/*
25061911Scokane	 * make_dev registers the cdev to access the 3dfx card from /dev
25161911Scokane	 *	use hex here for the dev num, simply to provide better support if > 10
252215334Sdougb	 * voodoo cards, for the mad. The user must set the link.
25361911Scokane	 * Why would we want that many voodoo cards anyhow?
25461911Scokane	 */
255104111Sphk	tdfx_info->devt = make_dev(&tdfx_cdev, device_get_unit(dev),
256108323Srwatson		UID_ROOT, GID_WHEEL, 0600, "3dfx%x", device_get_unit(dev));
257191056Sed	tdfx_info->devt->si_drv1 = tdfx_info;
25861911Scokane
25961911Scokane	return 0;
26061911Scokane}
26161911Scokane
26261911Scokanestatic int
26361911Scokanetdfx_detach(device_t dev) {
26461911Scokane	struct tdfx_softc* tdfx_info;
26561911Scokane	int retval;
26661911Scokane	tdfx_info = device_get_softc(dev);
26761911Scokane
26861911Scokane	/* Delete allocated resource, of course */
26963488Scokane	bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid,
27061911Scokane			tdfx_info->memrange);
27163488Scokane
27263488Scokane	/* Release extended Voodoo3/Banshee resources */
27363488Scokane	if(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE ||
27463488Scokane			pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) {
27563488Scokane		if(tdfx_info->memrange2 != NULL)
27663488Scokane			bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid2,
27763488Scokane				tdfx_info->memrange);
27864085Scokane	/*	if(tdfx_info->piorange != NULL)
27963488Scokane			bus_release_resource(dev, SYS_RES_IOPORT, tdfx_info->piorid,
28064085Scokane				tdfx_info->piorange);*/
28163488Scokane	}
28263488Scokane
28361911Scokane	/* Though it is safe to leave the WRCOMB support since the
28461911Scokane		mem driver checks for it, we should remove it in order
28561911Scokane		to free an MTRR for another device */
28661911Scokane	retval = tdfx_clrmtrr(dev);
28761931Scokane#ifdef DEBUG
28861911Scokane	if(retval != 0)
28961911Scokane		printf("tdfx: For some reason, I couldn't clear the mtrr\n");
29061911Scokane#endif
29161989Scokane	/* Remove device entry when it can no longer be accessed */
29261989Scokane   destroy_dev(tdfx_info->devt);
29361911Scokane	return(0);
29461911Scokane}
29561911Scokane
29661911Scokanestatic int
29761911Scokanetdfx_shutdown(device_t dev) {
29861931Scokane#ifdef DEBUG
29961911Scokane	device_printf(dev, "tdfx: Device Shutdown\n");
30061911Scokane#endif
30161911Scokane	return 0;
30261911Scokane}
30361911Scokane
30461911Scokanestatic int
30561911Scokanetdfx_clrmtrr(device_t dev) {
30661911Scokane	/* This function removes the MTRR set by the attach call, so it can be used
30761911Scokane	 * in the future by other drivers.
30861911Scokane	 */
30961911Scokane	int retval, act;
31061911Scokane	struct tdfx_softc *tdfx_info = device_get_softc(dev);
31161911Scokane
31261911Scokane	act = MEMRANGE_SET_REMOVE;
31361911Scokane	retval = mem_range_attr_set(&tdfx_info->mrdesc, &act);
31461911Scokane	return retval;
31561911Scokane}
31661911Scokane
31761911Scokanestatic int
31861911Scokanetdfx_setmtrr(device_t dev) {
31961911Scokane	/*
32061911Scokane	 * This is the MTRR setting function for the 3dfx card. It is called from
32161911Scokane	 * tdfx_attach. If we can't set the MTRR properly, it's not the end of the
32261911Scokane	 * world. We can still continue, just with slightly (very slightly) degraded
32361911Scokane	 * performance.
32461911Scokane	 */
32561911Scokane	int retval = 0, act;
32661911Scokane	struct tdfx_softc *tdfx_info = device_get_softc(dev);
32761911Scokane
32861911Scokane	/* The older Voodoo cards have a shorter memrange than the newer ones */
32961911Scokane	if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1) || (pci_get_devid(dev) ==
33063488Scokane			PCI_DEVICE_3DFX_VOODOO2)) {
33161911Scokane		tdfx_info->mrdesc.mr_len = 0x400000;
33263488Scokane
33363488Scokane		/* The memory descriptor is described as the top 15 bits of the real
33463488Scokane			address */
33563488Scokane		tdfx_info->mrdesc.mr_base = tdfx_info->addr0 & 0xfffe0000;
33663488Scokane	}
33761911Scokane	else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) ||
33863488Scokane			(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE)) {
33961911Scokane		tdfx_info->mrdesc.mr_len = 0x1000000;
34063488Scokane		/* The Voodoo3 and Banshee LFB is the second memory address */
34163488Scokane		/* The memory descriptor is described as the top 15 bits of the real
34263488Scokane			address */
34363488Scokane		tdfx_info->mrdesc.mr_base = tdfx_info->addr1 & 0xfffe0000;
34463488Scokane	}
34563488Scokane	else
34663488Scokane		 return 0;
34761911Scokane	/*
34861911Scokane    *	The Alliance Pro Motion AT3D was not mentioned in the linux
34961911Scokane	 * driver as far as MTRR support goes, so I just won't put the
35061911Scokane	 * code in here for it. This is where it should go, though.
35161911Scokane	 */
35261911Scokane
35361911Scokane	/* Firstly, try to set write combining */
35461911Scokane	tdfx_info->mrdesc.mr_flags = MDF_WRITECOMBINE;
35561911Scokane	bcopy("tdfx", &tdfx_info->mrdesc.mr_owner, 4);
35661911Scokane	act = MEMRANGE_SET_UPDATE;
35761911Scokane	retval = mem_range_attr_set(&tdfx_info->mrdesc, &act);
35861911Scokane
35961911Scokane	if(retval == 0) {
36061931Scokane#ifdef DEBUG
36161911Scokane		device_printf(dev, "MTRR Set Correctly for tdfx\n");
36261911Scokane#endif
36361911Scokane	} else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO2) ||
36461911Scokane		(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1)) {
36561911Scokane		/* if, for some reason we can't set the WRCOMB range with the V1/V2, we
36661911Scokane		 * can still possibly use the UNCACHEABLE region for it instead, and help
36761911Scokane		 * out in a small way */
36861911Scokane		tdfx_info->mrdesc.mr_flags = MDF_UNCACHEABLE;
36961911Scokane		/* This length of 1000h was taken from the linux device driver... */
37061911Scokane		tdfx_info->mrdesc.mr_len = 0x1000;
37161911Scokane
37261911Scokane		/*
37361911Scokane		 * If, for some reason, we can't set the MTRR (N/A?) we may still continue
37461911Scokane		 */
37561931Scokane#ifdef DEBUG
376142253Ssam		device_printf(dev, "MTRR Set Type Uncacheable %x\n",
377142253Ssam		    (u_int32_t)tdfx_info->mrdesc.mr_base);
37861911Scokane#endif
37961911Scokane	}
38061931Scokane#ifdef DEBUG
38161911Scokane	else {
38261911Scokane		device_printf(dev, "Couldn't Set MTRR\n");
38361911Scokane		return 0;
38461911Scokane	}
38561911Scokane#endif
38661911Scokane	return 0;
38761911Scokane}
38861911Scokane
38961911Scokanestatic int
390130585Sphktdfx_open(struct cdev *dev, int flags, int fmt, struct thread *td)
39161911Scokane{
39261911Scokane	/*
39361911Scokane	 *	The open cdev method handles open(2) calls to /dev/3dfx[n]
39461911Scokane	 * We can pretty much allow any opening of the device.
39561911Scokane	 */
396191056Sed	struct tdfx_softc *tdfx_info = dev->si_drv1;
39761911Scokane	if(tdfx_info->busy != 0) return EBUSY;
39861931Scokane#ifdef	DEBUG
39983366Sjulian	printf("3dfx: Opened by #%d\n", td->td_proc->p_pid);
40061911Scokane#endif
40161911Scokane	/* Set the driver as busy */
40261911Scokane	tdfx_info->busy++;
40361911Scokane	return 0;
40461911Scokane}
40561911Scokane
40661911Scokanestatic int
407130585Sphktdfx_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
40861911Scokane{
40961911Scokane	/*
41061911Scokane	 *	The close cdev method handles close(2) calls to /dev/3dfx[n]
41161911Scokane	 * We'll always want to close the device when it's called.
41261911Scokane	 */
413191056Sed	struct tdfx_softc *tdfx_info = dev->si_drv1;
41461911Scokane	if(tdfx_info->busy == 0) return EBADF;
41561911Scokane	tdfx_info->busy = 0;
41661931Scokane#ifdef	DEBUG
41783366Sjulian	printf("Closed by #%d\n", td->td_proc->p_pid);
41861911Scokane#endif
41961911Scokane	return 0;
42061911Scokane}
42161911Scokane
42261911Scokanestatic int
423201223Srnolandtdfx_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr,
424201223Srnoland    int nprot, vm_memattr_t *memattr)
42561911Scokane{
42661911Scokane	/*
42761911Scokane	 * mmap(2) is called by a user process to request that an area of memory
42861911Scokane	 * associated with this device be mapped for the process to work with. Nprot
42961911Scokane	 * holds the protections requested, PROT_READ, PROT_WRITE, or both.
43061911Scokane	 */
43166910Scokane
43266910Scokane	/**** OLD GET CONFIG ****/
43366910Scokane	/* struct tdfx_softc* tdfx_info; */
43461911Scokane
43561911Scokane	/* Get the configuration for our card XXX*/
436191056Sed	/*tdfx_info = dev->si_drv1; */
43766910Scokane	/************************/
43866910Scokane
43966910Scokane	struct tdfx_softc* tdfx_info[2];
44061911Scokane
44166910Scokane	tdfx_info[0] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 0);
44266910Scokane
44361911Scokane	/* If, for some reason, its not configured, we bail out */
44466910Scokane	if(tdfx_info[0] == NULL) {
44561931Scokane#ifdef	DEBUG
44661911Scokane	   printf("tdfx: tdfx_info (softc) is NULL\n");
44761911Scokane#endif
44861911Scokane	   return -1;
44961911Scokane	}
45066910Scokane
45161911Scokane	/* We must stay within the bound of our address space */
45266910Scokane	if((offset & 0xff000000) == tdfx_info[0]->addr0) {
45361911Scokane		offset &= 0xffffff;
454111462Smux		*paddr = rman_get_start(tdfx_info[0]->memrange) + offset;
455111462Smux		return 0;
45666910Scokane	}
45766910Scokane
45866910Scokane	if(tdfx_count > 1) {
45966910Scokane		tdfx_info[1] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 1);
46066910Scokane		if((offset & 0xff000000) == tdfx_info[1]->addr0) {
46166910Scokane			offset &= 0xffffff;
462111462Smux			*paddr = rman_get_start(tdfx_info[1]->memrange) +
463111462Smux			    offset;
464111462Smux			return 0;
46566910Scokane		}
46666910Scokane	}
46763488Scokane
46863488Scokane	/* See if the Banshee/V3 LFB is being requested */
46966910Scokane	/*if(tdfx_info->memrange2 != NULL && (offset & 0xff000000) ==
47064085Scokane			tdfx_info->addr1) {
47163488Scokane	  	offset &= 0xffffff;
47266910Scokane		return atop(rman_get_start(tdfx_info[1]->memrange2) + offset);
47366910Scokane	}*/ /* VoodooNG code */
47463488Scokane
47566910Scokane	/* The ret call */
47661911Scokane	/* atop -> address to page
47761911Scokane	 * rman_get_start, get the (struct resource*)->r_start member,
47861911Scokane	 * the mapping base address.
47961911Scokane	 */
48066910Scokane	return -1;
48161911Scokane}
48261911Scokane
48361911Scokanestatic int
48461911Scokanetdfx_query_boards(void) {
48561911Scokane	/*
48661911Scokane    *	This returns the number of installed tdfx cards, we have been keeping
48761911Scokane	 * count, look at tdfx_attach
48861911Scokane	 */
48961911Scokane	return tdfx_count;
49061911Scokane}
49161911Scokane
49261911Scokanestatic int
49361911Scokanetdfx_query_fetch(u_int cmd, struct tdfx_pio_data *piod)
49461911Scokane{
49561911Scokane	/* XXX Comment this later, after careful inspection and spring cleaning :) */
49661911Scokane	/* Various return values 8bit-32bit */
49761911Scokane	u_int8_t  ret_byte;
49861911Scokane	u_int16_t ret_word;
49961911Scokane	u_int32_t ret_dword;
50061911Scokane	struct tdfx_softc* tdfx_info = NULL;
50161911Scokane
50261911Scokane	/* This one depend on the tdfx_* structs being properly initialized */
50361911Scokane
50461911Scokane	/*piod->device &= 0xf;*/
50561911Scokane	if((piod == NULL) ||(tdfx_count <= piod->device) ||
50661911Scokane			(piod->device < 0)) {
50761931Scokane#ifdef DEBUG
50861911Scokane		printf("tdfx: Bad device or internal struct in tdfx_query_fetch\n");
50961911Scokane#endif
51061911Scokane		return -EINVAL;
51161911Scokane	}
51261911Scokane
51361911Scokane	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
51461911Scokane			piod->device);
51561911Scokane
51661911Scokane	if(tdfx_info == NULL) return -ENXIO;
51761911Scokane
51861911Scokane	/* We must restrict the size reads from the port, since to high or low of a
51961911Scokane	 * size witll result in wrong data being passed, and that's bad */
52061911Scokane	/* A few of these were pulled during the attach phase */
52161911Scokane	switch(piod->port) {
52261911Scokane		case PCI_VENDOR_ID_FREEBSD:
52361911Scokane			if(piod->size != 2) return -EINVAL;
52461911Scokane			copyout(&tdfx_info->vendor, piod->value, piod->size);
52561911Scokane			return 0;
52661911Scokane		case PCI_DEVICE_ID_FREEBSD:
52761911Scokane			if(piod->size != 2) return -EINVAL;
52861911Scokane			copyout(&tdfx_info->type, piod->value, piod->size);
52961911Scokane			return 0;
53061911Scokane		case PCI_BASE_ADDRESS_0_FREEBSD:
53161911Scokane			if(piod->size != 4) return -EINVAL;
53261911Scokane			copyout(&tdfx_info->addr0, piod->value, piod->size);
53361911Scokane			return 0;
53465146Scokane		case PCI_BASE_ADDRESS_1_FREEBSD:
53565146Scokane			if(piod->size != 4) return -EINVAL;
53665146Scokane			copyout(&tdfx_info->addr1, piod->value, piod->size);
53765146Scokane			return 0;
53865146Scokane		case PCI_PRIBUS_FREEBSD:
53965146Scokane			if(piod->size != 1) return -EINVAL;
54065146Scokane			break;
54165146Scokane		case PCI_IOBASE_0_FREEBSD:
54265146Scokane			if(piod->size != 2) return -EINVAL;
54365146Scokane			break;
54465146Scokane		case PCI_IOLIMIT_0_FREEBSD:
54565146Scokane			if(piod->size != 2) return -EINVAL;
54665146Scokane			break;
54761911Scokane		case SST1_PCI_SPECIAL1_FREEBSD:
54861911Scokane			if(piod->size != 4) return -EINVAL;
54961911Scokane			break;
55061911Scokane		case PCI_REVISION_ID_FREEBSD:
55161911Scokane			if(piod->size != 1) return -EINVAL;
55261911Scokane			break;
55361911Scokane		case SST1_PCI_SPECIAL4_FREEBSD:
55461911Scokane			if(piod->size != 4) return -EINVAL;
55561911Scokane			break;
55661911Scokane		default:
55761911Scokane			return -EINVAL;
55861911Scokane	}
55961911Scokane
56061911Scokane
56161911Scokane	/* Read the value and return */
56261911Scokane	switch(piod->size) {
56361911Scokane		case 1:
56461911Scokane			ret_byte = pci_read_config(tdfx_info[piod->device].dev,
56561911Scokane					piod->port, 1);
56661911Scokane			copyout(&ret_byte, piod->value, 1);
56761911Scokane			break;
56861911Scokane		case 2:
56961911Scokane			ret_word = pci_read_config(tdfx_info[piod->device].dev,
57061911Scokane					piod->port, 2);
57161911Scokane			copyout(&ret_word, piod->value, 2);
57261911Scokane			break;
57361911Scokane		case 4:
57461911Scokane			ret_dword = pci_read_config(tdfx_info[piod->device].dev,
57561911Scokane					piod->port, 4);
57661911Scokane			copyout(&ret_dword, piod->value, 4);
57761911Scokane			break;
57861911Scokane		default:
57961911Scokane			return -EINVAL;
58061911Scokane	}
58161911Scokane	return 0;
58261911Scokane}
58361911Scokane
58461911Scokanestatic int
58561911Scokanetdfx_query_update(u_int cmd, struct tdfx_pio_data *piod)
58661911Scokane{
58761911Scokane	/* XXX Comment this later, after careful inspection and spring cleaning :) */
58861911Scokane	/* Return vals */
58961911Scokane	u_int8_t  ret_byte;
59061911Scokane	u_int16_t ret_word;
59161911Scokane	u_int32_t ret_dword;
59261911Scokane
59361911Scokane	/* Port vals, mask */
59461911Scokane	u_int32_t retval, preval, mask;
59561911Scokane	struct tdfx_softc* tdfx_info = NULL;
59661911Scokane
59761911Scokane
59861911Scokane	if((piod == NULL) || (piod->device >= (tdfx_count &
59961911Scokane					0xf))) {
60061931Scokane#ifdef DEBUG
60161911Scokane		printf("tdfx: Bad struct or device in tdfx_query_update\n");
60261911Scokane#endif
60361911Scokane		return -EINVAL;
60461911Scokane	}
60561911Scokane
60661911Scokane	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
60761911Scokane			piod->device);
60861911Scokane	if(tdfx_info == NULL) return -ENXIO;
60961911Scokane	/* Code below this line in the fuction was taken from the
61061911Scokane	 * Linux driver and converted for freebsd. */
61161911Scokane
61261911Scokane	/* Check the size for all the ports, to make sure stuff doesn't get messed up
61361911Scokane	 * by poorly written clients */
61461911Scokane
61561911Scokane	switch(piod->port) {
61661911Scokane		case PCI_COMMAND_FREEBSD:
61761911Scokane			if(piod->size != 2) return -EINVAL;
61861911Scokane			break;
61961911Scokane		case SST1_PCI_SPECIAL1_FREEBSD:
62061911Scokane			if(piod->size != 4) return -EINVAL;
62161911Scokane			break;
62261911Scokane		case SST1_PCI_SPECIAL2_FREEBSD:
62361911Scokane			if(piod->size != 4) return -EINVAL;
62461911Scokane			break;
62561911Scokane		case SST1_PCI_SPECIAL3_FREEBSD:
62661911Scokane			if(piod->size != 4) return -EINVAL;
62761911Scokane			break;
62861911Scokane		case SST1_PCI_SPECIAL4_FREEBSD:
62961911Scokane			if(piod->size != 4) return -EINVAL;
63061911Scokane			break;
63161911Scokane		default:
63261911Scokane			return -EINVAL;
63361911Scokane	}
63461911Scokane	/* Read the current value */
63561911Scokane	retval = pci_read_config(tdfx_info->dev, piod->port & ~3, 4);
63661911Scokane
63761911Scokane	/* These set up a mask to use, since apparently they wanted to write 4 bytes
63861911Scokane	 * at once to the ports */
63961911Scokane	switch (piod->size) {
64061911Scokane		case 1:
64161911Scokane			copyin(piod->value, &ret_byte, 1);
64261911Scokane			preval = ret_byte << (8 * (piod->port & 0x3));
64361911Scokane			mask = 0xff << (8 * (piod->port & 0x3));
64461911Scokane			break;
64561911Scokane		case 2:
64661911Scokane			copyin(piod->value, &ret_word, 2);
64761911Scokane			preval = ret_word << (8 * (piod->port & 0x3));
64861911Scokane			mask = 0xffff << (8 * (piod->port & 0x3));
64961911Scokane			break;
65061911Scokane		case 4:
65161911Scokane			copyin(piod->value, &ret_dword, 4);
65261911Scokane			preval = ret_dword;
65361911Scokane			mask = ~0;
65461911Scokane			break;
65561911Scokane		default:
65661911Scokane			return -EINVAL;
65761911Scokane	}
65861911Scokane	/* Finally, combine the values and write it to the port */
65961911Scokane	retval = (retval & ~mask) | preval;
66061911Scokane	pci_write_config(tdfx_info->dev, piod->port & ~3, retval, 4);
66161911Scokane
66261911Scokane	return 0;
66361911Scokane}
66461911Scokane
66563488Scokane/* For both of these, I added a variable named workport of type u_int so
66663488Scokane * that I could eliminate the warning about my data type size. The
66763488Scokane * applications expect the port to be of type short, so I needed to change
66863488Scokane * this within the function */
66961911Scokanestatic int
67061911Scokanetdfx_do_pio_rd(struct tdfx_pio_data *piod)
67161911Scokane{
67261911Scokane	/* Return val */
67361911Scokane	u_int8_t  ret_byte;
67463488Scokane	u_int 	 workport;
67565146Scokane	struct tdfx_softc *tdfx_info =
67665146Scokane		(struct tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device);
67765146Scokane
67861911Scokane	/* Restricts the access of ports other than those we use */
67965146Scokane	if(((piod->port != VGA_INPUT_STATUS_1C) || (piod->port != SC_INDEX) ||
68065146Scokane		(piod->port != SC_DATA) || (piod->port != VGA_MISC_OUTPUT_READ)) &&
68165146Scokane		(piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max))
68261911Scokane		return -EPERM;
68361911Scokane
68461911Scokane	/* All VGA STATUS REGS are byte registers, size should never be > 1 */
68561911Scokane	if(piod->size != 1) {
68661911Scokane		return -EINVAL;
68761911Scokane	}
68861911Scokane
68961911Scokane	/* Write the data to the intended port */
69063488Scokane	workport = piod->port;
69163488Scokane	ret_byte = inb(workport);
69261911Scokane	copyout(&ret_byte, piod->value, sizeof(u_int8_t));
69361911Scokane	return 0;
69461911Scokane}
69561911Scokane
69661911Scokanestatic int
69761911Scokanetdfx_do_pio_wt(struct tdfx_pio_data *piod)
69861911Scokane{
69961911Scokane	/* return val */
70061911Scokane	u_int8_t  ret_byte;
70163488Scokane	u_int		 workport;
70265146Scokane	struct tdfx_softc *tdfx_info = (struct
70365146Scokane			tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device);
70461911Scokane	/* Replace old switch w/ massive if(...) */
70561911Scokane	/* Restricts the access of ports other than those we use */
70665146Scokane	if(((piod->port != SC_INDEX) && (piod->port != SC_DATA) &&
70765146Scokane		(piod->port != VGA_MISC_OUTPUT_READ)) /* Can't write VGA_ST_1C */ &&
70865146Scokane		(piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max))
70961911Scokane		return -EPERM;
71061911Scokane
71161911Scokane	/* All VGA STATUS REGS are byte registers, size should never be > 1 */
71261911Scokane	if(piod->size != 1) {
71361911Scokane		return -EINVAL;
71461911Scokane	}
71561911Scokane
71661911Scokane	/* Write the data to the intended port */
71761911Scokane	copyin(piod->value, &ret_byte, sizeof(u_int8_t));
71863488Scokane	workport = piod->port;
71963488Scokane	outb(workport, ret_byte);
72061911Scokane	return 0;
72161911Scokane}
72261911Scokane
72361911Scokanestatic int
72461911Scokanetdfx_do_query(u_int cmd, struct tdfx_pio_data *piod)
72561911Scokane{
72661911Scokane	/* There are three sub-commands to the query 0x33 */
72761911Scokane	switch(_IOC_NR(cmd)) {
72861911Scokane		case 2:
72961911Scokane			return tdfx_query_boards();
73061911Scokane			break;
73161911Scokane		case 3:
73261911Scokane			return tdfx_query_fetch(cmd, piod);
73361911Scokane			break;
73461911Scokane		case 4:
73561911Scokane			return tdfx_query_update(cmd, piod);
73661911Scokane			break;
73761911Scokane		default:
73861911Scokane			/* In case we are thrown a bogus sub-command! */
73961931Scokane#ifdef DEBUG
74061911Scokane			printf("Bad Sub-cmd: 0x%x\n", _IOC_NR(cmd));
74161911Scokane#endif
74261911Scokane			return -EINVAL;
743115494Sphk	}
74461911Scokane}
74561911Scokane
74661911Scokanestatic int
74761911Scokanetdfx_do_pio(u_int cmd, struct tdfx_pio_data *piod)
74861911Scokane{
74961911Scokane	/* Two types of PIO, INPUT and OUTPUT, as the name suggests */
75061911Scokane	switch(_IOC_DIR(cmd)) {
75161911Scokane		case IOCV_OUT:
75261911Scokane			return tdfx_do_pio_rd(piod);
75361911Scokane			break;
75461911Scokane		case IOCV_IN:
75561911Scokane			return tdfx_do_pio_wt(piod);
75661911Scokane			break;
75761911Scokane		default:
75861911Scokane			return -EINVAL;
759115494Sphk	}
76061911Scokane}
76161911Scokane
76261911Scokane/* Calls to ioctl(2) eventually end up here. Unhandled ioctls return an ENXIO,
76361911Scokane * normally, you would read in the data pointed to by data, then write your
76461911Scokane * output to it. The ioctl *should* normally return zero if everything is
76561911Scokane * alright, but 3dfx didn't make it that way...
76661911Scokane *
76761911Scokane * For all of the ioctl code, in the event of a real error,
76861911Scokane * we return -Exxxx rather than simply Exxxx. The reason for this
76961911Scokane * is that the ioctls actually RET information back to the program
77061911Scokane * sometimes, rather than filling it in the passed structure. We
77161911Scokane * want to distinguish errors from useful data, and maintain compatibility.
77261911Scokane *
77361911Scokane * There is this portion of the proc struct called p_retval[], we can store a
77483366Sjulian * return value in td->td_retval[0] and place the return value if it is positive
77561911Scokane * in there, then we can return 0 (good). If the return value is negative, we
77661911Scokane * can return -retval and the error should be properly handled.
77761911Scokane */
77861911Scokanestatic int
779130585Sphktdfx_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
78061911Scokane{
78161911Scokane	int retval = 0;
78261911Scokane	struct tdfx_pio_data *piod = (struct tdfx_pio_data*)data;
78361931Scokane#ifdef	DEBUG
784106578Sjhb	printf("IOCTL'd by #%d, cmd: 0x%x, data: %p\n", td->td_proc->p_pid, (u_int32_t)cmd,
785106578Sjhb			piod);
78661911Scokane#endif
78761911Scokane	switch(_IOC_TYPE(cmd)) {
78861911Scokane		/* Return the real error if negative, or simply stick the valid return
78983366Sjulian		 * in td->td_retval */
79061911Scokane	case 0x33:
79161911Scokane			/* The '3'(0x33) type IOCTL is for querying the installed cards */
79283366Sjulian			if((retval = tdfx_do_query(cmd, piod)) > 0) td->td_retval[0] = retval;
79361911Scokane			else return -retval;
79461911Scokane			break;
79561911Scokane		case 0:
79661911Scokane			/* The 0 type IOCTL is for programmed I/O methods */
79783366Sjulian			if((tdfx_do_pio(cmd, piod)) > 0) td->td_retval[0] = retval;
79861911Scokane			else return -retval;
79961911Scokane			break;
80061911Scokane		default:
80161911Scokane			/* Technically, we won't reach this from linux emu, but when glide
80261911Scokane			 * finally gets ported, watch out! */
80361931Scokane#ifdef DEBUG
80483366Sjulian			printf("Bad IOCTL from #%d\n", td->td_proc->p_pid);
80561911Scokane#endif
80661911Scokane			return ENXIO;
80761911Scokane	}
80861911Scokane
80961911Scokane	return 0;
81061911Scokane}
81161911Scokane
81261911Scokane/* This is the device driver struct. This is sent to the driver subsystem to
81361911Scokane * register the method structure and the info strcut space for this particular
81461911Scokane * instance of the driver.
81561911Scokane */
81661911Scokanestatic driver_t tdfx_driver = {
81761911Scokane	"tdfx",
81861911Scokane	tdfx_methods,
81961911Scokane	sizeof(struct tdfx_softc),
82061911Scokane};
82161911Scokane
82261911Scokane/* Tell Mr. Kernel about us! */
82361911ScokaneDRIVER_MODULE(tdfx, pci, tdfx_driver, tdfx_devclass, 0, 0);
824177147ScokaneMODULE_DEPEND(tdfx, mem, 1, 1, 1);
825156260SyarMODULE_VERSION(tdfx, 1);
826